diff --git a/.Rbuildignore b/.Rbuildignore index 830bc699..dbdf278b 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -5,3 +5,7 @@ README.md ^\.github$ ^LICENSE\.md$ +^_pkgdown\.yml$ +^docs$ +^pkgdown$ +^codemeta\.json$ diff --git a/.github/workflows/R-CMD-check-beta.yaml b/.github/workflows/R-CMD-check-beta.yaml index 816679ac..8297c544 100644 --- a/.github/workflows/R-CMD-check-beta.yaml +++ b/.github/workflows/R-CMD-check-beta.yaml @@ -62,8 +62,7 @@ jobs: - name: Install macos dependiencies if: runner.os == 'macOS' run: | - install.packages("rgdal", type="binary") - shell: Rscript {0} + brew install gdal proj geos - name: Install dependencies run: | diff --git a/.github/workflows/R-CMD-check-dev.yaml b/.github/workflows/R-CMD-check-dev.yaml index cb126bf1..58a1eb58 100644 --- a/.github/workflows/R-CMD-check-dev.yaml +++ b/.github/workflows/R-CMD-check-dev.yaml @@ -62,8 +62,7 @@ jobs: - name: Install macos dependiencies if: runner.os == 'macOS' run: | - install.packages("rgdal", type="binary") - shell: Rscript {0} + brew install gdal proj geos - name: Install dependencies run: | diff --git a/.github/workflows/R-CMD-check-dev_Luis.yaml b/.github/workflows/R-CMD-check-dev_Luis.yaml index 8470c1d0..d1b88bf3 100644 --- a/.github/workflows/R-CMD-check-dev_Luis.yaml +++ b/.github/workflows/R-CMD-check-dev_Luis.yaml @@ -62,8 +62,7 @@ jobs: - name: Install macos dependiencies if: runner.os == 'macOS' run: | - install.packages("rgdal", type="binary") - shell: Rscript {0} + brew install gdal proj geos - name: Install dependencies run: | diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index ecebfefd..12909e35 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -60,15 +60,15 @@ jobs: do eval sudo $cmd done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') + sudo apt-get install libgdal-dev libproj-dev libgeos-dev libudunits2-dev netcdf-bin + - name: Install macos dependiencies if: runner.os == 'macOS' run: | - install.packages("rgdal", type="binary") - shell: Rscript {0} + brew install gdal proj geos - name: Install dependencies run: | - options(repos = c(CRAN = "https://cloud.r-project.org/" )) remotes::install_deps(dependencies = TRUE) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml new file mode 100644 index 00000000..63cbb18a --- /dev/null +++ b/.github/workflows/pkgdown.yaml @@ -0,0 +1,35 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/master/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + release: + types: [published] + workflow_dispatch: + +name: pkgdown + +jobs: + pkgdown: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v2 + + - uses: r-lib/actions/setup-pandoc@v1 + + - uses: r-lib/actions/setup-r@v1 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v1 + with: + extra-packages: pkgdown + needs: website + + - name: Deploy package + run: | + git config --local user.name "$GITHUB_ACTOR" + git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" + Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' diff --git a/.gitignore b/.gitignore index 5c1b57de..443ed37e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ dartR.Rproj dartR.Rproj /doc/ /Meta/ +docs +.RDataTmp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..95a0393a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# Contributing to dartR + + + +First of all, thanks for considering contributing to dartR! 👍 It's people like you that make it rewarding for us - the project maintainers - to work on dartR. 😊 + +dartR is an open source project, maintained by people who care. + +[repo]: https://github.com/green-striped-gecko/dartR +[issues]: https://github.com/green-striped-gecko/dartR/issues +[new_issue]: https://github.com/green-striped-gecko/dartR/issues/new +[website]: https://green-striped-gecko.github.io/dartR/ +[email]: mailto:Bernd.Gruber@canberra.edu.au +[google]: https://groups.google.com/g/dartr?pli=1 +[developer]: http://georges.biomatix.org/storage/app/media/uploaded-files/Tutorial_0_dartR_for_the_Developer_2.0_19-Feb-22.pdf + +## Code of conduct + +Please note that this project is released with a [Contributor Code of Conduct](code_of_conduct.md). By participating in this project you agree to abide by its terms. + +## How you can contribute + +There are several ways you can contribute to this project. If you want to know more about why and how to contribute to open source projects like this one, see this [Open Source Guide](https://opensource.guide/how-to-contribute/). + +We have prepared a [document][developer] describing how independent developers can contribute to dartR. This document explains the structure of the functions in dartR, programming style, the data structure of the genlight object used in dartR, workflow and many other topics. + +### Share the love ❤️ + +Think dartR is useful? Let others discover it, by telling them in person, via Twitter or a blog post. + +If you use dartR in your research, please support us by citing dartR. You can find the citation information by typing in the R console: + +```{r}I +citation("dartR”) +``` + +### Ask a question ⁉️ + +Using dartR and got stuck? Browse the [documentation][website] to see if you can find a solution. Still stuck? Post your question in our [Google groups forum][new_issue] and we'll try to do our best to address it. + +Want to ask a question in private? Contact the package maintainer by [email][mailto:email]. + +### Propose an idea 💡 + +Have an idea for a new dartR feature? Take a look at the [documentation][website] and [issue list][issues] to see if it isn't included or suggested yet. If not, suggest your idea as an [issue on GitHub][new_issue]. While we can't promise to implement your idea, it helps to: + +* Explain in detail how it would work. +* Keep the scope as narrow as possible. + +See below if you want to contribute code for your idea as well. + +### Report a bug 🐛 + +Using dartR and discovered a bug? That's annoying! Don't let others have the same experience and report it as an [issue on GitHub][new_issue] so we can fix it. A good bug report makes it easier for us to do so, so please include: + +* Your operating system name and version (e.g. Mac OS 10.13.6). +* Any details about your local setup that might be helpful in troubleshooting. +* Detailed steps to reproduce the bug. + +### Improve the documentation 📖 + +Noticed a typo on the website? Think a function could use a better example? Good documentation makes all the difference, so your help to improve it is very welcome! + +#### The website + +[This website][website] is generated with [`pkgdown`](http://pkgdown.r-lib.org/). That means we don't have to write any html: content is pulled together from documentation in the code, vignettes, [Markdown](https://guides.github.com/features/mastering-markdown/) files, the package `DESCRIPTION` and `_pkgdown.yml` settings. If you know your way around `pkgdown`, you can [propose a file change](https://help.github.com/articles/editing-files-in-another-user-s-repository/) to improve documentation. If not, [report an issue][new_issue] and we can point you in the right direction. + +#### Function documentation + +Functions are described as comments near their code and translated to documentation using [`roxygen2`](https://klutometis.github.io/roxygen/). If you want to improve a function description: + +1. Go to `R/` directory in the [code repository][repo]. +2. Look for the file with the name of the function. +3. [Propose a file change](https://help.github.com/articles/editing-files-in-another-user-s-repository/) to update the function documentation in the roxygen comments (starting with `#'`). + +### Contribute code 📝 + +Care to fix bugs or implement new functionality for dartR? Awesome! 👏 Have a look at the [issue list][issues] and leave a comment on the things you want to work on. See also the development guidelines below. + +## Development guidelines + +We try to follow the [GitHub flow](https://guides.github.com/introduction/flow/) for development. + +1. Fork [this repo][repo] and clone it to your computer. To learn more about this process, see [this guide](https://guides.github.com/activities/forking/). +2. If you have forked and cloned the project before and it has been a while since you worked on it, [pull changes from the original repo](https://help.github.com/articles/merging-an-upstream-repository-into-your-fork/) to your clone by using `git pull upstream master`. +3. Open the RStudio project file (`.Rproj`). +4. Make your changes: + * Write your code. + * Test your code (bonus points for adding unit tests). + * Document your code (see function documentation above). + * Check your code with `devtools::check()` and aim for 0 errors and warnings. +5. Commit and push your changes. +6. Submit a [pull request](https://guides.github.com/activities/forking/#making-a-pull-request). \ No newline at end of file diff --git a/DESCRIPTION b/DESCRIPTION index bac904b4..89847118 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -54,6 +54,7 @@ Suggests: label.switching, leaflet, leaflet.minicharts, + maptools, markdown, mmod, networkD3, @@ -83,10 +84,12 @@ Suggests: terra, tibble, tidyverse, - vcfR + vcfR, + zoo, + viridis License: GPL (>= 3) LazyData: true -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.1 NeedsCompilation: no Packaged: 2021-04-28 23:28:46 UTC; s425824 Author: Bernd Gruber [aut, cre], @@ -98,3 +101,6 @@ Author: Bernd Gruber [aut, cre], Lindsay V. Clark [ctb], Floriaan Devloo-Delva [ctb], Eric Archer [ctb] +Config/testthat/edition: 3 +URL: https://green-striped-gecko.github.io/dartR/ +BugReports: https://groups.google.com/g/dartr?pli=1 diff --git a/NAMESPACE b/NAMESPACE index fbf16658..e5643e27 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,6 +8,7 @@ export(gl.Ho) export(gl.LDNe) export(gl.alf) export(gl.amova) +export(gl.assign.grm) export(gl.assign.mahalanobis) export(gl.assign.pa) export(gl.assign.pca) @@ -34,6 +35,7 @@ export(gl.filter.callrate) export(gl.filter.hamming) export(gl.filter.heterozygosity) export(gl.filter.hwe) +export(gl.filter.ld) export(gl.filter.locmetric) export(gl.filter.maf) export(gl.filter.monomorphs) @@ -58,6 +60,8 @@ export(gl.join) export(gl.keep.ind) export(gl.keep.loc) export(gl.keep.pop) +export(gl.ld.distance) +export(gl.ld.haplotype) export(gl.list.reports) export(gl.load) export(gl.make.recode.ind) @@ -119,6 +123,7 @@ export(gl.sim.ind) export(gl.sim.mutate) export(gl.sim.offspring) export(gl.smearplot) +export(gl.spatial.autoCorr) export(gl.subsample.loci) export(gl.test.heterozygosity) export(gl.tree.nj) @@ -146,6 +151,10 @@ export(gl2svdquartets) export(gl2treemix) export(gl2vcf) export(is.fixed) +export(utils.assignment) +export(utils.assignment_2) +export(utils.assignment_3) +export(utils.assignment_4) export(utils.basic.stats) export(utils.check.datatype) export(utils.dist.binary) @@ -157,6 +166,7 @@ export(utils.n.var.invariant) export(utils.outflank) export(utils.outflank.MakeDiploidFSTMat) export(utils.outflank.plotter) +export(utils.spautocor) export(utils.structure.evanno) export(utils.structure.genind2gtypes) export(utils.structure.run) @@ -206,6 +216,7 @@ importFrom(dplyr,starts_with) importFrom(dplyr,ungroup) importFrom(dplyr,vars) importFrom(grDevices,cm.colors) +importFrom(grDevices,col2rgb) importFrom(grDevices,colorRampPalette) importFrom(grDevices,hcl) importFrom(grDevices,hcl.pals) @@ -268,6 +279,7 @@ importFrom(stats,variable.names) importFrom(stringr,str_pad) importFrom(stringr,str_trim) importFrom(tidyr,pivot_longer) +importFrom(tidyr,pivot_wider) importFrom(utils,available.packages) importFrom(utils,combn) importFrom(utils,edit) diff --git a/R/datasets.r b/R/datasets.r index cfc993d2..ac735e30 100644 --- a/R/datasets.r +++ b/R/datasets.r @@ -33,8 +33,8 @@ #' A simulated genlight object created to run a landscape genetic example #' #'This a test data set to run a landscape genetics example. It contains 10 -#'populations of 30 individuals each and each individual has 300 loci. There are -#'no covariates for individuals or loci. +#'populations of 30 individuals each and each individual has 300 loci. There +#'are no covariates for individuals or loci. #' @name possums.gl #' @format genlight object #' @docType data @@ -127,8 +127,8 @@ NULL #' library(PopGenReport) #' read.csv( paste(.libPaths()[1],'/dartR/extdata/platy.csv',sep='' )) #' platy <- read.genetable( paste(.libPaths()[1],'/dartR/extdata/platy.csv', -#' sep='' ), ind=1, pop=2, lat=3, long=4, other.min=5, other.max=6, oneColPerAll=FALSE, -#' sep='/') +#' sep='' ), ind=1, pop=2, lat=3, long=4, other.min=5, other.max=6, +#' oneColPerAll=FALSE, sep='/') #' platy.gl <- gi2gl(platy, parallel=FALSE) #' df.loc <- data.frame(RepAvg = runif(nLoc(platy.gl)), CallRate = 1) #' platy.gl@other$loc.metrics <- df.loc diff --git a/R/gl.LDNe.r b/R/gl.LDNe.r index 0c23220f..8cf8aeee 100644 --- a/R/gl.LDNe.r +++ b/R/gl.LDNe.r @@ -151,7 +151,7 @@ gl.LDNe <- function(x, } #change into tempdir (run it there) - old.path = getwd() + old.path <-getwd() setwd(tempdir()) system(cmd) res <- read.delim(outfile) diff --git a/R/gl.amova.r b/R/gl.amova.r index e42ca4f4..6c773669 100644 --- a/R/gl.amova.r +++ b/R/gl.amova.r @@ -21,7 +21,8 @@ #' degrees of freedom, and a vector of variance components. #' @importFrom StAMPP stamppNeisD #' @export -#' @author Bernd Gruber (bugs? Post to \url{https://groups.google.com/d/forum/dartr}) +#' @author Bernd Gruber (bugs? Post to +#' \url{https://groups.google.com/d/forum/dartr}) #' @examples #' #permutations should be higher, here set to 10 because of speed #' out <- gl.amova(bandicoot.gl, permutations=10) @@ -64,21 +65,21 @@ gl.amova <- function(x, sample <- row.names(geno) pop.names <- pop(geno2) ploidy <- ploidy(geno2) - geno = geno * (1 / ploidy) - geno[is.na(geno)] = NaN + geno <-geno * (1 / ploidy) + geno[is.na(geno)] <- NaN format <- vector(length = length(geno[, 1])) - format[1:length(geno[, 1])] = "genlight" + format[1:length(geno[, 1])] <- "genlight" pops <- unique(pop.names) pop.num <- vector(length = length(geno[, 1])) for (i in 1:length(geno[, 1])) { - pop.num[i] = which(pop.names[i] == pops) + pop.num[i] <- which(pop.names[i] == pops) } genoLHS <- as.data.frame(cbind(sample, pop.names, pop.num, ploidy, format)) geno <- cbind(genoLHS, geno) - geno[, 2] = as.character(pop.names) - geno[, 4] = as.numeric(as.character(geno[, 4])) - row.names(geno) = NULL + geno[, 2] <- as.character(pop.names) + geno[, 4] <- as.numeric(as.character(geno[, 4])) + row.names(geno) <- NULL } pop.names <- geno[, 2] pop.names <- factor(pop.names) diff --git a/R/gl.assign.grm.r b/R/gl.assign.grm.r new file mode 100644 index 00000000..b4311da5 --- /dev/null +++ b/R/gl.assign.grm.r @@ -0,0 +1,88 @@ +#' @name gl.assign.grm +#' @title Population assignment using grm +#' @description +#' This function takes one individual and estimates +#' their probability of coming from individual populations +#' from multilocus genotype frequencies. +# +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param unknown Name of the individual to be assigned to a population [required]. +# @param inbreeding_par The inbreeding parameter [default 0]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details +#' This function is a re-implementation of the function multilocus_assignment +#' from package gstudio. +#' Description of the method used in this function can be found at: +#' https://dyerlab.github.io/applied_population_genetics/population-assignment.html +#' @return A \code{data.frame} consisting of assignment probabilities for each +#' population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' res <- gl.assign.grm(platypus.gl,unknown="T27") +#' @export +#' +gl.assign.grm <- function(x, + unknown, + verbose=NULL){ + + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + +# FLAG SCRIPT START +funname <- match.call()[[1]] +utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + +# CHECK DATATYPE +datatype <- utils.check.datatype(x, verbose = verbose) + + +if (unknown %in% indNames(x) == FALSE) { + stop(error( + paste(" Individual", unknown, "is not in the genlight object\n") + )) +} + +# DO THE JOB +# Assign the unknown individual to population 'unknown' +vec <- as.vector(pop(x)) +vec[indNames(x) == unknown] <- "unknown" +# Note, population containing the unknown has been reduced in size by 1 +pop(x) <- as.factor(vec) + +x_grm <- gl.grm(x,plotheatmap = FALSE,verbose=0) +x_grm[upper.tri(x_grm, diag = TRUE)] <- NA +x_columns <- as.data.frame(as.table(x_grm)) +x_columns <- x_columns[which(!is.na(x_columns$Freq)), ] + +x_columns_2 <- x_columns[which(x_columns$Var1 == unknown | + x_columns$Var2 == unknown),] + +x_columns_2 <- rbind(x_columns_2,data.frame(Var1=x_columns_2$Var2,Var2=x_columns_2$Var1,Freq=x_columns_2$Freq)) + +x_merge_1a <- data.frame(Var1=indNames(x),pop_name=as.character(pop(x))) +x_merge_1b <- merge(x_merge_1a,x_columns_2,by="Var1") + +x_merge_1b$Var2 <- as.character(x_merge_1b$Var2) + +x_merge_1b <- x_merge_1b[which(x_merge_1b$pop_name!="unknown"),] + +x_merge_1b$pop_name <- as.factor(x_merge_1b$pop_name) + +x_split <- split(x_merge_1b,f=x_merge_1b$pop_name) + +rel_list <- unlist(lapply(x_split,function(y){ + return(mean(y$Freq)) +})) + +rel_list <- rel_list[order(rel_list,decreasing = TRUE)] + +res <- names(rel_list)[1] + +return(res) + +} \ No newline at end of file diff --git a/R/gl.assign.mahalanobis.r b/R/gl.assign.mahalanobis.r index e41d333a..c7a64055 100644 --- a/R/gl.assign.mahalanobis.r +++ b/R/gl.assign.mahalanobis.r @@ -1,10 +1,10 @@ #' @name gl.assign.mahalanobis -#' @title Assign an individual of unknown provenance to population based on Mahalanobis -#' Distance +#' @title Assign an individual of unknown provenance to population based on +#' Mahalanobis Distance #' @description #' This script assigns an individual of unknown provenance to one or more target -#' populations based on the unknown individual's proximity to population centroids; -#' proximity is estimated using Mahalanobis Distance. +#' populations based on the unknown individual's proximity to population +#' centroids; proximity is estimated using Mahalanobis Distance. #' #' The following process is followed: #' \enumerate{ @@ -85,7 +85,8 @@ #' @examples #' \dontrun{ #' #Test run with a focal individual from the Macleay River (EmmacMaclGeor) -#' test <- gl.assign.pa(testset.gl, unknown='UC_01044', nmin=10, threshold=1,verbose=3) +#' test <- gl.assign.pa(testset.gl, unknown='UC_01044', nmin=10, threshold=1, +#' verbose=3) #' test_2 <- gl.assign.pca(test, unknown='UC_01044', plevel=0.95, verbose=3) #' df <- gl.assign.mahalanobis(test_2, unknown='UC_01044', verbose=3) #' } @@ -116,7 +117,8 @@ gl.assign.mahalanobis <- function(x, if (!(unknown %in% indNames(x))) { stop( error( - "Fatal Error: Unknown must be listed among the individuals in the genlight object!\n" + "Fatal Error: Unknown must be listed among the individuals in + the genlight object!\n" ) ) } @@ -137,7 +139,8 @@ gl.assign.mahalanobis <- function(x, # DO THE JOB if (nPop(x) < 2) { if(verbose >= 2){ - cat(warn(" Only one population, including the unknown, no putative source identified.\n")) + cat(warn(" Only one population, including the unknown, no putative + source identified.\n")) } # Add a row df[i,1] <- unknown @@ -165,16 +168,22 @@ gl.assign.mahalanobis <- function(x, if(verbose >= 2){ cat(report(" Plotting the unknown against putative populations\n")) } - suppressWarnings(suppressMessages(gl.pcoa.plot(pcoa,x,ellipse=TRUE,plevel=plevel,verbose=0))) + suppressWarnings(suppressMessages(gl.pcoa.plot(pcoa, + x, + ellipse=TRUE, + plevel=plevel, + verbose=0))) } - # Determine the number of dimensions for confidence envelope (the ordination and dimension reduction) From the eigenvalue + # Determine the number of dimensions for confidence envelope (the + # ordination and dimension reduction) From the eigenvalue # distribution s <- sum(pcoa$eig) e <- round(pcoa$eig * 100 / s, 1) e.sign <- e[e > mean(e,na.rm=TRUE)] first.est <- length(e.sign) - # From the number of populations, including the unknown sec.est <- nPop(x) + # From the number of populations, including the unknown + # sec.est <- nPop(x) # cat(' Number of populations, including the unknown:',sec.est,'\n') if (verbose >= 2) { @@ -191,13 +200,15 @@ gl.assign.mahalanobis <- function(x, } dim <- min(first.est, dim.limit) if (verbose >= 2) { - cat(report(" Dimension of confidence envelope set at", dim, "\n")) + cat(report(" Dimension of confidence envelope set at", dim, + "\n")) } pcoa$scores <- pcoa$scores[, 1:dim] # Add population names to the scores c <- - data.frame(cbind(pcoa$scores, as.character(pop(x))), stringsAsFactors = FALSE) + data.frame(cbind(pcoa$scores, as.character(pop(x))), + stringsAsFactors = FALSE) colnames(c)[dim + 1] <- "pop" # Create a set of data without the unknown @@ -233,12 +244,14 @@ gl.assign.mahalanobis <- function(x, if (pval["unknown"] >= 1-plevel) { assign <- "yes" } else { - assign="no" + assign <- "no" } # Create a dataframe to hold the results if(i==1){ - df <- data.frame(unknown=NA,pop=NA,MahalD=NA,pval=NA,critval=NA,assign="NA") - # df <- data.frame(unknown=NA,pop=NA,MahalD=NA,wtMahalD=NA,pval=NA,critval=NA,assign="NA") + df <- data.frame(unknown=NA,pop=NA,MahalD=NA,pval=NA,critval=NA, + assign="NA") + # df <- data.frame(unknown=NA,pop=NA,MahalD=NA,wtMahalD=NA,pval=NA, + # critval=NA,assign="NA") } # # Add a row # df[i,1] <- unknown @@ -263,10 +276,11 @@ gl.assign.mahalanobis <- function(x, print(df) } # Extract the best match, and report - best <- as.character(df[df$assign == "yes"][1,2]) + best <- as.character(df[df$assign == "yes",][1,2]) if (verbose >= 3) { - cat(" Best assignment is the population with the larges probability of assignment, in this case", + cat(" Best assignment is the population with the larges probability + of assignment, in this case", best, "\n" ) diff --git a/R/gl.assign.pa.r b/R/gl.assign.pa.r index 85a8e464..d35db1ba 100644 --- a/R/gl.assign.pa.r +++ b/R/gl.assign.pa.r @@ -1,6 +1,6 @@ #' @name gl.assign.pa -#' @title Eliminates populations as possible source populations for an individual -#' of unknown provenance, using private alleles +#' @title Eliminates populations as possible source populations for an +#' individual of unknown provenance, using private alleles #' @description #' This script eliminates from consideration as putative source populations, #' those populations for which the individual has too many private alleles. The @@ -38,7 +38,8 @@ #' @importFrom stats dnorm qnorm #' @export #' -#' @author Custodian: Arthur Georges -- Post to #' \url{https://groups.google.com/d/forum/dartr} +#' @author Custodian: Arthur Georges -- Post to +#' \url{https://groups.google.com/d/forum/dartr} #' @examples #' # Test run with a focal individual from the Macleay River (EmmacMaclGeor) #' test <- gl.assign.pa(testset.gl, unknown='UC_00146', nmin=10, threshold=1, @@ -71,14 +72,16 @@ gl.assign.pa <- function(x, if (!all(test, na.rm = FALSE)) { stop( error( - "Fatal Error: nominated focal individual (of unknown provenance) is not present in the dataset!\n" + "Fatal Error: nominated focal individual (of unknown provenance) + is not present in the dataset!\n" ) ) } if (nmin <= 0 & verbose >= 1) { cat( warn( - " Warning: the minimum size of the target population must be greater than zero, set to 10\n" + " Warning: the minimum size of the target population must be + greater than zero, set to 10\n" ) ) nmin <- 10 @@ -86,7 +89,8 @@ gl.assign.pa <- function(x, if (threshold < 0 & verbose >= 1) { cat( warn( - " Warning: the threshold for private alleles must be non-negative, set to 0\n" + " Warning: the threshold for private alleles must be + non-negative, set to 0\n" ) ) threshold <- 0 @@ -96,7 +100,8 @@ gl.assign.pa <- function(x, if (n.best < 1 & verbose >= 1) { cat( warn( - " Warning: the n.best parameter for retention of best match populations must be a positive integer, set to NULL\n" + " Warning: the n.best parameter for retention of best + match populations must be a positive integer, set to NULL\n" ) ) n.best <- NULL @@ -109,11 +114,13 @@ gl.assign.pa <- function(x, hard.min <- 10 if (verbose >= 1 & (nmin < hard.min)) { cat(warn( - " Warning: The specified minimum sample size is less than 10 individuals\n" + " Warning: The specified minimum sample size is less than 10 + individuals\n" )) cat( warn( - " Risk of alleles present in the unknown being missed during sampling of populations with sample sizes less than 10\n" + " Risk of alleles present in the unknown being missed during + sampling of populations with sample sizes less than 10\n" ) ) } @@ -121,20 +128,22 @@ gl.assign.pa <- function(x, # Assign the unknown individual to population 'unknown' vec <- as.vector(pop(x)) vec[indNames(x) == unknown] <- "unknown" - pop(x) <- - as.factor(vec) # Note, population containing the unknown has been reduced in size by 1 + # Note, population containing the unknown has been reduced in size by 1 + pop(x) <- as.factor(vec) # Remove loci scored as NA for the unknown a <- x[pop(x) == "unknown",] - b <- - data.frame(as.matrix(a)) # Fuck, it changed the locus names, replaced hyphens with periods - names(b) <- locNames(a) # Change them back + # Fuck, it changed the locus names, replaced hyphens with periods + b <- data.frame(as.matrix(a)) + # Change them back + names(b) <- locNames(a) c <- names(b)[is.na(b)] if (length(c) > 0) { x <- gl.drop.loc(x, loc.list = c, verbose = 0) } - # Split the genlight object into one containing the unknown and one containing the remaining populations + # Split the genlight object into one containing the unknown and one + # containing the remaining populations unknowns <- gl.keep.pop(x, pop.list = "unknown", verbose = 0) knowns <- gl.drop.pop(x, pop.list = "unknown", verbose = 0) @@ -163,7 +172,8 @@ gl.assign.pa <- function(x, if (verbose >= 1) { cat( warn( - " Warning: Some retained populations have sample sizes less than", + " Warning: Some retained populations have sample sizes less + than", hard.min, ":", pop.warn, @@ -173,7 +183,8 @@ gl.assign.pa <- function(x, } if (verbose >= 1) { cat(warn( - " Substantial risk of private alleles arising as sampling error\n\n" + " Substantial risk of private alleles arising as sampling + error\n\n" )) } } @@ -185,7 +196,8 @@ gl.assign.pa <- function(x, if (verbose >= 0) { stop( error( - "Fatal Error: Number of unknown focal individuals > 1; population label 'unknown' already in use\n" + "Fatal Error: Number of unknown focal individuals > 1; + population label 'unknown' already in use\n" ) ) } @@ -221,23 +233,28 @@ gl.assign.pa <- function(x, unknown.gen <- unknown.ind[j] pop.gen <- gen[, j] if (length(pop.gen) >= nmin) { - # Where the unknown focal individual is homozygous reference allele [aa] + # Where the unknown focal individual is homozygous reference + #allele [aa] if (unknown.gen == 0) { - # If all individuals in the target population are bb [not aa or ab] then focal individual has private allele [a] + # If all individuals in the target population are bb [not + # aa or ab] then focal individual has private allele [a] if (all(pop.gen == 2, na.rm = TRUE)) { count[i] <- count[i] + 1 } } - # Where the unknown focal individual is homozygous for the alternate allele [bb] + # Where the unknown focal individual is homozygous for the + #alternate allele [bb] if (unknown.gen == 2) { - # If all individuals in the target population are aa [not bb or ab] then focal individual has private allele [b] + # If all individuals in the target population are aa [not + #bb or ab] then focal individual has private allele [b] if (all(pop.gen == 0, na.rm = TRUE)) { count[i] <- count[i] + 1 } } # Where the unknown focal individual is heterozgous [ab] if (unknown.gen == 1) { - # If all individuals in the target population are aa, then [b] is private or if bb, then [a] is private + # If all individuals in the target population are aa, then + #[b] is private or if bb, then [a] is private if ((all(pop.gen == 0, na.rm = TRUE)) || (all(pop.gen == 2, na.rm = TRUE))) { count[i] <- count[i] + 1 @@ -250,7 +267,8 @@ gl.assign.pa <- function(x, # Print out results if (verbose >= 3) { - cat(" Table showing populations against number of loci with private alleles\n") + cat(" Table showing populations against number of loci with private + alleles\n") } counter <- 1 retain <- NULL @@ -278,7 +296,8 @@ gl.assign.pa <- function(x, gl <- gl.keep.pop( x, - pop.list = c(levels(pop(knowns))[count <= threshold], "unknown"), + pop.list = c(levels(pop(knowns))[count <= threshold], + "unknown"), mono.rm = TRUE, verbose = 0 ) @@ -292,17 +311,18 @@ gl.assign.pa <- function(x, ) } - # Check that there is more than one population to assign (excluding 'unknown') + # Check that there is more than one population to assign (excluding + # 'unknown') if (is.null(n.best)) { if (verbose >= 2) { if (nPop(gl) == 1) { # Taking into account the unknown as a population - if (verbose >= 2) { - cat( - report( - " There are no populations retained for assignment.", - " The unknown may not belong to one of the target populations.", - " Returning genlight object for the unknown individual only\n" + if (verbose >= 2) { + cat( + report( + " There are no populations retained for assignment.", + " The unknown may not belong to one of the target populations.", + " Returning genlight object for the unknown individual only\n" ) ) } @@ -324,12 +344,12 @@ gl.assign.pa <- function(x, } else { if (verbose >= 2) { if (length(retain) == 0) { - if (verbose >= 2) { - cat( - report( - " There are no populations retained for assignment.", - " The unknown may not belong to one of the target populations.", - " Returning genlight object for the unknown individual only\n" + if (verbose >= 2) { + cat( + report( + " There are no populations retained for assignment.", + " The unknown may not belong to one of the target populations.", + " Returning genlight object for the unknown individual only\n" ) ) } diff --git a/R/gl.assign.pca.r b/R/gl.assign.pca.r index e2aea639..057efe22 100644 --- a/R/gl.assign.pca.r +++ b/R/gl.assign.pca.r @@ -23,11 +23,12 @@ #' alleles have been detected and the position of the unknown in relation to the #' confidence ellipses as is plotted by this script. Note, this plot is #' considering only the top two dimensions of the ordination, and so an unknown -#' lying outside the confidence ellipse can be unambiguously interpreted as it lying outside -#' the confidence envelope. However, if the unknown lies inside the confidence -#' ellipse in two dimensions, then it may still lie outside the confidence -#' envelope in deeper dimensions. This second step is good for eliminating populations from -#' consideration, but does not provide confidence in assignment. +#' lying outside the confidence ellipse can be unambiguously interpreted as it +#' lying outside the confidence envelope. However, if the unknown lies inside +#' the confidence ellipse in two dimensions, then it may still lie outside the +#' confidence envelope in deeper dimensions. This second step is good for +#' eliminating populations from consideration, but does not provide confidence +#' in assignment. #' #' The third step is to consider the assignment probabilities, using the script #' gl.assign.mahalanobis(). This approach calculates the squared Generalised @@ -60,7 +61,8 @@ #' @importFrom stats dnorm qnorm #' @export #' -#' @author Custodian: Arthur Georges -- Post to \url{https://groups.google.com/d/forum/dartr} +#' @author Custodian: Arthur Georges -- Post to +#' \url{https://groups.google.com/d/forum/dartr} #' @examples #' \dontrun{ #' #Test run with a focal individual from the Macleay River (EmmacMaclGeor) @@ -104,7 +106,8 @@ gl.assign.pca <- function(x, if (nPop(x) < 2) { stop( error( - "Fatal Error: Only one population, including the unknown, no putative source\n" + "Fatal Error: Only one population, including the unknown, no + putative source\n" ) ) } @@ -114,7 +117,8 @@ gl.assign.pca <- function(x, if (!(unknown %in% indNames(x))) { stop( error( - "Fatal Error: Unknown must be listed among the individuals in the genlight object!\n" + "Fatal Error: Unknown must be listed among the individuals in + the genlight object!\n" ) ) } @@ -138,7 +142,8 @@ gl.assign.pca <- function(x, # Ordinate a reduced space of 2 dimensions if (verbose >= 2){ - cat(report(" Calculating a PCA to represent the unknown in the context of putative sources\n")) + cat(report(" Calculating a PCA to represent the unknown in the context + of putative sources\n")) } pcoa <- gl.pcoa(x, nfactors = 2, verbose = 0) # Plot @@ -151,7 +156,8 @@ gl.assign.pca <- function(x, df <- data.frame(pcoa$scores) df <- cbind(as.character(pop(x)), df) names(df) <- c("pop", "x", "y") - # Determine if the unknown lies within the confidence ellipses specified by plevel + # Determine if the unknown lies within the confidence ellipses specified by + #plevel result <- data.frame() count <- 0 for (i in popNames(x)) { @@ -172,7 +178,8 @@ gl.assign.pca <- function(x, nhits <- length(result$pop[result$hit]) nohits <- length(result$pop[!result$hit]) if(verbose >= 2){ - cat(report(" Eliminating populations for which the unknown is outside their confidence envelope\n")) + cat(report(" Eliminating populations for which the unknown is outside + their confidence envelope\n")) } if (verbose >= 3) { if (nhits > 0) { @@ -196,10 +203,12 @@ gl.assign.pca <- function(x, x2 <- x } else { x2 <- - gl.drop.pop(x, pop.list = result[result$hit == FALSE, "pop"], verbose = 0) + gl.drop.pop(x, pop.list = result[result$hit == FALSE, "pop"], + verbose = 0) } if(verbose >= 2){ - cat(report(" Returning a genlight object with remaining putative source populations plus the unknown\n")) + cat(report(" Returning a genlight object with remaining putative source + populations plus the unknown\n")) } # FLAG SCRIPT END diff --git a/R/gl.basic.stats.r b/R/gl.basic.stats.r index 8c03d3b3..41defd3e 100644 --- a/R/gl.basic.stats.r +++ b/R/gl.basic.stats.r @@ -44,7 +44,9 @@ gl.basic.stats <- function(x, # DO THE JOB out <- - hierfstat::basic.stats(hierfstat::genind2hierfstat(gl2gi(x, verbose = 0)), digits = digits) + hierfstat::basic.stats(hierfstat::genind2hierfstat(gl2gi(x, + verbose = 0)), + digits = digits) # FLAG SCRIPT END diff --git a/R/gl.blast.r b/R/gl.blast.r index 9d8c37ac..dbb484e5 100644 --- a/R/gl.blast.r +++ b/R/gl.blast.r @@ -206,9 +206,10 @@ gl.blast <- function(x, "" ) if (grepl("\\s", path_makeblastdb)) { - stop( - error( - "The path to the executable for makeblastdb has spaces. Please move it\n to a path without spaces so BLAST can work.\n\n" + stop( + error( + " The path to the executable for makeblastdb has spaces. + Please move it\n to a path without spaces so BLAST can work.\n\n" ) ) } @@ -217,7 +218,8 @@ gl.blast <- function(x, if (all(path_makeblastdb == "")) { stop( error( - "Executable for makeblastdb not found! Please make sure that the software\n is correctly installed.\n\n" + " Executable for makeblastdb not found! Please make sure that + the software\n is correctly installed.\n\n" ) ) } @@ -250,7 +252,8 @@ gl.blast <- function(x, if (grepl("\\s", path_blastn)) { stop( error( - "The path to the executable for blastn has spaces. Please move it to a\n path without spaces so BLAST can work.\n\n" + "The path to the executable for blastn has spaces. Please + move it to a\n path without spaces so BLAST can work.\n\n" ) ) } @@ -259,7 +262,8 @@ gl.blast <- function(x, if (all(path_blastn == "")) { stop( error( - "Executable for blastn not found! Please make sure that the software is\n correctly installed.\n\n" + "Executable for blastn not found! Please make sure that the + software is\n correctly installed.\n\n" ) ) } @@ -287,7 +291,8 @@ gl.blast <- function(x, " -outfmt ", "\"", 6, - " qseqid sacc stitle qseq sseq nident mismatch pident length evalue bitscore qstart qend sstart send gapopen gaps qlen slen\"" + " qseqid sacc stitle qseq sseq nident mismatch pident length evalue + bitscore qstart qend sstart send gapopen gaps qlen slen\"" ) ) @@ -338,7 +343,8 @@ gl.blast <- function(x, "slen" ) - # calculate percentage overlap ratio of the alignment length divided by the query length or subject length (whichever is shortest of + # calculate percentage overlap ratio of the alignment length divided by the + # query length or subject length (whichever is shortest of # the two lengths) min_length_BF <- apply(blast_res_unfiltered[, c("qlen", "slen")], 1, min) @@ -354,7 +360,8 @@ gl.blast <- function(x, # splitting hits by sequence all_hits <- split(x = blast_res_filtered, f = blast_res_filtered$qseqid) - # ordering by first considering the highest percentage identity, then the highest percentage overlap, then the highest bitscore. Only + # ordering by first considering the highest percentage identity, then the + # highest percentage overlap, then the highest bitscore. Only # one hit per sequence is kept based on these selection criteria. one_hit_temp <- lapply(all_hits, function(x) { x[order(x$pident, @@ -395,14 +402,16 @@ gl.blast <- function(x, temp_one_hit <- tempfile(pattern = "Blast_one_hit_") # saving to tempdir - saveRDS(list(match_call, blast_res_unfiltered), file = temp_blast_unfiltered) + saveRDS(list(match_call, blast_res_unfiltered), + file = temp_blast_unfiltered) saveRDS(list(match_call, blast_res_filtered), file = temp_blast_filtered) saveRDS(list(match_call, one_hit), file = temp_one_hit) if (verbose >= 2) { cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.check.verbosity.r b/R/gl.check.verbosity.r index bd95c23f..e081f82f 100644 --- a/R/gl.check.verbosity.r +++ b/R/gl.check.verbosity.r @@ -24,7 +24,8 @@ gl.check.verbosity <- function(x = NULL) { } else { cat( warn( - "Warning: Parameter verbose must be an integer in the range 0 to 5, set to 2\n" + "Warning: Parameter verbose must be an integer in the range + 0 to 5, set to 2\n" ) ) verbose <- 2 diff --git a/R/gl.collapse.r b/R/gl.collapse.r index 7814e153..7a226b06 100644 --- a/R/gl.collapse.r +++ b/R/gl.collapse.r @@ -9,12 +9,14 @@ #' The script then applies the new population assignments to the genlight object #' and recalculates the distance and associated matrices. #' -#' @param fd Name of the list of matrices produced by gl.fixed.diff() [required]. +#' @param fd Name of the list of matrices produced by gl.fixed.diff() +#' [required]. #' @param tloc Threshold defining a fixed difference (e.g. 0.05 implies 95:5 vs #' 5:95 is fixed) [default 0]. #' @param tpop Threshold number of fixed differences above which populations #' will not be amalgamated [default 0]. -#' @param pb If TRUE, show a progress bar on time consuming loops [default FALSE]. +#' @param pb If TRUE, show a progress bar on time consuming loops +#' [default FALSE]. #' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, #' progress log; 3, progress and results summary; 5, full report #' [default 2 or as specified using gl.set.verbosity] @@ -31,7 +33,8 @@ #' } #' @importFrom methods show #' @export -#' @author Custodian: Arthur Georges -- Post to \url{https://groups.google.com/d/forum/dartr} +#' @author Custodian: Arthur Georges -- Post to +#' \url{https://groups.google.com/d/forum/dartr} #' @examples #' fd <- gl.fixed.diff(testset.gl,tloc=0.05) #' fd @@ -62,7 +65,8 @@ gl.collapse <- function(fd, if (tloc > 0.5 || tloc < 0) { stop(error( - "Fatal Error: Parameter tloc should be positive in the range 0 to 0.5\n" + "Fatal Error: Parameter tloc should be positive in the range 0 to + 0.5\n" )) } @@ -78,7 +82,8 @@ gl.collapse <- function(fd, if (tloc > 0) { cat( report( - " Comparing populations for fixed differences with tolerance", + " Comparing populations for fixed differences with + tolerance", tloc, "\n" ) @@ -92,7 +97,8 @@ gl.collapse <- function(fd, if (tpop == 1) { cat( report( - " Amalgamating populations with corrobrated fixed differences, tpop =", + " Amalgamating populations with corrobrated fixed + differences, tpop =", tpop, "\n" ) @@ -247,17 +253,18 @@ gl.collapse <- function(fd, if (pb) { cat("\n") } - cat( - report( - "Returning a list of class 'fd' containing the new genlight object and square matricies, as follows:\n", - " [[1]] $gl -- input genlight object;\n", - " [[2]] $fd -- raw fixed differences;\n", - " [[3]] $pcfd -- percent fixed differences;\n", - " [[4]] $nobs -- mean no. of individuals used in each comparison;\n", - " [[5]] $nloc -- total number of loci used in each comparison;\n", - " [[6]] $expfpos -- NAs, populated by gl.fixed.diff [by simulation];\n", - " [[7]] $sdfpos -- NAs, populated by gl.fixed.diff [by simulation];\n", - " [[8]] $prob -- NAs, populated by gl.fixed.diff [by simulation].\n" + cat( +report( +"Returning a list of class 'fd' containing the new genlight object and square +matricies, as follows:\n", +" [[1]] $gl -- input genlight object;\n", +" [[2]] $fd -- raw fixed differences;\n", +" [[3]] $pcfd -- percent fixed differences;\n", +" [[4]] $nobs -- mean no. of individuals used in each comparison;\n", +" [[5]] $nloc -- total number of loci used in each comparison;\n", +" [[6]] $expfpos -- NAs, populated by gl.fixed.diff [by simulation];\n", +" [[7]] $sdfpos -- NAs, populated by gl.fixed.diff [by simulation];\n", +" [[8]] $prob -- NAs, populated by gl.fixed.diff [by simulation].\n" ) ) } diff --git a/R/gl.compliance.check.r b/R/gl.compliance.check.r index 6fc7069d..be6cd425 100644 --- a/R/gl.compliance.check.r +++ b/R/gl.compliance.check.r @@ -86,7 +86,8 @@ gl.compliance.check <- function(x, if (verbose >= 1) { cat( error( - " Error: SNP data must be scored NA, 0 or 1 or 2, revisit data input\n" + " Error: SNP data must be scored NA, 0 or 1 or 2, + revisit data input\n" ) ) } @@ -101,7 +102,8 @@ gl.compliance.check <- function(x, if (verbose >= 1) { cat( report( - " Tag P/A data (SilicoDArT) scored 1, 0 (present or absent) confirmed\n" + " Tag P/A data (SilicoDArT) scored 1, 0 (present or + absent) confirmed\n" ) ) } @@ -109,7 +111,8 @@ gl.compliance.check <- function(x, if (verbose >= 1) { cat( error( - " Error: Tag P/A data (SilicoDArT) must be scored NA for missing, 0 for absent or 1 for present, revisit data input\n" + " Error: Tag P/A data (SilicoDArT) must be scored NA + for missing, 0 for absent or 1 for present, revisit data input\n" ) ) } @@ -170,7 +173,10 @@ gl.compliance.check <- function(x, if (nLoc(x) != nrow(x@other$loc.metrics)) { cat( warn( - " The number of rows in the loc.metrics table does not match the number of loci! This is potentially a major problem if there is a mismatch of the loci with the metadata. Trace back to identify the cause.\n" + " The number of rows in the loc.metrics table does not match + the number of loci! This is potentially a major problem if there + is a mismatch of the loci with the metadata. Trace back to + identify the cause.\n" ) ) } @@ -185,7 +191,8 @@ gl.compliance.check <- function(x, if (verbose >= 2) { cat( warn( - " Individual names are not unique. Appending an extra number to make them unique.\n" + " Individual names are not unique. Appending an extra + number to make them unique.\n" ) ) } @@ -224,7 +231,8 @@ gl.compliance.check <- function(x, if (verbose >= 1) { cat( warn( - " Population assignments not detected, individuals assigned to a single population labelled 'pop1'\n" + " Population assignments not detected, individuals assigned + to a single population labelled 'pop1'\n" ) ) } @@ -241,7 +249,7 @@ gl.compliance.check <- function(x, x@other$latlon <- x@other$latlong } - if (!is.null(x@other$latlon) | !is.null(x@other$latlon$long)) { + if (!is.null(x@other$latlon$long)) { x@other$latlon$lon <- x@other$latlon$long } @@ -250,7 +258,8 @@ gl.compliance.check <- function(x, x@other$latlon$long <- NULL if (verbose >= 2) { cat(report( - " Spelling of coordinates checked and changed if necessary to lat/lon\n" + " Spelling of coordinates checked and changed if necessary to + lat/lon\n" )) } diff --git a/R/gl.costdistances.r b/R/gl.costdistances.r index 31d34d61..80d7cd1a 100644 --- a/R/gl.costdistances.r +++ b/R/gl.costdistances.r @@ -20,9 +20,11 @@ #' \dontrun{ #' data(possums.gl) #' library(raster) #needed for that example -#' landscape.sim <- readRDS(system.file('extdata','landscape.sim.rdata', package='dartR')) +#' landscape.sim <- readRDS(system.file('extdata','landscape.sim.rdata', +#' package='dartR')) #' #calculate mean centers of individuals per population -#' xy <- apply(possums.gl@other$xy, 2, function(x) tapply(x, pop(possums.gl), mean)) +#' xy <- apply(possums.gl@other$xy, 2, function(x) tapply(x, pop(possums.gl), +#' mean)) #' cd <- gl.costdistances(landscape.sim, xy, method='leastcost', NN=8) #' round(cd,3) #' } @@ -55,14 +57,16 @@ gl.costdistances <- function(landscape, if (is.null(locs@other$latlon)) { stop( error( - "No locations were provided in the genlight object [@other$latlon].\n" + "No locations were provided in the genlight object + [@other$latlon].\n" ) ) } if (is.null(pop(locs))) { cat( warn( - "No population definition provided, hence I will calculate costdistances between individuals\n" + "No population definition provided, hence I will calculate + costdistances between individuals\n" ) ) pop(locs) <- indNames(locs) diff --git a/R/gl.define.pop.r b/R/gl.define.pop.r index 1c831da8..746a3a99 100644 --- a/R/gl.define.pop.r +++ b/R/gl.define.pop.r @@ -1,5 +1,6 @@ #' @name gl.define.pop -#' @title Defines a new population in a genlight object for specified individuals +#' @title Defines a new population in a genlight object for specified +#' individuals #' @description #' The script reassigns existing individuals to a new population and removes #' their existing population assignment. @@ -18,7 +19,8 @@ #' \url{https://groups.google.com/d/forum/dartr} #' @examples #' popNames(testset.gl) -#' gl <- gl.define.pop(testset.gl, ind.list=c('AA019073','AA004859'), new='newguys') +#' gl <- gl.define.pop(testset.gl, ind.list=c('AA019073','AA004859'), +#' new='newguys') #' popNames(gl) #' indNames(gl)[pop(gl)=='newguys'] #' @export @@ -41,13 +43,15 @@ gl.define.pop <- function(x, # STANDARD ERROR CHECKING - # Set a population if none is specified (such as if the genlight object has been generated manually) + # Set a population if none is specified (such as if the genlight object has + #been generated manually) if (is.null(pop(x)) | is.na(length(pop(x))) | length(pop(x)) <= 0) { if (verbose >= 2) { cat( warn( - " Population assignments not detected, individuals assigned to a single population labelled 'pop1'\n" + " Population assignments not detected, individuals assigned + to a single population labelled 'pop1'\n" ) ) } diff --git a/R/gl.diagnostics.hwe.r b/R/gl.diagnostics.hwe.r index 95334677..36324016 100644 --- a/R/gl.diagnostics.hwe.r +++ b/R/gl.diagnostics.hwe.r @@ -5,8 +5,8 @@ #' proportions. This function helps diagnose potential problems. #' @inheritParams gl.report.hwe #' @inheritParams utils.jackknife -#' @param n.cores The number of cores to use. If "auto" [default], it will -#' use all but one available cores. +#' @param n.cores The number of cores to use. If "auto", it will +#' use all but one available cores [default "auto"]. #' @param bins Number of bins to display in histograms [default 20]. #' @param stdErr Whether standard errors for Fis and Fst should be computed #' (default: TRUE) @@ -16,7 +16,8 @@ #' expected number of significant HWE tests [default two_colors_contrast]. #' @param plot_theme User specified theme [default theme_dartR()]. #' @details This function initially runs \code{\link{gl.report.hwe}} and reports -#' the ternary plots. The remaining outputs follow the recommendations from Waples +#' the ternary plots. The remaining outputs follow the recommendations from +#' Waples #' (2015) paper and De Meeûs 2018. These include: #' \enumerate{ #' \item A histogram @@ -42,18 +43,24 @@ #' the returned list (if \code{stdErr=TRUE}). These are computed with the #' Jackknife method over loci (See De Meeûs 2007 for details on how this is #' computed) and it may take some time for these computations to complete. -#' De Meeûs 2018 suggests that under a global significant heterozygosity deficit: +#' De Meeûs 2018 suggests that under a global significant heterozygosity +#' deficit: +#' #' - if the #' correlation between Fis and Fst is strongly positive, and StdErrFis >> #' StdErrFst, Null alleles are likely to be the cause. +#' #' - if the correlation #' between Fis and Fst is ~0 or mildly positive, and StdErrFis > StdErrFst, #' Wahlund may be the cause. +#' #' - if the correlation between Fis and Fst is ~0, and -#' StdErrFis ~ StdErrFst, selfing or sib mating could to be the cause. It is +#' StdErrFis ~ StdErrFst, selfing or sib mating could to be the cause. +#' +#' It is #' important to realise that these statistics only suggest a pattern (pointers). -#' Their absence is not conclusive evidence of the absence of the problem, as their -#' presence does not confirm the cause of the problem. +#' Their absence is not conclusive evidence of the absence of the problem, as +#' their presence does not confirm the cause of the problem. #' \item A table where the #' number of observed and expected significant HWE tests are reported by each #' population, indicating whether these are due to heterozygosity excess or @@ -72,9 +79,8 @@ #' @author Custodian: Carlo Pacioni -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples -#' \donttest{ -#' res <- gl.diagnostics.hwe(x = gl.filter.allna(platypus.gl[,1:50]),n.cores=1) -#' } +#' res <- gl.diagnostics.hwe(x = gl.filter.allna(platypus.gl[,1:50]), +#' stdErr=FALSE, n.cores=1) #' @references \itemize{ #' \item de Meeûs, T., McCoy, K.D., Prugnolle, F., #' Chevillon, C., Durand, P., Hurtrez-Boussès, S., Renaud, F., 2007. Population @@ -121,7 +127,8 @@ gl.diagnostics.hwe <- function(x, # DO THE JOB # Set NULL to variables to pass CRAN checks - Prob<-Sig<-N<-Locus<-Population<-Freq<-Data<-dumpop<-Deficiency<-Fis<-Excess<-pvalue<-ChiSquare<-Fst<-gen <-He <-value<- variable <-fst_obs<-NULL + Prob<-Sig<-N<-Locus<-Population<-Freq<-Data<-dumpop<-Deficiency<-Fis<- + Excess<-pvalue<-ChiSquare<-Fst<-gen <-He <-value<- variable <-fst_obs<-NULL # Helper function extractParam <- function(i, l, param) { @@ -166,7 +173,8 @@ gl.diagnostics.hwe <- function(x, # Include the non-sig tests nTimesBypop.df <- - rbind(data.frame(Var1 = 0, Freq = nTimesBypop["no_sig", sum(N)]), nTimesBypop.df) + rbind(data.frame(Var1 = 0, Freq = nTimesBypop["no_sig", sum(N)]), + nTimesBypop.df) nTimesBypop.df$Data <- "Observed" # Generate the null distribution @@ -192,7 +200,8 @@ gl.diagnostics.hwe <- function(x, scale_y_log10() + xlab("Number of populations in which loci depart from HWE") + ylab("Count") + - ggtitle(label = "Number of significant HWE tests for\nthe same locus in multiple populations")+ + ggtitle(label = "Number of significant HWE tests for\nthe same locus in + multiple populations")+ plot_theme # Collate HWE tests and Fis per locus and pop @@ -236,10 +245,12 @@ gl.diagnostics.hwe <- function(x, # Fis vs Fst plot - corr <- round(cor(Fstats$perloc$Fis, Fstats$perloc$Fst, "pairwise.complete.obs"), 3) + corr <- round(cor(Fstats$perloc$Fis, Fstats$perloc$Fst, + "pairwise.complete.obs"), 3) p3 <- ggplot(Fstats$perloc, aes(Fst, Fis)) + geom_point(size=2,color=colors_barplot[1],alpha=0.5) + - geom_smooth(method = "lm",color=colors_barplot[2],fill=colors_barplot[2],size=1) + + geom_smooth(method = "lm",color=colors_barplot[2],fill=colors_barplot[2], + size=1) + annotate("text", x=min(Fstats$perloc$Fst, na.rm = TRUE) + (max(Fstats$perloc$Fst, na.rm = TRUE) - @@ -263,8 +274,11 @@ gl.diagnostics.hwe <- function(x, stdErrFst <- sqrt(var(jckFst)/nLoc(x)) stdErrFis <- sqrt(var(jckFis)/nLoc(x)) - cat(report("The variation of Fis and Fst, respectively\n (measured as standard error with the Jackknife method - see De Meeus 2018) is:", - paste(c(stdErrFis, stdErrFst), collapse = ", "), "\n Fis vs Fst ratio is:", + cat(report("The variation of Fis and Fst, respectively\n (measured as + standard error with the Jackknife method - see De Meeus 2018) + is:", + paste(c(stdErrFis, stdErrFst), collapse = ", "), "\n Fis vs Fst + ratio is:", round(stdErrFis/stdErrFst, 2), "\n")) } @@ -299,7 +313,8 @@ gl.diagnostics.hwe <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using gl.list.reports() + and gl.print.reports()\n" ) ) } diff --git a/R/gl.dist.ind.r b/R/gl.dist.ind.r index 45e70b14..bde2913b 100644 --- a/R/gl.dist.ind.r +++ b/R/gl.dist.ind.r @@ -45,9 +45,9 @@ #' @export #' @author Author(s): Arthur Georges. Custodian: Arthur Georges -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples -#' D <- gl.dist.ind(testset.gl, method='euclidean',scale=TRUE) -#' D <- gl.dist.ind(testset.gl, method='manhattan') -#' D <- gl.dist.ind(testset.gs, method='Jaccard',swap=TRUE) +#' D <- gl.dist.ind(testset.gl[1:20,], method='euclidean',scale=TRUE) +#' D <- gl.dist.ind(testset.gl[1:20,], method='manhattan') +#' D <- gl.dist.ind(testset.gs[1:20,], method='Jaccard',swap=TRUE) gl.dist.ind <- function(x, method = NULL, diff --git a/R/gl.dist.pop.r b/R/gl.dist.pop.r index ceca1c3a..9bb940f9 100644 --- a/R/gl.dist.pop.r +++ b/R/gl.dist.pop.r @@ -121,7 +121,7 @@ gl.dist.pop <- function(x, # Convert to a pop x locus matrix f <- reshape2::dcast(f, popn ~ locus, value.var = "frequency") # Reassign names to the populations, and convert from percentages to proportions - row.names(f) = f[, 1] + row.names(f) <- f[, 1] f <- f[,-c(1)] p <- f / 100 diff --git a/R/gl.drop.ind.r b/R/gl.drop.ind.r index afc1acc3..0c43cec8 100644 --- a/R/gl.drop.ind.r +++ b/R/gl.drop.ind.r @@ -90,7 +90,7 @@ gl.drop.ind <- function(x, x <- x[inds_to_drop,] # Monomorphic loci may have been created - x@other$loc.metrics.flags$monomorphs == FALSE + x@other$loc.metrics.flags$monomorphs <- FALSE # Remove monomorphic loci if (mono.rm) { diff --git a/R/gl.drop.loc.r b/R/gl.drop.loc.r index 05b32b04..67554507 100644 --- a/R/gl.drop.loc.r +++ b/R/gl.drop.loc.r @@ -73,19 +73,37 @@ gl.drop.loc <- function(x, } if (flag == "both" || flag == "list") { - for (case in loc.list) { - if (!(case %in% locNames(x))) { - if(verbose >= 2){ - cat( - warn( - " Warning: Listed loci", - case, - "not present in the dataset -- ignored\n" - )) - } - loc.list <- loc.list[!(loc.list == case)] - } + + tmp1 <- loc.list %in% locNames(x) + tmp2 <- which(tmp1 == FALSE) + if(length(tmp2)>0){ + + if(verbose >= 2){ + cat( + warn( + " Warning: Listed loci", + paste(locNames(x)[tmp2],collapse = " "), + "not present in the dataset -- ignored\n" + )) } + + loc.list <- loc.list[-tmp2] + + } + + # for (case in loc.list) { + # if (!(case %in% locNames(x))) { + # if(verbose >= 2){ + # cat( + # warn( + # " Warning: Listed loci", + # case, + # "not present in the dataset -- ignored\n" + # )) + # } + # loc.list <- loc.list[!(loc.list == case)] + # } + # } } if (flag == "range") { diff --git a/R/gl.drop.pop.r b/R/gl.drop.pop.r index 95608864..dc58621a 100644 --- a/R/gl.drop.pop.r +++ b/R/gl.drop.pop.r @@ -136,7 +136,7 @@ gl.drop.pop <- function(x, pop.hold <- pop.hold[pops_to_drop] # Monomorphic loci may have been created - x@other$loc.metrics.flags$monomorphs == FALSE + x@other$loc.metrics.flags$monomorphs <- FALSE # Remove monomorphic loci if (mono.rm) { diff --git a/R/gl.filter.allna.r b/R/gl.filter.allna.r index 0ff573d2..f0d62c38 100644 --- a/R/gl.filter.allna.r +++ b/R/gl.filter.allna.r @@ -41,7 +41,7 @@ #' # Tag P/A data #' result <- gl.filter.allna(testset.gs, verbose=3) #' -#' @family filters and filter reports +#' @family filter functions #' @import utils patchwork #' @export @@ -58,12 +58,14 @@ gl.filter.allna <- function(x, build = "Josh", verbosity = verbose) - # CHECK DATATYPE datatype <- utils.check.datatype(x,verbose=verbose) # recurrence clash + # recurrence clash + # CHECK DATATYPE datatype <- utils.check.datatype(x,verbose=verbose) if (is(x, "genlight")) { if (is.null(ploidy(x))) { stop( error( - "Fatal Error: ploidy not set in the genlight object, run gl <- gl.compliance.check(gl)\n" + "Fatal Error: ploidy not set in the genlight object, run + gl <- gl.compliance.check(gl)\n" ) ) } @@ -83,7 +85,8 @@ gl.filter.allna <- function(x, } else { stop( error( - "Fatal Error -- SNP or SilicoDArT coding misspecified, run gl <- gl.compliance.check(gl)." + "Fatal Error -- SNP or SilicoDArT coding misspecified, run + gl <- gl.compliance.check(gl)." ) ) } @@ -95,13 +98,15 @@ gl.filter.allna <- function(x, if (by.pop==FALSE){ cat( report( - " Identifying and removing loci and individuals scored all missing (NA)\n" + " Identifying and removing loci and individuals scored all + missing (NA)\n" ) ) } else { cat( report( - " Identifying and removing loci that are all missing (NA) in any one individual\n" + " Identifying and removing loci that are all missing (NA) + in any one individual\n" ) ) } @@ -122,7 +127,7 @@ gl.filter.allna <- function(x, if (all(is.na(row))) { loc.list[i] <- l.names[i] if (all(is.na(row))) { - na.counter = na.counter + 1 + na.counter <-na.counter + 1 } } } @@ -140,7 +145,7 @@ gl.filter.allna <- function(x, ) } x2 <- x[, !x$loc.names %in% loc.list] - x2@other$loc.metrics <- x@other$loc.metrics[!x$loc.names %in% loc.list, ] + x2@other$loc.metrics <- x@other$loc.metrics[!x$loc.names %in% loc.list, ] x <- x2 if (verbose >= 2) { cat(" Deleted\n") @@ -149,7 +154,7 @@ gl.filter.allna <- function(x, # Consider individuals if(verbose >=2){ - cat(report(" Deleting individuals that are scored as all missing (NA)\n")) + cat(report(" Deleting individuals that are scored as all missing (NA)\n")) } na.counter <- 0 ind.list <- array(NA, nInd(x)) @@ -161,7 +166,7 @@ gl.filter.allna <- function(x, if (all(is.na(col))) { ind.list[i] <- i.names[i] if (all(is.na(col))) { - na.counter = na.counter + 1 + na.counter <-na.counter + 1 } } } @@ -187,7 +192,7 @@ gl.filter.allna <- function(x, if (by.pop==TRUE){ if(verbose >=2){ - cat(report(" Deleting loci that are all missing (NA) in any one population\n")) +cat(report(" Deleting loci that are all missing (NA) in any one population\n")) } total <- 0 loc.list <- NULL @@ -206,16 +211,19 @@ gl.filter.allna <- function(x, } loc.list <- unique(loc.list) if (verbose >= 3){ - cat("\n Loci all NA in one or more populations:",length(loc.list),"deleted\n\n") +cat("\n Loci all NA in one or more populations:",length(loc.list), + "deleted\n\n") } x <- gl.drop.loc(x,loc.list=loc.list,verbose=0) } if (recalc) { - # Recalculate all metrics, including Call Rate (flags reset in utils scripts) + # Recalculate all metrics, including Call Rate (flags reset in utils + #scripts) x <- gl.recalc.metrics(x, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except allna (dealt with elsewhere) + # Reset the flags as FALSE for all metrics except all na (dealt with + #elsewhere) x@other$loc.metrics.flags$AvgPIC <- FALSE x@other$loc.metrics.flags$OneRatioRef <- FALSE x@other$loc.metrics.flags$OneRatioSnp <- FALSE diff --git a/R/gl.filter.callrate.r b/R/gl.filter.callrate.r index 126f3c01..1c951c85 100644 --- a/R/gl.filter.callrate.r +++ b/R/gl.filter.callrate.r @@ -14,7 +14,8 @@ #' @details #' Because this filter operates on call rate, this function recalculates Call #' Rate, if necessary, before filtering. If individuals are removed using -#' method='ind', then the call rate stored in the genlight object is, optionally, +#' method='ind', then the call rate stored in the genlight object is, +#' optionally, #' recalculated after filtering. #' #' Note that when filtering individuals on call rate, the initial call rate is @@ -40,7 +41,8 @@ #' #' @param x Name of the genlight object containing the SNP data, or the genind #' object containing the SilocoDArT data [required]. -#' @param method Use method='loc' to specify that loci are to be filtered, 'ind' to specify +#' @param method Use method='loc' to specify that loci are to be filtered, 'ind' +#' to specify #' that specimens are to be filtered, 'pop' to remove loci that fail to meet the #' specified threshold in any one population [default 'loc']. #' @param threshold Threshold value below which loci will be removed @@ -70,13 +72,19 @@ #' #' @examples #' # SNP data -#' result <- gl.filter.callrate(testset.gl[1:10], method='loc', threshold=0.8, verbose=3) -#' result <- gl.filter.callrate(testset.gl[1:10], method='ind', threshold=0.8, verbose=3) -#' result <- gl.filter.callrate(testset.gl[1:10], method='pop', threshold=0.8, verbose=3) +#' result <- gl.filter.callrate(testset.gl[1:10], method='loc', threshold=0.8, +#' verbose=3) +#' result <- gl.filter.callrate(testset.gl[1:10], method='ind', threshold=0.8, +#' verbose=3) +#' result <- gl.filter.callrate(testset.gl[1:10], method='pop', threshold=0.8, +#' verbose=3) #' # Tag P/A data -#' result <- gl.filter.callrate(testset.gs[1:10], method='loc', threshold=0.95, verbose=3) -#' result <- gl.filter.callrate(testset.gs[1:10], method='ind', threshold=0.8, verbose=3) -#' result <- gl.filter.callrate(testset.gs[1:10], method='pop', threshold=0.8, verbose=3) +#' result <- gl.filter.callrate(testset.gs[1:10], method='loc', +#' threshold=0.95, verbose=3) +#' result <- gl.filter.callrate(testset.gs[1:10], method='ind', +#' threshold=0.8, verbose=3) +#' result <- gl.filter.callrate(testset.gs[1:10], method='pop', +#' threshold=0.8, verbose=3) #' #' @seealso \code{\link{gl.report.callrate}} #' @family filter functions @@ -114,13 +122,14 @@ gl.filter.callrate <- function(x, if (verbose >= 2) { cat( warn( - " Warning: Data may include monomorphic loci in call rate calculations for filtering\n" + " Warning: Data may include monomorphic loci in call rate + calculations for filtering\n" ) ) } } - # Check call rate up to date if (x@other$loc.metrics.flags$CallRate == FALSE){ + # Check call rate up to date if (x@other$loc.metrics.flags$CallRate == FALSE){ if (verbose >= 2) { cat(report(" Recalculating Call Rate\n")) } @@ -129,14 +138,15 @@ gl.filter.callrate <- function(x, # Suppress plotting on verbose == 0 if (verbose == 0) { - plot.out = FALSE + plot.out <-FALSE } # Method if (method != "ind" & method != "loc" & method != "pop") { cat( warn( - " Warning: method must be either \"loc\" or \"ind\" or \"pop\", set to \"loc\" \n" +" Warning: method must be either \"loc\" or \"ind\" or \"pop\", set to +\"loc\" \n" ) ) method <- "loc" @@ -145,7 +155,7 @@ gl.filter.callrate <- function(x, # Threshold if (threshold < 0 | threshold > 1) { cat(warn( - " Warning: threshold must be an integer between 0 and 1, set to 0.95\n" +" Warning: threshold must be an integer between 0 and 1, set to 0.95\n" )) threshold <- 0.95 } @@ -167,7 +177,8 @@ gl.filter.callrate <- function(x, } n0 <- nLoc(x) - # Remove loci with NA count <= 1-threshold index <- colMeans(is.na(as.matrix(x))) < threshold + # Remove loci with NA count <= 1-threshold index <- + #colMeans(is.na(as.matrix(x))) < threshold index <- x@other$loc.metrics$CallRate >= threshold x2 <- x[, index] x2@other$loc.metrics <- x@other$loc.metrics[index,] @@ -220,10 +231,11 @@ gl.filter.callrate <- function(x, x2 <- gl.filter.monomorphs(x2, verbose = 0) } if (recalc) { - # Recalculate all metrics, including Call Rate (flags reset in utils scripts) + # Recalculate all metrics, including Call Rate (flags reset in utils scripts) x2 <- gl.recalc.metrics(x2, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except Call Rate (dealt with elsewhere) +# Reset the flags as FALSE for all metrics except Call Rate (dealt with + #elsewhere) x2@other$loc.metrics.flags$AvgPIC <- FALSE x2@other$loc.metrics.flags$OneRatioRef <- FALSE x2@other$loc.metrics.flags$OneRatioSnp <- FALSE @@ -301,10 +313,11 @@ gl.filter.callrate <- function(x, x2 <- gl.filter.monomorphs(x2, verbose = 0) } if (recalc) { - # Recalculate all metrics, including Call Rate (flags reset in utils scripts) +# Recalculate all metrics, including Call Rate (flags reset in utils scripts) x2 <- gl.recalc.metrics(x2, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except Call Rate (dealt with elsewhere) +# Reset the flags as FALSE for all metrics except Call Rate (dealt with + #elsewhere) x2@other$loc.metrics.flags$AvgPIC <- FALSE x2@other$loc.metrics.flags$OneRatioRef <- FALSE x2@other$loc.metrics.flags$OneRatioSnp <- FALSE @@ -329,7 +342,8 @@ gl.filter.callrate <- function(x, report( "Recursively removing individuals with call rate <", threshold, - ", recalculating Call Rate after deleting monomorphs, and repeating until final Call Rate is >=", + ", recalculating Call Rate after deleting monomorphs, + and repeating until final Call Rate is >=", threshold, "\n" ) @@ -338,14 +352,16 @@ gl.filter.callrate <- function(x, for (i in 1:10) { # Recalculate the callrate ind.call.rate <- 1 - rowSums(is.na(as.matrix(x))) / nLoc(x) - # Extract those individuals with a call rate greater or equal to the threshold + # Extract those individuals with a call rate greater or equal to + #the threshold x2 <- x[ind.call.rate >= threshold,] if (nInd(x2) == nInd(x)) { break } - # for some reason that eludes me, this also (appropriately) filters the latlons and the covariates, but see above for + # for some reason that eludes me, this also (appropriately) + #filters the latlons and the covariates, but see above for # locus filtering if (verbose > 2) { cat( @@ -368,7 +384,7 @@ gl.filter.callrate <- function(x, if (verbose >= 3) { cat( report( - " List of individuals deleted (CallRate <= ", + " List of individuals deleted (CallRate <= ", threshold, ":\n" ) @@ -390,11 +406,12 @@ gl.filter.callrate <- function(x, gl.filter.monomorphs(x2, verbose = 0) } if (recalc) { - # Recalculate all metrics, including Call Rate (flags reset in utils scripts) +# Recalculate all metrics, including Call Rate (flags reset in utils scripts) x2 <- gl.recalc.metrics(x2, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except Call Rate (dealt with elsewhere) +# Reset the flags as FALSE for all metrics except Call Rate (dealt with + #elsewhere) x2@other$loc.metrics.flags$AvgPIC <- FALSE x2@other$loc.metrics.flags$OneRatioRef <- FALSE x2@other$loc.metrics.flags$OneRatioSnp <- FALSE @@ -457,7 +474,8 @@ gl.filter.callrate <- function(x, if (verbose >= 2) { cat( report( - " Removing loci based on Call Rate by population\n Call Rate must be equal to or exceed threshold =", + " Removing loci based on Call Rate by population\n + Call Rate must be equal to or exceed threshold =", threshold, "in all populations\n" ) @@ -528,10 +546,11 @@ gl.filter.callrate <- function(x, x2 <- gl.filter.monomorphs(x2, verbose = 0) } if (recalc) { - # Recalculate all metrics, including Call Rate (flags reset in utils scripts) +# Recalculate all metrics, including Call Rate (flags reset in utils scripts) x2 <- gl.recalc.metrics(x2, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except Call Rate (dealt with elsewhere) +# Reset the flags as FALSE for all metrics except Call Rate (dealt with + #elsewhere) x2@other$loc.metrics.flags$AvgPIC <- FALSE x2@other$loc.metrics.flags$OneRatioRef <- FALSE x2@other$loc.metrics.flags$OneRatioSnp <- FALSE @@ -586,11 +605,11 @@ gl.filter.callrate <- function(x, if (!recursive) { cat( warn( - " Warning: Some individuals with a Call Rate initially >=", +" Warning: Some individuals with a Call Rate initially >=", threshold, "may have a final CallRate lower than", threshold, - "when call rate is recalculated after removing resultant monomorphic loci\n" +"when call rate is recalculated after removing resultant monomorphic loci\n" ) ) } @@ -615,7 +634,8 @@ gl.filter.callrate <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.hamming.r b/R/gl.filter.hamming.r index 3e2946b0..fe0b2fd7 100644 --- a/R/gl.filter.hamming.r +++ b/R/gl.filter.hamming.r @@ -19,10 +19,12 @@ #' If a pair of DNA sequences are of differing length, the longer is truncated. #' #' The algorithm is that of Johann de Jong -#' \url{https://johanndejong.wordpress.com/2015/10/02/faster-hamming-distance-in-r-2/} +#'\url{https://johanndejong.wordpress.com/2015/10/02/ +#'faster-hamming-distance-in-r-2/} #' as implemented in \code{\link{utils.hamming}}. #' -#' Only one of two loci are retained if their Hamming distance is less that a specified +#' Only one of two loci are retained if their Hamming distance is less that a +#' specified #' percentage. 5 base differences out of 100 bases is a 20% Hamming distance. #' #' @param x Name of the genlight object containing the SNP data [required]. @@ -87,7 +89,8 @@ gl.filter.hamming <- function(x, if (threshold < 0 || threshold > 1) { cat( warn( - " Warning: Parameter 'threshold' must be an integer between 0 and 1, set to 0.2\n" + " Warning: Parameter 'threshold' must be an integer between 0 + and 1, set to 0.2\n" ) ) threshold <- 0.2 @@ -109,12 +112,14 @@ gl.filter.hamming <- function(x, if (verbose >= 3) { cat( report( - " Note: Hamming distance ranges from zero (sequence identity) to 1 (no bases shared at any position)\n" + " Note: Hamming distance ranges from zero (sequence identity) + to 1 (no bases shared at any position)\n" ) ) cat( report( - " Note: Calculating pairwise Hamming distances between trimmed reference sequence tags\n" + " Note: Calculating pairwise Hamming distances between trimmed + reference sequence tags\n" ) ) } @@ -198,10 +203,13 @@ gl.filter.hamming <- function(x, ylab("Count") + plot_theme - # if (datatype=='SilicoDArT'){ rdepth <- x2@other$loc.metrics$AvgReadDepth } else if (datatype=='SNP'){ rdepth <- + # if (datatype=='SilicoDArT'){ rdepth <- + #x2@other$loc.metrics$AvgReadDepth } else if + #(datatype=='SNP'){ rdepth <- # x2@other$loc.metrics$rdepth } plotvar <- d[d >= threshold] - # min <- min(plotvar,threshold) min <- trunc(min*100)/100 max <- max(plotvar,threshold,na.rm=TRUE) max <- ceiling(max*10)/10 + # min <- min(plotvar,threshold) min <- trunc(min*100)/100 max <- + #max(plotvar,threshold,na.rm=TRUE) max <- ceiling(max*10)/10 if (datatype == "SNP") { xlabel <- "Post-filter SNP Hamming Distance" } else { @@ -256,7 +264,8 @@ gl.filter.hamming <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.heterozygosity.r b/R/gl.filter.heterozygosity.r index eeb83516..cf64b04b 100644 --- a/R/gl.filter.heterozygosity.r +++ b/R/gl.filter.heterozygosity.r @@ -17,6 +17,7 @@ #' @author Custodian: Luis Mijangos -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @importFrom plyr join +#' @family filter functions #' @examples #' result <- gl.filter.heterozygosity(testset.gl,t.upper=0.06,verbose=3) #' tmp <- gl.report.heterozygosity(result,method='ind') @@ -57,7 +58,8 @@ gl.filter.heterozygosity <- function(x, if (!x@other$loc.metrics.flags$monomorphs) { cat( warn( - " Warning: genlight object contains monomorphic loci which will be factored into heterozygosity estimates\n" + " Warning: genlight object contains monomorphic loci which will + be factored into heterozygosity estimates\n" ) ) } diff --git a/R/gl.filter.hwe.r b/R/gl.filter.hwe.r index 56cfee1e..6dc60d9c 100644 --- a/R/gl.filter.hwe.r +++ b/R/gl.filter.hwe.r @@ -6,14 +6,17 @@ #' proportions based on observed frequencies of reference homozygotes, #' heterozygotes and alternate homozygotes. #' -#' Loci are filtered out if they show HWE departure in any one population. +#' Loci are filtered out if they show HWE departure either in any one population +#' (n.pop.threshold =1) or in at least X number of populations +#' (n.pop.threshold > 1). #' #' @param x Name of the genlight object containing the SNP data [required]. #' @param subset Way to group individuals to perform H-W tests. Either a vector #' with population names, 'each', 'all' (see details) [default 'each']. #' @param n.pop.threshold The minimum number of populations where the same locus -#' has to be out of hwe to be removed [default 1] -#' @param method_sig Method for determining statistical significance: 'ChiSquare' +#' has to be out of H-W proportions to be removed [default 1]. +#' @param method_sig Method for determining statistical significance: +#' 'ChiSquare' #' or 'Exact' [default 'Exact']. #' @param multi_comp Whether to adjust p-values for multiple comparisons #' [default FALSE]. @@ -46,7 +49,8 @@ #' \itemize{ #' \item Merging all populations in the dataset using subset = 'all'. #' \item Within each population separately using: subset = 'each'. -#' \item Within selected populations using for example: subset = c('pop1','pop2'). +#' \item Within selected populations using for example: subset = +#' c('pop1','pop2'). #' } #' #' Two different statistical methods to test for deviations from Hardy Weinberg @@ -80,10 +84,13 @@ #' Correction for multiple tests can be applied using the following methods #' based on the function \code{\link[stats]{p.adjust}}: #' \itemize{ -#' \item 'holm' is also known as the sequential Bonferroni technique (Rice, 1989). -#' This method has a greater statistical power than the standard Bonferroni test, +#' \item 'holm' is also known as the sequential Bonferroni technique +#' (Rice, 1989). +#' This method has a greater statistical power than the standard Bonferroni +#' test, #' however this method becomes very stringent when many tests are performed and -#' many real deviations from the null hypothesis can go undetected (Waples, 2015). +#' many real deviations from the null hypothesis can go undetected +#' (Waples, 2015). #' \item 'hochberg' based on Hochberg, 1988. #' \item 'hommel' based on Hommel, 1988. This method is more powerful than #' Hochberg's, but the difference is usually small. @@ -103,8 +110,10 @@ #' The number of tests on which the adjustment for multiple comparisons is #' the number of populations times the number of loci. #' -#' \bold{From v2.1} \code{gl.filter.hwe} takes the argument \code{n.pop.threshold}. -#' if \code{n.pop.threshold > 1} loci will be removed only if they are concurrently +#' \bold{From v2.1} \code{gl.filter.hwe} takes the argument +#' \code{n.pop.threshold}. +#' if \code{n.pop.threshold > 1} loci will be removed only if they are +#' concurrently #' significant (after adjustment if applied) out of hwe in >= #' \code{n.pop.threshold > 1}. #' @@ -122,9 +131,11 @@ #' \item Graffelman, J. (2015). Exploring Diallelic Genetic Markers: The Hardy #' Weinberg Package. Journal of Statistical Software 64:1-23. #' \item Graffelman, J. & Morales-Camarena, J. (2008). Graphical tests for -#' Hardy-Weinberg equilibrium based on the ternary plot. Human Heredity 65:77-84. +#' Hardy-Weinberg equilibrium based on the ternary plot. Human Heredity +#' 65:77-84. #' \item Graffelman, J., & Moreno, V. (2013). The mid p-value in exact tests for -#' Hardy-Weinberg equilibrium. Statistical applications in genetics and molecular +#' Hardy-Weinberg equilibrium. Statistical applications in genetics and +#' molecular #' biology, 12(4), 433-448. #' \item Hochberg, Y. (1988). A sharper Bonferroni procedure for multiple tests #' of significance. Biometrika, 75, 800–803. @@ -140,7 +151,7 @@ #' } #' @seealso \code{\link{gl.report.hwe}} #' @rawNamespace import(data.table, except = c(melt,dcast)) -#' @family filters/filter reports +#' @family filter functions #' @export gl.filter.hwe <- function(x, @@ -180,7 +191,8 @@ gl.filter.hwe <- function(x, cat(error(" Detected Presence/Absence (SilicoDArT) data\n")) stop( error( - "Cannot calculate HWE from fragment presence/absence data. Please provide a SNP dataset.\n" + "Cannot calculate HWE from fragment presence/absence data. + Please provide a SNP dataset.\n" ) ) } @@ -188,7 +200,8 @@ gl.filter.hwe <- function(x, if (alpha_val < 0 | alpha_val > 1) { cat( warn( - " Warning: level of significance per locus alpha must be an integer between 0 and 1, set to 0.05\n" + " Warning: level of significance per locus alpha must be an + integer between 0 and 1, set to 0.05\n" ) ) alpha_val <- 0.05 @@ -208,7 +221,8 @@ gl.filter.hwe <- function(x, if (verbose >= 3) { cat( warn( - " Warning: Significance of tests may indicate heterogeneity among populations\n\n" + " Warning: Significance of tests may indicate heterogeneity + among populations\n\n" ) ) } @@ -233,7 +247,8 @@ gl.filter.hwe <- function(x, if (pops_hwe == 0) { stop( error( - "Fatal Error: subset parameter must be \"each\", \"all\", or a list of populations existing in the dataset\n" + "Fatal Error: subset parameter must be \"each\", \"all\", or + a list of populations existing in the dataset\n" ) ) } @@ -254,7 +269,8 @@ gl.filter.hwe <- function(x, if (verbose >= 3) { cat( warn( - " Warning: Significance of tests may indicate heterogeneity among populations\n\n" + " Warning: Significance of tests may indicate heterogeneity + among populations\n\n" ) ) } @@ -312,7 +328,8 @@ gl.filter.hwe <- function(x, if (length(poplist) < 1) { stop( error( - "No populations left after removing populations with low sample size and populations with monomorphic loci" + "No populations left after removing populations with low sample + size and populations with monomorphic loci" ) ) } @@ -433,7 +450,8 @@ gl.filter.hwe <- function(x, cat( " Deleted", length(failed.loci), - "loci with significant departure from HWE, after correction for multiple tests using the", + "loci with significant departure from HWE, after correction for + multiple tests using the", multi_comp_method, "method at experiment-wide alpha =", alpha_val, @@ -452,7 +470,8 @@ gl.filter.hwe <- function(x, cat(" Loci retained:", nLoc(hold), "\n\n") cat( important( - " Adjustment of p-values for multiple comparisons vary with sample size\n" + " Adjustment of p-values for multiple comparisons vary with + sample size\n" ) ) } diff --git a/R/gl.filter.ld.r b/R/gl.filter.ld.r new file mode 100644 index 00000000..a819497c --- /dev/null +++ b/R/gl.filter.ld.r @@ -0,0 +1,122 @@ +#' @name gl.filter.ld +#' @title Filters loci based on linkage disequilibrium (LD) +#' @description +#' This function uses the statistic set in the parameter \code{stat_keep} from +#' function \code{\link{gl.report.ld.map}} to choose the SNP to keep when two +#' SNPs are in LD. When a SNP is selected to be filtered out in each pairwise +#' comparison, the function stores its name in a list. In subsequent pairwise +#' comparisons, if the SNP is already in the list, the other SNP will be kept. +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param ld_report Output from function \code{\link{gl.report.ld.map}} +#' [required]. +#' @param threshold Threshold value above which loci will be removed +#' [default 0.2]. +#' @param pop.limit Minimum number of populations in which LD should be more +#' than the threshold for a locus to be filtered out. +#' The default value is half of the populations [default ceiling(nPop(x)/2)]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @return The reduced genlight object. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' test <- bandicoot.gl +#' test <- gl.filter.callrate(test,threshold = 1) +#' res <- gl.report.ld.map(test) +#' res_2 <- gl.filter.ld(x=test,ld_report = res) +#' res_3 <- gl.report.ld.map(res_2) +#' @seealso \code{\link{gl.report.ld.map}} +#' @family filter functions +#' @export + +gl.filter.ld <- function(x, + ld_report, + threshold = 0.2, + pop.limit = ceiling(nPop(x) / 2), + verbose = NULL) { + + x_hold <- x + + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + # FUNCTION SPECIFIC ERROR CHECKING + + # Check monomorphs have been removed up to date + if (x@other$loc.metrics.flags$monomorphs == FALSE) { + if (verbose >= 2) { + cat( + warn( + " Warning: Data may include monomorphic loci in call rate + calculations for filtering\n" + ) + ) + } + } + + x <- gl.keep.pop(x,pop.list = as.character(unique(ld_report$pop)),verbose = 0) + + ld_tmp <- ld_report[ld_report$ld_stat >= threshold, ] + ld_tmp$test_stat <- ld_tmp$locus_a.stat_keep >= ld_tmp$locus_b.stat_keep + ld_tmp$pop <- as.factor(ld_tmp$pop) + ld_tmp_pop <- split(ld_tmp, f = ld_tmp$pop) + + loci_list <- vector(mode = "list", length = length(ld_tmp_pop)) + + for (i in 1:length(ld_tmp_pop)) { + ld_pop <- ld_tmp_pop[[i]] + for (y in 1:nrow(ld_pop)) { + if (ld_pop[y, "test_stat"] == TRUE) { + loci_tmp <- ld_pop[y, "locus_b.snp.name"] + } else{ + loci_tmp <- ld_pop[y, "locus_a.snp.name"] + } + + if (loci_tmp %in% loci_list[[i]]) { + next + } else{ + loci_list[[i]] <- c(loci_list[[i]], loci_tmp) + } + + } + } + + loci_list_res <- Reduce("c", loci_list) + loci_names_tmp <- names(table(loci_list_res)) + loci_names <- loci_names_tmp[table(loci_list_res) >= pop.limit] + + x2 <- gl.drop.loc(x_hold, loc.list = loci_names, verbose = 0) + + # REPORT A SUMMARY + if (verbose >= 2) { + cat(" Summary of filtered dataset\n") + cat(paste(" LD for loci >", threshold, "\n")) + cat(paste(" Original No. of loci :", nLoc(x), "\n")) + cat(paste(" No. of loci retained:", nLoc(x2), "\n")) + cat(paste(" No. of populations: ", nPop(x2), "\n")) + } + + # ADD TO HISTORY + + nh <- length(x2@other$history) + x2@other$history[[nh + 1]] <- match.call() + + # FLAG SCRIPT END + + if (verbose > 0) { + cat(report("Completed:", funname, "\n")) + } + + return(invisible(x2)) + +} diff --git a/R/gl.filter.locmetric.r b/R/gl.filter.locmetric.r index 4f7b1131..189d22b8 100644 --- a/R/gl.filter.locmetric.r +++ b/R/gl.filter.locmetric.r @@ -1,4 +1,4 @@ -#' @name gl.filter.locmetrics +#' @name gl.filter.locmetric #' @title Filters loci on the basis of numeric information stored in #' other$loc.metrics in a genlight \{adegenet\} object #' @description @@ -6,6 +6,16 @@ #' to filter loci. The loci to keep can be within the upper and lower thresholds #' ('within') or outside of the upper and lower thresholds ('outside'). #' +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param metric Name of the metric to be used for filtering [required]. +#' @param upper Filter upper threshold [required]. +#' @param lower Filter lower threshold [required]. +#' @param keep Whether keep loci within of upper and lower thresholds or keep +#' loci outside of upper and lower thresholds [within]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details #' The fields that are included in dartR, and a short description, are found #' below. Optionally, the user can also set his/her own filter by adding a #' vector into $other$loc.metrics as shown in the example. @@ -35,17 +45,9 @@ #' \item RepAvg - proportion of technical replicate assay pairs for which the #' marker score is consistent. #' } -#' @param x Name of the genlight object containing the SNP data [required]. -#' @param metric Name of the metric to be used for filtering [required]. -#' @param upper Filter upper threshold [required]. -#' @param lower Filter lower threshold [required]. -#' @param keep Whether keep loci within of upper and lower thresholds or keep -#' loci outside of upper and lower thresholds [within]. -#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, -#' progress log; 3, progress and results summary; 5, full report -#' [default 2, unless specified using gl.set.verbosity]. #' @return The reduced genlight dataset. #' @export +#' @family filter functions #' @author Luis Mijangos -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples @@ -77,18 +79,21 @@ gl.filter.locmetric <- function(x, if (nLoc(x) != nrow(x@other$loc.metrics)) { stop( error( - "The number of rows in the loc.metrics table does not match the number of loci in your genlight object!" + "The number of rows in the loc.metrics table does not match the + number of loci in your genlight object!" ) ) } - # Set a population if none is specified (such as if the genlight object has been generated manually) + # Set a population if none is specified (such as if the genlight object has + #been generated manually) if (is.null(pop(x)) | is.na(length(pop(x))) | length(pop(x)) <= 0) { if (verbose >= 2) { cat( report( - " Population assignments not detected, individuals assigned to a single population labelled 'pop1'\n" + " Population assignments not detected, individuals assigned + to a single population labelled 'pop1'\n" ) ) } @@ -102,7 +107,8 @@ gl.filter.locmetric <- function(x, cat(warn(" Warning: genlight object contains monomorphic loci\n")) } - # FUNCTION SPECIFIC ERROR CHECKING check whether the field exists in the genlight object + # FUNCTION SPECIFIC ERROR CHECKING check whether the field exists in the + #genlight object if (!(metric %in% colnames(x$other$loc.metrics))) { stop(error("Fatal Error: name of the metric not found\n")) } diff --git a/R/gl.filter.maf.r b/R/gl.filter.maf.r index ffacce50..2542922c 100644 --- a/R/gl.filter.maf.r +++ b/R/gl.filter.maf.r @@ -1,5 +1,6 @@ #' @name gl.filter.maf -#' @title Filters loci on the basis of minor allele frequency (MAF) in a genlight +#' @title Filters loci on the basis of minor allele frequency (MAF) in a +#' genlight #' {adegenet} object #' @description #' This script calculates the minor allele frequency for each locus and updates @@ -10,8 +11,10 @@ #' Careful consideration needs to be given to the settings to be used for this #' fucntion. When the filter is applied globally (i.e. \code{by.pop=FALSE}) but #' the data include multiple population, there is the risk to remove markers -#' because the allele frequencies is low (at global level) but the allele frequencies -#' for the same markers may be high within some of the populations (especially if +#' because the allele frequencies is low (at global level) but the allele +#' frequencies +#' for the same markers may be high within some of the populations (especially +#' if #' the per-population sample size is small). Similarly, not always it is a #' sensible choice to run this function using \code{by.pop=TRUE} because allele #' that are rare in a population may be very common in other, but the (possible) @@ -19,16 +22,17 @@ #' Where the purpose of filtering for MAF is to remove possible spurious alleles #' (i.e. sequencing errors), it is perhaps better to filter based on the number #' of times an allele is observed (MAC, Minimum Allele Count), under the -#' assumption that if an allele is observed >MAC, it is fairly rare to be an error. +#' assumption that if an allele is observed >MAC, it is fairly rare to be an +#' error. #' \bold{From v2.1} The threshold can take values > 1. In this case, these are #' interpreted as a threshold for MAC. #' #' #' @param x Name of the genlight object containing the SNP data [required]. #' @param threshold Threshold MAF -- loci with a MAF less than the threshold -#' will be removed [default 0.01]. If a value > 1 is provided it will be +#' will be removed. If a value > 1 is provided it will be #' interpreted as MAC (i.e. the minimum number of times an allele needs to be -#' observed). +#' observed) [default 0.01]. #' @param by.pop Whether MAF should be calculated by population [default FALSE]. #' @param pop.limit Minimum number of populations in which MAF should be less #' than the threshold for a locus to be filtered out. Only used if by.pop=TRUE. @@ -52,6 +56,7 @@ #' [default 2, unless specified using gl.set.verbosity]. #' @return The reduced genlight dataset #' @export +#' @family filter functions #' @author Custodian: Luis Mijangos -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples @@ -92,18 +97,21 @@ gl.filter.maf <- function(x, if (nLoc(x) != nrow(x@other$loc.metrics)) { stop( error( - "The number of rows in the loc.metrics table does not match the number of loci in your genlight object!" + "The number of rows in the loc.metrics table does not match the + number of loci in your genlight object!" ) ) } - # Set a population if none is specified (such as if the genlight object has been generated manually) + # Set a population if none is specified (such as if the genlight object has + #been generated manually) if (is.null(pop(x)) | is.na(length(pop(x))) | length(pop(x)) <= 0) { if (verbose >= 2) { cat( report( - " Population assignments not detected, individuals assigned to a single population labelled 'pop1'\n" + " Population assignments not detected, individuals assigned + to a single population labelled 'pop1'\n" ) ) } @@ -118,11 +126,13 @@ gl.filter.maf <- function(x, } # FUNCTION SPECIFIC ERROR CHECKING - if(threshold >= 1) threshold <- threshold/(length(indNames(x))*mean(ploidy(x))) + if(threshold >= 1) threshold <- threshold/(length(indNames(x))* + mean(ploidy(x))) if (threshold > 0.5 | threshold <= 0) { cat( warn( - " Warning: threshold must be in the range (0,0.5], but usually small, set to 0.01\n" + " Warning: threshold must be in the range (0,0.5], but usually + small, set to 0.01\n" ) ) threshold <- 0.01 @@ -133,7 +143,8 @@ gl.filter.maf <- function(x, if(by.pop){ if (verbose >= 2) { cat(report( - " Removing loci with MAF <",threshold, "in at least",pop.limit,"populations and recalculating FreqHoms and FreqHets\n" + " Removing loci with MAF <",threshold, "in at least",pop.limit, + "populations and recalculating FreqHoms and FreqHets\n" )) } #x <- utils.recalc.maf(x, verbose = 0) @@ -159,7 +170,8 @@ gl.filter.maf <- function(x, # Recalculate the relevant loc.metrics if (verbose >= 2) { cat(report( - " Removing loci with MAF <",threshold, "over all the dataset and recalculating FreqHoms and FreqHets\n" + " Removing loci with MAF <",threshold, "over all the dataset + and recalculating FreqHoms and FreqHets\n" )) } @@ -184,7 +196,7 @@ gl.filter.maf <- function(x, p1 <- ggplot(as.data.frame(maf_pre), aes(x = maf)) + - geom_histogram(bins = bins,color = plot_colors_all[1],fill = plot_colors_all[2]) + +geom_histogram(bins=bins,color=plot_colors_all[1],fill = plot_colors_all[2]) + coord_cartesian(xlim = c(min, 0.5)) + geom_vline(xintercept = threshold,color = "red",size = 1) + xlab("Pre-filter SNP MAF\nOver all populations") + @@ -198,7 +210,7 @@ gl.filter.maf <- function(x, p2 <- ggplot(as.data.frame(maf_post), aes(x = maf)) + - geom_histogram(bins = bins,color = plot_colors_all[1],fill = plot_colors_all[2]) + +geom_histogram(bins = bins,color = plot_colors_all[1],fill=plot_colors_all[2]) + coord_cartesian(xlim = c(min, 0.5)) + geom_vline(xintercept = threshold,color = "red", size = 1) + xlab("Post-filter SNP MAF\nOver all populations") + @@ -221,7 +233,7 @@ gl.filter.maf <- function(x, p_temp <- ggplot(as.data.frame(mafs_per_pop), aes(x = mafs_per_pop)) + - geom_histogram(bins = bins, color = "black", fill = plot_colors_pop(bins)) + +geom_histogram(bins = bins, color = "black", fill = plot_colors_pop(bins)) + geom_vline(xintercept = threshold,color = "red",size = 1) + xlab("Pre-filter SNP MAF") + ylab("Count") + @@ -244,7 +256,7 @@ gl.filter.maf <- function(x, p_temp <- ggplot(as.data.frame(mafs_per_pop), aes(x = mafs_per_pop)) + - geom_histogram(bins = bins, color = "black", fill = plot_colors_pop(bins)) + +geom_histogram(bins = bins, color = "black", fill = plot_colors_pop(bins)) + geom_vline(xintercept = threshold,color = "red",size = 1) + xlab("Post-filter SNP MAF\n") + ylab("Count") + @@ -259,7 +271,8 @@ gl.filter.maf <- function(x, return(plot_temp) }) - # Check for status -- any populations with ind > ind.limit; and is nPop > 1 + # Check for status -- any populations with ind > ind.limit; and is + #nPop > 1 ind_per_pop <- unlist(lapply(pops_maf_pre, nInd)) test_pop <- @@ -312,7 +325,8 @@ gl.filter.maf <- function(x, if (verbose >= 1) { cat( important( - " No populations met minimum limits on number of individuals or loci, reporting for overall\n" + " No populations met minimum limits on number of + individuals or loci, reporting for overall\n" ) ) } @@ -345,7 +359,8 @@ gl.filter.maf <- function(x, if (verbose >= 3) { cat( important( - " Only one population met minimum limits on number of individuals or loci\n" + " Only one population met minimum limits on number of + individuals or loci\n" ) ) } @@ -413,7 +428,8 @@ gl.filter.maf <- function(x, # Recalculate all metrics(flags reset in utils scripts) x2 <- gl.recalc.metrics(x2, verbose = verbose) } else { - # Reset the flags as FALSE for all metrics except MAF (dealt with elsewhere) + + # Reset the flags as FALSE for all metrics except MAF (dealt with elsewhere) x2@other$loc.metrics.flags$AvgPIC <- FALSE x2@other$loc.metrics.flags$OneRatioRef <- FALSE x2@other$loc.metrics.flags$OneRatioSnp <- FALSE @@ -462,7 +478,8 @@ gl.filter.maf <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.monomorphs.r b/R/gl.filter.monomorphs.r index e5c589a5..5de24733 100644 --- a/R/gl.filter.monomorphs.r +++ b/R/gl.filter.monomorphs.r @@ -27,7 +27,7 @@ #' # Tag P/A data #' result <- gl.filter.monomorphs(testset.gs, verbose=3) #' -#' @family filters and filter reports +#' @family filter functions #' @import utils patchwork #' @importFrom plyr count #' @export @@ -58,62 +58,68 @@ gl.filter.monomorphs <- function(x, } # Tag presence/absence data - if (datatype == "SilicoDArT") { - nL <- nLoc(x) - matrix <- as.matrix(x) - l.names <- locNames(x) - for (i in 1:nL) { - row <- matrix[, i] # Row for each locus - if (all(row == 0, na.rm = TRUE) | - all(row == 1, na.rm = TRUE) | all(is.na(row))) { - loc.list[i] <- l.names[i] - if (all(is.na(row))) { - na.counter = na.counter + 1 - } - } - } - } - + # if (datatype == "SilicoDArT") { + # nL <- nLoc(x) + # matrix <- as.matrix(x) + # l.names <- locNames(x) + # for (i in 1:nL) { + # row <- matrix[, i] # Row for each locus + # if (all(row == 0, na.rm = TRUE) | + # all(row == 1, na.rm = TRUE) | all(is.na(row))) { + # loc.list[i] <- l.names[i] + # if (all(is.na(row))) { + # na.counter <-na.counter + 1 + # } + # } + # } + # } + # # SNP data - if (datatype == "SNP") { - nL <- nLoc(x) - matrix <- as.matrix(x) - lN <- locNames(x) - for (i in 1:nL) { - row <- matrix[, i] # Row for each locus - if (all(row == 0, na.rm = TRUE) | - all(row == 2, na.rm = TRUE) | all(is.na(row))) { - loc.list[i] <- lN[i] - if (all(is.na(row))) { - na.counter = na.counter + 1 - } - } - } - } + # if (datatype == "SNP") { + # nL <- nLoc(x) + # matrix <- as.matrix(x) + # lN <- locNames(x) + # for (i in 1:nL) { + # row <- matrix[, i] # Row for each locus + # if (all(row == 0, na.rm = TRUE) | + # all(row == 2, na.rm = TRUE) | all(is.na(row))) { + # loc.list[i] <- lN[i] + # if (all(is.na(row))) { + # na.counter <-na.counter + 1 + # } + # } + # } + + mono_tmp <- gl.alf(x) + loc.list <- rownames(mono_tmp[which(mono_tmp$alf1==1 | + mono_tmp$alf1 == 0),]) + loc.list_NA <- rownames(mono_tmp[which(is.na(mono_tmp$alf1)),]) + # } # Remove NAs from list of monomorphic loci and loci with all NAs - loc.list <- loc.list[!is.na(loc.list)] + # loc.list <- loc.list[!is.na(loc.list)] # remove monomorphic loc and loci with all NAs + loc.list <- c(loc.list,loc.list_NA) + if (length(loc.list > 0)) { if (verbose >= 2) { - cat(" Removing monomorphic loci\n") + cat(report(" Removing monomorphic loci and loci with all missing + data\n")) } x <- gl.drop.loc(x, loc.list = loc.list, verbose = 0) } else { if (verbose >= 2) { - cat(" No monomorphic loci to remove\n") + cat(report(" No monomorphic loci to remove\n")) } } # Report results if (verbose >= 3) { cat(" Original No. of loci:", nLoc(hold), "\n") - cat(" Monomorphic loci:", - nLoc(hold) - nLoc(x) - na.counter, - "\n") - cat(" Loci scored all NA:", na.counter, "\n") + cat(" Monomorphic loci:", nLoc(hold) - nLoc(x), "\n") + cat(" Loci scored all NA:", length(loc.list_NA), "\n") cat(" No. of loci deleted:", nLoc(hold) - nLoc(x), "\n") cat(" No. of loci retained:", nLoc(x), "\n") cat(" No. of individuals:", nInd(x), "\n") diff --git a/R/gl.filter.overshoot.r b/R/gl.filter.overshoot.r index a6d20600..02f4801d 100644 --- a/R/gl.filter.overshoot.r +++ b/R/gl.filter.overshoot.r @@ -22,6 +22,7 @@ #' [default 2, unless specified using gl.set.verbosity]. #' @return A new genlight object with the recalcitrant loci deleted #' @export +#' @family filter functions #' @author Custodian: Arthur Georges -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples @@ -47,7 +48,8 @@ gl.filter.overshoot <- function(x, if (datatype == "SilicoDArT") { stop( error( - " Detected Presence/Absence (SilicoDArT) data. Please supply a SNP dataset\n" + " Detected Presence/Absence (SilicoDArT) data. Please supply a + SNP dataset\n" ) ) } @@ -57,13 +59,16 @@ gl.filter.overshoot <- function(x, if (length(x@other$loc.metrics$TrimmedSequence) != nLoc(x)) { stop( error( - "Fatal Error: Data must include Trimmed Sequences for each loci in a column called 'TrimmedSequence' in the @other$loc.metrics slot.\n" + "Fatal Error: Data must include Trimmed Sequences for each loci + in a column called 'TrimmedSequence' in the @other$loc.metrics + slot.\n" ) ) } if (length(x@other$loc.metrics$SnpPosition) != nLoc(x)) { stop(error( - "Fatal Error: Data must include position information for each loci.\n" + "Fatal Error: Data must include position information for each + loci.\n" )) } @@ -71,7 +76,8 @@ gl.filter.overshoot <- function(x, if (verbose >= 2) { cat(report( - " Identifying loci for which the SNP has been trimmed with the adaptor\n" + " Identifying loci for which the SNP has been trimmed with the + adaptor\n" )) } @@ -128,7 +134,8 @@ gl.filter.overshoot <- function(x, )) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.pa.r b/R/gl.filter.pa.r index c522b30f..db7f9d5e 100644 --- a/R/gl.filter.pa.r +++ b/R/gl.filter.pa.r @@ -20,11 +20,13 @@ #' @return The reduced genlight dataset, containing now only fixed and private #' alleles. #' @export +#' @family filter functions #' @author Authors: Bernd Gruber & Ella Kelly (University of Melbourne); #' Custodian: Luis Mijangos -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples -#' result <- gl.filter.pa(testset.gl, pop1=pop(testset.gl)[1], pop2=pop(testset.gl)[2],verbose=3) +#' result <- gl.filter.pa(testset.gl, pop1=pop(testset.gl)[1], +#' pop2=pop(testset.gl)[2],verbose=3) gl.filter.pa <- function(x, pop1, @@ -52,14 +54,16 @@ gl.filter.pa <- function(x, p2 <- as.matrix(pops[[pop2]]) p1alf <- colMeans(p1, na.rm = T) / 2 p2alf <- colMeans(p2, na.rm = T) / 2 + # private alleles for pop 1 priv1 <- c(names(p1alf)[p2alf == 0 & p1alf != 0], names(p1alf)[p2alf == 1 & - p1alf != 1]) # private alleles for pop 1 + p1alf != 1]) + # private alleles for pop 2 priv2 <- c(names(p2alf)[p1alf == 0 & p2alf != 0], names(p2alf)[p1alf == 1 & - p2alf != 1]) # private alleles for pop 2 + p2alf != 1]) pfLoci <- unique(c(priv1, priv2)) # put all together index <- locNames(x) %in% pfLoci if (invers) diff --git a/R/gl.filter.parent.offspring.r b/R/gl.filter.parent.offspring.r index 0911ae55..0b6106e7 100644 --- a/R/gl.filter.parent.offspring.r +++ b/R/gl.filter.parent.offspring.r @@ -134,7 +134,8 @@ gl.filter.parent.offspring <- function(x, if (verbose >= 2) { cat( report( - " Generating null expectation for distribution of counts of pedigree incompatibility\n" + " Generating null expectation for distribution of counts of + pedigree incompatibility\n" ) ) } @@ -144,18 +145,21 @@ gl.filter.parent.offspring <- function(x, if (is.null(x@other$loc.metrics$RepAvg)) { cat( warn( - " Dataset does not include RepAvg among the locus metrics, therefore the reproducibility filter was not used\n" + " Dataset does not include RepAvg among the locus metrics, + therefore the reproducibility filter was not used\n" ) ) } else { x <- - gl.filter.reproducibility(x, threshold = min.reproducibility, verbose = 0) + gl.filter.reproducibility(x, threshold = min.reproducibility, + verbose = 0) } # Filter stringently on read depth, to further minimize miscalls if (is.null(x@other$loc.metrics$rdepth)) { cat( warn( - " Dataset does not include rdepth among the locus metrics, therefore the read depth filter was not used\n" + " Dataset does not include rdepth among the locus metrics, + therefore the read depth filter was not used\n" ) ) } else { @@ -187,12 +191,14 @@ gl.filter.parent.offspring <- function(x, if (verbose >= 2) { cat( report( - " Identifying outliers with lower than expected counts of pedigree inconsistencies\n" + " Identifying outliers with lower than expected counts of + pedigree inconsistencies\n" ) ) } title <- - paste0("SNP data (DArTSeq)\nCounts of pedigree incompatible loci per pair") + paste0("SNP data (DArTSeq)\nCounts of pedigree incompatible loci per + pair") counts_plot <- as.data.frame(counts) @@ -225,7 +231,7 @@ gl.filter.parent.offspring <- function(x, # if individuals in parent offspring relationship are found if (length(lower.extremes) > 0) { tmp <- count - tmp[lower.tri(tmp)] = t(tmp)[lower.tri(tmp)] + tmp[lower.tri(tmp)] <-t(tmp)[lower.tri(tmp)] for (i in 1:length(outliers$Outlier)) { # Identify tmp2 <- tmp[tmp == outliers$Outlier[i]] @@ -233,7 +239,8 @@ gl.filter.parent.offspring <- function(x, outliers$ind2[i] <- popNames(x)[!is.na(tmp2)][2] # Z-scores zscore <- - (mean(count, na.rm = TRUE) - outliers$Outlier[i]) / sd(count, na.rm = TRUE) + (mean(count, na.rm = TRUE) - outliers$Outlier[i]) / + sd(count, na.rm = TRUE) outliers$zscore[i] <- round(zscore, 2) outliers$p[i] <- round(pnorm( @@ -278,14 +285,19 @@ gl.filter.parent.offspring <- function(x, } ind_to_remove_temp <- - outliers[!duplicated(outliers[, c("ind1", "ind2")]), c("ind1", "ind2")] + outliers[!duplicated(outliers[, c("ind1", "ind2")]), + c("ind1", "ind2")] ind_to_remove <- vector() for (i in 1:nrow(ind_to_remove_temp)) { ind_to_remove_temp_2 <- unname(unlist(ind_to_remove_temp[i, ])) ind_1 <- - sum(glNA(hold[which(indNames(hold) == ind_to_remove_temp_2[1])], alleleAsUnit = FALSE)) + sum(glNA(hold[which(indNames(hold) == + ind_to_remove_temp_2[1])], + alleleAsUnit = FALSE)) ind_2 <- - sum(glNA(hold[which(indNames(hold) == ind_to_remove_temp_2[2])], alleleAsUnit = FALSE)) + sum(glNA(hold[which(indNames(hold) == + ind_to_remove_temp_2[2])], + alleleAsUnit = FALSE)) ind_to_remove_temp_3 <- as.data.frame(cbind(ind_to_remove_temp_2, c(ind_1, ind_2))) colnames(ind_to_remove_temp_3) <- @@ -305,7 +317,8 @@ gl.filter.parent.offspring <- function(x, } ind_to_remove_temp <- - outliers[!duplicated(outliers[, c("ind1", "ind2")]), c("ind1", "ind2")] + outliers[!duplicated(outliers[, c("ind1", "ind2")]), + c("ind1", "ind2")] ind_to_remove <- apply(ind_to_remove_temp, 1, function(x) { x[sample(1:2, 1)] @@ -323,7 +336,7 @@ gl.filter.parent.offspring <- function(x, # REPORT THE RESULTS if (verbose >= 2) { cat(" \nInitial number of individuals:", nInd(x), "\n") - cat(" Pairs of individuals in a parent offspring relationship:\n\n") + cat(" Pairs of individuals in a parent offspring relationship:\n\n") print(outliers) cat(" \nIndividuals removed: ") cat(ind_to_remove, sep = "\n") @@ -336,7 +349,9 @@ gl.filter.parent.offspring <- function(x, if (verbose > 0) { cat( important( - "No individuals were found to be in parent offspring relationship, therefore the genlight object is returned unchanged.\n" + "No individuals were found to be in parent offspring + relationship, therefore the genlight object is returned + unchanged.\n" ) ) } @@ -373,7 +388,8 @@ gl.filter.parent.offspring <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.rdepth.r b/R/gl.filter.rdepth.r index 2deb89d5..0b8506f1 100644 --- a/R/gl.filter.rdepth.r +++ b/R/gl.filter.rdepth.r @@ -49,7 +49,7 @@ #' #' @seealso \code{\link{gl.filter.rdepth}} #' -#' @family filters and filter reports +#' @family filter functions #' @import patchwork #' @export @@ -192,7 +192,8 @@ gl.filter.rdepth <- function(x, )) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.reproducibility.r b/R/gl.filter.reproducibility.r index 00c070ff..4f500bff 100644 --- a/R/gl.filter.reproducibility.r +++ b/R/gl.filter.reproducibility.r @@ -37,7 +37,7 @@ #' gl.report.reproducibility(testset.gs) #' result <- gl.filter.reproducibility(testset.gs, threshold=0.99) #' @seealso \code{\link{gl.report.reproducibility}} -#' @family filters and filter reports +#' @family filter functions #' @import patchwork #' @export @@ -65,7 +65,8 @@ gl.filter.reproducibility <- function(x, if (threshold < 0 | threshold > 1) { cat( warn( - " Warning: Threshold value for repeatability measure must be between 0 and 1, set to 0.99\n" + " Warning: Threshold value for repeatability measure must be + between 0 and 1, set to 0.99\n" ) ) threshold <- 0.99 @@ -74,7 +75,8 @@ gl.filter.reproducibility <- function(x, if (is.null(x@other$loc.metrics$Reproducibility)) { stop( error( - "Fatal Error: Dataset does not include Reproducibility among the locus metrics, cannot be calculated!" + "Fatal Error: Dataset does not include Reproducibility among + the locus metrics, cannot be calculated!" ) ) } @@ -83,7 +85,8 @@ gl.filter.reproducibility <- function(x, if (is.null(x@other$loc.metrics$RepAvg)) { stop( error( - "Fatal Error: Dataset does not include RepAvg among the locus metrics, cannot be calculated!" + "Fatal Error: Dataset does not include RepAvg among the + locus metrics, cannot be calculated!" ) ) } @@ -216,7 +219,8 @@ gl.filter.reproducibility <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.filter.secondaries.r b/R/gl.filter.secondaries.r index 03c18923..2777a34e 100644 --- a/R/gl.filter.secondaries.r +++ b/R/gl.filter.secondaries.r @@ -24,7 +24,7 @@ #' @examples #' gl.report.secondaries(testset.gl) #' result <- gl.filter.secondaries(testset.gl) -#' @family filters and filter reports +#' @family filter functions #' @importFrom stats dpois #' @import patchwork #' @export @@ -61,13 +61,15 @@ gl.filter.secondaries <- function(x, if (verbose > 1) { cat( report( - " Selecting one SNP per sequence tag based on best RepAvg and AvgPIC\n" + " Selecting one SNP per sequence tag based on best RepAvg + and AvgPIC\n" ) ) } loc.order <- order( - x@other$loc.metrics$AlleleID,-x@other$loc.metrics$RepAvg,-x@other$loc.metrics$AvgPIC + x@other$loc.metrics$AlleleID,-x@other$loc.metrics$RepAvg, + -x@other$loc.metrics$AvgPIC ) x <- x[, loc.order] x@other$loc.metrics <- x@other$loc.metrics[loc.order, ] @@ -83,7 +85,8 @@ gl.filter.secondaries <- function(x, # Extract the clone ID number a <- strsplit(as.character(x@other$loc.metrics$AlleleID), "\\|") b <- unlist(a)[c(TRUE, FALSE, FALSE)] - # Identify and remove secondaries from the genlight object, including the metadata + # Identify and remove secondaries from the genlight object, including the + #metadata x <- x[, duplicated(b) == FALSE] x@other$loc.metrics <- x@other$loc.metrics[duplicated(b) == FALSE, ] diff --git a/R/gl.filter.sexlinked.r b/R/gl.filter.sexlinked.r index dde0496b..89b721fc 100644 --- a/R/gl.filter.sexlinked.r +++ b/R/gl.filter.sexlinked.r @@ -105,7 +105,9 @@ gl.filter.sexlinked <- function(x, if (is.null(filter)) { stop( error( - "Filter option needs to be set to either 'keep' or 'drop'. Please refer to the help pages if in doubt. [?gl.filter.sexlinked]." + "Filter option needs to be set to either 'keep' or 'drop'. + Please refer to the help pages if in doubt. + [?gl.filter.sexlinked]." ) ) } @@ -122,7 +124,8 @@ gl.filter.sexlinked <- function(x, # DO THE JOB - # sex should be provided as it is not a default setting, if not provided it will be searched here: reproducibility + # sex should be provided as it is not a default setting, if not provided it + #will be searched here: reproducibility if (is.null(sex)) { sex <- x@other$ind.metrics$sex } @@ -130,7 +133,9 @@ gl.filter.sexlinked <- function(x, if (is.null(sex)) { stop( error( - "No definition for the sex of individuals is provided. If not provided via the function is needs to be at gl@other$ind.metrics$sex." + "No definition for the sex of individuals is provided. If not + provided via the function is needs to be at + gl@other$ind.metrics$sex." ) ) } @@ -138,7 +143,9 @@ gl.filter.sexlinked <- function(x, if (length(sex) != nInd(x)) { stop( error( - "The number of individuals and the number of entries defining the sex do not match. Check your genlight object and your sex defining column." + "The number of individuals and the number of entries defining + the sex do not match. Check your genlight object and your sex + defining column." ) ) } @@ -160,7 +167,8 @@ gl.filter.sexlinked <- function(x, # For each individual f <- array(data = NA, dim = c(ncol(matf), 3)) for (i in 1:ncol(matf)) { - # sum of genotypes 0 for homozygous for reference allele, 1 for heterozygous and 2 for homozygous for alternative allele + # sum of genotypes 0 for homozygous for reference allele, 1 for + #heterozygous and 2 for homozygous for alternative allele for (j in 1:3) { dummy <- sum(matf[, i] == (j - 1), na.rm = T) if (is.na(dummy)) @@ -170,7 +178,8 @@ gl.filter.sexlinked <- function(x, } dff <- data.frame(f) row.names(dff) <- locNames(x) - # genotypes 0 for homozygous for reference allele is in F0, 1 for heterozygous is in F1 and 2 for homozygous for alternative + # genotypes 0 for homozygous for reference allele is in F0, 1 for + #heterozygous is in F1 and 2 for homozygous for alternative # allele is in F2 colnames(dff) <- c("F0", "F1", "F2") @@ -188,7 +197,8 @@ gl.filter.sexlinked <- function(x, } dfm <- data.frame(m) row.names(dfm) <- locNames(x) - # genotypes 0 for homozygous for reference allele is in M0, 1 for heterozygous is in M1 and 2 for homozygous for alternative + # genotypes 0 for homozygous for reference allele is in M0, 1 for + #heterozygous is in M1 and 2 for homozygous for alternative # allele is in M2 colnames(dfm) <- c("M0", "M1", "M2") @@ -201,18 +211,21 @@ gl.filter.sexlinked <- function(x, # Check for hets in all males, homs in all females (XY); ditto for ZW sumf <- df$F0 + df$F1 + df$F2 summ <- df$M0 + df$M1 + df$M2 - # Pull loci that are 100% homozygous for females and 100% heterozygous for males + # Pull loci that are 100% homozygous for females and 100% heterozygous + #for males indexxy <- ((df$F0 / (sumf) >= (1 - t.hom) | df$F2 / (sumf) >= (1 - t.hom)) & df$M1 / (summ) >= (1 - t.het)) - # when all loci are homozygous for the reference allele e.g. df$F0/(sumf), the division is NaN. So, all the NaN's are set as + # when all loci are homozygous for the reference allele e.g. + #df$F0/(sumf), the division is NaN. So, all the NaN's are set as # TRUE indexxy[is.na(indexxy)] <- TRUE if (sum(indexxy, na.rm = T) > 0) { xy <- cbind(locnr = which(indexxy == TRUE), df[indexxy, ]) - # when F0, F1, F2 or M0, M1, M2 are all 0 due to NAs heterozygosity is NaN. these cases are removed + # when F0, F1, F2 or M0, M1, M2 are all 0 due to NAs heterozygosity + #is NaN. these cases are removed xy$fhet <- xy$F1 / (xy$F0 + xy$F1 + xy$F2) xy$mhet <- xy$M1 / (xy$M0 + xy$M1 + xy$M2) xy <- xy[complete.cases(xy), ] @@ -222,18 +235,21 @@ gl.filter.sexlinked <- function(x, xy <- data.frame() } - # Pull loci that are 100% homozygous for males and 100% heterozygous for females + # Pull loci that are 100% homozygous for males and 100% heterozygous for + #females indexzw <- ((df$M0 / (summ) >= (1 - t.hom) | df$M2 / (summ) >= (1 - t.hom)) & df$F1 / (sumf) >= (1 - t.het)) - # when all loci are homozygous for the reference allele e.g. df$M0/(summ), the division is NaN. So all the NaN's are set as + # when all loci are homozygous for the reference allele e.g. + #df$M0/(summ), the division is NaN. So all the NaN's are set as # TRUE indexzw[is.na(indexzw)] <- TRUE if (sum(indexzw, na.rm = T) > 0) { zw <- cbind(locnr = which(indexzw == TRUE), df[indexzw, ]) - # when F0, F1, F2 or M0, M1, M2 are all 0 due to NAs heterozygosity is NaN. these cases are removed + # when F0, F1, F2 or M0, M1, M2 are all 0 due to NAs heterozygosity + #is NaN. these cases are removed zw$fhet <- zw$F1 / (zw$F0 + zw$F1 + zw$F2) zw$mhet <- zw$M1 / (zw$M0 + zw$M1 + zw$M2) zw <- zw[complete.cases(zw), ] @@ -255,39 +271,42 @@ gl.filter.sexlinked <- function(x, if (nrow(zw) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" + " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" ) ) } else { if (verbose > 0) { - cat(" Sex linked loci consistent with female heterogamety (ZZ/ZW)\n\n") - cat( - paste( - " Threshold proportion for homozygotes in the heterozygotic sex (ZW) is", +cat(" Sex linked loci consistent with female heterogamety (ZZ/ZW)\n\n") +cat( +paste( +" Threshold proportion for homozygotes in the heterozygotic sex (ZW) is", t.hom, "\n" ) ) cat( paste( - " Threshold proportion for heterozygotes in the homozygotic sex (ZZ) is", +" Threshold proportion for heterozygotes in the homozygotic sex (ZZ) is", t.het, "\n\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat(" - F0 is the number of homozygous loci for the reference allele in females\n") - cat(" - F1 is the number of heterozygous loci in females\n") - cat(" - F2 is the number of homozygous loci for the alternative allele in females\n") - cat(" - M0 is the number of homozygous loci for the reference allele in males\n") - cat(" - M1 is the number of heterozygous loci in males\n") - cat(" - M2 is the number of homozygous loci for the alternative allele in males\n") - cat(" - fhet is heterozygosity in females\n") - cat(" - mhet is heterozygosity in males\n\n") - print(zw) - cat( - important( - " Note: The most reliable putative markers will have a read depth > 10.\n" +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of homozygous loci for the reference allele in + females\n") +cat("- F1 is the number of heterozygous loci in females\n") +cat("- F2 is the number of homozygous loci for the alternative allele in + females\n") +cat("- M0 is the number of homozygous loci for the reference allele in males\n") +cat("- M1 is the number of heterozygous loci in males\n") +cat("- M2 is the number of homozygous loci for the alternative allele in + males\n") +cat("- fhet is heterozygosity in females\n") +cat("- mhet is heterozygosity in males\n\n") +print(zw) +cat( +important( +" Note: The most reliable putative markers will have a read depth > 10.\n" ) ) } @@ -296,39 +315,42 @@ gl.filter.sexlinked <- function(x, if (nrow(xy) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with male heterogamety (XX/XY)\n" +" No sex linked markers consistent with male heterogamety (XX/XY)\n" ) ) } else { if (verbose > 0) { - cat(" Sex linked loci consistent with male heterogamety (XX/XY)\n\n") +cat(" Sex linked loci consistent with male heterogamety (XX/XY)\n\n") cat( paste( - " Threshold proportion for homozygotes in the heterozygotic sex (XY) is", +" Threshold proportion for homozygotes in the heterozygotic sex (XY) is", t.hom, "\n" ) ) cat( paste( - " Threshold proportion for heterozygotes in the homozygotic sex (XX) is", +" Threshold proportion for heterozygotes in the homozygotic sex (XX) is", t.het, "\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat(" - F0 is the number of homozygous loci for the reference allele in females\n") - cat(" - F1 is the number of heterozygous loci in females\n") - cat(" - F2 is the number of homozygous loci for the alternative allele in females\n") - cat(" - M0 is the number of homozygous loci for the reference allele in males\n") - cat(" - M1 is the number of heterozygous loci in males\n") - cat(" - M2 is the number of homozygous loci for the alternative allele in males\n") - cat(" - fhet is heterozygosity in females\n") - cat(" - mhet is heterozygosity in males\n") +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of homozygous loci for the reference allele in + females\n") +cat("- F1 is the number of heterozygous loci in females\n") +cat("- F2 is the number of homozygous loci for the alternative allele in + females\n") +cat("- M0 is the number of homozygous loci for the reference allele in males\n") +cat("- M1 is the number of heterozygous loci in males\n") +cat("- M2 is the number of homozygous loci for the alternative allele in + males\n") +cat("- fhet is heterozygosity in females\n") +cat("- mhet is heterozygosity in males\n") print(xy) cat( important( - " \nNote: The most reliable putative markers will have a read depth > 10.\n\n" +" \nNote: The most reliable putative markers will have a read depth > 10.\n\n" ) ) } @@ -380,7 +402,8 @@ gl.filter.sexlinked <- function(x, alpha = 1 / 3, size = 3, color = three_colors[2] - ) + xlab("Female Heterozygosity") + ylab("Male Heterozygosity") + xlim(0, 1) + ylim(0, 1) + + ) + xlab("Female Heterozygosity") + + ylab("Male Heterozygosity") + xlim(0, 1) + ylim(0, 1) + plot_theme suppressWarnings(print(gg)) @@ -435,7 +458,8 @@ gl.filter.sexlinked <- function(x, indexzw <- (df$F1 / (sumf) >= (1 - t.pres) & df$M0 / (summ) >= (1 - t.pres)) - # when all loci are homozygous for the reference allele e.g. df$M0/(summ), the division is NaN. So all the NAN's are set as TRUE + # when all loci are homozygous for the reference allele e.g. + #df$M0/(summ), the division is NaN. So all the NAN's are set as TRUE indexzw[is.na(indexzw)] <- TRUE if (sum(indexzw, na.rm = T) > 0) { @@ -450,7 +474,8 @@ gl.filter.sexlinked <- function(x, indexxy <- (df$M1 / (summ) >= (1 - t.pres) & df$F0 / (sumf) >= (1 - t.pres)) - # when all loci are homozygous for the reference allele e.g. df$F0/(sumf), the division is NaN. So, all the NaN's are set as TRUE + # when all loci are homozygous for the reference allele e.g. + #df$F0/(sumf), the division is NaN. So, all the NaN's are set as TRUE indexxy[is.na(indexxy)] <- TRUE if (sum(indexxy, na.rm = T) > 0) { @@ -464,18 +489,18 @@ gl.filter.sexlinked <- function(x, if (nrow(zw) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" + " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" ) ) } else { if (verbose > 0) { - cat("\n Sex linked loci consistent with female heterogamety (ZZ/ZW)\n") +cat("\n Sex linked loci consistent with female heterogamety (ZZ/ZW)\n") cat(paste( " Threshold proportion for presence/absence is", t.pres, "\n" )) - cat(" - locnr is the location of the locus in the input genlight object\n") + cat(" - locnr is the location of the locus in the input genlight object\n") cat(" - F0 is the number of loci absent in females\n") cat(" - F1 is the number of loci present in females\n") cat(" - M0 is the number of loci absent in males\n") @@ -483,7 +508,7 @@ gl.filter.sexlinked <- function(x, print(zw) cat( important( - " Note: The most reliable putative markers will have a read depth > 10.\n" + " Note: The most reliable putative markers will have a read depth > 10.\n" ) ) } @@ -492,18 +517,18 @@ gl.filter.sexlinked <- function(x, if (verbose > 0) cat( important( - " No sex linked markers consistent with male heterogamety (XX/XY)\n" +" No sex linked markers consistent with male heterogamety (XX/XY)\n" ) ) } else { if (verbose > 0) { - cat(" Sex linked loci consistent with male heterogamety (XX/XY)\n") +cat(" Sex linked loci consistent with male heterogamety (XX/XY)\n") cat(paste( " Threshold proportion for presence/absence is", t.pres, "\n" )) - cat(" - locnr is the location of the locus in the input genlight object\n") +cat(" - locnr is the location of the locus in the input genlight object\n") cat(" - F0 is the number of loci absent in females\n") cat(" - F1 is the number of loci present in females\n") cat(" - M0 is the number of loci absent in males\n") @@ -511,7 +536,7 @@ gl.filter.sexlinked <- function(x, print(xy) cat( important( - " Note: The most reliable putative markers will have a read depth > 10.\n" +" Note: The most reliable putative markers will have a read depth > 10.\n" ) ) } @@ -564,7 +589,8 @@ gl.filter.sexlinked <- function(x, alpha = 1 / 3, size = 3, color = three_colors[2] - ) + xlab("% present in females") + ylab("% present in males") + xlim(0, 1) + ylim(0, 1) + plot_theme + ) + xlab("% present in females") + + ylab("% present in males") + xlim(0, 1) + ylim(0, 1) + plot_theme suppressWarnings(print(gg)) } @@ -602,7 +628,8 @@ gl.filter.sexlinked <- function(x, if (verbose > 0) { cat( important( - " No sex-linked loci identified and filter option was 'keep', therefore NULL is returned.\n" +" No sex-linked loci identified and filter option was 'keep', therefore NULL is +returned.\n" ) ) } @@ -612,7 +639,8 @@ gl.filter.sexlinked <- function(x, if (verbose > 0) { cat( important( - " No sex-linked loci identified and filter option was 'drop', therefore the genlight object is returned unchanged.\n" + " No sex-linked loci identified and filter option was 'drop', therefore the + genlight object is returned unchanged.\n" ) ) } diff --git a/R/gl.filter.taglength.r b/R/gl.filter.taglength.r index 38a01a46..d212413f 100644 --- a/R/gl.filter.taglength.r +++ b/R/gl.filter.taglength.r @@ -18,6 +18,7 @@ #' @export #' @author Custodian: Arthur Georges -- Post to #' \url{https://groups.google.com/d/forum/dartr} +#' @family filter functions #' @examples #' # SNP data #' gl.report.taglength(testset.gl) @@ -49,14 +50,17 @@ gl.filter.taglength <- function(x, if (length(x@other$loc.metrics$TrimmedSequence) != nLoc(x)) { stop( error( - "Fatal Error: Data must include Trimmed Sequences for each loci in a column called 'TrimmedSequence' in the @other$loc.metrics slot.\n" + "Fatal Error: Data must include Trimmed Sequences for each loci + in a column called 'TrimmedSequence' in the @other$loc.metrics + slot.\n" ) ) } if (upper < lower) { cat( warn( - " Warning: Parameter 'upper' must be greater than parameter 'lower', swapping\n" + " Warning: Parameter 'upper' must be greater than parameter + 'lower', swapping\n" ) ) tmp <- upper @@ -66,7 +70,8 @@ gl.filter.taglength <- function(x, if (lower < 0 | lower > 250) { cat( warn( - " Warning: Parameter 'verbose' must be an integer between 0 and 250 , set to 20\n" + " Warning: Parameter 'verbose' must be an integer between 0 and + 250 , set to 20\n" ) ) lower <- 20 @@ -74,7 +79,8 @@ gl.filter.taglength <- function(x, if (upper < 0 | upper > 250) { cat( warn( - " Warning: Parameter 'upper' must be an integer between 0 and 250 , set to 69\n" + " Warning: Parameter 'upper' must be an integer between 0 and + 250 , set to 69\n" ) ) upper <- 69 diff --git a/R/gl.grm.network.r b/R/gl.grm.network.r index 880858fc..f0e5e313 100644 --- a/R/gl.grm.network.r +++ b/R/gl.grm.network.r @@ -11,32 +11,83 @@ #' \code{\link{gl.grm}} [required]. #' @param x A genlight object from which the G matrix was generated [required]. #' @param method One of 'fr', 'kk', 'gh' or 'mds' [default 'fr']. -#' @param node.size Size of the symbols for the network nodes [default 6]. +#' @param node.size Size of the symbols for the network nodes [default 8]. #' @param node.label TRUE to display node labels [default TRUE]. #' @param node.label.size Size of the node labels [default 3]. #' @param node.label.color Color of the text of the node labels #' [default 'black']. -#' @param relatedness_factor Factor of relatedness[default 0.5]. +#' @param link.color Color palette for links [default NULL]. +#' @param link.size Size of the links [default 2]. +#' @param relatedness_factor Factor of relatedness [default 0.125]. #' @param title Title for the plot #' [default 'Network based on genomic relationship matrix']. #' @param palette_discrete A discrete palette for the color of populations or a #' list with as many colors as there are populations in the dataset -#' [default discrete_palette]. +#' [default NULL]. #' @param save2tmp If TRUE, saves any ggplots and listings to the session #' temporary directory (tempdir) [default FALSE]. #' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, #' progress log ; 3, progress and results summary; 5, full report #' [default 2 or as specified using gl.set.verbosity]. +#' @details +#' The gl.grm.network function takes a genomic relationship matrix (GRM) +#' generated by the gl.grm function to represent the relationship among +#' individuals in the dataset as a network diagram. To generate the GRM, the +#' function gl.grm uses the function A.mat from package rrBLUP, which implements +#' the approach developed by Endelman and Jannink (2012). +#' +#' The GRM is an estimate of the proportion of alleles that two individuals have +#' in common. It is generated by estimating the covariance of the genotypes +#' between two individuals, i.e. how much genotypes in the two individuals +#' correspond with each other. This covariance depends on the probability that +#' alleles at a random locus are identical by state (IBS). Two alleles are +#' IBS if they represent the same allele. Two alleles are identical by +#' descent (IBD) if one is a physical copy of the other or if they are both +#' physical copies of the same ancestral allele. Note that IBD is complicated +#' to determine. IBD implies IBS, but not conversely. However, as the number +#' of SNPs in a dataset increases, the mean probability of IBS approaches +#' the mean probability of IBD. +#' +#' It follows that the off-diagonal elements of the GRM are two times the +#' kinship coefficient, i.e. the probability that two alleles at a random locus +#' drawn from two individuals are IBD. Additionally, the diagonal elements of +#' the GRM are 1+f, where f is the inbreeding coefficient of each individual, +#' i.e. the probability that the two alleles at a random locus are IBD. +#' +#' Choosing a meaningful threshold to represent the relationship between +#' individuals is tricky because IBD is not an absolute state but is relative to +#' a reference population for which there is generally little information so +#' that we can estimate the kinship of a pair of individuals only relative to +#' some other quantity. To deal with this, we can use the average inbreeding +#' coefficient of the diagonal elements as the reference value. For this, the +#' function subtracts 1 from the mean of the diagonal elements of the GRM. In a +#' second step, the off-diagonal elements are divided by 2, and finally, the +#' mean of the diagonal elements is subtracted from each off-diagonal element +#' after dividing them by 2. This approach is similar to the one used by +#' Goudet et al. (2018). +#' +#' Below is a table modified from Speed & Balding (2015) showing kinship values, +#' and their confidence intervals (CI), for different relationships that could +#' be used to guide the choosing of the relatedness threshold in the function. #' -#' @details -#' As identity by descent is not an absolute state, but is relative to a -#' reference population for which there is generally little information, we can -#' estimate the kinship of a pair of individuals only relative to some other -#' quantity (Goudet et al., 2018). In this script, we use the average inbreeding -#' coefficient (1-f) of the diagonal elements as the reference value. This -#' reference value is then subtracted from the inbreeding coefficient of each -#' pair of distinct individuals. This approach is similar to the used by Goudet -#' et al. (2018). +#'|Relationship|Kinship|95% CI| +#'|Identical twins/clones/same individual | 0.5 | - | +#' +#'|Sibling/Parent-Offspring | 0.25 | (0.204, 0.296)| +#' +#'|Half-sibling | 0.125 | (0.092, 0.158)| +#' +#'|First cousin | 0.062 | (0.038, 0.089)| +#' +#'|Half-cousin | 0.031 | (0.012, 0.055)| +#' +#'|Second cousin | 0.016 | (0.004, 0.031)| +#' +#'|Half-second cousin | 0.008 | (0.001, 0.020)| +#' +#'|Third cousin | 0.004 | (0.000, 0.012)| +#' +#'|Unrelated | 0 | - | #' #' Four layout options are implemented in this function: #'\itemize{ @@ -49,31 +100,25 @@ #' (package igraph) #' } #' -#' The threshold for relatedness to be represented as a link in the network is -#' specified as a quantile. Those relatedness measures above the quantile are -#' plotted as links, those below the quantile are not. Often you are looking -#' for relatedness outliers in comparison with the overall relatedness among -#' individuals, so a very conservative quantile is used (e.g. 0.004), but -#' ultimately, this decision is made as a matter of trial and error. One way to -#' approach this trial and error is to try to achieve a sparse set of links -#' between unrelated 'background' individuals so that the stronger links are -#' preferentially shown. -#' #' @return A network plot showing relatedness between individuals #' @author Custodian: Arthur Georges -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @examples -#' gl_test <- bandicoot.gl -#' # five populations in gl_test -#' nPop(gl_test) -#' # color list for population colors -#' pop_colors <- c('deepskyblue','green','gray','orange','deeppink') -#' G_out <- gl.grm(gl_test,plotheatmap=FALSE) -#' gl.grm.network(G_out, gl_test, palette_discrete = pop_colors, relatedness_factor = 0.25) +#' t1 <- possums.gl +#' # filtering on call rate +#' t1 <- gl.filter.callrate(t1) +#' # relatedness matrix +#' res <- gl.grm(t1,plotheatmap = FALSE) +#' # relatedness network +#' res2 <- gl.grm.network(res,t1,relatedness_factor = 0.125) #'@references #'\itemize{ +#'\item Endelman, J. B. , Jannink, J.-L. (2012). Shrinkage estimation of the +#'realized relationship matrix. G3: Genes, Genomics, Genetics 2, 1405. #'\item Goudet, J., Kay, T., & Weir, B. S. (2018). How to estimate kinship. #'Molecular Ecology, 27(20), 4121-4135. +#'\item Speed, D., & Balding, D. J. (2015). Relatedness in the post-genomic era: +#'is it still useful?. Nature Reviews Genetics, 16(1), 33-44. #' } #' @seealso \code{\link{gl.grm}} #' @family inbreeding functions @@ -82,13 +127,15 @@ gl.grm.network <- function(G, x, method = "fr", - node.size = 6, + node.size = 8, node.label = TRUE, node.label.size = 2, node.label.color = "black", - relatedness_factor = 0.25, - title = "Network based on a genomic relationship matrix", - palette_discrete = discrete_palette, + link.color = NULL, + link.size = 2, + relatedness_factor = 0.125, + title = "Network based on a genomic relationship matrix", + palette_discrete = NULL, save2tmp = FALSE, verbose = NULL) { # SET VERBOSITY @@ -103,13 +150,15 @@ gl.grm.network <- function(G, # CHECK DATATYPE datatype <- utils.check.datatype(x, verbose = verbose) - # FUNCTION SPECIFIC ERROR CHECKING Set a population if none is specified (such as if the genlight object has been generated manually) + # FUNCTION SPECIFIC ERROR CHECKING Set a population if none is specified + # (such as if the genlight object has been generated manually) if (is.null(pop(x)) | is.na(length(pop(x))) | length(pop(x)) <= 0) { if (verbose >= 2) { cat( important( - " Population assignments not detected, individuals assigned to a single population labelled 'pop1'\n" + " Population assignments not detected, individuals assigned + to a single population labelled 'pop1'\n" ) ) } @@ -131,34 +180,48 @@ gl.grm.network <- function(G, method == "kk" || method == "gh" || method == "mds")) { cat(warn( - "Warning: Layout method must be one of fr, or kk, gh or mds, set to fr\n" + "Warning: Layout method must be one of fr, or kk, gh or mds, set to fr\n" )) method <- "fr" } # DO THE JOB G2 <- G - G2[upper.tri(G2, diag = T)] <- NA + G2[upper.tri(G2, diag = TRUE)] <- NA links <- as.data.frame(as.table(G2)) links <- links[which(!is.na(links$Freq)), ] colnames(links) <- c("from", "to", "weight") - # using the average inbreeding coefficient (1-f) of the diagonal elements as the reference value + # using the average inbreeding coefficient (1-f) of the diagonal elements as + #the reference value MS <- mean(diag(G) - 1) + # the result of the GRM is the summation of the IBD of each allele . links$kinship <- (links$weight / 2) - MS - nodes <- - data.frame(cbind(x$ind.names, as.character(pop(x)))) + links_tmp <- links[,c(1,2,4)] + links_tmp <- rbind(links_tmp,cbind(from=indNames(x), + to=indNames(x),kinship=0)) + links_matrix <- as.matrix(reshape2::acast(links_tmp, + from~to, value.var="kinship")) + links_matrix <- apply(links_matrix, 2, as.numeric) + rownames(links_matrix) <- colnames(links_matrix) + + links_plot_tmp <- links_tmp[links_tmp$kinship>relatedness_factor,] + links_plot_2 <- links_plot_tmp[,c("from","kinship")] + colnames(links_plot_2) <- c("label.node","kinship") + links_plot_3 <- links_plot_tmp[,c("to","kinship")] + colnames(links_plot_3) <- c("label.node","kinship") + links_plot <- rbind(links_plot_2,links_plot_3) + + nodes <- data.frame(cbind(x$ind.names, as.character(pop(x)))) colnames(nodes) <- c("name", "pop") - network <- - igraph::graph_from_data_frame(d = links, + network <- igraph::graph_from_data_frame(d = links, vertices = nodes, directed = FALSE) - # q <- stats::quantile(links$weight, p = 1-alpha) network.FS <- igraph::delete_edges(network, igraph::E(network)[links$weight < q ]) q <- relatedness_factor network.FS <- igraph::delete_edges(network, igraph::E(network)[links$kinship < q]) @@ -189,7 +252,8 @@ gl.grm.network <- function(G, # get edges, which are pairs of node IDs edgelist <- igraph::get.edgelist(network.FS, names = F) - # convert to a four column edge data frame with source and destination coordinates + # convert to a four column edge data frame with source and destination + # coordinates edges <- data.frame(plotcord[edgelist[, 1], ], plotcord[edgelist[, 2], ]) # using kinship for the size of the edges @@ -207,7 +271,16 @@ gl.grm.network <- function(G, plotcord <- merge(plotcord, pop_df, by = "label.node") plotcord$pop <- as.factor(plotcord$pop) + plotcord <- merge(plotcord,links_plot,by="label.node",all.x = TRUE) + plotcord[is.na(plotcord$kinship),"kinship"] <- 0 + plotcord$kinship <- as.numeric(plotcord$kinship) + plotcord$kinship <- scales::rescale(plotcord$kinship, to = c(0.1, 1)) + # assigning colors to populations + if(is.null(palette_discrete)){ + palette_discrete <- discrete_palette + } + if (is(palette_discrete, "function")) { colors_pops <- palette_discrete(length(levels(pop(x)))) } @@ -216,27 +289,29 @@ gl.grm.network <- function(G, colors_pops <- palette_discrete } - names(colors_pops) <- as.character(levels(x$pop)) + if(is.null(link.color)){ + link.color <- diverging_palette + } + names(colors_pops) <- as.character(levels(x$pop)) + pal <- link.color(10) + size <- NULL p1 <- - ggplot() + geom_segment(data = edges, - aes( - x = X1, - y = Y1, - xend = X2, - yend = Y2 - ), - size = 1.5) + geom_point(data = plotcord, - aes(x = X1, - y = X2, color = pop), - size = node.size) + coord_fixed(ratio = 1) + theme_void() + ggtitle(paste(title, "\n[", layout.name, "]")) + theme( - legend.position = "bottom", - plot.title = element_text( - hjust = 0.5, - face = "bold", - size = 14 - ) - ) + scale_color_manual(name = "Populations", values = colors_pops) + ggplot() + + geom_segment(data = edges, + aes( x = X1, y = Y1, xend = X2,yend = Y2,color = size), + size = link.size) + + scale_colour_gradientn(name = "Relatedness",colours = pal) + + geom_point(data = plotcord,aes(x = X1,y = X2, fill = pop), + pch = 21, + size = node.size, + alpha=plotcord$kinship) + + scale_fill_manual(name = "Populations", values = colors_pops)+ + coord_fixed(ratio = 1) + + theme_void() + + ggtitle(paste(title, "\n[", layout.name, "]")) + + theme(legend.position = "bottom", + plot.title = element_text( hjust = 0.5, face = "bold",size = 14)) if (node.label == T) { p1 <- @@ -271,7 +346,8 @@ gl.grm.network <- function(G, cat(report(" Saving the ggplot to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } @@ -285,6 +361,6 @@ gl.grm.network <- function(G, # RETURN - invisible(p1) + return(invisible(list(p1,links_matrix))) } diff --git a/R/gl.grm.r b/R/gl.grm.r index 8813a1c1..5480da06 100644 --- a/R/gl.grm.r +++ b/R/gl.grm.r @@ -1,7 +1,7 @@ #' @name gl.grm #' @title Calculates an identity by descent matrix #' @description -#' This function calculates the mean probability of identity by descent (IBD) +#' This function calculates the mean probability of identity by state (IBS) #' across loci that would result from all the possible crosses of the #' individuals analyzed. IBD is calculated by an additive relationship matrix #' approach developed by Endelman and Jannink (2012) as implemented in the @@ -59,7 +59,7 @@ gl.grm <- function(x, palette_discrete = discrete_palette, palette_convergent = convergent_palette, legendx = 0, - legendy = 1, + legendy = 0.5, verbose = NULL, ...) { # SET VERBOSITY diff --git a/R/gl.ibd.r b/R/gl.ibd.r index ec117e99..b907a720 100644 --- a/R/gl.ibd.r +++ b/R/gl.ibd.r @@ -18,7 +18,7 @@ #' 'D' [\link[StAMPP]{stamppNeisD}] or individual based 'propShared', #' [gl.propShared], 'euclidean' [gl.dist.ind, method='Euclidean'] [default "Fst"]. #' @param coordinates Can be either 'latlon', 'xy' or a two column data.frame -#' with column names 'lat','lon', 'x', 'y') Coordinates are provided via +#' with column names 'lat','lon', 'x', 'y'). Coordinates are provided via #' \code{gl@other$latlon} ['latlon'] or via \code{gl@other$xy} ['xy']. If latlon #' data will be projected to meters using Mercator system [google maps] or if #' xy then distance is directly calculated on the coordinates. @@ -26,16 +26,12 @@ #' [default NULL]. #' @param Dgeo Euclidean distance matrix if no genlight object is provided #' [default NULL]. -#' @param Dgeo_trans Transformation to be used on the Euclidean distances. see -#' Dgen_trans [default 'log(Dgeo)']. +#' @param Dgeo_trans Transformation to be used on the Euclidean distances. See +#' Dgen_trans [default "Dgeo"]. #' @param Dgen_trans You can provide a formula to transform the genetic -#' distance. For example Rousset (see below) suggests to study -#' \code{Fst/(1-Fst)} against log transformed distances as this is the -#' expectations of Fst versus distances in the case of a stepping stone model. -#' The transformation can be applied as a formula using Dgen as the variable to -#' be transformed. So, for the Fst transformation of Rousset use -#' \code{Dgen_trans = 'Dgen/(1-Dgen)'. Any valid R expression can be used here. -#' [default 'Dgen', which is the identity function.]} +#' distance. The transformation can be applied as a formula using Dgen as the +#' variable to be transformed. For example: \code{Dgen_trans = 'Dgen/(1-Dgen)'. +#' Any valid R expression can be used here [default 'Dgen', which is the identity function.]} #' @param permutations Number of permutations in the Mantel test [default 999]. #' @param plot.out Should an isolation by distance plot be returned #' [default TRUE]. @@ -95,7 +91,7 @@ gl.ibd <- function(x = NULL, coordinates = "latlon", Dgen = NULL, Dgeo = NULL, - Dgeo_trans = "log(Dgeo)", + Dgeo_trans = "Dgeo", Dgen_trans = "Dgen", permutations = 999, plot.out = TRUE, @@ -106,7 +102,7 @@ gl.ibd <- function(x = NULL, # CHECK IF PACKAGES ARE INSTALLED if (!(requireNamespace("dismo", quietly = TRUE))) { stop(error( - "Package dismo needed for this function to work. Please install it." + "Package dismo needed for this function to work. Please install it.\n" )) } else { @@ -117,8 +113,9 @@ gl.ibd <- function(x = NULL, verbose <- gl.check.verbosity(verbose) - if (!is.null(x)) + if (!is.null(x)){ dt <- utils.check.datatype(x, verbose = 0) + } # specific error checks @@ -126,16 +123,16 @@ gl.ibd <- function(x = NULL, if (verbose > 0) cat( report( - "Analysis performed using provided genetic and Euclidean distance matrices. If a genlight object is provided, it is ignored." + "Analysis performed using provided genetic and Euclidean distance matrices. If a genlight object is provided, it is ignored.\n" ) ) - ta = "dgendgeo" + ta <-"dgendgeo" } if (is(x, "genlight")) { if (verbose > 0) cat(report("Analysis performed on the genlight object.\n")) - ta = "genlight" + ta <-"genlight" } # check coordinates (if no Dgen and Dgeo is provided) @@ -145,7 +142,7 @@ gl.ibd <- function(x = NULL, if (coordinates == "latlon") { if (is.null(x@other$latlon)) stop(error( - "Cannot find coordinates in x@other$latlon" + "Cannot find coordinates in x@other$latlon.\n" )) coords <- dismo::Mercator(x@other$latlon[, c("lon", "lat")]) @@ -156,14 +153,14 @@ gl.ibd <- function(x = NULL, ) ) } - coordstring = "x@other$latlon (Mercator transformed)" + coordstring <-"x@other$latlon (Mercator transformed)" } if (coordinates == "xy") { if (is.null(x@other$xy)) - stop(error("Cannot find coordinates in x@other$xy")) + stop(error("Cannot find coordinates in x@other$xy.\n")) coords <- x@other$xy - coordstring = "x@other$xy" + coordstring <-"x@other$xy" } } @@ -171,47 +168,51 @@ gl.ibd <- function(x = NULL, if (is(coordinates, "data.frame")) { if (length(setdiff(colnames(coordinates), c("lat", "lon"))) == 0) { coords <- dismo::Mercator(coordinates[, c("lon", "lat")]) - coordstring = "data.frame lat/lon (Mercator transformed)" + coordstring <-"data.frame lat/lon (Mercator transformed)" } if (length(setdiff(colnames(coordinates), c("x", "y"))) == 0) { coords <- coordinates[, c("x", "y")] - coordstring = "data.frame x/y" + coordstring <-"data.frame x/y" } - if (is.null(coords)) + if (is.null(coords)){ stop( error( - "No valid coordinates provided. check the provided data.frame and its format." + "No valid coordinates provided. check the provided data.frame and its format.\n" ) ) } + } - if (is.null(coords)) - stop(error("No valid coordinates provided!")) + if (is.null(coords)){ + stop(error("No valid coordinates provided!\n")) + } # make sure coordinates have the correct length - if (nrow(coords) != nInd(x) & ta == "genlight") + if (nrow(coords) != nInd(x) & ta == "genlight"){ stop(error( - "Cannot find coordinates for each individual in slot @other$latlon" + "Cannot find coordinates for each individual in slot @other$latlon.\n" )) + } - typedis = NULL + typedis <-NULL if (distance == "Fst" | distance == "D") { - typedis = "pop" + typedis <-"pop" } if (distance == "propShared" | distance == "euclidean") { - typedis = "ind" + typedis <-"ind" } - if (typedis == "pop" & nPop(x) < 2) + if (typedis == "pop" & nPop(x) < 2){ stop( error( - "You specified a population based distance, but there is either no population or only a single population specified within your genlight object. Check via table(pop(genlight))." + "You specified a population based distance, but there is either no population or only a single population specified within your genlight object. Check via table(pop(genlight)).\n" ) ) + } if (is.null(Dgeo) & typedis == "pop") { if (nPop(x) > 1) { @@ -222,7 +223,7 @@ gl.ibd <- function(x = NULL, } else { stop( error( - "Less than 2 populations provided, therefore no pairwise distances can be calculated" + "Less than 2 populations provided, therefore no pairwise distances can be calculated.\n" ) ) } @@ -234,7 +235,7 @@ gl.ibd <- function(x = NULL, } else { stop( error( - "Less than 2 individuals provided, therefore no pairwise distances can be calculated" + "Less than 2 individuals provided, therefore no pairwise distances can be calculated.\n" ) ) } @@ -273,9 +274,9 @@ gl.ibd <- function(x = NULL, } } else { # end of ta=='genlight' ta='dgendgeo - coordstring = "Dgeo provided." - distance = "Dgen provided" - typedis = "ind" + coordstring <-"Dgeo provided." + distance <-"Dgen provided" + typedis <-"ind" } # make sure both matrices are distance objects if provided via Dgen and Dgeo directly Dgen <- as.dist(Dgen) @@ -288,7 +289,7 @@ gl.ibd <- function(x = NULL, if (sum(is.infinite(Dgeo)) > 0) { stop( error( - "Most likely some pairwise individual distances were zero and the transformation created missing values [e.g. log(Dgeo)]. This affects the Mantel test and points are omitted from the plot. Consider adding a suitable tranformation e.g. an offset to your Dgeo transformation if using a log transformation [e.g. Dgeo_trans='log(Dgeo+1)'] or adding some 'noise' to the coordinates." + "Most likely some pairwise individual distances were zero and the transformation created missing values [e.g. log(Dgeo)]. This affects the Mantel test and points are omitted from the plot. Consider adding a suitable tranformation e.g. an offset to your Dgeo transformation if using a log transformation [e.g. Dgeo_trans='log(Dgeo+1)'] or adding some 'noise' to the coordinates.\n" ) ) @@ -296,12 +297,12 @@ gl.ibd <- function(x = NULL, if (is.null(Dgeo)) stop(error( - "Cannot calculate distance matrix or no distance matrix provided!" + "Cannot calculate distance matrix or no distance matrix provided\n!" )) if (is.null(Dgen)) stop( error( - "Cannot calculate genetic distance matrix or no genetic distance matrix provided!" + "Cannot calculate genetic distance matrix or no genetic distance matrix provided!\n" ) ) @@ -333,8 +334,13 @@ gl.ibd <- function(x = NULL, res <- data.frame(Dgen = as.numeric(Dgen), Dgeo = as.numeric(Dgeo)) if (is.null(paircols)) { + p3 <- - ggplot(res, aes(x = Dgeo, y = Dgen)) + geom_point() + geom_smooth(method = "lm", se = TRUE) + ylab(Dgen_trans) + xlab(Dgeo_trans) + + ggplot(res, aes(x = Dgeo, y = Dgen)) + + geom_point() + + geom_smooth(method = "lm", se = TRUE) + + ylab(Dgen_trans) + + xlab(Dgeo_trans) + annotate( "text", label = lm_eqn(res), @@ -342,12 +348,12 @@ gl.ibd <- function(x = NULL, y = -Inf, parse = TRUE, hjust = 1.05, - vjust = 0 - ) + plot_theme + vjust = 0) + + plot_theme + } else { Legend <- col2 <- NA #ggplot bug - cols <- - which(lower.tri(as.matrix(Dgen)), arr.ind = T) + cols <- which(lower.tri(as.matrix(Dgen)), arr.ind = T) c1 <- cols[, 2] c2 <- cols[, 1] cn <- colnames(as.matrix(Dgen)) @@ -356,7 +362,7 @@ gl.ibd <- function(x = NULL, if (is(x, "genlight")) cn <- pop(x) else - cn = rownames(as.matrix(Dgen)) + cn <-rownames(as.matrix(Dgen)) } res <- data.frame( @@ -366,22 +372,20 @@ gl.ibd <- function(x = NULL, col2 = cn[c2] ) p3 <- - ggplot(res) + geom_point(aes(Dgeo, Dgen, col = Legend), size = 5) + geom_point(aes(Dgeo, Dgen, col = col2), size = 2) + geom_point(aes(Dgeo, - Dgen), - size = 2, - shape = 1) + guides(size = "none", - color = guide_legend(title = "Populations")) + geom_smooth(aes(x = Dgeo, - y = Dgen), - method = "lm", - se = TRUE) + ylab(Dgen_trans) + annotate( - "text", - label = lm_eqn(res), - x = Inf, - y = -Inf, - parse = TRUE, - hjust = 1.05, - vjust = 0 - ) + xlab(Dgeo_trans) + plot_theme + ggplot(res) + + geom_point(aes(Dgeo, Dgen, col = Legend), size = 5) + + geom_point(aes(Dgeo, Dgen, col = col2), size = 2) + + geom_point(aes(Dgeo, Dgen),size = 2,shape = 1) + + guides(size = "none",color = guide_legend(title = "Populations")) + + geom_smooth(aes(x = Dgeo, y = Dgen),method = "lm", se = TRUE) + + ylab(Dgen_trans) + + annotate("text",label = lm_eqn(res), + x = Inf, + y = -Inf, + parse = TRUE, + hjust = 1.05, + vjust = 0) + + xlab(Dgeo_trans) + plot_theme } diff --git a/R/gl.install.vanilla.dartR.r b/R/gl.install.vanilla.dartR.r index d372990d..1b374695 100644 --- a/R/gl.install.vanilla.dartR.r +++ b/R/gl.install.vanilla.dartR.r @@ -203,9 +203,9 @@ gl.install.vanilla.dartR <- function(flavour = NULL, if (verbose>= 2 & is.null(err) & !is.null(flavour)) { if (flavour != "CRAN") { - fl = paste0("Github [", flavour, "]") + fl <-paste0("Github [", flavour, "]") } else { - fl = "CRAN" + fl <-"CRAN" cat(report( paste( " You have installed dartR", diff --git a/R/gl.ld.distance.r b/R/gl.ld.distance.r new file mode 100644 index 00000000..48f015b8 --- /dev/null +++ b/R/gl.ld.distance.r @@ -0,0 +1,179 @@ +#' @name gl.ld.distance +#' @title Plots linkage disequilibrium against distance by population +#' disequilibrium patterns +#' @description +#' The function creates a plot showing +#' the pairwise LD measure against distance in number of base pairs pooled over +#' all the chromosomes and a red line representing the threshold (R.squared = +#' 0.2) that is commonly used to imply that two loci are unlinked (Delourme et +#' al., 2013; Li et al., 2014). +#' @param ld_report Output from function \code{\link{gl.report.ld.map}} +#' [required]. +#' @param ld_resolution Resolution at which LD should be reported in number of +#' base pairs [default NULL]. +#' @param pop_colors A color palette for box plots by population or a list +#' with as many colors as there are populations in the dataset +#' [default NULL]. +#' @param plot_theme User specified theme [default NULL]. +#' @param plot.out Specify if plot is to be produced [default TRUE]. +#' @param save2tmp If TRUE, saves any ggplots and listings to the session +#' temporary directory (tempdir) [default FALSE]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @references +#' \itemize{ +#' \item Delourme, R., Falentin, C., Fomeju, B. F., Boillot, M., Lassalle, G., +#' André, I., . . . Marty, A. (2013). High-density SNP-based genetic map +#' development and linkage disequilibrium assessment in Brassica napusL. BMC +#' genomics, 14(1), 120. +#' \item Li, X., Han, Y., Wei, Y., Acharya, A., Farmer, A. D., Ho, J., . . . +#' Brummer, E. C. (2014). Development of an alfalfa SNP array and its use to +#' evaluate patterns of population structure and linkage disequilibrium. PLoS +#' One, 9(1), e84329. +#' } +#' @return A dataframe with information of LD against distance by population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' x <- platypus.gl +#' x <- gl.filter.callrate(x,threshold = 1) +#' x$chromosome <- as.factor(x$other$loc.metrics$Chrom_Platypus_Chrom_NCBIv1) +#' x$position <- x$other$loc.metrics$ChromPos_Platypus_Chrom_NCBIv1 +#' ld_res <- gl.report.ld.map(x,ld_max_pairwise = 10000000) +#' ld_res_2 <- gl.ld.distance(ld_res,ld_resolution=1000000) +#' @family ld functions +#' @export + +gl.ld.distance <- function(ld_report, + ld_resolution = 100000, + pop_colors = NULL, + plot_theme = NULL, + plot.out = TRUE, + save2tmp = FALSE, + verbose = NULL){ + + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # DO THE JOB + + ld_max_pairwise <- max(ld_report$distance) + break_bins <- c(seq(1, ld_max_pairwise, ld_resolution), ld_max_pairwise) + + split_df <- split(ld_report, f = ld_report$pop) + split_df <- lapply(split_df, function(x) { + x[order(x$distance), ] + }) + bins_ld_temp <- + lapply(split_df, function(x) { + fields::stats.bin(x$distance, x$ld_stat, breaks = break_bins) + }) + bins_ld <- + lapply(seq_along(bins_ld_temp), function(i) { + as.data.frame(cbind( + names(bins_ld_temp[i]), + unname(bins_ld_temp[[i]]$breaks[2:length(bins_ld_temp[[i]]$breaks)]), + unname(bins_ld_temp[[i]]$stats[2, ]) + )) + }) + bins_ld <- rbindlist(bins_ld) + colnames(bins_ld) <- c("pop", "distance", "ld_stat") + bins_ld$pop <- as.factor(bins_ld$pop) + bins_ld$distance <- as.numeric(bins_ld$distance) + bins_ld$ld_stat <- as.numeric(bins_ld$ld_stat) + + # pairwise LD by population + + pops <- as.factor(unique(ld_report$pop)) + + if(is.null(plot_theme)){ + plot_theme = theme_dartR() + } + + if(is.null(pop_colors)){ + pop_colors <- discrete_palette(length(levels(pops))) + } + + if (is(pop_colors, "function")) { + pop_colors <- pop_colors(length(levels(pops))) + } + + if (!is(pop_colors,"function")) { + pop_colors <- pop_colors + } + + distance <- ld_stat <- NULL + p3 <- + ggplot(bins_ld, aes(x = distance, y = ld_stat, colour = pop)) + + geom_line(size = 1) + + geom_point(size = 2) + + geom_hline(aes(yintercept = 0.2, + colour = "LD threshold for unlinked loci"),color="red", + size = 1) + + xlab("Base pairs") + + ylab("Linkage disequilibrium") + + labs(color = "") + + scale_color_manual(values = pop_colors) + + plot_theme + + theme(legend.position = "bottom") + + + # PRINTING OUTPUTS + if(plot.out){ + print(p3) + } + print(bins_ld,row.names = FALSE) + + # SAVE INTERMEDIATES TO TEMPDIR creating temp file names + if (save2tmp) { + if (plot.out) { + temp_plot <- tempfile(pattern = "Plot_") + match_call <- + paste0(names(match.call()), + "_", + as.character(match.call()), + collapse = "_") + # saving to tempdir + saveRDS(list(match_call, p3), file = temp_plot) + if (verbose >= 2) { + cat(report(" Saving the ggplot to session tempfile\n")) + } + } + temp_table <- tempfile(pattern = "Table_") + saveRDS(list(match_call, bins_ld), file = temp_table) + if (verbose >= 2) { + cat(report(" Saving tabulation to session tempfile\n")) + cat( + report( + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" + ) + ) + } + } + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + return(invisible(bins_ld)) + +} + + + + + + + diff --git a/R/gl.ld.haplotype.r b/R/gl.ld.haplotype.r new file mode 100644 index 00000000..93972797 --- /dev/null +++ b/R/gl.ld.haplotype.r @@ -0,0 +1,715 @@ +#' @name gl.ld.haplotype +#' @title Visualize patterns of linkage disequilibrium and identification of +#' haplotypes +#' @description +#' This function plots a Linkage disequilibrium (LD) heatmap, where the colour +#' shading indicates the strength of LD. Chromosome positions (Mbp) are shown on +#' the horizontal axis, and haplotypes appear as triangles and delimited by +#' dark yellow vertical lines. Numbers identifying each haplotype are shown in +#' the upper part of the plot. +#' +#' The heatmap also shows heterozygosity for each SNP. +#' +#' The function identifies haplotypes based on contiguous SNPs that are in +#' linkage disequilibrium using as threshold \code{ld_threshold_haplo} and +#' containing more than \code{min_snps} SNPs. +#' +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param pop_name Name of the population to analyse. If NULL all the +#' populations are analised [default NULL]. +#' @param chrom_name Nme of the chromosome to analyse. If NULL all the +#' chromosomes are analised [default NULL]. +#' @param ld_max_pairwise Maximum distance in number of base pairs at which LD +#' should be calculated [default 10000000]. +#' @param maf Minor allele frequency (by population) threshold to filter out +#' loci. If a value > 1 is provided it will be interpreted as MAC (i.e. the +#' minimum number of times an allele needs to be observed) [default 0.05]. +#' @param ld_stat The LD measure to be calculated: "LLR", "OR", "Q", "Covar", +#' "D.prime", "R.squared", and "R". See \code{\link[snpStats]{ld}} +#' (package snpStats) for details [default "R.squared"]. +#' @param ind.limit Minimum number of individuals that a population should +#' contain to take it in account to report loci in LD [default 10]. +#' @param min_snps Minimum number of SNPs that should have a haplotype to call +#' it [default 10]. +#' @param ld_threshold_haplo Minimum LD between adjacent SNPs to call a +#' haplotype [default 0.5]. +#' @param coordinates A vector of two elements with the start and end +#' coordinates in base pairs to which restrict the +#' analysis e.g. c(1,1000000) [default NULL]. +#' @param color_haplo Color palette for haplotype plot. See details +#' [default "viridis"]. +#' @param color_het Color for heterozygosity [default "deeppink"]. +#' @param plot.out Specify if heatmap plot is to be produced [default TRUE]. +#' @param save2tmp If TRUE, saves any ggplots and listings to the session +#' temporary directory (tempdir) [default FALSE]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' +#' @details +#' The information for SNP's position should be stored in the genlight accessor +#' "@@position" and the SNP's chromosome name in the accessor "@@chromosome" +#' (see examples). The function will then calculate LD within each chromosome. +#' +#' The output of the function includes a table with the haplotypes +#' that were identified and their location. +#' +#' Colors of the heatmap (\code{color_haplo}) are based on the function +#' \code{\link[viridis]{scale_fill_viridis}} from package \code{viridis}. +#' Other color palettes options are "magma", "inferno", "plasma", "viridis", +#' "cividis", "rocket", "mako" and "turbo". +#' @return A table with the haplotypes that were identified. +#' @family ld functions +#' @examples +#' x <- platypus.gl +#' x <- gl.filter.callrate(x,threshold = 1) +#' x$chromosome <- as.factor(x$other$loc.metrics$Chrom_Platypus_Chrom_NCBIv1) +#' x$position <- x$other$loc.metrics$ChromPos_Platypus_Chrom_NCBIv1 +#' ld_res <- gl.ld.haplotype(x,chrom_name = "NC_041728.1_chromosome_1", +#' ld_max_pairwise = 10000000 ) +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @export + +gl.ld.haplotype <- function(x, + pop_name = NULL, + chrom_name = NULL, + ld_max_pairwise = 10000000, + maf = 0.05, + ld_stat = "R.squared", + ind.limit = 10, + min_snps = 10, + ld_threshold_haplo = 0.5, + coordinates = NULL, + color_haplo = "viridis", + color_het = "deeppink", + plot.out = TRUE, + save2tmp = FALSE, + verbose = NULL) { + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + # FUNCTION SPECIFIC ERROR CHECKING + + # check if packages are installed + pkg <- "snpStats" + if (!(requireNamespace(pkg, quietly = TRUE))) { + stop(error( + "Package", + pkg, + " needed for this function to work. Please install it." + )) + } + pkg <- "fields" + if (!(requireNamespace(pkg, quietly = TRUE))) { + stop(error( + "Package", + pkg, + " needed for this function to work. Please install it." + )) + } + + # DO THE JOB + + group <- het <- lat <- long <- NULL + + if (is.null(chrom_name) == FALSE) { + chrom_tmp <- unlist(lapply(chrom_name, function(y) { + which(x$chromosome == y) + })) + + chrom_tmp <- chrom_tmp[order(chrom_tmp)] + + x <- gl.keep.loc(x , loc.list = locNames(x)[chrom_tmp], verbose = 0) + + } + + if (is.null(pop_name) == FALSE) { + x <- gl.keep.pop(x, pop.list = pop_name, verbose = 0) + } + + x_list <- seppop(x) + + if(is.null(coordinates) == FALSE){ + cat(report(" Restricting the analysis from",coordinates[1],"to", + coordinates[2],"base pairs\n")) + x_list <- lapply(x_list,function(y){ + loc_names <- which(y$position >= coordinates[1] & + y$position <= coordinates[2]) + y <- gl.keep.loc(y,loc.list = locNames(y)[loc_names],verbose = 0) + return(y) + }) + } + +haplo_table <- as.data.frame(matrix(nrow = 1,ncol = 10)) +colnames(haplo_table) <- c("population","chromosome","haplotype","start","end", + "start_ld_plot","end_ld_plot","midpoint", + "midpoint_ld_plot","labels") + +chr_list <- as.character(unique(x$chromosome)) + +p <- NULL + +# p <- rep(list(as.list(rep(NA, length(chr_list)))), length(names(x_list))) +# names(p) <- names(x_list) +# p <- lapply(p, function(x) { +# names(x) <- paste0("chr_", chr_list) +# return(x) +# }) + + for (pop_n in 1:length(x_list)) { + pop_ld <- x_list[[pop_n]] + pop_name <- popNames(pop_ld) + + if (nInd(pop_ld) <= ind.limit) { + cat(warn( + paste( + " Skipping population", + pop_name, + "from analysis because it has less than", + ind.limit, + "individuals.\n" + ) + )) + next() + } + + if (verbose >= 2) { + cat(report(" Calculating pairwise LD in population", pop_name, "\n")) + } + # ordering SNPs by chromosome and position + hold <- pop_ld + pop_ld <- hold[, order(hold$chromosome, hold$position)] + pop_ld$other$loc.metrics <- + hold$other$loc.metrics[order(hold$chromosome, hold$position), ] + pop_ld <- gl.recalc.metrics(pop_ld, verbose = 0) + if (maf > 0) { + pop_ld <- gl.filter.maf(pop_ld, threshold = maf, verbose = 0) + } + gl2plink(pop_ld, + outfile = paste0("gl_plink", "_", pop_name), + verbose = 0) + + # Read a pedfile as "SnpMatrix" object using a modified version of the + # function read.pedfile from package snpStats + snp_stats <- + utils.read.ped( + file = paste0(tempdir(), "/", "gl_plink", "_", pop_name, ".ped"), + snps = paste0(tempdir(), "/", "gl_plink", "_", pop_name, ".map") , + sep = " ", + show_warnings = FALSE, + na.strings = NA + ) + + ld_map <- snp_stats$map + colnames(ld_map) <- + c("chr", + "snp.name", + "null", + "loc_bp", + "allele.1", + "allele.2") + ld_map$chr <- pop_ld$chromosome + genotype <- snp_stats$genotypes + colnames(genotype@.Data) <- ld_map$loc_bp + + for (chrom in 1:length(chr_list)) { + chr_name <- chr_list[chrom] + if (verbose >= 2) { + cat(report(" Analysing chromosome", chr_name, "\n")) + } + ld_loci <- which(ld_map$chr == chr_list[chrom]) + ld_map_loci <- ld_map[ld_loci, ] + genotype_loci <- genotype[, ld_loci] + # removing loci that have the same location + dupl_loci <- which(duplicated(ld_map_loci$loc_bp)) + if (length(dupl_loci) > 0) { + ld_map_loci <- ld_map_loci[-dupl_loci,] + genotype_loci <- genotype_loci[,-dupl_loci] + } + if (nrow(ld_map_loci) <= 1) { + next + } + + # this is the mean distance between each snp which is used to determine + # the depth at which LD analyses are performed + mean_dis <- mean(diff(ld_map_loci$loc_bp)) + ld_depth_b <- ceiling((ld_max_pairwise / mean_dis)) - 1 + + if(ld_depth_b<5){ + cat(warn(" The maximum distance at which LD should be calculated + (ld_max_pairwise) is too short for chromosome",chr_name, + ". Setting this distance to",round(mean_dis*5,0),"bp\n" )) + ld_depth_b <- 5 + } + #function to calculate LD + ld_snps <- snpStats::ld(genotype_loci, depth = ld_depth_b, + stats = ld_stat) + + ld_matrix_2 <- ld_snps + colnames(ld_matrix_2) <- rownames(ld_matrix_2) + rownames(ld_matrix_2) <- 1:nrow(ld_matrix_2) + colnames(ld_matrix_2) <- 1:ncol(ld_matrix_2) + ld_columns_2 <- as.data.frame(as.table(as.matrix(ld_matrix_2))) + #remove cases where LD was not calculated + ld_columns_2 <- ld_columns_2[-ld_columns_2$Freq < 0, ] + ld_columns_2$Var1 <- as.numeric(as.character(ld_columns_2$Var1)) + ld_columns_2$Var2 <- as.numeric(as.character(ld_columns_2$Var2)) + ld_columns_2 <- ld_columns_2[complete.cases(ld_columns_2), ] + raster_haplo <- raster::rasterFromXYZ(ld_columns_2) + polygon_haplo <- + raster::rasterToPolygons( + raster_haplo, + fun = NULL, + n = 4, + na.rm = TRUE, + digits = 12, + dissolve = TRUE + ) + polygon_haplo <- maptools::elide(polygon_haplo, rotate = 45) + polygon_haplo$id <- rownames(as.data.frame(polygon_haplo)) + #this only has the coordinates + polygon_haplo.pts <- fortify(polygon_haplo, polygon_haplo = "id") + # add the attributes back + polygon_haplo.df <- + merge(polygon_haplo.pts, + polygon_haplo, + by = "id", + type = 'left') + width_poly <- round(max(polygon_haplo.df$long), 0) + height_poly <- max(polygon_haplo.df$lat) + + reduce_factor <- nrow(ld_map_loci) / width_poly + enlarge_factor <- width_poly / nrow(ld_map_loci) + #this is to correct the cell size unit when the polygon figure is more or + # less than 1000 units + correction_factor <- (width_poly / 1000) + + ld_matrix_3 <- ld_snps + + ld_matrix_3 <- as.matrix(ld_matrix_3) + dimnames(ld_matrix_3) <- NULL + matrix_rotate <- + rotate.matrix(x = ld_matrix_3, + angle = -45, + method = "simple") + matrix_rotate_2 <- matrix_rotate + matrix_rotate_2[matrix_rotate_2 == 0] <- NA + matrix_rotate_3 <- + t(zoo::na.locf( + t(matrix_rotate_2), + fromLast = FALSE, + na.rm = TRUE + )) + + means_col <- apply(matrix_rotate_3, 2, var, na.rm = TRUE) + means_col[means_col == 0] <- NA + means_col <- zoo::na.locf(means_col, fromLast = TRUE) + means_col <- means_col * 2 + df_col <- + as.data.frame(matrix(ncol = 2 , nrow = length(means_col))) + df_col[, 1] <- 1:nrow(df_col) + df_col[, 2] <- means_col + df_col[, 2] <- + scales::rescale(df_col[, 2], to = c(0, height_poly)) + means_col_2 <- means_col + + mean_column <- as.numeric(summary(means_col_2, na.rm = TRUE)[1:6]) + mean_column <- + scales::rescale(mean_column, to = c(0, height_poly)) + +# as the matrix was rotated the position of the snps is not correct anymore. +# the following code reassigns the snp position based on the second row from +# bottom to top of the rotated matrix. the first two and the last snps are +# removed to take in account the snps that are not present in the second row + second_row_temp <- which(!is.na(matrix_rotate_3[, 1])) - 1 + second_row <- second_row_temp[length(second_row_temp)] + second_row_2 <- matrix_rotate_2[second_row, ] + second_row_3 <- second_row_2 + + row_snp <- as.numeric(rownames(ld_snps)) + reassign_loc <- row_snp[3:length(row_snp)] + reassign_loc <- reassign_loc[1:length(reassign_loc) - 1] + + element_reassign_loc <- 1 + for (loc in 1:length(second_row_2)) { + if (is.na(second_row_2[loc])) { + next + } else{ + second_row_3[loc] <- reassign_loc[element_reassign_loc] + element_reassign_loc <- element_reassign_loc + 1 + } + } + + # putting back the first two snps and the last that were removed + second_row_3[1:2] <- row_snp[1:2] + second_row_3[length(second_row_3)] <- row_snp[length(row_snp)] + #filling the NAs + second_row_4 <- + zoo::na.locf(second_row_3, fromLast = TRUE, na.rm = FALSE) + second_row_4 <- + zoo::na.locf(second_row_4, fromLast = FALSE, na.rm = FALSE) + + second_row_ver_2 <- zoo::na.locf(second_row_2, fromLast = TRUE) + first_row <- which(!is.na(matrix_rotate_3[, 1]))[1] + first_row_2 <- matrix_rotate_3[first_row, ] + first_row_2 <- round(first_row_2, 2) + + #this is SNP's heterozygosity to be calculated alone + het_tmp <- utils.basic.stats(pop_ld) + snp_het_alone <- + data.frame(position = pop_ld$position, + het = unname(het_tmp$Hs)) + snp_het_alone$position <- 1:nrow(snp_het_alone) + snp_het_alone[, 1] <- + scales::rescale(snp_het_alone[, 1], to = c(0, width_poly)) + snp_het_alone[, 2] <- + scales::rescale(snp_het_alone[, 2], to = c(0, height_poly)) + + # identifying haplotypes + if(any(first_row_2>ld_threshold_haplo)){ + haplo_loc_test <- first_row_2 >= ld_threshold_haplo + haplo_loc_test <- c(haplo_loc_test, FALSE) + start_haplo <- NULL + end_haplo <- NULL + for (i in 1:(length(haplo_loc_test) - 1)) { + if (haplo_loc_test[i] == haplo_loc_test[i + 1]) { + next() + } + + if (haplo_loc_test[i] == TRUE) { + end_haplo_temp <- i + end_haplo <- c(end_haplo, end_haplo_temp) + } + if (haplo_loc_test[i] == FALSE) { + start_haplo_temp <- i + start_haplo <- c(start_haplo, start_haplo_temp) + } + } + + start_haplo <- c(1, start_haplo) + + start_haplo_2 <- second_row_4[start_haplo] + end_haplo_2 <- second_row_4[end_haplo] + if(length(start_haplo_2)!=length(end_haplo_2)){ + start_haplo_2 <- start_haplo_2[-length(start_haplo_2)] + } + haplo_1_ver_2 <- as.data.frame(cbind(start_haplo_2, end_haplo_2)) + haplo_1_ver_2$size <- (haplo_1_ver_2[, 2] - haplo_1_ver_2[, 1]) + + n_snps <- as.matrix(haplo_1_ver_2[, 1:2]) + n_snps[, 1] <- n_snps[, 1] + 1 + n_snps <- n_snps[which(n_snps[, 1] != n_snps[, 2]), ] + + if(!is.matrix(n_snps)){ + n_snps <- as.matrix(t(n_snps)) + } + + n_snps <- n_snps[!duplicated(n_snps[, 1]), ] + + if(!is.matrix(n_snps)){ + n_snps <- as.matrix(t(n_snps)) + } + + n_snps <- n_snps[!duplicated(n_snps[, 2]), ] + + + df.4.cut <- + as.data.frame(table(cut(row_snp, breaks = n_snps)), stringsAsFactors = + FALSE) + df.4.cut <- df.4.cut[which(df.4.cut$Freq >= min_snps), ] + if(nrow(df.4.cut)<1){ + cat(warn(" No haplotypes with more than ",min_snps,"were found. + Try using a lower threshold.\n")) + next() + } + df.4.cut_3 <- gsub("[][()]", "", df.4.cut$Var1, ",") + df.4.cut_3 <- strsplit(df.4.cut_3, ",") + df.4.cut_4 <- lapply(df.4.cut_3, as.numeric) + df.4.cut_4 <- as.data.frame(plyr::laply(df.4.cut_4, rbind)) + df.4.cut_4[, 3] <- (df.4.cut_4[, 2] - df.4.cut_4[, 1]) + + # this is to calculate the real distance in bp of the polygon figure of LD + + real_distance <- c(0, second_row_4) + real_distance_2 <- diff(real_distance) + real_distance_3 <- cumsum(real_distance_2) + real_distance_4 <- + as.data.frame(cbind(1:length(real_distance_3), real_distance_3)) + + test_var <- unname(unlist(df.4.cut_4[, 1:2])) + + location_test <- + lapply(test_var, findInterval, vec = as.numeric(paste( + unlist(real_distance_4$real_distance_3) + ))) + location_test_2 <- unlist(location_test) + test_var_2 <- + as.data.frame(cbind(1:length(location_test_2), location_test_2)) + + hap_blocks <- + as.data.frame(cbind( + paste0("CHR_", 1:nrow(df.4.cut_4)), + rep(1, times = nrow(df.4.cut_4)), + df.4.cut_4[, 1:3], + df.4.cut$Freq + )) + colnames(hap_blocks) <- + c("BLOCK", "CHR" , "BP1" , "BP2" , "SIZE", "NSNP") + + locations_temp <- + as.data.frame(cbind(hap_blocks$BP1, hap_blocks$BP2)) + locations_temp_2 <- locations_temp + colnames(locations_temp_2) <- c("start", "end") + locations_temp_2$start_ld_plot <- + unlist(lapply(locations_temp_2$start, findInterval, vec = as.numeric(paste( + unlist(real_distance_4$real_distance_3) + )))) + locations_temp_2$end_ld_plot <- + unlist(lapply(locations_temp_2$end, findInterval, vec = as.numeric(paste( + unlist(real_distance_4$real_distance_3) + )))) + locations_temp_2$midpoint <- + (locations_temp_2$start + locations_temp_2$end) / 2 + locations_temp_2$midpoint_ld_plot <- + (locations_temp_2$start_ld_plot + locations_temp_2$end_ld_plot) / 2 + locations_temp_2$labels <- + paste0(as.character(round(locations_temp_2$start /1000000, 0)), "-", + as.character(round(locations_temp_2$end / 1000000, 0))) + + haplo_temp_a <- + locations_temp_2[which(as.numeric(row.names(locations_temp_2)) %% 2 == 1), ] + haplo_temp_b <- + locations_temp_2[which(as.numeric(row.names(locations_temp_2)) %% 2 == 0), ] + + ticks_breaks <- + c(locations_temp_2$start_ld_plot, + locations_temp_2$end_ld_plot) + ticks_breaks <- ticks_breaks[order(ticks_breaks)] + ticks_lab <- c(locations_temp_2$start, locations_temp_2$end) + ticks_lab <- round(ticks_lab / 1000000, 0) + ticks_lab <- as.character(ticks_lab[order(ticks_lab)]) + + ticks_joint <- as.data.frame(cbind(ticks_breaks, ticks_lab)) + ticks_joint <- ticks_joint[!duplicated(ticks_joint$ticks_lab), ] + ticks_joint$ticks_lab <- as.character(ticks_joint$ticks_lab) + ticks_joint$ticks_breaks <- + as.numeric(as.character(ticks_joint$ticks_breaks)) + + colors_plot <- + c("Heterozygosity" = color_het, + "Haplotypes limits" = "lightgoldenrod3") + labels_haplo <- as.character(1:nrow(locations_temp_2)) + + # for an unknown reason, the name of the fill variable in the geom_polygon + # changes between Freq and layer. So when there is a error in displaying + # the graphic, the name of this variable has to be changed for it to work + + haplo_table_tmp <- rbind(haplo_temp_a,haplo_temp_b) + haplo_table_tmp <- cbind(haplotype=as.numeric(rownames(haplo_table_tmp)), + haplo_table_tmp) + haplo_table_tmp <- haplo_table_tmp[order(haplo_table_tmp$haplotype),] + haplo_table_tmp <- cbind(chromosome=chr_name,haplo_table_tmp) + haplo_table_tmp <- cbind(population=pop_name,haplo_table_tmp) + + haplo_table <- rbind(haplo_table,haplo_table_tmp) + + p_temp <- NULL + + p_temp <- ggplot() + + geom_rect( + aes( + xmin = haplo_temp_a$start_ld_plot, + xmax = haplo_temp_a$end_ld_plot, + ymin = min(polygon_haplo.df$lat) - 30, + ymax = max(polygon_haplo.df$lat) + 30 + ), + color = "cornsilk3", + fill = "cornsilk3" + ) + + geom_rect( + aes( + xmin = haplo_temp_b$start_ld_plot, + xmax = haplo_temp_b$end_ld_plot, + ymin = min(polygon_haplo.df$lat) - 30, + ymax = max(polygon_haplo.df$lat) + 30 + ), + color = "cornsilk4", + fill = "cornsilk4" + ) + + geom_polygon(data = polygon_haplo.df, + aes(long, lat, group = group, + fill = polygon_haplo.df[, 8])) + + viridis::scale_fill_viridis(name = ld_stat, option = color_haplo) + + geom_vline(aes( + xintercept = c( + haplo_temp_b$start_ld_plot, + haplo_temp_b$end_ld_plot, + haplo_temp_a$start_ld_plot, + haplo_temp_a$end_ld_plo + ), + color = "Haplotypes limits" + ), size = 1) + + + geom_line( + data = snp_het_alone, + aes(x = position, y = het, color = "Heterozygosity"), + inherit.aes = FALSE, + size = 1 / 2, + alpha = 1 + ) + + annotate( + "text", + x = locations_temp_2$midpoint_ld_plot, + y = max(polygon_haplo.df$lat) + 13, + label = labels_haplo , + size = 3, + color = "black" + ) + + annotate( + "text", + x = locations_temp_2$midpoint_ld_plot, + y = min(polygon_haplo.df$lat) - 13, + label = locations_temp_2$labels , + size = 3, + color = "black" + ) + + labs( + x = "Chromosome location (Mbp)", + y = "Het", + title = paste("Population",pop_name,"Chromosome", chr_name, "-", nLoc(pop_ld), "SNPs") + ) + + scale_x_continuous(breaks = ticks_joint$ticks_breaks, + labels = ticks_joint$ticks_lab) + + scale_colour_manual(name = "", values = colors_plot) + + theme_void() + + theme( + legend.position = "top", + legend.text = element_text(size = 10), + axis.ticks.y = element_blank(), + axis.title.y = element_blank(), + axis.text.y = element_blank(), + axis.title.x = element_text(hjust = 0.5), + axis.ticks.x = element_blank(), + axis.text.x = element_blank() + ) + + coord_fixed(ratio = 1 / 1) + + # p <- c(p,p_temp) + + # p[[pop_n]][[chrom]] <- p_temp + + # # PRINTING OUTPUTS + if (plot.out) { + print(p_temp) + } + + }else{ + cat(warn(" No haplotypes were identified for chromosome",chr_name,"\n")) + + colors_plot <- c("Heterozygosity" = color_het) + + p_temp <- NULL + + p_temp <- ggplot() + + geom_polygon(data = polygon_haplo.df, + aes(long, lat, group = group, + fill = polygon_haplo.df[, 8])) + + viridis::scale_fill_viridis(name = ld_stat, option = color_haplo) + + geom_line( + data = snp_het_alone, + aes(x = position, y = het, color = "Heterozygosity"), + inherit.aes = FALSE, + size = 1 / 2, + alpha = 1 + ) + + labs( + x = "Chromosome location (Mbp)", + y = "Het", + title = paste("Population",pop_name,"Chromosome", chr_name, "-", nLoc(pop_ld), "SNPs") + ) + + # scale_x_continuous(breaks = ticks_joint$ticks_breaks, + # labels = ticks_joint$ticks_lab) + + scale_colour_manual(name = "", values = colors_plot) + + theme_void() + + theme( + legend.position = "top", + legend.text = element_text(size = 10), + axis.ticks.y = element_blank(), + axis.title.y = element_blank(), + axis.text.y = element_blank(), + axis.title.x = element_text(hjust = 0.5), + axis.ticks.x = element_blank(), + axis.text.x = element_blank() + ) + + coord_fixed(ratio = 1 / 1) + + # p <- c(p,p_temp) + + # p[[pop_n]][[chrom]] <- p_temp + + # PRINTING OUTPUTS + if (plot.out) { + print(p_temp) + } + + } + } + } + +# # PRINTING OUTPUTS +if (plot.out) { + print(p) +} + +haplo_table <- haplo_table[-1,] +print(haplo_table,row.names = FALSE) + +# SAVE INTERMEDIATES TO TEMPDIR creating temp file names +if (save2tmp) { + if (plot.out) { + temp_plot <- tempfile(pattern = "Plot_") + match_call <- + paste0(names(match.call()), + "_", + as.character(match.call()), + collapse = "_") + # saving to tempdir + saveRDS(list(match_call, p), file = temp_plot) + if (verbose >= 2) { + cat(report(" Saving the ggplot to session tempfile\n")) + } + } + temp_table <- tempfile(pattern = "Table_") + saveRDS(list(match_call, haplo_table), file = temp_table) + if (verbose >= 2) { + cat(report(" Saving tabulation to session tempfile\n")) + cat( + report( + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" + ) + ) + } +} + +# FLAG SCRIPT END + +if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) +} + +# RETURN + +return(invisible(haplo_table)) + +} diff --git a/R/gl.map.interactive.r b/R/gl.map.interactive.r index dabfe40d..5898ea5b 100644 --- a/R/gl.map.interactive.r +++ b/R/gl.map.interactive.r @@ -14,9 +14,20 @@ #' populations [default TRUE]. #' @param pop.labels.cex Size of population labels [default 12]. #' @param ind.circles Should individuals plotted as circles [default TRUE]. -#' @param ind.circle.cols colors off circles. Colors can be provided as usual by names (e.g. "black") and are re-cycled. So a color c("blue","red") colors individuals alternatively between blue and red using the genlight object order of individuals. For transparency see parameter ind.circle.transparency. Defaults to rainbow colors by population if not provided. If you want to have your own colors for each population, check the platypus.gl example below. -#' @param ind.circle.cex (size or circles in pixels ). Defaults to 10. -#' @param ind.circle.transparency Transparency of circles between 0=invisible and 1=no transparency. Defaults to 0.8. +#' @param ind.circle.cols Colors of circles. Colors can be provided as usual by +#' names (e.g. "black") and are re-cycled. So a color c("blue","red") colors +#' individuals alternatively between blue and red using the genlight object +#' order of individuals. For transparency see parameter +#' ind.circle.transparency. Defaults to rainbow colors by population if not +#' provided. If you want to have your own colors for each population, check +#' the platypus.gl example below. +#' @param ind.circle.cex (size or circles in pixels ) [default 10]. +#' @param ind.circle.transparency Transparency of circles between 0=invisible +#' and 1=no transparency. Defaults to 0.8. +#' @param palette_links Color palette for the links in case a matrix is provided +#' [default NULL]. +#' @param leg_title Legend's title for the links in case a matrix is provided +#' [default NULL]. #' @param provider Passed to leaflet [default "Esri.NatGeoWorldMap"]. #' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, #' progress log; 3, progress and results summary; 5, full report @@ -28,6 +39,19 @@ #' A wrapper around the \pkg{leaflet} package. For possible background #' maps check as specified via the provider: #' \url{http://leaflet-extras.github.io/leaflet-providers/preview/index.html} +#' +#' The palette_links argument can be any of the following: +#' A character vector of RGB or named colors. Examples: palette(), +#' c("#000000", "#0000FF", "#FFFFFF"), topo.colors(10) +#' +#' The name of an RColorBrewer palette, e.g. "BuPu" or "Greens". +#' +#' The full name of a viridis palette: "viridis", "magma", "inferno", +#' or "plasma". +#' +#' A function that receives a single value between 0 and 1 and returns a color. +#' Examples: colorRamp(c("#000000", "#FFFFFF"), interpolate = "spline"). +#' #' @author Bernd Gruber -- Post to \url{https://groups.google.com/d/forum/dartr} #' @examples #' gl.map.interactive(bandicoot.gl) @@ -35,7 +59,6 @@ #' gl.map.interactive(platypus.gl, ind.circle.cols=cols, ind.circle.cex=10, #' ind.circle.transparency=0.5) - gl.map.interactive <- function(x, matrix = NULL, standard = TRUE, @@ -44,8 +67,10 @@ gl.map.interactive <- function(x, pop.labels.cex = 12, ind.circles = TRUE, ind.circle.cols = NULL, - ind.circle.cex=10, - ind.circle.transparency=0.8, + ind.circle.cex = 10, + ind.circle.transparency = 0.8, + palette_links = NULL, + leg_title = NULL, provider = "Esri.NatGeoWorldMap", verbose = NULL) { @@ -85,7 +110,7 @@ gl.map.interactive <- function(x, if (sum(colnames(x@other$latlon) %in% c("lat", "lon")) != 2) { stop(error( - "Coordinates under gl@other$latlon are not named 'lat' and 'lon'." + "Coordinates under gl@other$latlon are not named 'lat' and 'lon'." )) } @@ -93,7 +118,8 @@ gl.map.interactive <- function(x, if (nrow(matrix) != nInd(x) & nrow(matrix) != nPop(x)) { stop( error( - "The dimension of the provided matrix does neither match the number of individuals nor the number of populations." +"The dimension of the provided matrix does neither match the number of +individuals nor the number of populations." ) ) } @@ -150,10 +176,13 @@ gl.map.interactive <- function(x, } if (!is.null(matrix)) { + matrix <- matrix[order(indNames(x)),] # standardize if (standard) { matrix[, ] <- - ((matrix[, ] - min(matrix, na.rm = T)) / (max(matrix, na.rm = T) - min(matrix, na.rm = T))) * 9 + 1 + ((matrix[, ] - min(matrix, na.rm = T)) / + (max(matrix, na.rm = T) - + min(matrix, na.rm = T))) * 9 + 1 } if (nrow(matrix) == nPop(x)) { @@ -161,22 +190,39 @@ gl.map.interactive <- function(x, } else { xys <- df } + + if(is.null(palette_links)){ + palette_links <- + diverging_palette(length(unique(unlist(unname(as.vector(matrix)))))) + } + qpal <- leaflet::colorNumeric( + palette = palette_links, + domain = unique(unlist(unname(as.vector(matrix))))) + if (symmetric) { for (ii in 1:nrow(matrix)) { for (i in ii:nrow(matrix)) { - if (!is.null(matrix[i, ii])) + if (!is.null(matrix[i, ii]) & matrix[i, ii] > 0 ){ m <- m %>% leaflet::addPolylines( lng = c(xys[i, "lon"], xys[ii, "lon"]), lat = c(xys[i, "lat"], xys[ii, "lat"]), - weight = matrix[i, - ii], - color = "#0000FF", + color = qpal(matrix[i,ii]), opacity = 1 ) + }else{ + next() + } } } + m <- m %>% leaflet::addLegend( + pal = qpal, + values = unique(unlist(unname(as.vector(matrix)))), + group = "addPolylines", + position = "bottomleft", + title = leg_title) + } if (!symmetric) { @@ -187,14 +233,17 @@ gl.map.interactive <- function(x, to <- xys[ii, ] if (!is.null(matrix[i, ii]) & !is.null(matrix[ii, i])) { - if (matrix[i, ii] > matrix[ii, i]) - lcols = "#FFAA00" - else - lcols = "#00AAFF" - if (matrix[i, ii] == matrix[ii, i]) - lcols = "#00AA00" - } else - lcols = "#333333" + if (matrix[i, ii] > matrix[ii, i]){ + lcols <-"#FFAA00" + }else{ + lcols <-"#00AAFF" + } + if (matrix[i, ii] == matrix[ii, i]){ + lcols <-"#00AA00" + } + } else{ + lcols <-"#333333" + } m <- m %>% leaflet.minicharts::addFlows( lng0 = as.numeric(from["lon"]), @@ -204,7 +253,7 @@ gl.map.interactive <- function(x, flow = matrix[i, ii], color = lcols, maxThickness = 10, - minThickness = 1, + minThickness = 0, maxFlow = max(matrix, na.rm = T), opacity = 0.8 diff --git a/R/gl.nhybrids.r b/R/gl.nhybrids.r index 2a602eb3..611f0c61 100644 --- a/R/gl.nhybrids.r +++ b/R/gl.nhybrids.r @@ -764,7 +764,7 @@ gl.nhybrids <- function(gl, if (flag == "bothpar" & plot == TRUE) { # Read in the results of the New Hybrids analysis - F1.test <- read.csv(file = "aa-Pofz.csv") + F1.test <- read.csv(file = "aa-PofZ.csv") # Pull out results for F1 hybrids only, defined by posterior probability >= pprob F1.test <- F1.test[(F1.test$F1 >= pprob), ] # Use the id of the F1 hybrids to subset the genlight object containing the loci with fixed differences used in the analysis diff --git a/R/gl.pcoa.plot.r b/R/gl.pcoa.plot.r index aafa4620..c1477454 100644 --- a/R/gl.pcoa.plot.r +++ b/R/gl.pcoa.plot.r @@ -146,10 +146,12 @@ gl.pcoa.plot <- function(glPca, # CHECK DATATYPE datatype1 <- - utils.check.datatype(glPca, accept = c("glPca","list"), verbose = verbose) + utils.check.datatype(glPca, accept = c("glPca","list"), + verbose = verbose) datatype2 <- utils.check.datatype(x, - accept = c("SNP", "SilicoDArT", "fd", "dist","list"), + accept = c("SNP", "SilicoDArT", "fd", + "dist","list"), verbose = verbose) # SCRIPT SPECIFIC ERROR CHECKING @@ -488,8 +490,7 @@ gl.pcoa.plot <- function(glPca, plott + geom_hline(yintercept = 0) + geom_vline(xintercept = 0) + theme(legend.position = "none") - # Scale the axes in proportion to % explained, if requested if(scale==TRUE) { plott <- plott + - # coord_fixed(ratio=e[yaxis]/e[xaxis]) } + # Scale the axes in proportion to % explained, if requested if(scale==TRUE) if (scale == TRUE) { plott <- plott + coord_fixed(ratio = e[yaxis]/ e[xaxis]) } @@ -545,10 +546,9 @@ gl.pcoa.plot <- function(glPca, ) ) + labs(x = xlab, y = ylab) + geom_hline(yintercept = 0) + geom_vline(xintercept = 0) + theme(legend.position = "none") - # Scale the axes in proportion to % explained, if requested if(scale==TRUE) { plott <- plott + - # coord_fixed(ratio=e[yaxis]/e[xaxis]) } + # Scale the axes in proportion to % explained, if requested if(scale==TRUE) if (scale == TRUE) { - plott <- plott + coord_fixed(ratio = 1) + plott <- plott + coord_fixed(ratio = e[yaxis]/ e[xaxis]) } # Add ellipses if requested if (ellipse == TRUE) { @@ -630,10 +630,9 @@ gl.pcoa.plot <- function(glPca, } plott <- plott + geom_hline(yintercept = 0) + geom_vline(xintercept = 0) - # Scale the axes in proportion to % explained, if requested if(scale==TRUE) { plott <- plott + - # coord_fixed(ratio=e[yaxis]/e[xaxis]) } + # Scale the axes in proportion to % explained, if requested if(scale==TRUE) if (scale == TRUE) { - plott <- plott + coord_fixed(ratio = 1) + plott <- plott + coord_fixed(ratio = e[yaxis]/ e[xaxis]) } # Add ellipses if requested if (ellipse == TRUE) { @@ -692,10 +691,9 @@ gl.pcoa.plot <- function(glPca, } plott <- plott + geom_hline(yintercept = 0) + geom_vline(xintercept = 0) + theme(legend.position = "none") - # Scale the axes in proportion to % explained, if requested if(scale==TRUE) { plott <- plott + - # coord_fixed(ratio=e[yaxis]/e[xaxis]) } + # Scale the axes in proportion to % explained, if requested if(scale==TRUE) if (scale == TRUE) { - plott <- plott + coord_fixed(ratio = 1) + plott <- plott + coord_fixed(ratio = e[yaxis]/ e[xaxis]) } # Add ellipses if requested if (ellipse == TRUE) { diff --git a/R/gl.pcoa.r b/R/gl.pcoa.r index dce4bb14..3abbaa20 100644 --- a/R/gl.pcoa.r +++ b/R/gl.pcoa.r @@ -51,7 +51,7 @@ #' representation. #' #' The measure of distance between entities in a PCA is the Pearson Correlation -#' Coefficent, essentially a standardized Euclidean distance. This is both a +#' Coefficient, essentially a standardized Euclidean distance. This is both a #' metric distance and a Euclidean distance. In PCoA, the second source of #' stress is the choice of distance measure or dissimilarity measure. While any #' distance or dissimilarity matrix can be represented in an ordinated space, diff --git a/R/gl.play.history.r b/R/gl.play.history.r index 0455e270..8a74ff1c 100644 --- a/R/gl.play.history.r +++ b/R/gl.play.history.r @@ -51,7 +51,7 @@ gl.play.history <- function(x, for (i in 1:length(hist2)) { glhist <- hist2[[i]] - narg = length(glhist) + narg <-length(glhist) ll <- list() ll[1:(narg - 1)] <- glhist[2:narg] names(ll) <- names(glhist[2:narg]) diff --git a/R/gl.plot.network.r b/R/gl.plot.network.r index 06e42920..e352ac9f 100644 --- a/R/gl.plot.network.r +++ b/R/gl.plot.network.r @@ -152,7 +152,7 @@ gl.plot.network <- function(D, directed = FALSE) if (!is.null(x)) { - colors = rainbow(nlevels(pop(x))) + colors <-rainbow(nlevels(pop(x))) my_colors <- colors[pop(x)] } else { my_colors <- "red" diff --git a/R/gl.plot.structure.r b/R/gl.plot.structure.r index f08bd934..eedaf858 100644 --- a/R/gl.plot.structure.r +++ b/R/gl.plot.structure.r @@ -114,7 +114,7 @@ gl.plot.structure <- function(sr, if (!is(sr, "structure.result")) { stop(error( - "sr is not a structure result object returned from gl.run.structure." + "sr is not a structure result object returned from gl.run.structure.\n" )) } @@ -213,7 +213,7 @@ gl.plot.structure <- function(sr, } }else{ - res[[length(res)+1]] <- res_tmp_3 + res[[length(res)+1]] <- res_tmp_3 } } diff --git a/R/gl.print.history.r b/R/gl.print.history.r index f2782901..895cd6ea 100644 --- a/R/gl.print.history.r +++ b/R/gl.print.history.r @@ -49,7 +49,7 @@ gl.print.history <- function(x = NULL, dd <- data.frame(nr = 1:nh, history = as.character(hist2)) # max width - dd$history = sapply(lapply(dd$history, strwrap, width = 80), + dd$history <-sapply(lapply(dd$history, strwrap, width = 80), paste, collapse = "\n") print(knitr::kable(dd, align = c("c", "l", "l"))) diff --git a/R/gl.read.dart.r b/R/gl.read.dart.r index fa30d639..75f1006e 100644 --- a/R/gl.read.dart.r +++ b/R/gl.read.dart.r @@ -81,7 +81,7 @@ gl.read.dart <- function(filename, verbosity = verbose) if (verbose == 0) { - probar = FALSE + probar <-FALSE } # DO THE JOB @@ -103,7 +103,7 @@ gl.read.dart <- function(filename, ) glout <- utils.dart2genlight( - dout, + dart= dout, ind.metafile = ind.metafile, probar = probar, verbose = verbose diff --git a/R/gl.read.silicodart.r b/R/gl.read.silicodart.r index 8cf2c2d6..a7f55c7b 100644 --- a/R/gl.read.silicodart.r +++ b/R/gl.read.silicodart.r @@ -146,7 +146,7 @@ gl.read.silicodart <- function(filename, } datas <- snpraw[, (lmet + 1):ncol(snpraw)] - nrows = 1 #there is no two row SilicoFormat?? + nrows <-1 #there is no two row SilicoFormat?? stdmetricscols <- 1:lmet if (verbose >= 2) { @@ -230,7 +230,7 @@ gl.read.silicodart <- function(filename, header = T, stringsAsFactors = T) # is there an entry for every individual - id.col = match("id", names(ind.cov)) + id.col <-match("id", names(ind.cov)) if (is.na(id.col)) { stop(error("Fatal Error: There is no id column\n")) @@ -273,7 +273,7 @@ gl.read.silicodart <- function(filename, } } - pop.col = match("pop", names(ind.cov)) + pop.col <-match("pop", names(ind.cov)) if (is.na(pop.col)) { cat(warn(" Please note: there is no pop column\n")) @@ -284,8 +284,8 @@ gl.read.silicodart <- function(filename, cat(report(" Added pop factor.\n")) } - lat.col = match("lat", names(ind.cov)) - lon.col = match("lon", names(ind.cov)) + lat.col <-match("lat", names(ind.cov)) + lon.col <-match("lon", names(ind.cov)) if (is.na(lat.col)) { cat(warn(" Please note: there is no lat column\n")) diff --git a/R/gl.read.vcf.r b/R/gl.read.vcf.r index 17fa7db2..43f82455 100644 --- a/R/gl.read.vcf.r +++ b/R/gl.read.vcf.r @@ -26,10 +26,10 @@ gl.read.vcf <- function(vcffile, build = "Jackson", verbosity = verbose) - x = NULL + x <- NULL if (!(requireNamespace("vcfR", quietly = TRUE))) { - stop(error("To use this function you need to install package: vcfR.")) + stop(error("To use this function you need to install package: vcfR.\n")) } else { vcf <- vcfR::read.vcfR(file = vcffile, verbose = verbose) myRef <- vcfR::getREF(vcf) @@ -38,9 +38,31 @@ gl.read.vcf <- function(vcffile, x <- vcfR::vcfR2genlight(vcf) x@loc.all <- loc.all + # adding SNP information from VCF + info_tmp_1 <- vcf@fix[,6:7] + info_tmp_2 <- vcfR::getINFO(vcf) + if(any(is.na(info_tmp_2[1]) | is.na(info_tmp_1[1]))==TRUE){ + info <- info_tmp_1 + colnames(info) <- c("QUAL","FILTER") + }else{ + info_tmp_2 <- as.data.frame(do.call(rbind,stringr::str_split(info_tmp_2,pattern = "=|;"))) + info <- info_tmp_2[,seq(2,ncol(info_tmp_2),2)] + info <- cbind(info_tmp_1,info) + colnames(info) <- c("QUAL","FILTER",unname(unlist(info_tmp_2[1,seq(1,ncol(info_tmp_2),2)]))) + } + + # identify which SNPs have more than 2 alleles + if("AC" %in% colnames(info)){ + more_alleles <- grep(",",info$AC) + info <- info[-more_alleles,] + info[] <- lapply(info, as.numeric) + } + ploidy(x) <- 2 x <- gl.compliance.check(x) + x$other$loc.metrics <- cbind(x$other$loc.metrics,info) + # add history x@other$history <- list(match.call()) x <- gl.recalc.metrics(x) diff --git a/R/gl.report.bases.r b/R/gl.report.bases.r index 34765f59..0a6f7f71 100644 --- a/R/gl.report.bases.r +++ b/R/gl.report.bases.r @@ -48,7 +48,7 @@ #' #' # Tag P/A data #' out <- gl.report.bases(testset.gs) #' -#' @family reporting functions +#' @family report functions #' @import stringr #' @import patchwork #' @export @@ -76,7 +76,8 @@ gl.report.bases <- function(x, if (!any(names(x@other$loc.metrics) == "TrimmedSequence")) { stop(error( - " Fatal Error: Dataset does not include variable TrimmedSequence!\n" + " Fatal Error: Dataset does not include variable + TrimmedSequence!\n" )) } @@ -130,19 +131,27 @@ gl.report.bases <- function(x, # Sum the transitions and transversions tv <- - sum(str_count(state.change, "A>C")) + sum(stringr::str_count(state.change, "C>A")) + sum(stringr::str_count(state.change, "G>T")) + - sum(stringr::str_count(state.change, "T>G")) + sum(stringr::str_count(state.change, "A>T")) + sum(stringr::str_count(state.change, - "T>A")) + sum(stringr::str_count(state.change, "G>C")) + sum(stringr::str_count(state.change, "C>G")) + sum(str_count(state.change, "A>C")) + + sum(stringr::str_count(state.change, "C>A")) + + sum(stringr::str_count(state.change, "G>T")) + + sum(stringr::str_count(state.change, "T>G")) + + sum(stringr::str_count(state.change, "A>T")) + + sum(stringr::str_count(state.change, "T>A")) + + sum(stringr::str_count(state.change, "G>C")) + + sum(stringr::str_count(state.change, "C>G")) ts <- - sum(stringr::str_count(state.change, "A>G")) + sum(stringr::str_count(state.change, "G>A")) + sum(stringr::str_count(state.change, - "C>T")) + sum(stringr::str_count(state.change, "T>C")) + sum(stringr::str_count(state.change, "A>G")) + + sum(stringr::str_count(state.change, "G>A")) + + sum(stringr::str_count(state.change, "C>T")) + + sum(stringr::str_count(state.change, "T>C")) if (verbose >= 2) { if (ts + tv != length(x@other$loc.metrics$TrimmedSequence)) { cat( warn( - " Warning: Sum of transitions plus transversions does not equal number of loci.\n" + " Warning: Sum of transitions plus transversions does + not equal number of loci.\n" ) ) } @@ -179,7 +188,8 @@ gl.report.bases <- function(x, if (verbose >= 2) { cat( important( - " Tag P/A data (SilicoDArT), transition/transversions cannot be calculated\n" + " Tag P/A data (SilicoDArT), transition/transversions + cannot be calculated\n" ) ) } @@ -203,10 +213,12 @@ gl.report.bases <- function(x, df <- data.frame(bases = bases, freq = freq) p1 <- - ggplot(data = df, aes(x = bases, y = freq)) + geom_bar(stat = "identity", - color = plot_colors[1], - fill = plot_colors[2]) + xlab("Bases") + - ylab("Percent Frequency") + ggtitle(title) + plot_theme + ggplot(data = df, aes(x = bases, y = freq)) + + geom_bar(stat="identity",color=plot_colors[1],fill=plot_colors[2]) + + xlab("Bases") + + ylab("Percent Frequency") + + ggtitle(title) + + plot_theme if (datatype == "SNP") { bases <- c("Ts", "Tv") @@ -214,10 +226,12 @@ gl.report.bases <- function(x, df2 <- data.frame(bases = bases, freq = freq) p2 <- - ggplot(data = df2, aes(x = bases, y = freq)) + geom_bar(stat = "identity", - color = plot_colors[1], - fill = plot_colors[2]) + - xlab("Mutation Type") + ylab("Percent Frequency") + ggtitle(paste("SNP: Ts/Tv Rates [ratio =", round(ratio, 2), "]")) + plot_theme + ggplot(data = df2, aes(x = bases, y = freq)) + + geom_bar(stat="identity",color=plot_colors[1],fill=plot_colors[2]) + + xlab("Mutation Type") + + ylab("Percent Frequency") + + ggtitle(paste("SNP: Ts/Tv Rates [ratio =",round(ratio,2),"]")) + + plot_theme p3 <- (p1 / p2) # Using package patchwork } else { @@ -231,9 +245,9 @@ gl.report.bases <- function(x, cat( report( " Returning a list containing - [[1]] $freq -- the table of base frequencies and transition/transversion ratios; - [[2]] $plotbases -- ggplot bargraph of base frequencies; - [[3]] $plottstv -- ggplot bargraph of transitions and transversions." +[[1]] $freq -- the table of base frequencies and transition/transversion ratios; +[[2]] $plotbases -- ggplot bargraph of base frequencies; +[[3]] $plottstv -- ggplot bargraph of transitions and transversions." ) ) } @@ -262,7 +276,8 @@ gl.report.bases <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.callrate.r b/R/gl.report.callrate.r index 7b663207..e80ae7bd 100644 --- a/R/gl.report.callrate.r +++ b/R/gl.report.callrate.r @@ -10,6 +10,7 @@ #' (SilicoDArT) data [required]. #' @param method Specify the type of report by locus (method='loc') or #' individual (method='ind') [default 'loc']. +#' @param by_pop Whether report by population [default FALSE]. #' @param plot.out Specify if plot is to be produced [default TRUE]. #' @param plot_theme User specified theme [default theme_dartR()]. #' @param plot_colors Vector with two color names for the borders and fill @@ -49,12 +50,13 @@ #' gl.report.callrate(test.gs) #' gl.report.callrate(test.gs,method='ind') #' @seealso \code{\link{gl.filter.callrate}} -#' @family filters and filter reports +#' @family report functions #' @import patchwork #' @export gl.report.callrate <- function(x, method = "loc", + by_pop = FALSE, plot.out = TRUE, plot_theme = theme_dartR(), plot_colors = two_colors, @@ -101,26 +103,26 @@ gl.report.callrate <- function(x, # Boxplot p1 <- ggplot(data.frame(callrate), aes(y = callrate)) + - geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + geom_boxplot(color=plot_colors[1], fill = plot_colors[2]) + coord_flip() + plot_theme + xlim(range = c(-1, 1)) + ylim(min, 1) + ylab(" ") + - theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + theme(axis.text.y=element_blank(),axis.ticks.y=element_blank()) + ggtitle(title1) # Histogram p2 <- ggplot(data.frame(callrate), aes(x = callrate)) + - geom_histogram(bins = bins,color = plot_colors[1],fill = plot_colors[2]) + + geom_histogram(bins = bins,color = plot_colors[1],fill = plot_colors[2]) + coord_cartesian(xlim = c(min, 1)) + xlab("Call rate") + ylab("Count") + plot_theme # plots by population - if(nPop(x)>1){ + if(nPop(x)>1 & by_pop==TRUE){ pops <- seppop(x) cat(" Reporting Call Rate by population\n") @@ -129,18 +131,19 @@ gl.report.callrate <- function(x, pop_tmp <- utils.recalc.callrate(z, verbose = 0) c_rate_tmp <- pop_tmp$other$loc.metrics$CallRate p_temp <- - ggplot(as.data.frame(c_rate_tmp), aes(x = c_rate_tmp)) + - geom_histogram(bins = bins, color = plot_colors[1],fill = plot_colors[2]) + + ggplot(as.data.frame(c_rate_tmp), aes(x = c_rate_tmp)) + + geom_histogram(bins = bins, color = plot_colors[1],fill = plot_colors[2]) + xlab("Call rate") + ylab("Count") + coord_cartesian(xlim = c(min, 1)) + plot_theme + ggtitle(paste(popNames(z), "n =", nInd(z))) - cat(" Population:",popNames(pop_tmp),"\n") - cat(" No. of loci =", nLoc(pop_tmp), "\n") - cat(" No. of individuals =", nInd(pop_tmp), "\n") - cat(" Mean Call Rate",mean(pop_tmp$other$loc.metrics$CallRate,na.rm = TRUE) ,"\n\n") +cat(" Population:",popNames(pop_tmp),"\n") +cat(" No. of loci =", nLoc(pop_tmp), "\n") +cat(" No. of individuals =", nInd(pop_tmp), "\n") +cat(" Mean Call Rate",mean(pop_tmp$other$loc.metrics$CallRate,na.rm = TRUE) , + "\n\n") return(p_temp) }) @@ -198,7 +201,7 @@ gl.report.callrate <- function(x, } - ########### FOR METHOD BASED ON INDIVIDUAL Calculate the call rate by individual +########### FOR METHOD BASED ON INDIVIDUAL Calculate the call rate by individual if (method == "ind") { ind.call.rate <- 1 - rowSums(is.na(as.matrix(x))) / nLoc(x) if (plot.out) { @@ -222,13 +225,13 @@ gl.report.callrate <- function(x, xlim(range = c(-1, 1)) + ylim(min, 1) + ylab(" ") + - theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + ggtitle(title1) # Histogram p2 <- ggplot(data.frame(ind.call.rate), aes(x = ind.call.rate)) + - geom_histogram(bins = bins, color = plot_colors[1],fill = plot_colors[2]) + + geom_histogram(bins = bins, color = plot_colors[1],fill = plot_colors[2]) + coord_cartesian(xlim = c(min, 1)) + xlab("Call rate") + ylab("Count") + @@ -249,7 +252,7 @@ gl.report.callrate <- function(x, as.matrix(x) )) / (nLoc(x) * nInd(x)), 2), "\n\n") - # Determine the loss of individuals for a given threshold using quantiles + # Determine the loss of individuals for a given threshold using quantiles quantile_res <- quantile(ind.call.rate, probs = seq(0, 1, 1 / 20)) retained <- unlist(lapply(quantile_res, function(y) { @@ -279,6 +282,15 @@ gl.report.callrate <- function(x, df <- df[order(-df$Quantile), ] df$Quantile <- paste0(df$Quantile, "%") rownames(df) <- NULL + + ind.call.rate_pop <- as.data.frame(cbind(names(ind.call.rate), + as.character(pop(x)), + ind.call.rate)) + colnames(ind.call.rate_pop) <- c("ind_name","pop","missing_data") +ind.call.rate_pop <- ind.call.rate_pop[order(ind.call.rate_pop$pop, + ind.call.rate_pop$missing_data, + decreasing = TRUE),] + } # PRINTING OUTPUTS @@ -287,7 +299,7 @@ gl.report.callrate <- function(x, p3 <- (p1 / p2) + plot_layout(heights = c(1, 4)) print(p3) - if(nPop(x)>1 & method == "loc"){ + if(nPop(x)>1 & method == "loc" & by_pop == TRUE){ row_plots <- ceiling(nPop(x) / 3) p4 <- wrap_plots(c_rate_plots) p4 <- p4 + plot_layout(ncol = 3, nrow = row_plots) @@ -295,6 +307,10 @@ gl.report.callrate <- function(x, } } print(df) + cat("\n\n") + if (method == "ind") { + print(ind.call.rate_pop, row.names = FALSE) + } # SAVE INTERMEDIATES TO TEMPDIR @@ -323,11 +339,16 @@ gl.report.callrate <- function(x, } temp_table <- tempfile(pattern = "Table_") saveRDS(list(match_call, df), file = temp_table) + if (method == "ind") { + temp_table_2 <- tempfile(pattern = "Table2_") + saveRDS(list(ind.call.rate_pop, df), file = temp_table_2) + } if (verbose >= 2) { cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.diversity.r b/R/gl.report.diversity.r index 132bc372..9cc37697 100644 --- a/R/gl.report.diversity.r +++ b/R/gl.report.diversity.r @@ -36,6 +36,27 @@ #' #'\strong{ Function's output } #' +#' If the function's parameter "table" = "DH" (the default value) is used, the +#' output of the function is 20 tables. +#' +#'The first two show the number of loci used. The name of each of the rest of +#'the tables starts with three terms separated by underscores. +#' +#'The first term refers to the q value (0 to 2). +#' +#'The second term refers to whether it is the diversity measure (H) or its +#'transformation to Hill numbers (D). +#' +#'The third term refers to whether the diversity is calculated within +#'populations (alpha) or between populations (beta). +#' +#'In the case of alpha diversity tables, standard deviations have their own +#'table, which finishes with a fourth term: "sd". +#' +#'In the case of beta diversity tables, standard deviations are in the upper +#'triangle of the matrix and diversity values are in the lower triangle of the +#'matrix. +#' #' Plots are saved to the temporal directory (tempdir) and can be accessed with #' the function \code{\link{gl.print.reports}} and listed with the function #' \code{\link{gl.list.reports}}. Note that they can be accessed only in the @@ -59,7 +80,7 @@ #' div$two_H_beta #' names(div) #' -#' @family reporting functions +#' @family report functions #' #' @references #'Sherwin, W.B., Chao, A., Johst, L., Smouse, P.E. (2017). Information Theory @@ -178,10 +199,6 @@ gl.report.diversity <- function(x, dummys <- apply(mat_shannon, 2, shannon) - # p <- (2 * p + hets) / 2 q <- (2 * q + hets) / 2 total <- colSums(mat,na.rm = T) p <- colMeans(as.matrix(x), na.rm = T)/2 p <- - # p[!is.na(p)] #ignore loci with just missing data logp <- ifelse(!is.finite(log(p)), 0, log(p)) log1_p <- ifelse(!is.finite(log(1 - # - p)), 0, log(1 - p)) dummys <- -(p * logp + (1 - p) * log1_p) - return(list( estH = mean(dummys), sdH = sd(dummys), @@ -346,8 +363,9 @@ gl.report.diversity <- function(x, tt <- table(c(i0, i1, i2)) index <- as.numeric(names(tt)[tt == 3]) dummys <- - one_H_alpha_all[i0 %in% index] - (one_H_alpha_es[[x[1]]]$dummys[i1 %in% index] + one_H_alpha_es[[x[2]]]$dummys[i2 %in% - index]) / 2 + one_H_alpha_all[i0 %in% index] - + (one_H_alpha_es[[x[1]]]$dummys[i1 %in% index] + + one_H_alpha_es[[x[2]]]$dummys[i2 %in% index]) / 2 return(list( estH = mean(dummys), sdH = sd(dummys), @@ -400,10 +418,12 @@ gl.report.diversity <- function(x, index <- as.numeric(names(tt)[tt == 3]) m2Ha <- - (two_H_alpha_es[[x[1]]]$dummys[i1 %in% index] + two_H_alpha_es[[x[2]]]$dummys[i2 %in% index]) / + (two_H_alpha_es[[x[1]]]$dummys[i1 %in% index] + + two_H_alpha_es[[x[2]]]$dummys[i2 %in% index]) / 2 dummys <- - ((two_H_alpha_all[i0 %in% index] - m2Ha) / (1 - m2Ha)) * (npops / (npops - 1)) + ((two_H_alpha_all[i0 %in% index] - m2Ha) / + (1 - m2Ha)) * (npops / (npops - 1)) return(list( estH = mean(dummys), sdH = sd(dummys), @@ -602,7 +622,8 @@ gl.report.diversity <- function(x, cat(report(" Saving ggplot(s) to the session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.hamming.r b/R/gl.report.hamming.r index c8d33189..fd90a851 100644 --- a/R/gl.report.hamming.r +++ b/R/gl.report.hamming.r @@ -52,7 +52,8 @@ #' } #' #' @return Returns unaltered genlight object -#' @author Custodian: Arthur Georges -- Post to \url{https://groups.google.com/d/forum/dartr} +#' @author Custodian: Arthur Georges -- Post to +#' \url{https://groups.google.com/d/forum/dartr} #' #' @examples #' gl.report.hamming(testset.gl[,1:100]) @@ -60,7 +61,7 @@ #' #' @seealso \code{\link{gl.filter.hamming}} #' -#' @family filters and filter reports +#' @family report functions #' @importFrom stats sd #' @import patchwork #' @export @@ -96,8 +97,9 @@ gl.report.hamming <- function(x, if (rs < 0 | rs > taglength) { stop( error( - "Fatal Error: Length of restriction enzyme recognition sequence must be greater than zero, - and less that the maximum length of a sequence tag; usually it is less than 9\n" + "Fatal Error: Length of restriction enzyme recognition sequence + must be greater than zero, and less that the maximum length of a + sequence tag; usually it is less than 9\n" ) ) } @@ -126,19 +128,21 @@ gl.report.hamming <- function(x, if (verbose >= 3) { cat( report( - " Hamming distance ranges from zero (sequence identity) to 1 (no bases shared at any position)\n" + " Hamming distance ranges from zero (sequence identity) to 1 + (no bases shared at any position)\n" ) ) } if (verbose >= 2) { cat( report( - " Calculating pairwise Hamming distances between trimmed Reference sequence tags\n" + " Calculating pairwise Hamming distances between trimmed + Reference sequence tags\n" ) ) } - count = 0 + count <-0 nL <- nLoc(x) d <- rep(NA, (((nL - 1) * nL) / 2)) @@ -155,11 +159,13 @@ gl.report.hamming <- function(x, # get title for plots if (datatype == "SNP") { title <- - paste0("SNP data (DArTSeq)\nPairwise Hamming Distance between sequence tags") + paste0("SNP data (DArTSeq)\nPairwise Hamming Distance between + sequence tags") } else { title <- paste0( - "Fragment P/A data (SilicoDArT)\nPairwise Hamming Distance between sequence tags" + "Fragment P/A data (SilicoDArT)\nPairwise Hamming Distance + between sequence tags" ) } @@ -167,7 +173,8 @@ gl.report.hamming <- function(x, if (plot.out) { cat( report( - " Plotting boxplot and histogram of Hamming distance, showing a threshold of", + " Plotting boxplot and histogram of Hamming distance, + showing a threshold of", threshold, "bp [HD", round(tld, 2), @@ -179,27 +186,32 @@ gl.report.hamming <- function(x, # Boxplot p1 <- - ggplot(as.data.frame(d), aes(y = d)) + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + geom_hline(yintercept = tld, - color = "red", - size = 1) + coord_flip() + plot_theme + xlim(range = c(-1, 1)) + ylim(0, 1) + ylab(" ") + theme(axis.text.y = element_blank(), - axis.ticks.y = element_blank()) + ggtitle(title) + ggplot(as.data.frame(d), aes(y = d)) + + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + geom_hline(yintercept = tld,color = "red", size = 1) + + coord_flip() + + plot_theme + + xlim(range = c(-1, 1)) + + ylim(0, 1) + + ylab(" ") + + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + ggtitle(title) # Histogram p2 <- - ggplot(as.data.frame(d), aes(x = d)) + geom_histogram(bins = 50, - color = plot_colors[1], - fill = plot_colors[2]) + geom_vline(xintercept = tld, - color = "red", - size = 1) + coord_cartesian(xlim = c(0, 1)) + xlab("Hamming distance") + ylab("Count") + annotate( - geom = "text", - x = tld + - 0.2, - y = max(graphics::hist( - d, breaks = seq(0, 1, by = 1 / 50), plot = FALSE - )$counts) * 0.75, - label = paste("Threshold of\n", threshold, - "bp [HD", round(tld, 2), "]") - ) + plot_theme + ggplot(as.data.frame(d), aes(x = d)) + + geom_histogram(bins = 50, color = plot_colors[1],fill = plot_colors[2]) + + geom_vline(xintercept = tld,color = "red",size = 1) + + coord_cartesian(xlim = c(0, 1)) + + xlab("Hamming distance") + + ylab("Count") + + annotate(geom = "text", + x = tld + 0.2, + y = max(graphics::hist(d, breaks = seq(0, 1, by = 1 / 50), + plot = FALSE)$counts) * 0.75, + label = paste("Threshold of\n", threshold, + "bp [HD", round(tld, 2), "]")) + + plot_theme cat(" No. of loci =", nLoc(x), "\n") cat(" No. of individuals =", nInd(x), "\n") @@ -281,7 +293,8 @@ gl.report.hamming <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.heterozygosity.r b/R/gl.report.heterozygosity.r index e6655428..622bc29c 100644 --- a/R/gl.report.heterozygosity.r +++ b/R/gl.report.heterozygosity.r @@ -101,7 +101,7 @@ #' #' @seealso \code{\link{gl.filter.heterozygosity}} #' -#' @family reporting functions +#' @family report functions #' @export gl.report.heterozygosity <- function(x, @@ -131,7 +131,8 @@ gl.report.heterozygosity <- function(x, if (!(method == "pop" | method == "ind")) { cat( warn( - "Warning: Method must either be by population or by individual, set to method='pop'\n" + "Warning: Method must either be by population or by individual, + set to method='pop'\n" ) ) method <- "pop" @@ -139,13 +140,15 @@ gl.report.heterozygosity <- function(x, if (n.invariant < 0) { cat(warn( - "Warning: Number of invariant loci must be non-negative, set to zero\n" + "Warning: Number of invariant loci must be non-negative, set to + zero\n" )) n.invariant <- 0 if (verbose == 5) { cat( report( - " No. of invariant loci can be esimated using gl.report.secondaries\n" + " No. of invariant loci can be esimated using + gl.report.secondaries\n" ) ) } @@ -155,7 +158,10 @@ gl.report.heterozygosity <- function(x, n.invariant > 0) { cat( warn( - " Warning: Estimation of adjusted heterozygosity requires that secondaries not to be removed. A gl.filter.secondaries call was found in the history. This may cause the results to be incorrect\n" + " Warning: Estimation of adjusted heterozygosity requires that + secondaries not to be removed. A gl.filter.secondaries call was + found in the history. This may cause the results to be + incorrect\n" ) ) } @@ -173,7 +179,8 @@ gl.report.heterozygosity <- function(x, cat( warn( " No population assignments detected, - individuals assigned to a single population labelled 'pop1'\n" + individuals assigned to a single population + labelled 'pop1'\n" ) ) } @@ -188,7 +195,8 @@ gl.report.heterozygosity <- function(x, if (verbose >= 2) { cat( report( - " Calculating Observed Heterozygosities, averaged across loci, for each population\n" + " Calculating Observed Heterozygosities, averaged across + loci, for each population\n" ) ) } @@ -230,7 +238,8 @@ gl.report.heterozygosity <- function(x, Ho.adjSD <- sqrt(( mapply(function(x, Mean) - sum((x - Mean) ^ 2, na.rm = TRUE), Ho.loc, Mean = Ho.adj) + n.invariant * Ho.adj ^ + sum((x - Mean) ^ 2, na.rm = TRUE), Ho.loc, Mean = Ho.adj) + + n.invariant * Ho.adj ^ 2 ) / (n_loc + n.invariant - 1)) @@ -243,7 +252,8 @@ gl.report.heterozygosity <- function(x, # samples remove the loci that are completely missing if (length(loci.na) > 0) { nind <- - mean(nrow(as.matrix(x)) - colSums(is.na(as.matrix(x)))[-loci.na]) + mean(nrow(as.matrix(x)) - + colSums(is.na(as.matrix(x)))[-loci.na]) # the number of samples in the matrix the number of # non-genotyped samples } else { @@ -305,9 +315,11 @@ gl.report.heterozygosity <- function(x, Hexp.adjSD[i] <- sqrt((sum(( H - Hexp.adj[i] - ) ^ 2, na.rm = TRUE) + n.invariant * Hexp.adj[i] ^ 2) / (n_loc[i] + n.invariant - 1)) + ) ^ 2, na.rm = TRUE) + n.invariant * Hexp.adj[i] ^ 2) / + (n_loc[i] + n.invariant - 1)) ########## - FIS_temp <- 1 - (mean(unlist(Ho.loc[i]),na.rm = T) / mean(uH,na.rm = T)) + FIS_temp <- 1 - (mean(unlist(Ho.loc[i]),na.rm = T) / + mean(uH,na.rm = T)) FIS[i] <- mean(FIS_temp, na.rm = T) #FISSD[i] <- sd(FIS_temp, na.rm = T) } @@ -361,7 +373,7 @@ gl.report.heterozygosity <- function(x, colors_pops_plot <- colors_pops_plot[colors_pops_plot$variable == "Ho", ] colors_pops_plot <- - colors_pops_plot[order(as.character(colors_pops_plot$value)), ] + colors_pops_plot[order(as.character(colors_pops_plot$value)), ] p3 <- ggplot(df.ordered, @@ -420,7 +432,8 @@ gl.report.heterozygosity <- function(x, )) + geom_bar(position = "dodge", stat = "identity", color = "black") + - scale_fill_manual(values = df.ordered$color) + scale_x_discrete(labels = paste( + scale_fill_manual(values = df.ordered$color) + + scale_x_discrete(labels = paste( df.ordered$pop, round(df.ordered$nInd, 0), sep = " | " @@ -431,7 +444,9 @@ gl.report.heterozygosity <- function(x, axis.ticks.y = element_blank(), axis.title.y = element_blank(), legend.position = "none" - ) + labs(fill = "Population") + ggtitle("Adjusted Observed Heterozygosity by Population") + ) + + labs(fill = "Population") + + ggtitle("Adjusted Observed Heterozygosity by Population") p2 <- ggplot(df.ordered, aes( @@ -441,7 +456,8 @@ gl.report.heterozygosity <- function(x, )) + geom_bar(position = "dodge", stat = "identity", color = "black") + - scale_fill_manual(values = df.ordered$color) + scale_x_discrete(labels = paste( + scale_fill_manual(values = df.ordered$color) + + scale_x_discrete(labels = paste( df.ordered$pop, round(df.ordered$nInd, 0), sep = " | " @@ -458,7 +474,8 @@ gl.report.heterozygosity <- function(x, axis.title.y = element_blank(), legend.position = "none" ) + - labs(fill = "Population") + ggtitle("Adjusted Expected Heterozygosity by Population") + labs(fill = "Population") + + ggtitle("Adjusted Expected Heterozygosity by Population") p3 <- (p1 / p2) } @@ -466,47 +483,47 @@ gl.report.heterozygosity <- function(x, # OUTPUT REPORT if (verbose >= 3) { - cat(" Reporting Heterozygosity by Population\n") - cat("\n No. of loci =", nLoc(x), "\n") - cat(" No. of individuals =", nInd(x), "\n") - cat(" No. of populations =", nPop(x), "\n") - cat(" Minimum Observed Heterozygosity: ", round(min(df$Ho, na.rm = TRUE), 6)) +cat(" Reporting Heterozygosity by Population\n") +cat("\n No. of loci =", nLoc(x), "\n") +cat(" No. of individuals =", nInd(x), "\n") +cat(" No. of populations =", nPop(x), "\n") +cat(" Minimum Observed Heterozygosity: ", round(min(df$Ho, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(min(df$Ho.adj, na.rm = TRUE), 6), "]\n") + cat(" [Corrected:", round(min(df$Ho.adj, na.rm = TRUE), 6), "]\n") } else { cat("\n") } - cat(" Maximum Observed Heterozygosity: ", round(max(df$Ho, na.rm = TRUE), 6)) +cat(" Maximum Observed Heterozygosity: ", round(max(df$Ho, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(max(df$Ho.adj, na.rm = TRUE), 6), "]\n") + cat(" [Corrected:", round(max(df$Ho.adj, na.rm = TRUE), 6), "]\n") } else { cat("\n") } cat(" Average Observed Heterozygosity: ", round(mean(df$Ho, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(mean(df$Ho.adj, na.rm = TRUE), 6), "]\n\n") + cat(" [Corrected:", round(mean(df$Ho.adj, na.rm = TRUE), 6), "]\n\n") } else { cat("\n\n") } cat(" Minimum Unbiased Expected Heterozygosity: ", round(min(df$uHe, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(min(df$He.adj, na.rm = TRUE), 6), "]\n") + cat(" [Corrected:", round(min(df$He.adj, na.rm = TRUE), 6), "]\n") } else { cat("\n") } cat(" Maximum Unbiased Expected Heterozygosity: ", round(max(df$uHe, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(max(df$He.adj, na.rm = TRUE), 6), "]\n") + cat(" [Corrected:", round(max(df$He.adj, na.rm = TRUE), 6), "]\n") } else { cat("\n") } cat(" Average Unbiased Expected Heterozygosity: ", round(mean(df$uHe, na.rm = TRUE), 6)) if (n.invariant > 0) { - cat(" [Corrected:", round(mean(df$He.adj, na.rm = TRUE), 6), "]\n\n") + cat(" [Corrected:", round(mean(df$He.adj, na.rm = TRUE), 6), "]\n\n") } else { cat("\n\n") } @@ -519,7 +536,7 @@ gl.report.heterozygosity <- function(x, ) } else { cat( - " Heterozygosity estimates not corrected for uncalled invariant loci\n" + " Heterozygosity estimates not corrected for uncalled invariant loci\n" ) } } @@ -558,7 +575,7 @@ gl.report.heterozygosity <- function(x, " Calculating observed heterozygosity for individuals\n" )) cat(report( - " Note: No adjustment for invariant loci (n.invariant set to 0)\n" + " Note: No adjustment for invariant loci (n.invariant set to 0)\n" )) } # Convert to matrix @@ -589,23 +606,31 @@ gl.report.heterozygosity <- function(x, if (plot.out) { upper <- ceiling(max(df$Ho) * 10) / 10 p1 <- - ggplot(df, aes(y = Ho)) + geom_boxplot(color = plot_colors_ind[1], fill = plot_colors_ind[2]) + coord_flip() + plot_theme + - xlim(range = c(-1, 1)) + ylim(0, upper) + ylab(" ") + theme(axis.text.y = element_blank(), - axis.ticks.y = element_blank()) + + ggplot(df, aes(y = Ho)) + + geom_boxplot(color = plot_colors_ind[1], fill = plot_colors_ind[2]) + + coord_flip() + + plot_theme + + xlim(range = c(-1, 1)) + + ylim(0, upper) + + ylab(" ") + + theme(axis.text.y = element_blank(),axis.ticks.y = element_blank()) + ggtitle("Observed Heterozygosity by Individual") # Histogram p2 <- - ggplot(df, aes(x = Ho)) + geom_histogram(bins = 25, - color = plot_colors_ind[1], - fill = plot_colors_ind[2]) + coord_cartesian(xlim = c(0, - upper)) + xlab("Observed heterozygosity") + ylab("Count") + plot_theme + ggplot(df, aes(x = Ho)) + +geom_histogram(bins =25,color = plot_colors_ind[1],fill =plot_colors_ind[2]) + + coord_cartesian(xlim = c(0, upper)) + + xlab("Observed heterozygosity") + + ylab("Count") + + plot_theme } outliers_temp <- ggplot_build(p1)$data[[1]]$outliers[[1]] outliers <- - data.frame(ID = as.character(df$ind.name[df$Ho %in% outliers_temp]), Ho = outliers_temp) + data.frame(ID = as.character(df$ind.name[df$Ho %in% outliers_temp]), + Ho = outliers_temp) # OUTPUT REPORT if (verbose >= 3) { @@ -665,7 +690,8 @@ gl.report.heterozygosity <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.hwe.r b/R/gl.report.hwe.r index 4e817cb8..c14d16ad 100644 --- a/R/gl.report.hwe.r +++ b/R/gl.report.hwe.r @@ -1,13 +1,16 @@ #' @name gl.report.hwe #' @title Reports departure from Hardy-Weinberg proportions #' @description -#' Calculates the probabilities of agreement with H-W proportions based on observed -#' frequencies of reference homozygotes, heterozygotes and alternate homozygotes. +#' Calculates the probabilities of agreement with H-W proportions based on +#' observed +#' frequencies of reference homozygotes, heterozygotes and alternate +#' homozygotes. #' #' @param x Name of the genlight object containing the SNP data [required]. #' @param subset Way to group individuals to perform H-W tests. Either a vector #' with population names, 'each', 'all' (see details) [default 'each']. -#' @param method_sig Method for determining statistical significance: 'ChiSquare' +#' @param method_sig Method for determining statistical significance: +#' 'ChiSquare' #' or 'Exact' [default 'Exact']. #' @param multi_comp Whether to adjust p-values for multiple comparisons #' [default FALSE]. @@ -158,7 +161,7 @@ #' 76:887-893. #' } #' @seealso \code{\link{gl.filter.hwe}} -#' @family filters/filter reports +#' @family report functions #' @export gl.report.hwe <- function(x, @@ -211,7 +214,8 @@ gl.report.hwe <- function(x, cat(error(" Detected Presence/Absence (SilicoDArT) data\n")) stop( error( - "Cannot calculate HWE from fragment presence/absence data. Please provide a SNP dataset.\n" + "Cannot calculate HWE from fragment presence/absence data. + Please provide a SNP dataset.\n" ) ) } @@ -219,7 +223,8 @@ gl.report.hwe <- function(x, if (alpha_val < 0 | alpha_val > 1) { cat( warn( - " Warning: level of significance per locus alpha must be an integer between 0 and 1, set to 0.05\n" + " Warning: level of significance per locus alpha must be an + integer between 0 and 1, set to 0.05\n" ) ) alpha_val <- 0.05 @@ -235,7 +240,8 @@ gl.report.hwe <- function(x, if (verbose >= 3) { cat( warn( - " Warning: Significance of tests may indicate heterogeneity among populations\n\n" + " Warning: Significance of tests may indicate heterogeneity + among populations\n\n" ) ) } @@ -260,7 +266,8 @@ gl.report.hwe <- function(x, if (pops_hwe == 0) { stop( error( - "Fatal Error: subset parameter must be \"each\", \"all\", or a list of populations existing in the dataset\n" + "Fatal Error: subset parameter must be \"each\", \"all\", + or a list of populations existing in the dataset\n" ) ) } @@ -281,7 +288,8 @@ gl.report.hwe <- function(x, if (verbose >= 3) { cat( warn( - " Warning: Significance of tests may indicate heterogeneity among populations\n\n" + " Warning: Significance of tests may indicate + heterogeneity among populations\n\n" ) ) } @@ -339,7 +347,8 @@ gl.report.hwe <- function(x, if (length(poplist) < 1) { stop( error( - "No populations left after removing populations with low sample size and populations with monomorphic loci" + "No populations left after removing populations with low sample + size and populations with monomorphic loci" ) ) } @@ -582,12 +591,12 @@ gl.report.hwe <- function(x, seq_2 <- c(seq_2, length(p_list)) for (i in 1:ceiling((length(p_list) / max_plots))) { p_final <- - ggtern::grid.arrange(grobs = p_list[seq_1[i]:seq_2[i]], ncol = 2) + ggtern::grid.arrange(grobs = p_list[seq_1[i]:seq_2[i]], ncol = 2) # SAVE INTERMEDIATES TO TEMPDIR if (save2tmp) { # creating temp file names temp_plot <- - tempfile(pattern = paste0("Plot_", seq_1[i], "_to_", seq_2[i])) + tempfile(pattern = paste0("Plot_", seq_1[i], "_to_", seq_2[i])) # saving to tempdir saveRDS(list(match_call, p_final), file = temp_plot) if (verbose >= 2) { @@ -631,7 +640,8 @@ gl.report.hwe <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } @@ -640,7 +650,8 @@ gl.report.hwe <- function(x, # FLAG SCRIPT END if (verbose >= 1) { - cat(" Reporting significant departures from Hardy-Weinberg Equilibrium\n") + cat(" Reporting significant departures from Hardy-Weinberg + Equilibrium\n") if (nrow(df) == 0) { cat(" No significant departures\n") } else { @@ -649,7 +660,8 @@ gl.report.hwe <- function(x, "are listed\n") cat( important( - " Adjustment of p-values for multiple comparisons vary with sample size\n" + " Adjustment of p-values for multiple comparisons vary + with sample size\n" ) ) print(df, row.names = FALSE) diff --git a/R/gl.report.ld.map.r b/R/gl.report.ld.map.r index 05e6b4de..81739d53 100644 --- a/R/gl.report.ld.map.r +++ b/R/gl.report.ld.map.r @@ -1,29 +1,48 @@ #' @name gl.report.ld.map -#' @title Calculates pairwise linkage disequilibrium in SNPs mapped to a -#' reference genome +#' @title Calculates pairwise linkage disequilibrium by population #' @description -#' This function calculates pairwise linkage disequilibrium (LD) within each -#' chromosome and by population using the function \link[snpStats]{ld} -#' (package snpStats). +#' This function calculates pairwise linkage disequilibrium (LD) by population +#' using the function \code{\link[snpStats]{ld}} (package snpStats). #' -#' This function requires that SNPs to be mapped to a reference genome and the -#' information for SNP's position must be stored in the genlight accessor -#' position" and the SNP's chromosome name in the accessor chromosome. +#' If SNPs are not mapped to a reference genome, the parameter +#' \code{ld_max_pairwise} +#' should be set as NULL (the default). In this case, the +#' function will assign the same chromosome ("1") to all the SNPs in the dataset +#' and assign a sequence from 1 to n loci as the position of each SNP. The +#' function will then calculate LD for all possible SNP pair combinations. +#' +#' If SNPs are mapped to a reference genome, the parameter +#' \code{ld_max_pairwise} +#' should be filled out (i.e. not NULL). In this case, the +#' information for SNP's position should be stored in the genlight accessor +#' "@@position" and the SNP's chromosome name in the accessor "@@chromosome" +#' (see examples). The function will then calculate LD within each chromosome +#' and for all possible SNP pair combinations within a distance of +#' \code{ld_max_pairwise}. #' #' @param x Name of the genlight object containing the SNP data [required]. #' @param ld_max_pairwise Maximum distance in number of base pairs at which LD -#' should be calculated [default 1000000]. -#' @param ld_resolution Resolution at which LD should be reported in number of -#' base pairs [default 10000] -#' @param maf Minor allele frequency threshold to filter out loci [default 0.05]. +#' should be calculated [default NULL]. +#' @param maf Minor allele frequency (by population) threshold to filter out +#' loci. If a value > 1 is provided it will be interpreted as MAC (i.e. the +#' minimum number of times an allele needs to be observed) [default 0.05]. #' @param ld_stat The LD measure to be calculated: "LLR", "OR", "Q", "Covar", -#' "D.prime", "R.squared", and "R" [default "R.squared"]. +#' "D.prime", "R.squared", and "R". See \code{\link[snpStats]{ld}} +#' (package snpStats) for details [default "R.squared"]. +#' @param ind.limit Minimum number of individuals that a population should +#' contain to take it in account to report loci in LD [default 10]. +#' @param stat_keep Name of the column from the slot \code{loc.metrics} to be +#' used to choose SNP to be kept [default "AvgPIC"]. +#' @param ld_threshold_pops LD threshold to report in the plot of "Number of +#' populations in which the same SNP pair are in LD" [default 0.2]. #' @param plot.out Specify if plot is to be produced [default TRUE]. -#' @param stat_keep Name of the column from the slot loc.metrics to be used to -#' choose SNP to be kept [default "AvgPIC"]. -#' @param plot_theme User specified theme [default theme_dartR()]. -#' @param plot_colors Vector with two color names for the borders and fill -#' [default two_colors]. +#' @param plot_theme User specified theme [default NULL]. +#' @param histogram_colors Vector with two color names for the borders and fill +#' [default NULL]. +#' @param boxplot_colors A color palette for box plots by population or a list +#' with as many colors as there are populations in the dataset +#' [default NULL]. +#' @param bins Number of bins to display in histograms [default 50]. #' @param save2tmp If TRUE, saves any ggplots and listings to the session #' temporary directory (tempdir) [default FALSE]. #' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, @@ -31,41 +50,40 @@ #' [default 2, unless specified using gl.set.verbosity]. #' #' @details -#' This function reports pairwise LD of those SNPs in which the LD measure is -#' > 0 in all the populations. -#' The LD plot shows the pairwise LD measure against distance in number -#' of base pairs pooled over all the chromosomes and a red line representing the -#' threshold (R.squared = 0.2) that is commonly used to imply that two loci are -#' unlinked (Delourme et al., 2013; Li et al., 2014). -#' @references -#' \itemize{ -#' \item Delourme, R., Falentin, C., Fomeju, B. F., Boillot, M., Lassalle, G., André, -#' I., . . . Marty, A. (2013). High-density SNP-based genetic map development -#' and linkage disequilibrium assessment in Brassica napusL. BMC genomics, 14(1), 120. -#' \item Li, X., Han, Y., Wei, Y., Acharya, A., Farmer, A. D., Ho, J., . . . Brummer, -#' E. C. (2014). Development of an alfalfa SNP array and its use to evaluate -#' patterns of population structure and linkage disequilibrium. PLoS One, 9(1), e84329. -#' } +#' This function reports LD between SNP pairs by population. +#' The function \code{\link{gl.filter.ld}} filters out the SNPs in LD using as +#' input the results of \code{\link{gl.report.ld.map}}. The actual number of +#' SNPs to be filtered out depends on the parameters set in the function +#' \code{\link{gl.filter.ld}}. +#' +#' Boxplots of LD by population and +#' a histogram showing LD frequency are presented. +#' #' @return A dataframe with information for each SNP pair in LD. -#' @author Custodian: Luis Mijangos -- Post to \url{https://groups.google.com/d/forum/dartr} +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} #' @examples -#' \dontrun{ #' x <- platypus.gl +#' x <- gl.filter.callrate(x,threshold = 1) #' x$position <- x$other$loc.metrics$ChromPos_Platypus_Chrom_NCBIv1 -#' x$chromosome <- x$other$loc.metrics$Chrom_Platypus_Chrom_NCBIv1 -#' gl.report.ld.map(x,ld_resolution = 100000) -#' } +#' x$chromosome <- as.factor(x$other$loc.metrics$Chrom_Platypus_Chrom_NCBIv1) +#' ld_res <- gl.report.ld.map(x,ld_max_pairwise = 10000000) +#' @seealso \code{\link{gl.filter.ld}} +#' @family report functions #' @export gl.report.ld.map <- function(x, - ld_max_pairwise = 1000000, - ld_resolution = 10000, + ld_max_pairwise = NULL, maf = 0.05, ld_stat = "R.squared", + ind.limit = 10, stat_keep = "AvgPIC", + ld_threshold_pops = 0.2, plot.out = TRUE, - plot_theme = theme_dartR(), - plot_colors = two_colors, + plot_theme = NULL, + histogram_colors = NULL, + boxplot_colors = NULL, + bins = 50, save2tmp = FALSE, verbose = NULL) { # SET VERBOSITY @@ -101,6 +119,15 @@ gl.report.ld.map <- function(x, } # DO THE JOB + # by default SNPs are mapped to a reference genome + SNP_map <- TRUE + + if(is.null(ld_max_pairwise)){ + x$position <- 1:nLoc(x) + x$chromosome <- as.factor(rep("1",nLoc(x))) + # SNPs are not mapped to a reference genome + SNP_map <- FALSE + } x_list <- seppop(x) @@ -120,22 +147,25 @@ gl.report.ld.map <- function(x, ) for (i in 1:length(x_list)) { + pop_ld <- x_list[[i]] pop_name <- popNames(pop_ld) + + if(nInd(pop_ld)<=ind.limit){ + cat(warn(paste(" Skipping population",pop_name,"from analysis because + it has less than",ind.limit,"individuals.\n"))) + next() + } + if (verbose >= 2) { - cat(report("Calculating pairwise LD in population", pop_name, "\n")) + cat(report(" Calculating pairwise LD in population", pop_name, "\n")) } - # keeping only mapped loci - mapped <- which(pop_ld$position != 0) - pop_ld <- - gl.keep.loc(pop_ld, loc.list = locNames(pop_ld)[mapped], verbose = 0) # ordering SNPs by chromosome and position hold <- pop_ld pop_ld <- hold[, order(hold$chromosome, hold$position)] pop_ld$other$loc.metrics <- hold$other$loc.metrics[order(hold$chromosome, hold$position),] - pop_ld <- gl.filter.allna(pop_ld, verbose = 0) - pop_ld <- gl.recalc.metrics(pop_ld, verbose = 0) + pop_ld <- gl.recalc.metrics(pop_ld, verbose = 0) if (maf > 0) { pop_ld <- gl.filter.maf(pop_ld, threshold = maf, verbose = 0) } @@ -146,14 +176,15 @@ gl.report.ld.map <- function(x, verbose = 0 ) - # Read a pedfile as "SnpMatrix" object using a modified version of the function - # read.pedfile from package snpStats + # Read a pedfile as "SnpMatrix" object using a modified version of the + # function read.pedfile from package snpStats snp_stats <- utils.read.ped( file = paste0(tempdir(), "/", "gl_plink", "_", pop_name, ".ped"), snps = paste0(tempdir(), "/", "gl_plink", "_", pop_name, ".map") , sep = " ", - show_warnings = F + show_warnings = F, + na.strings = NA ) ld_map <- snp_stats$map @@ -183,26 +214,41 @@ gl.report.ld.map <- function(x, if (nrow(ld_map_loci) <= 1) { next } - # this is the mean distance between each snp which is used to determine the depth at which - # LD analyses are performed - mean_dis <- mean(diff(ld_map_loci$loc_bp)) - ld_depth_b <- ceiling((ld_max_pairwise / mean_dis)) - ld_snps <- - snpStats::ld(genotype_loci, depth = ld_depth_b, stats = ld_stat) #function to calculate LD + + #if SNPs are mapped to a reference genome + if(SNP_map==TRUE){ + + # this is the mean distance between each snp which is used to determine + # the depth at which LD analyses are performed + mean_dis <- mean(diff(ld_map_loci$loc_bp)) + ld_depth_b <- ceiling((ld_max_pairwise / mean_dis)) - 1 + #function to calculate LD + ld_snps <- snpStats::ld(genotype_loci, depth = ld_depth_b, + stats = ld_stat) + + #if SNPs are not mapped to a reference genome + }else{ + #function to calculate LD + ld_snps <- snpStats::ld(genotype_loci,genotype_loci, stats = ld_stat) + ld_snps[lower.tri(ld_snps,diag = TRUE)] <- 0 + ld_max_pairwise <- nLoc(x) + + } + ld_columns <- as.matrix(ld_snps) colnames(ld_columns) <- rownames(ld_columns) ld_columns <- as.data.frame(as.table(as.matrix(ld_columns))) - ld_columns <- - ld_columns[-ld_columns$Freq < 0,] #remove cases where LD was not calculated + # remove cases where LD was not calculated + ld_columns <- ld_columns[-ld_columns$Freq < 0,] ld_columns$Var1 <- as.numeric(as.character(ld_columns$Var1)) ld_columns$Var2 <- as.numeric(as.character(ld_columns$Var2)) - #determine the distance at which LD was calculated + # determine the distance at which LD was calculated ld_columns$dis <- ld_columns$Var2 - ld_columns$Var1 - #remove pairwise LD results that were calculated at larger distances than the required in the - # settings and then filtering and rearranging dataframes to match each other and then merge them - df_linkage_temp <- - ld_columns[which(ld_columns$dis <= ld_max_pairwise),] + # remove pairwise LD results that were calculated at larger distances than + # the required in the settings and then filtering and rearranging + # dataframes to match each other and then merge them + df_linkage_temp <- ld_columns[which(ld_columns$dis <= ld_max_pairwise),] if (nrow(df_linkage_temp) < 1) { next } @@ -212,20 +258,14 @@ gl.report.ld.map <- function(x, snp_loc <- ld_map_loci[, c("chr", "snp.name", "stat_keep", "loc_bp")] dtb <- data.table(snp_loc, key = "loc_bp") - t_locationb <- - ldtb[dtb, c("snp.name", "stat_keep"), nomatch = 0] - t_locationc <- - ldtc[dtb, c("snp.name", "stat_keep"), nomatch = 0] + t_locationb <- ldtb[dtb, c("snp.name", "stat_keep"), nomatch = 0] + t_locationc <- ldtc[dtb, c("snp.name", "stat_keep"), nomatch = 0] - df_linkage_temp <- - df_linkage_temp[order(df_linkage_temp$Var1),] + df_linkage_temp <- df_linkage_temp[order(df_linkage_temp$Var1),] df_linkage_temp <- cbind(df_linkage_temp, t_locationb) - df_linkage_temp <- - df_linkage_temp[order(df_linkage_temp$Var2),] + df_linkage_temp <- df_linkage_temp[order(df_linkage_temp$Var2),] df_linkage_temp <- cbind(df_linkage_temp, t_locationc) - df_linkage_temp <- - df_linkage_temp[order(df_linkage_temp$Var1),] - + df_linkage_temp <- df_linkage_temp[order(df_linkage_temp$Var1),] df_linkage_temp <- cbind(chr_list[chrom], df_linkage_temp) df_linkage_temp <- cbind(pop_name, df_linkage_temp) @@ -252,154 +292,96 @@ gl.report.ld.map <- function(x, } } - break_bins <- - c(seq(1, ld_max_pairwise, ld_resolution), ld_max_pairwise) - - split_df <- split(df_linkage, f = df_linkage$pop) - split_df <- lapply(split_df, function(x) { - x[order(x$distance), ] - }) - bins_ld_temp <- - lapply(split_df, function(x) { - fields::stats.bin(x$distance, x$ld_stat, breaks = break_bins) - }) - bins_ld <- - lapply(seq_along(bins_ld_temp), function(i) { - as.data.frame(cbind( - names(bins_ld_temp[i]), - unname(bins_ld_temp[[i]]$breaks[2:length(bins_ld_temp[[i]]$breaks)]), - unname(bins_ld_temp[[i]]$stats[2, ]) - )) - }) - bins_ld <- rbindlist(bins_ld) - colnames(bins_ld) <- c("pop", "distance", "ld_stat") - bins_ld$pop <- as.factor(bins_ld$pop) - bins_ld$distance <- as.numeric(bins_ld$distance) - bins_ld$ld_stat <- as.numeric(bins_ld$ld_stat) - - loci_pairs <- - lapply(split_df, function(x) { - unlist(x[, "locus_a_b"]) - }) - loci_pairs_all_pops <- Reduce(intersect, loci_pairs) - df_ld <- - df_linkage[df_linkage$locus_a_b %in% loci_pairs_all_pops, ] - df_ld <- df_ld[order(df_ld$locus_a_b), ] - - df_ld_split <- split(df_ld, f = df_ld$locus_a_b) - df_ld_mean_ld <- lapply(df_ld_split, function(x) { - mean(x$ld_stat) - }) - df_ld_mean_ld <- - as.data.frame(cbind(names(df_ld_mean_ld), unlist(df_ld_mean_ld))) - colnames(df_ld_mean_ld) <- c("snp_pair", "ld_stat") - - ld_stat_res <- as.numeric(df_ld_mean_ld$ld_stat) + df_ld <- df_linkage if (plot.out) { + + if(is.null(histogram_colors)){ + histogram_colors <- two_colors + } + + if(is.null(plot_theme)){ + plot_theme = theme_dartR() + } + + if(is.null(boxplot_colors)){ + boxplot_colors <- discrete_palette(length(levels(pop(x)))) + } + + if (is(boxplot_colors, "function")) { + boxplot_colors <- boxplot_colors(length(levels(pop(x)))) + } + + if (!is(boxplot_colors,"function")) { + boxplot_colors <- boxplot_colors + } + # get title for plots title1 <- "SNP data - Pairwise LD" - # Calculate minimum and maximum graph cutoffs - min_ld <- min(ld_stat_res, na.rm = TRUE) - min_ld <- trunc(min_ld * 100) / 100 - # Boxplot p1 <- - ggplot(data.frame(ld_stat_res), aes(y = ld_stat_res)) + - geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + - coord_flip() + + ggplot(df_ld, aes(x=pop,y = ld_stat,color=pop)) + + geom_boxplot() + plot_theme + - xlim(range = c(-1, 1)) + - ylim(min_ld, 1) + ylab(" ") + - theme(axis.text.y = element_blank(), - axis.ticks.y = element_blank()) + - ggtitle(title1) + scale_color_manual(values = boxplot_colors) + + ylab(ld_stat) + + ggtitle(title1) + + theme(legend.position = "bottom")+ + labs(title = "Pairwise LD by population", color = "") + + theme(axis.title.x=element_blank(), + axis.text.x = element_blank(), + axis.ticks.x = element_blank()) # Histogram p2 <- - ggplot(data.frame(ld_stat_res), aes(x = ld_stat_res)) + - geom_histogram(bins = 100, - color = plot_colors[1], - fill = plot_colors[2]) + - coord_cartesian(xlim = c(min_ld, 1)) + + ggplot(df_ld, aes(x = ld_stat)) + + geom_histogram(bins = bins, + color = histogram_colors[1], + fill = histogram_colors[2]) + xlab(ld_stat) + ylab("Count") + plot_theme - # pairwise LD by population - distance <- NULL - p4 <- - ggplot(bins_ld, aes(x = distance, y = ld_stat, colour = pop)) + - geom_line(size = 1) + - geom_point(size = 2) + - geom_hline(aes(yintercept = 0.2, colour = "LD threshold for unlinked loci"), - size = 1) + - labs(title = "Pairwise LD by population", color = "") + - xlab("Base pairs") + - ylab(ld_stat) + - plot_theme + - theme(legend.position = "bottom") + # Number of populations in which the same SNP pairs are in LD + df_ld_temp <- df_ld[which(df_ld$ld_stat>ld_threshold_pops),] + ld_pops_tmp <- table(rowSums(as.data.frame.matrix(with(df_ld_temp, + table(locus_a_b,pop))))) + ld_pops <- as.data.frame(cbind(as.numeric(names(ld_pops_tmp)), + unlist(unname(ld_pops_tmp)))) + colnames(ld_pops) <- c("pops","n_loc") + + n_loc <- pops <- NULL + + p3 <- ggplot(ld_pops,aes(x=pops,y=n_loc))+ + geom_col(color = histogram_colors[1],fill = histogram_colors[2]) + + ylab("Count")+ + xlab(paste("Number of populations in which the same SNP pair are in LD with an",ld_stat,">",ld_threshold_pops)) + + plot_theme } # Print out some statistics - stats <- summary(ld_stat_res) - cat(" Reporting pairwise LD\n") - cat(" No. of pairs of loci in LD =", length(ld_stat_res), "\n") - cat(" No. of individuals =", nInd(x), "\n") - cat(" Minimum : ", stats[1], "\n") - cat(" 1st quartile : ", stats[2], "\n") - cat(" Median : ", stats[3], "\n") - cat(" Mean : ", stats[4], "\n") - cat(" 3r quartile : ", stats[5], "\n") - cat(" Maximum : ", stats[6], "\n") - cat(" Missing Rate Overall: ", round(sum(is.na(as.matrix( - x - ))) / (nLoc(x) * nInd(x)), 2), "\n\n") - - # Determine the loss of loci for a given threshold - quantile_res <- - quantile( - ld_stat_res, - probs = seq(0, 1, 1 / 20), - type = 1, - na.rm = T - ) - retained <- - unlist(lapply(quantile_res, function(y) { - res <- length(ld_stat_res[ld_stat_res <= y]) - })) - pc.retained <- round(retained * 100 / length(ld_stat_res), 1) - filtered <- length(ld_stat_res) - retained - pc.filtered <- 100 - pc.retained - df <- - data.frame(as.numeric(sub("%", "", names(quantile_res))), - quantile_res, - retained, - pc.retained, - filtered, - pc.filtered) - colnames(df) <- - c("Quantile", - "Threshold", - "Retained", - "Percent", - "Filtered", - "Percent") - df <- df[order(-df$Quantile),] - df$Quantile <- paste0(df$Quantile, "%") - rownames(df) <- NULL - + # stats <- summary(ld_stat_res) + # cat(" Reporting pairwise LD\n") + # cat(" No. of pairs of loci in LD =", length(ld_stat_res), "\n") + # cat(" No. of individuals =", nInd(x), "\n") + # cat(" Minimum : ", stats[1], "\n") + # cat(" 1st quartile : ", stats[2], "\n") + # cat(" Median : ", stats[3], "\n") + # cat(" Mean : ", stats[4], "\n") + # cat(" 3r quartile : ", stats[5], "\n") + # cat(" Maximum : ", stats[6], "\n") + # cat(" Missing Rate Overall: ", round(sum(is.na(as.matrix( + # x + # ))) / (nLoc(x) * nInd(x)), 2), "\n\n") # PRINTING OUTPUTS if (plot.out) { - # using package patchwork - p3 <- (p1 / p2 / p4) + plot_layout(heights = c(1, 2, 2)) - print(p3) - + # using package patchwork + p4 <- p1 / p2 / p3 + print(p4) } - print(df) - + # SAVE INTERMEDIATES TO TEMPDIR # creating temp file names @@ -412,21 +394,11 @@ gl.report.ld.map <- function(x, as.character(match.call()), collapse = "_") # saving to tempdir - saveRDS(list(match_call, p3), file = temp_plot) + saveRDS(list(match_call, p4), file = temp_plot) if (verbose >= 2) { cat(report(" Saving the ggplot to session tempfile\n")) } } - temp_table <- tempfile(pattern = "Table_") - saveRDS(list(match_call, df), file = temp_table) - if (verbose >= 2) { - cat(report(" Saving tabulation to session tempfile\n")) - cat( - report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" - ) - ) - } } # FLAG SCRIPT END @@ -436,6 +408,6 @@ gl.report.ld.map <- function(x, } # RETURN - return(df_ld) + return(invisible(df_ld)) } diff --git a/R/gl.report.ld.r b/R/gl.report.ld.r index c2c8e943..65342506 100644 --- a/R/gl.report.ld.r +++ b/R/gl.report.ld.r @@ -385,7 +385,7 @@ gl.report.ld <- function(x, nobj <- name filename <- paste0("LD_", name, ".rdata") } else { - filename = "LDallp.rdata" + filename <-"LDallp.rdata" nobj <- "LDallp" } diff --git a/R/gl.report.locmetric.r b/R/gl.report.locmetric.r index 668080fe..d0ac1c0b 100644 --- a/R/gl.report.locmetric.r +++ b/R/gl.report.locmetric.r @@ -99,7 +99,7 @@ #' @seealso \code{\link{gl.filter.locmetric}}, \code{\link{gl.list.reports}}, #' \code{\link{gl.print.reports}} #' -#' @family filters and filter reports +#' @family report functions #' #' @export #' diff --git a/R/gl.report.maf.r b/R/gl.report.maf.r index 7e137e87..6de2fdcc 100644 --- a/R/gl.report.maf.r +++ b/R/gl.report.maf.r @@ -54,7 +54,7 @@ #' gl <- gl.report.maf(testset.gl) #' @seealso \code{\link{gl.filter.maf}}, \code{\link{gl.list.reports}}, #' \code{\link{gl.print.reports}} -#' @family filter reports functions +#' @family report functions #' @export gl.report.maf <- function(x, diff --git a/R/gl.report.monomorphs.r b/R/gl.report.monomorphs.r index 4f8cf506..dd871f58 100644 --- a/R/gl.report.monomorphs.r +++ b/R/gl.report.monomorphs.r @@ -26,7 +26,7 @@ #' # SilicoDArT data #' gl.report.monomorphs(testset.gs) #' @seealso \code{\link{gl.filter.monomorphs}} -#' @family filters and filter reports +#' @family report functions #' @export gl.report.monomorphs <- function(x, @@ -62,7 +62,7 @@ gl.report.monomorphs <- function(x, all(row == 1, na.rm = TRUE) | all(is.na(row))) { loc.list[i] <- lN[i] if (all(is.na(row))) { - na.counter = na.counter + 1 + na.counter <-na.counter + 1 } } } @@ -78,7 +78,7 @@ gl.report.monomorphs <- function(x, all(row == 2, na.rm = TRUE) | all(is.na(row))) { loc.list[i] <- lN[i] if (all(is.na(row))) { - na.counter = na.counter + 1 + na.counter <-na.counter + 1 } } } diff --git a/R/gl.report.overshoot.r b/R/gl.report.overshoot.r index d724cf2f..dd1af7cd 100644 --- a/R/gl.report.overshoot.r +++ b/R/gl.report.overshoot.r @@ -26,7 +26,7 @@ #' @examples #' gl.report.overshoot(testset.gl) #' @seealso \code{\link{gl.filter.overshoot}} -#' @family filter report functions +#' @family report functions #' @export gl.report.overshoot <- function(x, @@ -50,7 +50,8 @@ gl.report.overshoot <- function(x, cat(error(" Detected Presence/Absence (SilicoDArT) data\n")) stop( error( - "Cannot identify overshoot arising from SNPS deleted with adaptors for fragment presence/absence data. + "Cannot identify overshoot arising from SNPS deleted with + adaptors for fragment presence/absence data. Please provide a SNP dataset.\n" ) ) @@ -59,7 +60,8 @@ gl.report.overshoot <- function(x, if (length(x@other$loc.metrics$TrimmedSequence) != nLoc(x)) { stop( error( - "Fatal Error: Data must include Trimmed Sequences for each loci in a column called 'TrimmedSequence' + "Fatal Error: Data must include Trimmed Sequences for each loci + in a column called 'TrimmedSequence' in the @other$loc.metrics slot.\n" ) ) @@ -67,7 +69,8 @@ gl.report.overshoot <- function(x, if (length(x@other$loc.metrics$SnpPosition) != nLoc(x)) { stop(error( - "Fatal Error: Data must include position information for each loci.\n" + "Fatal Error: Data must include position information for each + loci.\n" )) } @@ -75,7 +78,8 @@ gl.report.overshoot <- function(x, if (verbose >= 2) { cat(report( - " Identifying loci for which the SNP has been trimmed with the adaptor\n" + " Identifying loci for which the SNP has been trimmed with the + adaptor\n" )) } @@ -118,7 +122,8 @@ gl.report.overshoot <- function(x, ) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.pa.r b/R/gl.report.pa.r index 35591f9c..5db2c45c 100644 --- a/R/gl.report.pa.r +++ b/R/gl.report.pa.r @@ -11,7 +11,7 @@ #' @param method Method to calculate private alleles: 'pairwise' comparison or #' compare each population against the rest 'one2rest' [default 'pairwise']. #' @param plot.out Specify if Sankey plot is to be produced [default TRUE]. -#' @param font_plot Numeric font size in pixels for the node text labels +#' @param font_plot Numeric font size in pixels for the node text labels #' [default 14]. #' @param map.interactive Specify whether an interactive map showing private #' alleles between populations is to be produced [default FALSE]. @@ -31,8 +31,8 @@ #' pairs of populations, otherwise it runs a single comparison between x and #' x2. #' -#'\strong{Hint:} in case you want to run comparisons between individuals -#'(assuming individual names are unique), you can simply redefine your +#'\strong{Hint:} in case you want to run comparisons between individuals +#'(assuming individual names are unique), you can simply redefine your #'population names with your individual names, as below: #' #' \code{pop(gl) <- indNames(gl)} @@ -74,10 +74,10 @@ #' #' @return A data.frame. Each row shows, for each pair of populations the number #' of individuals in each population, the number of loci with fixed differences -#' (same for both populations) in pop1 (compared to pop2) and vice versa. Same +#' (same for both populations) in pop1 (compared to pop2) and vice versa. Same #' for private alleles and finally the absolute mean allele frequency #' difference between loci (AFD). -#' @author Custodian: Bernd Gruber -- Post to +#' @author Custodian: Bernd Gruber -- Post to #' \url{https://groups.google.com/d/forum/dartr} #' @references \itemize{ #' \item Berner, D. (2019). Allele frequency difference AFD – an intuitive @@ -88,7 +88,7 @@ #' out <- gl.report.pa(testset.gl[1:20,]) #' @seealso \code{\link{gl.list.reports}}, #' \code{\link{gl.print.reports}} -#' @family reporting functions +#' @family report functions #' @importFrom tidyr pivot_longer #' @export @@ -97,402 +97,416 @@ gl.report.pa <- function(x, x2 = NULL, method = "pairwise", plot.out = TRUE, - font_plot =14, + font_plot = 14, map.interactive = FALSE, palette_discrete = discrete_palette, save2tmp = FALSE, verbose = NULL) { - # SET VERBOSITY - verbose <- gl.check.verbosity(verbose) - - # FLAG SCRIPT START - funname <- match.call()[[1]] - utils.flag.start(func = funname, - build = "Jackson", - verbosity = verbose) - - # CHECK DATATYPE - datatype1 <- utils.check.datatype(x, verbose = verbose) - if (!is.null(x2)) { - datatype2 <- utils.check.datatype(x2, verbose = verbose) + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jackson", + verbosity = verbose) + + # CHECK DATATYPE + datatype1 <- utils.check.datatype(x, verbose = verbose) + if (!is.null(x2)) { + datatype2 <- utils.check.datatype(x2, verbose = verbose) + } + + # FUNCTION SPECIFIC ERROR CHECKING + + # check if package is installed + pkg <- "tibble" + if (!(requireNamespace(pkg, quietly = TRUE))) { + stop(error( + "Package", + pkg, + " needed for this function to work. Please install it." + )) + } + # check if package is installed + pkg <- "networkD3" + if (!(requireNamespace(pkg, quietly = TRUE))) { + stop(error( + "Package", + pkg, + " needed for this function to work. Please install it." + )) + } + pkg <- "tidyverse" + if (!(requireNamespace(pkg, quietly = TRUE))) { + stop(error( + "Package", + pkg, + " needed for this function to work. Please install it." + )) + } + + if (!is.null(x2)) { + pops <- list(pop1 = x, pop2 = x2) + x <- rbind(x, x2) + } else { + if (length(unique(pop(x))) > 1) { + pops <- seppop(x) + } else { + stop( + error( + "Only one population provided. Check the @pop slot in your + genlight object.\n " + ) + ) } + } + + # DO THE JOB For method 'pairwise' + if (method == "pairwise") { + pc <- t(combn(length(pops), 2)) + pall <- + data.frame( + p1 = pc[, 1], + p2 = pc[, 2], + pop1 = names(pops)[pc[, 1]], + pop2 = names(pops)[pc[, 2]], + N1 = NA, + N2 = NA, + fixed = NA, + priv1 = NA, + priv2 = NA, + Chao1 = NA, + Chao2= NA, + totalpriv = NA, + AFD = NA + ) - # FUNCTION SPECIFIC ERROR CHECKING - - # check if package is installed - pkg <- "tibble" - if (!(requireNamespace(pkg, quietly = TRUE))) { - stop(error( - "Package", - pkg, - " needed for this function to work. Please install it." - )) - } - # check if package is installed - pkg <- "networkD3" - if (!(requireNamespace(pkg, quietly = TRUE))) { - stop(error( - "Package", - pkg, - " needed for this function to work. Please install it." - )) - } - pkg <- "tidyverse" - if (!(requireNamespace(pkg, quietly = TRUE))) { - stop(error( - "Package", - pkg, - " needed for this function to work. Please install it." - )) + for (i in 1:nrow(pc)) { + i1 <- pall[i, 1] + i2 <- pall[i, 2] + + p1 <- as.matrix(pops[[i1]]) + p2 <- as.matrix(pops[[i2]]) + p1alf <- colMeans(p1, na.rm = T) / 2 + p2alf <- colMeans(p2, na.rm = T) / 2 + + pall[i, c("N1","N2")] <- c(nrow(p1), nrow(p2)) + pall[i, "fixed"] <- sum(abs(p1alf - p2alf) == 1, na.rm = T) + + pall[i, "priv1"] <- sum(p2alf == 0 & + p1alf != 0, na.rm = T) + sum(p2alf == 1 & + p1alf != 1, na.rm = T) + pall[i, "priv2"] <- sum(p1alf == 0 & + p2alf != 0, na.rm = T) + sum(p1alf == 1 & + p2alf != 1, na.rm = T) + pall[i, "totalpriv"] <- pall[i, 8] + pall[i, 9] + pall[i, "AFD"] <- round(mean(abs(p1alf - p2alf), na.rm = T), 3) + + # pa_Chao <- utils.pa.Chao(x=x,pop1_m=pops[[i1]],pop2_m=pops[[i2]]) + # pall[i,"Chao1"] <- round(pa_Chao[[1]],0) + # pall[i,"Chao2"] <- round(pa_Chao[[2]],0) + } - if (!is.null(x2)) { - pops <- list(pop1 = x, pop2 = x2) - x <- rbind(x, x2) - } else { - if (length(unique(pop(x))) > 1) { - pops <- seppop(x) - } else { - stop( - error( - "Only one population provided. Check the @pop slot in your genlight object.\n " - ) - ) + if (plot.out) { + mm <- matrix(0, nPop(x), nPop(x)) + + for (i in 1:nrow(pall)) + mm[pall[i, 1], pall[i, 2]] <- pall$priv2[i] + for (i in 1:nrow(pall)) + mm[pall[i, 2], pall[i, 1]] <- pall$priv1[i] + + colnames(mm) <- popNames(x) + rownames(mm) <- popNames(x) + + data <- as.data.frame(mm) + value <- target <- name <- NULL + data_long <- + tibble::rownames_to_column(data, "source") + data_long <- tibble::as_tibble(data_long) + data_long <- + tidyr::pivot_longer(data_long,-source, "target") + data_long <- data_long[data_long$value > 0,] + + data_long$target <- + gsub("\\.", " ", data_long$target) + data_long$source <- paste0("src_", data_long$source) + data_long$target <- + paste0("trgt_", data_long$target) + + nodes <- + data.frame(name = unique(c( + data_long$source, data_long$target + )), + stringsAsFactors = FALSE) + nodes <- + tibble::tibble(name = unique(c( + data_long$source, data_long$target + )), + target = grepl("trgt_", name)) + + data_long$IDsource <- + match(data_long$source, nodes$name) - 1 + data_long$IDtarget <- + match(data_long$target, nodes$name) - 1 + + nodes$name <- gsub("src_", "", nodes$name) + nodes$name <- gsub("trgt_", "", nodes$name) + + # assigning colors to populations + if (is(palette_discrete, "function")) { + colors_pops <- palette_discrete(length(levels(pop(x)))) + } + + if (!is(palette_discrete, "function")) { + colors_pops <- palette_discrete + if (!any(grepl("#", colors_pops))) { + colors_pops <- gplots::col2hex(colors_pops) } + } + + colors_pops <- + paste0("\"", paste0(colors_pops, collapse = "\",\""), "\"") + + colorScal <- + paste("d3.scaleOrdinal().range([", colors_pops, "])") + # color links + data_long$color <- + gsub("src_", "", data_long$source) + + p3 <- + suppressMessages( + networkD3::sankeyNetwork( + Links = data_long, + Nodes = nodes, + Source = "IDsource", + Target = "IDtarget", + LinkGroup = "color", + Value = "value", + NodeID = "name", + sinksRight = FALSE, + units = "Private alleles", + colourScale = colorScal, + nodeWidth = 40, + fontSize = font_plot, + nodePadding = 10 + ) + ) } - - # DO THE JOB For method 'pairwise' - if (method == "pairwise") { - pc <- t(combn(length(pops), 2)) - pall <- - data.frame( - p1 = pc[, 1], - p2 = pc[, 2], - pop1 = names(pops)[pc[, 1]], - pop2 = names(pops)[pc[, 2]], - N1 = NA, - N2 = NA, - fixed = NA, - priv1 = NA, - priv2 = NA, - totalpriv = NA, - AFD = NA - ) + } + + # For method 'one2rest' + + if (method == "one2rest") { + pas <- as.data.frame(matrix(nrow = nPop(x), ncol = 11)) + colnames(pas) <- + c( + "p1", + "p2", + "pop1", + "pop2", + "N1", + "N2", + "fixed", + "priv1", + "priv2", + "totalpriv", + "AFD" + ) + for (y in 1:nPop(x)) { + gl2 <- x + pop(gl2) <- + factor(ifelse(pop(gl2) == popNames(x)[y], popNames(x)[y], "zzest")) + pops <- seppop(gl2) + pc <- t(combn(length(pops), 2)) + pall <- + data.frame( + p1 = pc[, 1], + p2 = pc[, 2], + pop1 = names(pops)[pc[, 1]], + pop2 = names(pops)[pc[, 2]], + N1 = NA, + N2 = NA, + fixed = NA, + priv1 = NA, + priv2 = NA, + totalpriv = NA, + AFD = NA + ) + + for (i in 1:nrow(pc)) { + i1 <- pall[i, 1] + i2 <- pall[i, 2] - for (i in 1:nrow(pc)) { - i1 = pall[i, 1] - i2 = pall[i, 2] - - p1 <- as.matrix(pops[[i1]]) - p2 <- as.matrix(pops[[i2]]) - p1alf <- colMeans(p1, na.rm = T) / 2 - p2alf <- colMeans(p2, na.rm = T) / 2 - - pall[i, 5:6] <- c(nrow(p1), nrow(p2)) - pall[i, 7] = sum(abs(p1alf - p2alf) == 1, na.rm = T) - - pall[i, 8] = sum(p2alf == 0 & - p1alf != 0, na.rm = T) + sum(p2alf == 1 & - p1alf != 1, na.rm = T) - pall[i, 9] = sum(p1alf == 0 & - p2alf != 0, na.rm = T) + sum(p1alf == 1 & - p2alf != 1, na.rm = T) - pall[i, 10] = pall[i, 8] + pall[i, 9] - pall[i, 11] = round(mean(abs(p1alf - p2alf), na.rm = T), 3) - } + p1 <- as.matrix(pops[[i1]]) + p2 <- as.matrix(pops[[i2]]) + p1alf <- colMeans(p1, na.rm = T) / 2 + p2alf <- colMeans(p2, na.rm = T) / 2 - if (plot.out) { - mm <- matrix(0, nPop(x), nPop(x)) - - for (i in 1:nrow(pall)) - mm[pall[i, 1], pall[i, 2]] <- pall$priv2[i] - for (i in 1:nrow(pall)) - mm[pall[i, 2], pall[i, 1]] <- pall$priv1[i] - - colnames(mm) <- popNames(x) - rownames(mm) <- popNames(x) - - data <- as.data.frame(mm) - value <- target <- name <- NULL - data_long <- - tibble::rownames_to_column(data, "source") - data_long <- tibble::as_tibble(data_long) - data_long <- - tidyr::pivot_longer(data_long, -source, "target") - data_long <- data_long[data_long$value > 0, ] - - data_long$target <- - gsub("\\.", " ", data_long$target) - data_long$source <- paste0("src_", data_long$source) - data_long$target <- - paste0("trgt_", data_long$target) - - nodes <- - data.frame(name = unique(c( - data_long$source, data_long$target - )), - stringsAsFactors = FALSE) - nodes <- - tibble::tibble(name = unique(c( - data_long$source, data_long$target - )), - target = grepl("trgt_", name)) - - data_long$IDsource <- - match(data_long$source, nodes$name) - 1 - data_long$IDtarget <- - match(data_long$target, nodes$name) - 1 - - nodes$name <- gsub("src_", "", nodes$name) - nodes$name <- gsub("trgt_", "", nodes$name) - - # assigning colors to populations - if (is(palette_discrete, "function")) { - colors_pops <- palette_discrete(length(levels(pop(x)))) - } - - if (!is(palette_discrete, "function")) { - colors_pops <- palette_discrete - if (!any(grepl("#", colors_pops))) { - colors_pops <- gplots::col2hex(colors_pops) - } - } - - colors_pops <- - paste0("\"", paste0(colors_pops, collapse = "\",\""), "\"") - - colorScal <- - paste("d3.scaleOrdinal().range([", colors_pops, "])") - # color links - data_long$color <- - gsub("src_", "", data_long$source) - - p3 <- - suppressMessages( - networkD3::sankeyNetwork( - Links = data_long, - Nodes = nodes, - Source = "IDsource", - Target = "IDtarget", - LinkGroup = "color", - Value = "value", - NodeID = "name", - sinksRight = FALSE, - units = "Private alleles", - colourScale = colorScal, - nodeWidth = 40, - fontSize = font_plot, - nodePadding = 10 - ) - ) - } - } - - # For method 'one2rest' - - if (method == "one2rest") { - pas <- as.data.frame(matrix(nrow = nPop(x), ncol = 11)) - colnames(pas) <- - c( - "p1", - "p2", - "pop1", - "pop2", - "N1", - "N2", - "fixed", - "priv1", - "priv2", - "totalpriv", - "AFD" - ) - for (y in 1:nPop(x)) { - gl2 <- x - pop(gl2) <- - factor(ifelse(pop(gl2) == popNames(x)[y], popNames(x)[y], "zzest")) - pops <- seppop(gl2) - pc <- t(combn(length(pops), 2)) - pall <- - data.frame( - p1 = pc[, 1], - p2 = pc[, 2], - pop1 = names(pops)[pc[, 1]], - pop2 = names(pops)[pc[, 2]], - N1 = NA, - N2 = NA, - fixed = NA, - priv1 = NA, - priv2 = NA, - totalpriv = NA, - AFD = NA - ) - - for (i in 1:nrow(pc)) { - i1 = pall[i, 1] - i2 = pall[i, 2] - - p1 <- as.matrix(pops[[i1]]) - p2 <- as.matrix(pops[[i2]]) - p1alf <- colMeans(p1, na.rm = T) / 2 - p2alf <- colMeans(p2, na.rm = T) / 2 - - pall[i, 5:6] <- c(nrow(p1), nrow(p2)) - pall[i, 7] = sum(abs(p1alf - p2alf) == 1, na.rm = T) - - pall[i, 8] = sum(p2alf == 0 & - p1alf != 0, na.rm = T) + sum(p2alf == 1 & - p1alf != 1, na.rm = T) - pall[i, 9] = sum(p1alf == 0 & - p2alf != 0, na.rm = T) + sum(p1alf == 1 & - p2alf != 1, na.rm = T) - pall[i, 10] = pall[i, 8] + pall[i, 9] - pall[i, 11] = round(mean(abs(p1alf - p2alf), na.rm = T), 3) - } - - pas[y, ] <- pall - } + pall[i, 5:6] <- c(nrow(p1), nrow(p2)) + pall[i, 7] <- sum(abs(p1alf - p2alf) == 1, na.rm = T) - pall <- pas - pall$pop2 <- "Rest" + pall[i, 8] <- sum(p2alf == 0 & + p1alf != 0, na.rm = T) + + sum(p2alf == 1 & p1alf != 1, na.rm = T) - if (plot.out) { - # assigning colors to populations - if (is(palette_discrete, "function")) { - colors_pops <- palette_discrete(length(levels(pop(x))) + 1) - } - - if (!is(palette_discrete, "function")) { - colors_pops <- palette_discrete - } - - data_long_1 <- - as.data.frame(matrix(nrow = nPop(x), ncol = 6)) - colnames(data_long_1) <- - c("source", - "target", - "value", - "IDsource", - "IDtarget", - "color") - data_long_1$source <- paste0(pall$pop1, " source") - data_long_1$target <- "Rest" - data_long_1$value <- pall$priv1 - data_long_1$IDsource <- (1:nPop(x)) - 1 - data_long_1$IDtarget <- (nPop(x) + 1) - 1 - data_long_1$color <- popNames(x) - - data_long_2 <- - as.data.frame(matrix(nrow = nPop(x), ncol = 6)) - colnames(data_long_2) <- - c("source", - "target", - "value", - "IDsource", - "IDtarget", - "color") - data_long_2$source <- "Rest" - data_long_2$target <- paste0(pall$pop1, " target") - data_long_2$value <- pall$priv2 - data_long_2$IDsource <- (nPop(x) + 1) - 1 - data_long_2$IDtarget <- (nPop(x) + 1):(nPop(x) * 2) - data_long_2$color <- "Rest" - - data_long <- rbind(data_long_1, data_long_2) - - nodes <- - as.data.frame(matrix(nrow = (nPop(x) * 2) + 1, ncol = 1)) - colnames(nodes) <- c("name") - nodes$name <- - c(data_long_1$source, "Rest", data_long_2$target) - - colors_pops <- - paste0("\"", paste0(colors_pops, collapse = "\",\""), "\"") - - colorScal <- - paste("d3.scaleOrdinal().range([", colors_pops, "])") - # color links - data_long$color <- - gsub("src_", "", data_long$source) - - p3 <- - suppressMessages( - networkD3::sankeyNetwork( - Links = data_long, - Nodes = nodes, - Source = "IDsource", - Target = "IDtarget", - LinkGroup = "color", - Value = "value", - NodeID = "name", - sinksRight = FALSE, - units = "Private alleles", - colourScale = colorScal, - iterations = 0, - nodeWidth = 40, - fontSize = 14, - nodePadding = 20 - ) - ) - } + pall[i, 9] <- sum(p1alf == 0 & + p2alf != 0, na.rm = T) + + sum(p1alf == 1 & p2alf != 1, na.rm = T) + + pall[i, 10] <- pall[i, 8] + pall[i, 9] + pall[i, 11] <- + round(mean(abs(p1alf - p2alf), na.rm = T), 3) + } + + pas[y,] <- pall } - df <- pall + pall <- pas + pall$pop2 <- "Rest" - # PRINTING OUTPUTS if (plot.out) { - if (map.interactive & (method == "pairwise")) { - labs <- popNames(x) - gl.map.interactive(x, matrix = mm, symmetric = FALSE) + # assigning colors to populations + if (is(palette_discrete, "function")) { + colors_pops <- palette_discrete(length(levels(pop(x))) + 1) + } + + if (!is(palette_discrete, "function")) { + colors_pops <- palette_discrete + # if colors are not in RGB format + if (grepl("#", colors_pops[1]) == FALSE) { + colors_pops <- RGB_colors(colors_pops) } - # using package patchwork - print(p3) - } - - if(verbose>0){ - print(df) + } + + data_long_1 <- + as.data.frame(matrix(nrow = nPop(x), ncol = 6)) + colnames(data_long_1) <- + c("source", + "target", + "value", + "IDsource", + "IDtarget", + "color") + data_long_1$source <- paste0(pall$pop1, " source") + data_long_1$target <- "Rest" + data_long_1$value <- pall$priv1 + data_long_1$IDsource <- (1:nPop(x)) - 1 + data_long_1$IDtarget <- (nPop(x) + 1) - 1 + data_long_1$color <- popNames(x) + + data_long_2 <- + as.data.frame(matrix(nrow = nPop(x), ncol = 6)) + colnames(data_long_2) <- + c("source", + "target", + "value", + "IDsource", + "IDtarget", + "color") + data_long_2$source <- "Rest" + data_long_2$target <- paste0(pall$pop1, " target") + data_long_2$value <- pall$priv2 + data_long_2$IDsource <- (nPop(x) + 1) - 1 + data_long_2$IDtarget <- (nPop(x) + 1):(nPop(x) * 2) + data_long_2$color <- "Rest" + + data_long <- rbind(data_long_1, data_long_2) + + nodes <- + as.data.frame(matrix(nrow = (nPop(x) * 2) + 1, ncol = 1)) + colnames(nodes) <- c("name") + nodes$name <- + c(data_long_1$source, "Rest", data_long_2$target) + + colors_pops <- + paste0("\"", paste0(colors_pops, collapse = "\",\""), "\"") + + colorScal <- + paste("d3.scaleOrdinal().range([", colors_pops, "])") + # color links + data_long$color <- + gsub("src_", "", data_long$source) + + p3 <- + suppressMessages( + networkD3::sankeyNetwork( + Links = data_long, + Nodes = nodes, + Source = "IDsource", + Target = "IDtarget", + LinkGroup = "color", + Value = "value", + NodeID = "name", + sinksRight = FALSE, + units = "Private alleles", + colourScale = colorScal, + iterations = 0, + nodeWidth = 40, + fontSize = 14, + nodePadding = 20 + ) + ) } - - if (verbose >= 2) { - cat(report( - " Table of private alleles and fixed differences returned\n" - )) + } + + df <- pall + + # PRINTING OUTPUTS + if (plot.out) { + if (map.interactive & (method == "pairwise")) { + labs <- popNames(x) + gl.map.interactive(x, matrix = mm, symmetric = FALSE) } - - # SAVE INTERMEDIATES TO TEMPDIR creating temp file names - if (save2tmp) { - if (plot.out) { - temp_plot <- tempfile(pattern = "Plot_") - match_call <- - paste0(names(match.call()), - "_", - as.character(match.call()), - collapse = "_") - # saving to tempdir - saveRDS(list(match_call, p3), file = temp_plot) - if (verbose >= 2) { - cat(report(" Saving the ggplot to session tempfile\n")) - } - } - temp_table <- tempfile(pattern = "Table_") - saveRDS(list(match_call, df), file = temp_table) - if (verbose >= 2) { - cat(report(" Saving tabulation to session tempfile\n")) - cat( - report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" - ) - ) - } + # using package patchwork + print(p3) + } + + if (verbose > 0) { + print(df) + } + + if (verbose >= 2) { + cat(report(" Table of private alleles and fixed differences returned\n")) + } + + # SAVE INTERMEDIATES TO TEMPDIR creating temp file names + if (save2tmp) { + if (plot.out) { + temp_plot <- tempfile(pattern = "Plot_") + match_call <- + paste0(names(match.call()), + "_", + as.character(match.call()), + collapse = "_") + # saving to tempdir + saveRDS(list(match_call, p3), file = temp_plot) + if (verbose >= 2) { + cat(report(" Saving the ggplot to session tempfile\n")) + } } - - # FLAG SCRIPT END - - if (verbose >= 1) { - cat(report("Completed:", funname, "\n")) + temp_table <- tempfile(pattern = "Table_") + saveRDS(list(match_call, df), file = temp_table) + if (verbose >= 2) { + cat(report(" Saving tabulation to session tempfile\n")) + cat( + report( + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" + ) + ) } - - # RETURN - - invisible(df) - + } + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + invisible(df) + } diff --git a/R/gl.report.parent.offspring.r b/R/gl.report.parent.offspring.r index 633f3b62..fe0c11cf 100644 --- a/R/gl.report.parent.offspring.r +++ b/R/gl.report.parent.offspring.r @@ -81,7 +81,7 @@ #' @seealso \code{\link{gl.list.reports}}, \code{\link{gl.report.rdepth}} , #' \code{\link{gl.print.reports}},\code{\link{gl.report.reproducibility}}, #' \code{\link{gl.filter.parent.offspring}} -#' @family reporting functions +#' @family report functions #' @importFrom stats median IQR #' @import patchwork #' @export @@ -113,7 +113,8 @@ gl.report.parent.offspring <- function(x, if (verbose >= 2) { cat( report( - " Generating null expectation for distribution of counts of pedigree incompatibility\n" + " Generating null expectation for distribution of counts of + pedigree incompatibility\n" ) ) } @@ -123,18 +124,22 @@ gl.report.parent.offspring <- function(x, if (is.null(x@other$loc.metrics$RepAvg)) { cat( warn( - " Dataset does not include RepAvg among the locus metrics, therefore the reproducibility filter was not used\n" + " Dataset does not include RepAvg among the locus metrics, + therefore the reproducibility filter was not used\n" ) ) } else { x <- - gl.filter.reproducibility(x, threshold = min.reproducibility, verbose = 0) + gl.filter.reproducibility(x, + threshold = min.reproducibility, + verbose = 0) } # Filter stringently on read depth, to further minimize miscalls if (is.null(x@other$loc.metrics$rdepth)) { cat( warn( - " Dataset does not include rdepth among the locus metrics, therefore the read depth filter was not used\n" + " Dataset does not include rdepth among the locus metrics, + therefore the read depth filter was not used\n" ) ) } else { @@ -166,19 +171,28 @@ gl.report.parent.offspring <- function(x, if (verbose >= 2) { cat( report( - " Identifying outliers with lower than expected counts of pedigree inconsistencies\n" + " Identifying outliers with lower than expected counts of + pedigree inconsistencies\n" ) ) } title <- - paste0("SNP data (DArTSeq)\nCounts of pedigree incompatible loci per pair") + paste0("SNP data (DArTSeq)\nCounts of pedigree incompatible loci per + pair") counts_plot <- as.data.frame(counts) # Boxplot p1 <- - ggplot(counts_plot, aes(y = counts)) + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + coord_flip() + plot_theme + xlim(range = c(-1, - 1)) + ylim(min(counts), max(counts)) + ylab(" ") + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + ggtitle(title) + ggplot(counts_plot, aes(y = counts)) + + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + coord_flip() + + plot_theme + + xlim(range = c(-1, 1)) + + ylim(min(counts), max(counts)) + + ylab(" ") + + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + ggtitle(title) outliers_temp <- ggplot_build(p1)$data[[1]]$outliers[[1]] @@ -196,7 +210,7 @@ gl.report.parent.offspring <- function(x, } if (length(lower.extremes) > 0) { tmp <- count - tmp[lower.tri(tmp)] = t(tmp)[lower.tri(tmp)] + tmp[lower.tri(tmp)] <-t(tmp)[lower.tri(tmp)] for (i in 1:length(outliers$Outlier)) { # Identify tmp2 <- tmp[tmp == outliers$Outlier[i]] @@ -204,7 +218,8 @@ gl.report.parent.offspring <- function(x, outliers$ind2[i] <- popNames(x)[!is.na(tmp2)][2] # Z-scores zscore <- - (mean(count, na.rm = TRUE) - outliers$Outlier[i]) / sd(count, na.rm = TRUE) + (mean(count, na.rm = TRUE) - outliers$Outlier[i]) / + sd(count, na.rm = TRUE) outliers$zscore[i] <- round(zscore, 2) outliers$p[i] <- round(pnorm( @@ -228,11 +243,12 @@ gl.report.parent.offspring <- function(x, # Histogram p2 <- - ggplot(counts_plot, aes(x = counts)) + geom_histogram(bins = 50, - color = plot_colors[1], - fill = plot_colors[2]) + geom_vline(xintercept = cutoff, - color = "red", - size = 1) + coord_cartesian(xlim = c(min(counts), max(counts))) + xlab("No. Pedigree incompatible") + ylab("Count") + + ggplot(counts_plot, aes(x = counts)) + + geom_histogram(bins = 50,color = plot_colors[1],fill = plot_colors[2]) + + geom_vline(xintercept = cutoff, color = "red", size = 1) + + coord_cartesian(xlim = c(min(counts), max(counts))) + + xlab("No. Pedigree incompatible") + + ylab("Count") + plot_theme # Output the outlier loci @@ -277,7 +293,8 @@ gl.report.parent.offspring <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.rdepth.r b/R/gl.report.rdepth.r index 12b28627..bd7a9220 100644 --- a/R/gl.report.rdepth.r +++ b/R/gl.report.rdepth.r @@ -41,7 +41,7 @@ #' df <- gl.report.rdepth(testset.gl) #' df <- gl.report.rdepth(testset.gs) #' @seealso \code{\link{gl.filter.rdepth}} -#' @family filters and filter reports +#' @family report functions #' @import patchwork #' @export @@ -100,15 +100,23 @@ gl.report.rdepth <- function(x, # Boxplot p1 <- - ggplot(data.frame(rdepth), aes(y = rdepth)) + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + coord_flip() + plot_theme + - xlim(range = c(-1, 1)) + ylim(c(0, max)) + ylab(" ") + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + ggtitle(title) + ggplot(data.frame(rdepth), aes(y = rdepth)) + + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + coord_flip() + plot_theme + + xlim(range = c(-1, 1)) + + ylim(c(0, max)) + + ylab(" ") + + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + ggtitle(title) # Histogram p2 <- - ggplot(data.frame(rdepth), aes(x = rdepth)) + geom_histogram(bins = 100, - color = plot_colors[1], - fill = plot_colors[2]) + xlim(c(0, - max)) + xlab("Read Depth") + ylab("Count") + plot_theme + ggplot(data.frame(rdepth), aes(x = rdepth)) + + geom_histogram(bins=100,color=plot_colors[1],fill = plot_colors[2]) + + xlim(c(0,max)) + + xlab("Read Depth") + + ylab("Count") + + plot_theme } # Print out some statistics @@ -183,7 +191,8 @@ gl.report.rdepth <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.reproducibility.r b/R/gl.report.reproducibility.r index 4e219ea9..8fb9d520 100644 --- a/R/gl.report.reproducibility.r +++ b/R/gl.report.reproducibility.r @@ -1,5 +1,6 @@ #' @name gl.report.reproducibility -#' @title Reports summary of RepAvg (repeatability averaged over both alleles for +#' @title Reports summary of RepAvg (repeatability averaged over both alleles +#' for #' each locus) or reproducibility (repeatability of the scores for fragment #' presence/absence) #' @description @@ -39,8 +40,9 @@ #' #' For examples of themes, see: #' \itemize{ -#' \item \url{https://ggplot2.tidyverse.org/reference/ggtheme.html} and \item -#' \url{https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/} +#' \item \url{https://ggplot2.tidyverse.org/reference/ggtheme.html} and +#' \item +#' \url{https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/} #' } #' #' @return An unaltered genlight object @@ -54,7 +56,7 @@ #' out <- gl.report.reproducibility(testset.gs) #' #' @seealso \code{\link{gl.filter.reproducibility}} -#' @family filters and filter reports +#' @family report functions #' @import patchwork #' @export @@ -106,13 +108,13 @@ gl.report.reproducibility <- function(x, plot_theme + ylim(c(min(repeatability), 1)) + ylab(" ") + - theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + theme(axis.text.y = element_blank(),axis.ticks.y=element_blank()) + ggtitle(title) # Histogram p2 <- ggplot(repeatability_plot, aes(x = repeatability)) + - geom_histogram(bins = 50, color = plot_colors[1],fill = plot_colors[2]) + + geom_histogram(bins = 50,color=plot_colors[1],fill=plot_colors[2]) + coord_cartesian(xlim = c(min(repeatability), 1)) + xlab("Repeatability") + ylab("Count") + @@ -191,7 +193,8 @@ gl.report.reproducibility <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.report.secondaries.r b/R/gl.report.secondaries.r index 8f9d8387..b2fa163c 100644 --- a/R/gl.report.secondaries.r +++ b/R/gl.report.secondaries.r @@ -94,7 +94,7 @@ #' @references Schmidt, T.L., Jasper, M.-E., Weeks, A.R., Hoffmann, A.A., 2021. #' Unbiased population heterozygosity estimates from genome-wide sequence #' data. Methods in Ecology and Evolution n/a. -#' @family Filter reports functions +#' @family report functions #' @importFrom stats dpois #' @import patchwork #' @export @@ -125,7 +125,8 @@ gl.report.secondaries <- function(x, isFALSE("CloneID" %in% names(x$other$loc.metrics))) { stop( error( - "Neither CloneID or AlleleID metrics were found in the slot loc.metrics, which are required for this function to work\n" + "Neither CloneID or AlleleID metrics were found in the slot + loc.metrics, which are required for this function to work\n" ) ) } @@ -141,7 +142,8 @@ gl.report.secondaries <- function(x, strsplit(as.character(x@other$loc.metrics$AlleleID), "\\|") b <- unlist(a)[c(TRUE, FALSE, FALSE)] - # set up to estimate variable and inv sites in sequenced tags, and mean tag length + # set up to estimate variable and inv sites in sequenced tags, and mean tag + # length proc.data <- data.table(x$other$loc.metrics) # using data.table if (isFALSE("CloneID" %in% names(x$other$loc.metrics))) { @@ -155,19 +157,23 @@ gl.report.secondaries <- function(x, # the number of invariant sites of the genotyped tags n.inv.gen <- - proc.data[data.table(unique(CloneID)), sum(n.invariant), mult = "first"] + proc.data[data.table(unique(CloneID)), + sum(n.invariant), mult = "first"] # the mean length of the sequenced tags mean.len.tag <- - proc.data[data.table(unique(CloneID)), mean(lenTrimSeq), mult = "first"] + proc.data[data.table(unique(CloneID)), + mean(lenTrimSeq), mult = "first"] } else { mean.len.tag <- taglength proc.data[, `:=`(n.variant, .N), by = CloneID] # The mean number of SNPs for each tag mean.nSNP.tag <- - proc.data[data.table(unique(CloneID)), mean(n.variant), mult = "first"] + proc.data[data.table(unique(CloneID)), + mean(n.variant), mult = "first"] # The number of tags n.inv.gen <- - round((mean.len.tag - mean.nSNP.tag) * proc.data[, length(unique(CloneID))], 0) + round((mean.len.tag - mean.nSNP.tag) * + proc.data[, length(unique(CloneID))], 0) cat(warn( paste( "The column 'TrimmedSequence' was not found in loc.metrics\n", @@ -191,11 +197,15 @@ gl.report.secondaries <- function(x, # Boxplot if (plot.out) { p1 <- - ggplot(secondaries_plot, aes(y = freqs)) + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + coord_flip() + plot_theme + - xlim(range = c(-1, 1)) + scale_y_discrete(limits = c(as.character(unique( - freqs_1 - )))) + theme(axis.text.y = element_blank(), - axis.ticks.y = element_blank()) + ggtitle("Boxplot") + ggplot(secondaries_plot, aes(y = freqs)) + + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + coord_flip() + + plot_theme + + xlim(range = c(-1, 1)) + + scale_y_discrete(limits = c(as.character(unique(freqs_1)))) + + theme(axis.text.y = element_blank(), + axis.ticks.y = element_blank()) + + ggtitle("Boxplot") # Barplot freqs_2 <- c(0, table(as.numeric(table(b)))) @@ -209,8 +219,12 @@ gl.report.secondaries <- function(x, freq <- NULL p2 <- - ggplot(secondaries_plot_2, aes(x = freq, y = count)) + geom_col(color = plot_colors[1], fill = plot_colors[2]) + xlab("Frequency") + - ylab("Count") + ggtitle("Observed Frequency of SNPs per Sequence Tag") + plot_theme + ggplot(secondaries_plot_2, aes(x = freq, y = count)) + + geom_col(color = plot_colors[1], fill = plot_colors[2]) + + xlab("Frequency") + + ylab("Count") + + ggtitle("Observed Frequency of SNPs per Sequence Tag") + + plot_theme } # Plot Histogram with estimate of the zero class @@ -234,7 +248,8 @@ gl.report.secondaries <- function(x, # Set convergence criterion delta <- 1e-05 - # Use the mean of the truncated distribution to compute lambda for the untruncated distribution + # Use the mean of the truncated distribution to compute lambda for the + # untruncated distribution k <- seed for (i in 1:nsim) { if (verbose >= 2) { @@ -253,7 +268,8 @@ gl.report.secondaries <- function(x, if (verbose >= 2) { cat( important( - " Failed to converge: No reliable estimate of invariant loci\n" + " Failed to converge: No reliable estimate of + invariant loci\n" ) ) } @@ -265,11 +281,15 @@ gl.report.secondaries <- function(x, # Size of the truncated distribution if (!fail) { - n <- sum(freqs) # Size of the truncated set - tp <- - 1 - dpois(x = 0, lambda = k) # Fraction that is the truncated set - rn <- round(n / tp, 0) # Estimate of the whole set - # cat('\n Estimated size of the zero class',round(dpois(x=0,lambda=k)*rn,0),'\n') Table for the reconstructed set + # Size of the truncated set + n <- sum(freqs) + # Fraction that is the truncated set + tp <- 1 - dpois(x = 0, lambda = k) + # Estimate of the whole set + rn <- round(n / tp, 0) + # cat('\n Estimated size of the zero class', + # round(dpois(x=0,lambda=k)*rn,0),'\n') + # Table for the reconstructed set reconstructed <- dpois(x = 0:(length(freqs) - 1), lambda = k) * rn reconstructed <- as.table(reconstructed) @@ -289,8 +309,12 @@ gl.report.secondaries <- function(x, # Barplot if (plot.out) { p3 <- - ggplot(reconstructed_plot, aes(x = freq, y = count)) + geom_col(color = plot_colors[1], fill = plot_colors[2]) + xlab("Frequency") + - ylab("Count") + ggtitle(title) + plot_theme + ggplot(reconstructed_plot, aes(x = freq, y = count)) + + geom_col(color = plot_colors[1], fill = plot_colors[2]) + + xlab("Frequency") + + ylab("Count") + + ggtitle(title) + + plot_theme # PRINTING OUTPUTS using package patchwork p4 <- @@ -318,7 +342,8 @@ gl.report.secondaries <- function(x, saveRDS(list(match_call, p4), file = temp_plot) if (verbose >= 2) { cat(report( - " Saving the plot in ggplot format to the session tempfile\n" + " Saving the plot in ggplot format to the session + tempfile\n" )) } # saving genlight object to tempdir @@ -326,12 +351,14 @@ gl.report.secondaries <- function(x, if (verbose >= 2) { cat( report( - " Saving the genlight object containing the filtered loci to the session tempfile\n" + " Saving the genlight object containing the filtered + loci to the session tempfile\n" ) ) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } @@ -371,7 +398,8 @@ gl.report.secondaries <- function(x, "\n") n.SNPs.secondaries <- table(duplicated(b))[2] cat( - " Number of secondary SNP loci that would be removed on filtering:", + " Number of secondary SNP loci that would be removed on + filtering:", n.SNPs.secondaries, "\n" ) @@ -385,7 +413,8 @@ gl.report.secondaries <- function(x, n.invariant <- round(n.invariant.tags * mean.len.tag + n.inv.gen, 0) cat( - " Total Number of invariant sites (including invariant sequence tags):", + " Total Number of invariant sites (including invariant sequence + tags):", n.invariant, "\n" ) diff --git a/R/gl.report.sexlinked.r b/R/gl.report.sexlinked.r index adabd7fb..26117de6 100644 --- a/R/gl.report.sexlinked.r +++ b/R/gl.report.sexlinked.r @@ -56,7 +56,7 @@ #' @examples #' out <- gl.report.sexlinked(testset.gl) #' out <- gl.report.sexlinked(testset.gs) -#' @family reporting functions +#' @family report functions #' @export gl.report.sexlinked <- function(x, @@ -113,8 +113,10 @@ gl.report.sexlinked <- function(x, stop( error( "No definition for the sex of individuals is provided. If not - provided via the function call it needs to be at gl@other$ind.metrics$sex. - Please refer to the help pages how you can define sex of individuals." + provided via the function call it needs to be at + gl@other$ind.metrics$sex. + Please refer to the help pages how you can define sex of + individuals." ) ) } @@ -122,8 +124,9 @@ gl.report.sexlinked <- function(x, if (length(sex) != nInd(x)) { stop( error( - "The number of individuals and the number of entries defining the - sex do not match. Check your genlight object and your sex defining column." + "The number of individuals and the number of entries defining + the sex do not match. Check your genlight object and your sex + defining column." ) ) } @@ -245,45 +248,47 @@ gl.report.sexlinked <- function(x, if (nrow(zw) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" + " No sex linked markers consistent with female + heterogamety (ZZ/ZW)\n" ) ) } else { if (verbose > 0) { - cat("\n Sex linked loci consistent with female heterogamety (ZZ/ZW)\n\n") + cat("\n Sex linked loci consistent with female heterogamety + (ZZ/ZW)\n\n") cat( paste( - " Threshold proportion for homozygotes in the heterozygotic sex (ZW) is", + " Threshold proportion for homozygotes in the + heterozygotic sex (ZW) is", t.hom, "\n" ) ) cat( paste( - " Threshold proportion for heterozygotes in the homozygotic sex (ZZ) is", + " Threshold proportion for heterozygotes in the + homozygotic sex (ZZ) is", t.het, "\n\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat( - " - F0 is the number of homozygous loci for the reference allele in females\n" - ) - cat(" - F1 is the number of heterozygous loci in females\n") - cat( - " - F2 is the number of homozygous loci for the alternative allele in females\n" - ) - cat(" - M0 is the number of homozygous loci for the reference allele in males\n") - cat(" - M1 is the number of heterozygous loci in males\n") - cat( - " - M2 is the number of homozygous loci for the alternative allele in males\n" - ) - cat(" - fhet is heterozygosity in females\n") - cat(" - mhet is heterozygosity in males\n\n") +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of homozygous loci for the reference allele in + females\n") +cat("- F1 is the number of heterozygous loci in females\n") +cat("- F2 is the number of homozygous loci for the alternative allele in + females\n") +cat("- M0 is the number of homozygous loci for the reference allele in males\n") +cat("- M1 is the number of heterozygous loci in males\n") +cat("- M2 is the number of homozygous loci for the alternative allele in + males\n") +cat("- fhet is heterozygosity in females\n") +cat("- mhet is heterozygosity in males\n\n") print(zw) cat( important( - " \nNote: The most reliable putative markers will have a read depth > 10.\n\n" + " \nNote: The most reliable putative markers will have + a read depth > 10.\n\n" ) ) } @@ -292,45 +297,46 @@ gl.report.sexlinked <- function(x, if (nrow(xy) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with male heterogamety (XX/XY)\n" + " No sex linked markers consistent with male heterogamety + (XX/XY)\n" ) ) } else { if (verbose > 0) { - cat("\n Sex linked loci consistent with male heterogamety (XX/XY)\n\n") + cat("\n Sex linked loci consistent with male heterogamety + (XX/XY)\n\n") cat( paste( - " Threshold proportion for homozygotes in the heterozygotic sex (XY) is", + " Threshold proportion for homozygotes in the + heterozygotic sex (XY) is", t.hom, "\n" ) ) cat( paste( - " Threshold proportion for heterozygotes in the homozygotic sex (XX) is", + " Threshold proportion for heterozygotes in the homozygotic sex (XX) is", t.het, "\n\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat( - " - F0 is the number of homozygous loci for the reference allele in females\n" - ) - cat(" - F1 is the number of heterozygous loci in females\n") - cat( - " - F2 is the number of homozygous loci for the alternative allele in females\n" - ) - cat(" - M0 is the number of homozygous loci for the reference allele in males\n") - cat(" - M1 is the number of heterozygous loci in males\n") - cat( - " - M2 is the number of homozygous loci for the alternative allele in males\n" +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of homozygous loci for the reference allele in + females\n") +cat("- F1 is the number of heterozygous loci in females\n") +cat("- F2 is the number of homozygous loci for the alternative allele in + females\n") +cat("- M0 is the number of homozygous loci for the reference allele in males\n") +cat("- M1 is the number of heterozygous loci in males\n") +cat("- M2 is the number of homozygous loci for the alternative allele in + males\n" ) - cat(" - fhet is heterozygosity in females\n") - cat(" - mhet is heterozygosity in males\n\n") +cat("- fhet is heterozygosity in females\n") +cat("- mhet is heterozygosity in males\n\n") print(xy) cat( important( - " \nNote: The most reliable putative markers will have a read depth > 10.\n\n" +" \nNote: The most reliable putative markers will have a read depth > 10.\n\n" ) ) } @@ -383,7 +389,10 @@ gl.report.sexlinked <- function(x, alpha = 1 / 3, size = 3, color = three_colors[2] - ) + xlab("Female Heterozygosity") + ylab("Male Heterozygosity") + xlim(0, 1) + ylim(0, 1) + + ) + xlab("Female Heterozygosity") + + ylab("Male Heterozygosity") + + xlim(0, 1) + + ylim(0, 1) + plot_theme suppressWarnings(print(gg)) @@ -474,12 +483,14 @@ gl.report.sexlinked <- function(x, if (nrow(zw) == 0 & verbose > 0) { cat( important( - " No sex linked markers consistent with female heterogamety (ZZ/ZW)\n" + " No sex linked markers consistent with female + heterogamety (ZZ/ZW)\n" ) ) } else { if (verbose > 0) { - cat("\n Sex linked loci consistent with female heterogamety (ZZ/ZW)\n\n") + cat("\n Sex linked loci consistent with female heterogamety + (ZZ/ZW)\n\n") cat( paste( " Threshold proportion for presence/absence is", @@ -487,15 +498,16 @@ gl.report.sexlinked <- function(x, "\n\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat(" - F0 is the number of loci absent in females\n") - cat(" - F1 is the number of loci present in females\n") - cat(" - M0 is the number of loci absent in males\n") - cat(" - M1 is the number of loci present in males\n\n") +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of loci absent in females\n") +cat("- F1 is the number of loci present in females\n") +cat("- M0 is the number of loci absent in males\n") +cat("- M1 is the number of loci present in males\n\n") print(zw) cat( important( - " \nNote: The most reliable putative markers will have a read depth > 10.\n\n" + " \nNote: The most reliable putative markers will have + a read depth > 10.\n\n" ) ) } @@ -504,12 +516,14 @@ gl.report.sexlinked <- function(x, if (verbose > 0) cat( important( - " No sex linked markers consistent with male heterogamety (XX/XY)\n" + " No sex linked markers consistent with male + heterogamety (XX/XY)\n" ) ) } else { if (verbose > 0) { - cat("\n Sex linked loci consistent with male heterogamety (XX/XY)\n\n") + cat("\n Sex linked loci consistent with male heterogamety + (XX/XY)\n\n") cat( paste( " Threshold proportion for presence/absence is", @@ -517,15 +531,16 @@ gl.report.sexlinked <- function(x, "\n\n" ) ) - cat(" - locnr is the location of the locus in the input genlight object\n") - cat(" - F0 is the number of loci absent in females\n") - cat(" - F1 is the number of loci present in females\n") - cat(" - M0 is the number of loci absent in males\n") - cat(" - M1 is the number of loci present in males\n\n") +cat("- locnr is the location of the locus in the input genlight object\n") +cat("- F0 is the number of loci absent in females\n") +cat("- F1 is the number of loci present in females\n") +cat("- M0 is the number of loci absent in males\n") +cat("- M1 is the number of loci present in males\n\n") print(xy) cat( important( - " \nNote: The most reliable putative markers will have a read depth > 10.\n\n" + " \nNote: The most reliable putative markers will have + a read depth > 10.\n\n" ) ) } @@ -578,7 +593,11 @@ gl.report.sexlinked <- function(x, alpha = 1 / 3, size = 3, color = three_colors[2] - ) + xlab("% present in females") + ylab("% present in males") + xlim(0, 1) + ylim(0, 1) + plot_theme + ) + xlab("% present in females") + + ylab("% present in males") + + xlim(0, 1) + + ylim(0, 1) + + plot_theme suppressWarnings(print(gg)) } diff --git a/R/gl.report.taglength.r b/R/gl.report.taglength.r index 3699903f..ad26485b 100644 --- a/R/gl.report.taglength.r +++ b/R/gl.report.taglength.r @@ -48,7 +48,7 @@ #' out <- gl.report.taglength(testset.gl) #' @seealso \code{\link{gl.filter.taglength}}, \code{\link{gl.list.reports}}, #' \code{\link{gl.print.reports}} -#' @family filters and filter reports +#' @family report functions #' @import patchwork #' @export @@ -75,7 +75,9 @@ gl.report.taglength <- function(x, if (length(x@other$loc.metrics$TrimmedSequence) != nLoc(x)) { stop( error( - "Fatal Error: Data must include Trimmed Sequences for each loci in a column called 'TrimmedSequence' in the @other$loc.metrics slot.\n" + "Fatal Error: Data must include Trimmed Sequences for each loci + in a column called 'TrimmedSequence' in the @other$loc.metrics + slot.\n" ) ) } @@ -90,15 +92,21 @@ gl.report.taglength <- function(x, # Boxplot p1 <- - ggplot(plot_tags, aes(y = tags)) + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + coord_flip() + plot_theme + xlim(range = c(-1, - 1)) + ylim(0, 100) + ylab(" ") + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + ggtitle("SNP data - Tag Length") + ggplot(plot_tags, aes(y = tags)) + + geom_boxplot(color = plot_colors[1], fill = plot_colors[2]) + + coord_flip() + plot_theme + xlim(range = c(-1,1)) + + ylim(0, 100) + ylab(" ") + + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank()) + + ggtitle("SNP data - Tag Length") # Histogram p2 <- - ggplot(plot_tags, aes(x = tags)) + geom_histogram(bins = 50, - color = plot_colors[1], - fill = plot_colors[2]) + coord_cartesian(xlim = c(0, - 100)) + xlab("Tag Length") + ylab("Count") + plot_theme + ggplot(plot_tags, aes(x = tags)) + + geom_histogram(bins = 50,color = plot_colors[1], fill = plot_colors[2]) + + coord_cartesian(xlim = c(0,100)) + + xlab("Tag Length") + + ylab("Count") + + plot_theme # Print out some statistics stats <- summary(nchar.tags) @@ -173,7 +181,8 @@ gl.report.taglength <- function(x, cat(report(" Saving tabulation to session tempfile\n")) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.run.structure.r b/R/gl.run.structure.r index 37c56282..0d04d505 100644 --- a/R/gl.run.structure.r +++ b/R/gl.run.structure.r @@ -7,7 +7,8 @@ #' functions from \code{strataG} #' #' @param x Name of the genlight object containing the SNP data [required]. -#' @param ... Parameters to specify the STRUCTURE run (check \code{structureRun} within strataG. +#' @param ... Parameters to specify the STRUCTURE run (check \code{structureRun} +#' within strataG. #' for more details). Parameters are passed to the \code{structureRun} function. #' For example you need to set the k.range and the type of model you would like #' to run (noadmix, locprior) etc. If those parameter names do not tell you @@ -47,7 +48,8 @@ #' \dontrun{ #' #CLUMPP needs to be installed to be able to run the example #' #bc <- bandicoot.gl[,1:100] -#' #sr <- gl.run.structure(bc, k.range = 2:5, num.k.rep = 3, exec = './structure.exe') +#' #sr <- gl.run.structure(bc, k.range = 2:5, num.k.rep = 3, +#' exec = './structure.exe') #' #ev <- gl.evanno(sr) #' #ev #' #qmat <- gl.plot.structure(sr, k=3, CLUMPP='d:/structure/') @@ -56,7 +58,8 @@ #' } #' @import patchwork ### @importFrom strataG genind2gtypes structureRun -#' @importFrom dplyr bind_rows mutate_at vars starts_with mutate group_by ungroup arrange n rename select everything n_distinct bind_rows starts_with +#' @importFrom dplyr bind_rows mutate_at vars starts_with mutate group_by +#' ungroup arrange n rename select everything n_distinct bind_rows starts_with #' @export ### @seealso \code{structureRun} #' @references @@ -82,7 +85,8 @@ gl.run.structure <- function(x, error( "Package ", pkg, - " needed for this function to work. Please install it using install.packages('devtools')." + " needed for this function to work. Please install it using + install.packages('devtools')." ) ) } @@ -92,9 +96,10 @@ gl.run.structure <- function(x, if (!structure) { stop(error( paste( - "Cannot find Structure executable in the exex path provided:\n", +"Cannot find Structure executable in the exex path provided:\n", exec, - "\nCheck the help page of ?gl.run.structure on how to download and the exec parameter to locate it." +"\nCheck the help page of ?gl.run.structure on how to download and the exec +parameter to locate it." ) )) } @@ -123,7 +128,8 @@ gl.run.structure <- function(x, ev <- utils.structure.evanno(sr) pa <- - ((ev$plots$mean.ln.k + ev$plots$mean.ln.k) / (ev$plots$ln.ppk + ev$plots$delta.k) + ((ev$plots$mean.ln.k + ev$plots$mean.ln.k) / (ev$plots$ln.ppk + + ev$plots$delta.k) ) + plot_theme # PRINTING OUTPUTS @@ -141,7 +147,8 @@ gl.run.structure <- function(x, # creating temp file names temp_plot <- - tempfile(pattern = paste0("Plot", paste0(nmc, "_", mc, collapse = "_"))) + tempfile(pattern = paste0("Plot", paste0(nmc, "_", mc, + collapse = "_"))) # saving to tempdir saveRDS(pa, file = temp_plot) @@ -155,7 +162,8 @@ gl.run.structure <- function(x, ) cat( report( - " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + " NOTE: Retrieve output files from tempdir using + gl.list.reports() and gl.print.reports()\n" ) ) } diff --git a/R/gl.select.colors.r b/R/gl.select.colors.r index 08105c95..bfcb1ed6 100644 --- a/R/gl.select.colors.r +++ b/R/gl.select.colors.r @@ -159,7 +159,7 @@ gl.select.colors <- function(x = NULL, # DO THE JOB if (is.null(library)) { - palette = NULL + palette <-NULL if (verbose >= 2) { cat(warn( " Warning: No color library or palette specified, set to default\n" diff --git a/R/gl.sim.WF.run.r b/R/gl.sim.WF.run.r index 5a1f0582..2fc3827e 100644 --- a/R/gl.sim.WF.run.r +++ b/R/gl.sim.WF.run.r @@ -142,19 +142,19 @@ gl.sim.WF.run <- sim_vars <- interactive_sim_run() sim_vars[sim_vars$variable=="population_size_phase2" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="population_size_phase2" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="population_size_phase2" ,"value"],"'") sim_vars[sim_vars$variable=="population_size_phase1" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="population_size_phase1" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="population_size_phase1" ,"value"],"'") sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"],"'") sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"],"'") sim_vars[sim_vars$variable=="natural_selection_model" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="natural_selection_model" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="natural_selection_model" ,"value"],"'") sim_vars <- sim_vars[order(sim_vars$variable),] @@ -193,19 +193,19 @@ gl.sim.WF.run <- sim_vars[val_change,"value"] <- unlist(input_list) sim_vars[sim_vars$variable=="population_size_phase2" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="population_size_phase2" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="population_size_phase2" ,"value"],"'") sim_vars[sim_vars$variable=="population_size_phase1" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="population_size_phase1" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="population_size_phase1" ,"value"],"'") sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase2" ,"value"],"'") sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="dispersal_type_phase1" ,"value"],"'") sim_vars[sim_vars$variable=="natural_selection_model" ,"value"] <- - paste0("'",sim_vars[sim_vars$variable=="natural_selection_model" ,"value"],"'") +paste0("'",sim_vars[sim_vars$variable=="natural_selection_model" ,"value"],"'") vars_assign <- unlist(unname( @@ -220,13 +220,16 @@ gl.sim.WF.run <- neutral_loci_location <- which(reference$type == "neutral" | reference$type == "real") - adv_loci <- which(reference$type=="mutation_adv" | reference$type=="advantageous") + adv_loci <- + which(reference$type=="mutation_adv" | reference$type=="advantageous") mutation_loci_adv <- which(reference$type == "mutation_adv" ) mutation_loci_del <- which(reference$type == "mutation_del") mutation_loci_neu <- which(reference$type == "mutation_neu") - mutation_loci_location <- c(mutation_loci_adv,mutation_loci_del,mutation_loci_neu) - mutation_loci_location <- mutation_loci_location[order(mutation_loci_location)] + mutation_loci_location <- + c(mutation_loci_adv,mutation_loci_del,mutation_loci_neu) + mutation_loci_location <- + mutation_loci_location[order(mutation_loci_location)] real <- which(reference$type == "real") @@ -235,18 +238,23 @@ gl.sim.WF.run <- # this is the option of real_freq from the reference table real_freq_table <- ref_vars[ref_vars$variable=="real_freq","value"] if(real_freq_table != real_freq){ - cat(error(" The value for the real_freq parameter was set differently in the simulations and in the creation of the reference table. They should be the same. Please check it\n")) + cat(error(" The value for the real_freq parameter was set differently in + the simulations and in the creation of the reference table. + They should be the same. Please check it\n")) stop() } # this is the option of real_loc from the reference table real_loc_table <- ref_vars[ref_vars$variable=="real_loc","value"] if(real_loc_table != real_loc){ - cat(error(" The value for the real_loc parameter was set differently in the simulations and in the creation of the reference table. They should be the same. Please check it\n")) + cat(error(" The value for the real_loc parameter was set differently in + the simulations and in the creation of the reference table. + They should be the same. Please check it\n")) stop() } - if( (real_pops ==TRUE | real_pop_size ==TRUE | real_loc ==TRUE | real_freq==TRUE) && is.null(x)){ + if( (real_pops ==TRUE | real_pop_size ==TRUE | real_loc ==TRUE | + real_freq==TRUE) && is.null(x)){ cat(error(" The real dataset to extract information is missing\n")) stop() } @@ -260,25 +268,26 @@ gl.sim.WF.run <- # This is the list to store the final genlight objects gen_store <- c(seq(1, number_generations, every_gen), number_generations) - final_res <- rep(list(as.list(rep(NA, length(gen_store)))), number_iterations) + final_res <- rep(list(as.list(rep(NA, length(gen_store)))), + number_iterations) loci_number <- nrow(reference) recombination_map <- reference[, c("c", "loc_bp", "loc_cM")] - # In order for the recombination rate to be accurate, we must account for - # the case when the probability of the total recombination rate is less than - # 1 (i.e. < 100 cM) or more than 1 (> 100 cM). For the first case, the program - # subtracts from 1 the sum of all the recombination rates and this value - # inserted in the last row of the recombination_map table. If this row is - # chosen as the recombination point, recombination does not occur. For - # example, if a chromosome of 20 cM’s is simulated, the last row of the - # recombination_map will have a value of 0.8 and therefore 80% of the times - # recombination will not occur. For the second case, having more than 100 cM, - # means that more than 1 recombination event occurs. So, one recombination - # event is perform for each 100 cM. Then, the program subtracts the number of - # recombination events from the sum of all the recombination rates and this - # value inserted in the last row of the recombination_map table, in the same - # way as in the first case. - # number of recombination events per meiosis + # In order for the recombination rate to be accurate, we must account for + # the case when the probability of the total recombination rate is less than + # 1 (i.e. < 100 cM) or more than 1 (> 100 cM). For the first case, the program + # subtracts from 1 the sum of all the recombination rates and this value + # inserted in the last row of the recombination_map table. If this row is + # chosen as the recombination point, recombination does not occur. For + # example, if a chromosome of 20 cM’s is simulated, the last row of the + # recombination_map will have a value of 0.8 and therefore 80% of the times + # recombination will not occur. For the second case, having more than 100 cM, + # means that more than 1 recombination event occurs. So, one recombination + # event is perform for each 100 cM. Then, the program subtracts the number of + # recombination events from the sum of all the recombination rates and this + # value inserted in the last row of the recombination_map table, in the same + # way as in the first case. + # number of recombination events per meiosis recom_event <- ceiling(sum(recombination_map[, "c"],na.rm = TRUE)) # filling the probability of recombination when the total recombination rate # is less than an integer (recom_event) and placing it at the end of the @@ -288,8 +297,9 @@ gl.sim.WF.run <- recombination_map[loci_number + 1, 2] <- recombination_map[loci_number, 2] recombination_map[loci_number + 1, 3] <- recombination_map[loci_number, 3] - # one is subtracted from the recombination map to account for the last row that - # was added in the recombination map to avoid that the recombination function crashes +# one is subtracted from the recombination map to account for the last row that +# was added in the recombination map to avoid that the recombination function + # crashes plink_map <- as.data.frame(matrix(nrow = nrow(reference), ncol = 4)) plink_map[, 1] <- reference$chr_name plink_map[, 2] <- rownames(reference) @@ -298,16 +308,21 @@ gl.sim.WF.run <- dispersal_type_phase2 <- gsub('\"', "", dispersal_type_phase2, fixed = TRUE) dispersal_type_phase1 <- gsub('\"', "", dispersal_type_phase1, fixed = TRUE) - natural_selection_model <- gsub('\"', "", natural_selection_model, fixed = TRUE) + natural_selection_model <- gsub('\"', "", natural_selection_model, + fixed = TRUE) chromosome_name <- gsub('\"', "", chromosome_name, fixed = TRUE) - population_size_phase2 <- gsub('\"', "", population_size_phase2, fixed = TRUE) + population_size_phase2 <- gsub('\"', "", population_size_phase2, + fixed = TRUE) - population_size_phase2 <- as.numeric(unlist(strsplit(population_size_phase2, " "))) + population_size_phase2 <- + as.numeric(unlist(strsplit(population_size_phase2, " "))) - population_size_phase1 <- gsub('\"', "", population_size_phase1, fixed = TRUE) + population_size_phase1 <- + gsub('\"', "", population_size_phase1, fixed = TRUE) - population_size_phase1 <- as.numeric(unlist(strsplit(population_size_phase1, " "))) + population_size_phase1 <- + as.numeric(unlist(strsplit(population_size_phase1, " "))) local_adap <- gsub('\"', "", local_adap, fixed = TRUE) @@ -333,7 +348,8 @@ gl.sim.WF.run <- if (real_freq == TRUE & !is.null(x) & real_loc == TRUE) { pop_list_freq_temp <- seppop(x) loc_to_keep <- - locNames(pop_list_freq_temp[[1]])[which(pop_list_freq_temp[[1]]$chromosome == chromosome_name)] +locNames(pop_list_freq_temp[[1]])[which(pop_list_freq_temp[[1]]$chromosome == + chromosome_name)] pop_list_freq_temp <- lapply(pop_list_freq_temp, gl.keep.loc, @@ -352,19 +368,20 @@ gl.sim.WF.run <- pop_list_freq <- rep(NA, number_pops) } - # This is to calculate the density of mutations per centimorgan. The density - # is based on the number of heterozygous loci in each individual. Based on HW - # equation (p^2+2pq+q^2), the proportion of heterozygotes (2pq) for each locus - # is calculated and then averaged. This proportion is then multiplied by the - # number of loci and divided by the length of the chromosome in centiMorgans. - # According to Haddrill 2010, the mean number of heterozygous deleterious - # mutations per fly is 5,000 deleterious mutations per individual, with an - # estimated mean selection coefficient (sh) of 1.1X10^-5. The chromosome - # arm 2L has 17% of the total number of non-synonymous mutations and is 55/2 - # cM long (cM are divided by two because there is no recombination in males), - # with these parameters the density per cM is (5000*0.17)/(55/2) = 30.9 + # This is to calculate the density of mutations per centimorgan. The density + # is based on the number of heterozygous loci in each individual. Based on HW + # equation (p^2+2pq+q^2), the proportion of heterozygotes (2pq) for each locus + # is calculated and then averaged. This proportion is then multiplied by the + # number of loci and divided by the length of the chromosome in centiMorgans. + # According to Haddrill 2010, the mean number of heterozygous deleterious + # mutations per fly is 5,000 deleterious mutations per individual, with an + # estimated mean selection coefficient (sh) of 1.1X10^-5. The chromosome + # arm 2L has 17% of the total number of non-synonymous mutations and is 55/2 + # cM long (cM are divided by two because there is no recombination in males), + # with these parameters the density per cM is (5000*0.17)/(55/2) = 30.9 freq_deleterious <- reference[-as.numeric(neutral_loci_location),] - freq_deleterious_b <- mean(2 * (freq_deleterious$q) * (1 - freq_deleterious$q)) + freq_deleterious_b <- mean(2 * (freq_deleterious$q) * + (1 - freq_deleterious$q)) density_mutations_per_cm <- (freq_deleterious_b * nrow(freq_deleterious)) / (recombination_map[loci_number, "loc_cM"] * 100) @@ -426,17 +443,19 @@ gl.sim.WF.run <- } if(phase1 == TRUE & number_pops_phase1!=number_pops_phase2 ){ - cat(error(" Number of populations in phase 1 and phase 2 must be the same\n")) +cat(error(" Number of populations in phase 1 and phase 2 must be the same\n")) stop() } if(length(population_size_phase2)!=number_pops_phase2){ - cat(error(" Number of entries for population sizes do not agree with the number of populations for phase 2\n")) + cat(error(" Number of entries for population sizes do not agree with + the number of populations for phase 2\n")) stop() } if(length(population_size_phase1)!=number_pops_phase1 & phase1==TRUE){ - cat(error(" Number of entries for population sizes do not agree with the number of populations for phase 1\n")) + cat(error(" Number of entries for population sizes do not agree with + the number of populations for phase 1\n")) stop() } @@ -510,12 +529,12 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); q_prob_t2 <- cbind(q_prob_t,1-q_prob_t) q_prob <- split(q_prob_t2, row(q_prob_t2)) - stringi::stri_sub_all(pop[individual_pop, 3], from=real,length = 1) <- +stringi::stri_sub_all(pop[individual_pop, 3], from=real,length = 1) <- mapply(function(y){sample(x=c(1,0),size=1,prob=y,replace=FALSE)}, q_prob, USE.NAMES = FALSE) - stringi::stri_sub_all(pop[individual_pop, 4], from=real,length = 1) <- +stringi::stri_sub_all(pop[individual_pop, 4], from=real,length = 1) <- mapply(function(y){sample(x=c(1,0),size=1,prob=y,replace=FALSE)}, q_prob, USE.NAMES = FALSE) @@ -571,7 +590,7 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (phase1 == TRUE) { if (same_line == TRUE) { - # pop_list_temp is used because pop_list is used to sample populations + # pop_list_temp is used because pop_list is used to sample populations pop_sample <- sample(pops_vector, 1) pop_list_temp <- lapply(pops_vector, function(x) { @@ -743,11 +762,14 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (offspring_pop[offspring_ind, "runif"] < mut_rate) { locus_to_mutate <- sample(mutation_loci_location, 1) mutation_loci_location <- - mutation_loci_location[-which(mutation_loci_location == locus_to_mutate)] - chromosomes <- c(offspring_pop[offspring_ind, 3], offspring_pop[offspring_ind, 4]) + mutation_loci_location[-which(mutation_loci_location == + locus_to_mutate)] + chromosomes <- c(offspring_pop[offspring_ind, 3], + offspring_pop[offspring_ind, 4]) chr_to_mutate <- sample(1:2, 1) chr_to_mutate_b <- chromosomes[chr_to_mutate] - substr(chr_to_mutate_b, as.numeric(locus_to_mutate), as.numeric(locus_to_mutate)) <- "1" + substr(chr_to_mutate_b, as.numeric(locus_to_mutate), + as.numeric(locus_to_mutate)) <- "1" chromosomes[chr_to_mutate] <- chr_to_mutate_b offspring_pop[offspring_ind, 3] <- chromosomes[1] offspring_pop[offspring_ind, 4] <- chromosomes[2] @@ -766,9 +788,12 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (selection == TRUE) { if(!is.null(local_adap)){ pops_local <- setdiff(pops_vector, local_adap) - reference_local <- replicate(length(pops_vector), reference[,c("s","h")], simplify = FALSE) + reference_local <- replicate(length(pops_vector), + reference[,c("s","h")], + simplify = FALSE) - reference_local[pops_local] <- lapply(reference_local[pops_local], function(y){ + reference_local[pops_local] <- lapply(reference_local[pops_local], + function(y){ y[adv_loci,"s"] <- 0 return(y) }) @@ -785,9 +810,12 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); } else if(!is.null(clinal_adap)){ pops_clinal <- seq(clinal_adap[1],clinal_adap[2],1) - reference_clinal_temp <- replicate(length(pops_vector), reference[,c("s","h")], simplify = FALSE) + reference_clinal_temp <- replicate(length(pops_vector), + reference[,c("s","h")], + simplify = FALSE) - clinal_s <- 1 - c(0,(1:(length(pops_clinal)-1)*(clinal_strength/100))) + clinal_s <- 1 - c(0,(1:(length(pops_clinal)-1)* + (clinal_strength/100))) reference_clinal <- lapply(pops_clinal,function(y){ reference_clinal_temp[[y]][adv_loci,"s"] <- @@ -824,8 +852,8 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); # testing whether any population became extinct, if so break the # iteration and pass to the next test_extinction <- unlist(lapply(pops_vector, function(x) { - length(which(offspring_list[[x]]$V1 == "Male")) < population_size / 2 | - length(which(offspring_list[[x]]$V1 == "Female")) < population_size / 2 + length(which(offspring_list[[x]]$V1 == "Male")) < population_size / 2 | + length(which(offspring_list[[x]]$V1 == "Female")) < population_size / 2 })) if (any(test_extinction == TRUE)) { @@ -844,9 +872,10 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (sample_percent != 100) { - population_size_temp <- round(population_size * (sample_percent/100)) + population_size_temp <- round(population_size * (sample_percent/100)) # Converting odd even population sizes to even - population_size_temp <- (population_size_temp %% 2 != 0) + population_size_temp + population_size_temp <- (population_size_temp %% 2 != 0) + + population_size_temp pop_list_temp <- lapply(pops_vector, function(x) { rbind(pop_list[[x]][sample(which(pop_list[[x]]$V1 == "Male"), size = population_size_temp[x] / 2),], @@ -864,7 +893,8 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); # formatting the values of the variables to be saved in the genlight # object s_vars_temp <- rbind(ref_vars, sim_vars) - s_vars_temp <- setNames(data.frame(t(s_vars_temp[,-1])), s_vars_temp[, 1]) + s_vars_temp <- setNames(data.frame(t(s_vars_temp[,-1])), + s_vars_temp[, 1]) s_vars_temp$generation <- generation s_vars_temp$iteration <- iteration s_vars_temp$seed <- seed @@ -900,9 +930,9 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (selection == FALSE) { pop_list <- lapply(pops_vector, function(x) { - rbind(offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Male"), + rbind(offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Male"), size = population_size[x] / 2),], - offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Female"), + offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Female"), size = population_size[x] / 2),]) }) } @@ -910,26 +940,26 @@ NumericVector p = NumericVector::create(q[z],1-q[z]); if (selection == TRUE & natural_selection_model == "absolute") { pop_list <- lapply(pops_vector, function(x) { - rbind(offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Male"), + rbind(offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Male"), size = population_size[x] / 2),], - offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Female"), + offspring_list[[x]][sample(which(offspring_list[[x]]$V1 == "Female"), size = population_size[x] / 2),]) }) } if (selection == TRUE & natural_selection_model == "relative") { - # We modeled selection as Lesecque et al. 2012: offspring are randomly - # selected to become parents of the next generation in proportion to - # their relative fitness, for example, if we had four individuals - # with fitness (W) of 0.1, 0.2, 0.3, and 0.2 the first individual - # would be selected on average 0.1/(0.1+0.2+0.3+0.2)=0.125 of the time - # to become parent of the next generation. The vector of probabilities - # used in sample is multiplied by two because in the selection function - # (selection_fun), the proportional relative fitness was calculated for - # all offspring together, and below the males and females are separated - # in groups, with the objective that exactly the parents of the next - # generation are half males and half females + # We modeled selection as Lesecque et al. 2012: offspring are randomly + # selected to become parents of the next generation in proportion to + # their relative fitness, for example, if we had four individuals + # with fitness (W) of 0.1, 0.2, 0.3, and 0.2 the first individual + # would be selected on average 0.1/(0.1+0.2+0.3+0.2)=0.125 of the time + # to become parent of the next generation. The vector of probabilities + # used in sample is multiplied by two because in the selection function + # (selection_fun), the proportional relative fitness was calculated for + # all offspring together, and below the males and females are separated + # in groups, with the objective that exactly the parents of the next + # generation are half males and half females pop_list <- lapply(pops_vector, function(x) { males_pop <- @@ -986,7 +1016,8 @@ Rcpp::cppFunction(plugins="cpp11", freqs <- make_freqs(pops_seqs) deleterious_eliminated <- which(freqs==0) - mutation_loci_location <- union(mutation_loci_location,deleterious_eliminated) + mutation_loci_location <- + union(mutation_loci_location,deleterious_eliminated) } #toc() @@ -1001,14 +1032,16 @@ Rcpp::cppFunction(plugins="cpp11", if (sample_percent < 100) { - population_size_temp <- round(population_size * (sample_percent/100)) + population_size_temp <- round(population_size * + (sample_percent/100)) # Converting odd population sizes to even - population_size_temp <- (population_size_temp %% 2 != 0) + population_size_temp + population_size_temp <- (population_size_temp %% 2 != 0) + + population_size_temp pop_list_temp <- lapply(pops_vector, function(x) { rbind(pop_list[[x]][sample(which(pop_list[[x]]$V1 == "Male"), - size = population_size_temp[x] / 2),], + size = population_size_temp[x] / 2),], pop_list[[x]][sample(which(pop_list[[x]]$V1 == "Female"), - size = population_size_temp[x] / 2),]) + size = population_size_temp[x] / 2),]) }) }else{ @@ -1021,7 +1054,8 @@ Rcpp::cppFunction(plugins="cpp11", # formatting the values of the variables to be saved in the genlight # object s_vars_temp <- rbind(ref_vars, sim_vars) - s_vars_temp <- setNames(data.frame(t(s_vars_temp[,-1])), s_vars_temp[, 1]) + s_vars_temp <- setNames(data.frame(t(s_vars_temp[,-1])), + s_vars_temp[, 1]) s_vars_temp$generation <- generation s_vars_temp$iteration <- iteration s_vars_temp$seed <- seed @@ -1030,8 +1064,10 @@ Rcpp::cppFunction(plugins="cpp11", s_vars_temp$file_dispersal <- file_dispersal if(dispersal==TRUE){ - s_vars_temp$number_transfers_phase2 <- paste(dispersal_pairs$number_transfers, collapse = " ") - s_vars_temp$transfer_each_gen_phase2 <- paste(dispersal_pairs$transfer_each_gen, collapse = " ") + s_vars_temp$number_transfers_phase2 <- + paste(dispersal_pairs$number_transfers, collapse = " ") + s_vars_temp$transfer_each_gen_phase2 <- + paste(dispersal_pairs$transfer_each_gen, collapse = " ") } final_res[[iteration]][[count_store]] <- @@ -1052,7 +1088,7 @@ Rcpp::cppFunction(plugins="cpp11", }else{ - popNames(final_res[[iteration]][[count_store]]) <- as.character(pops_vector) + popNames(final_res[[iteration]][[count_store]]) <- as.character(pops_vector) } diff --git a/R/gl.sim.emigration.r b/R/gl.sim.emigration.r index 14766d04..a5667d7a 100644 --- a/R/gl.sim.emigration.r +++ b/R/gl.sim.emigration.r @@ -51,12 +51,12 @@ gl.sim.emigration <- function(x, else p <- x pn <- names(p) - n.pops = length(p) + n.pops <-length(p) migs <- matrix(0, nrow = n.pops, ncol = n.pops) migrants <- NA if (is.null(emi.table)) { # convert disdis into prob - emi.m = emi.m / rep(colSums(emi.m), each = ncol(emi.m)) + emi.m <-emi.m / rep(colSums(emi.m), each = ncol(emi.m)) for (i in 1:n.pops) { pop.size <- nrow(p[[i]]) if (!is.null(pop.size)) { diff --git a/R/gl.sim.mutate.r b/R/gl.sim.mutate.r index f8d8dc87..eb64024d 100644 --- a/R/gl.sim.mutate.r +++ b/R/gl.sim.mutate.r @@ -32,7 +32,7 @@ gl.sim.mutate <- function(x, if (!cs %% 2) nv <- 1 else - nv = sample(c(0, 2), 1) + nv <-sample(c(0, 2), 1) xx[rl] <- nv x@gen[[ri]] <- new("SNPbin", xx) } #end missing diff --git a/R/gl.spatial.autoCorr.r b/R/gl.spatial.autoCorr.r new file mode 100644 index 00000000..0e24a99b --- /dev/null +++ b/R/gl.spatial.autoCorr.r @@ -0,0 +1,678 @@ +#' @name gl.spatial.autoCorr +#' @title Spatial autocorrelation following Smouse and Peakall 1999 +#' +#' @description Global spatial autocorrelation is a multivariate approach +#' combining all loci into a single analysis. The autocorrelation coefficient +#' "r" is calculated for each pair of individuals in each specified distance +#' class. For more information see Smouse and Peakall 1999, Peakall et al. 2003 +#' and Smouse et al. 2008. +#' +#' @details This function executes a modified version +#' of \code{spautocorr} from the package \code{PopGenReport}. Differently +#' from \code{PopGenReport}, this function also computes the 95\% confidence +#' intervals around the r via bootstraps, the 95% confidence interval around the +#' null hypothesis of no spatial structure and the one-tail test via permutation, +#' and the correction factor described by Peakall et al 2003. +#' +#' The input can be i) a genlight object (which has to have the latlon slot +#' populated), ii) a pair of \code{Dgeo} and \code{Dgen}, which have to be either +#' \code{matrix} or \code{dist} objects, or iii) a \code{list} of the +#' \code{matrix} or \code{dist} objects if the +#' analysis needs to be carried out for multiple populations (in this case, +#' all the elements of the \code{list} have to be of the same class (i.e. +#' \code{matrix} or \code{dist}) and the population order in the two lists has +#' to be the same. +#' +#' If the input is a genlight object, the function calculates the linear distance +#' for \code{Dgeo} and the relevant \code{Dgen} matrix (see \code{Dgen_method}) +#' for each population. +#' When the method selected is a genetic similarity matrix (e.g. "simple" +#' distance), the matrix is internally transformed with \code{1 - Dgen} so that +#' positive values of autocorrelation coefficients indicates more related +#' individuals similarly as implemented in GenAlEx. If the user provide the +#' distance matrices, care must be taken in interpreting the results because +#' similarity matrix will generate negative values for closely related +#' individuals. +#' +#' If \code{max(Dgeo)>1000} (e.g. the geographic distances are in thousands of +#' metres), values are divided by 1000 (in the example before these would then +#' become km) to facilitate readability of the plots. +#' +#' If \code{bins} is of length = 1 it is interpreted as the number of (even) +#' bins to use. In this case the starting point is always the minimum value in +#' the distance matrix, and the last is the maximum. If it is a numeric vector +#' of length>1, it is interpreted as the breaking points. In this case, the +#' first has to be the lowest value, and the last has to be the highest. There +#' are no internal checks for this and it is user responsibility to ensure that +#' distance classes are properly set up. If that is not the case, data that fall +#' outside the range provided will be dropped. The number of bins will be +#' \code{length(bins) - 1}. +#' +#' The permutation constructs the 95\% confidence intervals around the null +#' hypothesis of no spatial structure (this is a two-tail test). The same data +#' are also used to calculate the probability of the one-tail test (See +#' references below for details). +#' +#' Bootstrap calculations are skipped and \code{NA} is returned when the number +#' of possible combinations given the sample size of any given distance class is +#' < \code{reps}. +#' +#' Methods available to calculate genetic distances for SNP data: +#' \itemize{ +#' \item "propShared" using the function \code{\link{gl.propShared}}. +#' \item "grm" using the function \code{\link{gl.grm}}. +#' \item "Euclidean" using the function \code{\link{gl.dist.ind}}. +#' \item "Simple" using the function \code{\link{gl.dist.ind}}. +#' \item "Absolute" using the function \code{\link{gl.dist.ind}}. +#' \item "Manhattan" using the function \code{\link{gl.dist.ind}}. +#' } +#' +#' Methods available to calculate genetic distances for SilicoDArT data: +#' \itemize{ +#' \item "Euclidean" using the function \code{\link{gl.dist.ind}}. +#' \item "Simple" using the function \code{\link{gl.dist.ind}}. +#' \item "Jaccard" using the function \code{\link{gl.dist.ind}}. +#' \item "Bray-Curtis" using the function \code{\link{gl.dist.ind}}. +#' } +#' +#' Plots and table are saved to the temporal directory (tempdir) and can be +#' accessed with the function \code{\link{gl.print.reports}} and listed with +#' the function \code{\link{gl.list.reports}}. Note that they can be accessed +#' only in the current R session because tempdir is cleared each time that the +#' R session is closed. +#' +#' Examples of other themes that can be used can be consulted in \itemize{ +#' \item \url{https://ggplot2.tidyverse.org/reference/ggtheme.html} and \item +#' \url{https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/} +#' } +#' +#' @param x Genlight object [default NULL]. +#' @param Dgen Genetic distance matrix if no genlight object is provided +#' [default NULL]. +#' @param Dgeo Geographic distance matrix if no genlight object is provided. This +#' is typically an Euclidean distance but it can be any meaningful +#' (geographical) distance metrics [default NULL]. +#' @param coordinates Can be either 'latlon', 'xy' or a two column data.frame +#' with column names 'lat','lon', 'x', 'y') Coordinates are provided via +#' \code{gl@other$latlon} ['latlon'] or via \code{gl@other$xy} ['xy']. If latlon +#' data will be projected to meters using Mercator system [google maps] or if +#' xy then distance is directly calculated on the coordinates [default . +#' @param Dgen_method Method to calculate genetic distances. See details +#' [default "Euclidean"]. +#' @param Dgeo_trans Transformation to be used on the geographic distances. See +#' Dgen_trans [default "Dgeo"]. +#' @param Dgen_trans You can provide a formula to transform the genetic +#' distance. The transformation can be applied as a formula using Dgen as the +#' variable to be transformed. For example: \code{Dgen_trans = 'Dgen/(1-Dgen)'. +#' Any valid R expression can be used here +#' [default 'Dgen', which is the identity function.]} +#' @param bins The number of bins for the distance classes +#' (i.e. \code{length(bins) == 1)} or a vectors with the break points. See +#' details [default 5]. +#' @param reps The number to be used for permutation and bootstrap analyses +#' [default 100]. +#' @param plot.pops.together Plot all the populations in one plot. Confidence +#' intervals from permutations are not shown [default FALSE]. +#' @param permutation Whether permutation calculations for the null hypothesis +#' of no spatial structure should be carried out [default TRUE]. +#' @param bootstrap Whether bootstrap calculations to compute the 95\% confidence +#' intervals around r should be carried out [default TRUE]. +#' @param plot_theme Theme for the plot. See details [default NULL]. +#' @param plot_colors_pop A color palette for populations or a list with +#' as many colors as there are populations in the dataset [default NULL]. +#' @param CI_color Color for the shade of the 95\% confidence intervals around +#' the r estimates [default "red"]. +#' @param plot.out Specify if plot is to be produced [default TRUE]. +#' @param save2tmp If TRUE, saves any ggplots and listings to the session +#' temporary directory (tempdir) [default FALSE]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log ; 3, progress and results summary; 5, full report [default +#' NULL, unless specified using gl.set.verbosity]. +#' @return Returns a data frame with the following columns: +#' \enumerate{ +#' \item Bin The distance classes +#' \item N The number of pairwise comparisons within each distance class +#' \item r.uc The uncorrected autocorrelation coefficient +#' \item Correction the correction +#' \item r The corrected autocorrelation coefficient +#' \item L.r The corrected autocorrelation coefficient lower limit +#' (if \code{bootstap = TRUE}) +#' \item U.r The corrected autocorrelation coefficient upper limit +#' (if \code{bootstap = TRUE}) +#' \item L.r.null.uc The uncorrected lower limit for the null hypothesis of no +#' spatial autocorrelation (if \code{permutation = TRUE}) +#' \item U.r.null.uc The uncorrected upper limit for the null hypothesis of no +#' spatial autocorrelation (if \code{permutation = TRUE}) +#' \item L.r.null The corrected lower limit for the null hypothesis of no +#' spatial autocorrelation (if \code{permutation = TRUE}) +#' \item U.r.null The corrected upper limit for the null hypothesis of no +#' spatial autocorrelation (if \code{permutation = TRUE}) +#' \item p.one.tail The p value of the one tail statistical test +#' } +#' +#' @author Carlo Pacioni, Bernd Gruber & Luis Mijangos +#' (Post to \url{https://groups.google.com/d/forum/dartr}) +#' @references +#' \itemize{ +#' \item Smouse PE, Peakall R. 1999. Spatial autocorrelation analysis of +#' individual multiallele and multilocus genetic structure. Heredity 82: +#' 561-573. +#' \item Double, MC, et al. 2005. Dispersal, philopatry and infidelity: +#' dissecting local genetic structure in superb fairy-wrens (Malurus cyaneus). +#' Evolution 59, 625-635. +#' \item Peakall, R, et al. 2003. Spatial autocorrelation analysis offers new +#' insights into gene flow in the Australian bush rat, Rattus fuscipes. +#' Evolution 57, 1182-1195. +#' \item Smouse, PE, et al. 2008. A heterogeneity test for fine-scale genetic +#' structure. Molecular Ecology 17, 3389-3400. +#' \item Gonzales, E, et al. 2010. The impact of landscape disturbance on +#' spatial genetic structure in the Guanacaste tree, Enterolobium +#' cyclocarpum (Fabaceae). Journal of Heredity 101, 133-143. +#' \item Beck, N, et al. 2008. Social constraint and an absence of sex-biased +#' dispersal drive fine-scale genetic structure in white-winged choughs. +#' Molecular Ecology 17, 4346-4358. +#' } +#' @examples +#' res <- gl.spatial.autoCorr(platypus.gl, bins=seq(0,10000,2000)) +#' # using one population, showing sample size +#' test <- gl.keep.pop(platypus.gl,pop.list = "TENTERFIELD") +#' res <- gl.spatial.autoCorr(test, bins=seq(0,10000,2000),CI_color = "green") +#' @importFrom tidyr pivot_wider +#' @export + +gl.spatial.autoCorr <- function(x = NULL, + Dgeo = NULL, + Dgen = NULL, + coordinates = "latlon", + Dgen_method = "Euclidean", + Dgeo_trans = "Dgeo", + Dgen_trans = "Dgen", + bins = 5, + reps = 100, + plot.pops.together = FALSE, + permutation = TRUE, + bootstrap = TRUE, + plot_theme = NULL, + plot_colors_pop = NULL, + CI_color = "red", + plot.out = TRUE, + save2tmp = FALSE, + verbose = NULL) { + + # CHECK IF PACKAGES ARE INSTALLED + if (!(requireNamespace("dismo", quietly = TRUE))) { + stop(error( + "Package dismo needed for this function to work. Please install it.\n" + )) + } + + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jackson", + verbosity = verbose) + + # CHECK DATATYPE + if (!is.null(x)) { + dt <- utils.check.datatype(x, verbose = 0) + } + + # specific error checks + if (!is.numeric(bins)) { + stop(error(" The argument 'bins' should be a numeric vector\n")) + } + + if (!is.null(Dgen) & !is.null(Dgeo)) { + if (verbose > 0) + cat( + report( + " Analysis performed using provided genetic and Euclidean distance matrices. If a genlight object is provided, it is ignored.\n" + ) + ) + ta <-"dgendgeo" + } + + if (is(x, "genlight")) { + if (verbose > 0) + cat(report(" Analysis performed on the genlight object.\n")) + ta <-"genlight" + } + + # avoid global binding error + Bin <- + r <- + L.r <- + U.r <- L.r.null <- U.r.null <- Freq <- Var1 <- NULL + + # DO THE JOB # + + #### if a genlight object is provided #### + if (!is.null(x) & is(x, "genlight")) { + + pop_list <- seppop(x) + + Dgen_list <- list() + Dgeo_list <- list() + + for (i in seq_along(pop_list)) { + + if (verbose > 2) { + cat( + report(paste(" Analysing population",names(pop_list[i]),"\n") + ) + ) + } + + x_temp <- pop_list[[i]] + + # check coordinates (if no Dgen and Dgeo is provided) + coords <- NULL + if (is(coordinates, "character")) { + if (coordinates == "latlon") { + if (is.null(x_temp@other$latlon)) + stop(error( + "Cannot find coordinates in x@other$latlon" + )) + coords <- dismo::Mercator(x_temp@other$latlon[, c("lon", "lat")]) + coordstring <-"x@other$latlon (Mercator transformed)" + } + + if (coordinates == "xy") { + if (is.null(x_temp@other$xy)) + stop(error("Cannot find coordinates in x@other$xy")) + coords <- x_temp@other$xy + coordstring <-"x@other$xy" + } + } + + if (is(coordinates, "data.frame")) { + if (length(setdiff(colnames(coordinates), c("lat", "lon"))) == 0) { + coords <- dismo::Mercator(coordinates[, c("lon", "lat")]) + coordstring <-"data.frame lat/lon (Mercator transformed)" + } + + if (length(setdiff(colnames(coordinates), c("x", "y"))) == 0) { + coords <- coordinates[, c("x", "y")] + coordstring <-"data.frame x/y" + } + + if (is.null(coords)) { + stop( + error( + "No valid coordinates provided. Check the provided data.frame and its format.\n" + ) + ) + } + } + + if (is.null(coords)) { + stop(error("No valid coordinates provided!\n")) + } + + # make sure coordinates have the correct length + if (nrow(coords) != nInd(x_temp) & ta == "genlight") { + stop(error( + "Cannot find coordinates for each individual in slot @other$latlon.\n" + )) + } + + if (nInd(x_temp) > 1) { + Dgeo <- dist(coords) + } else { + stop( + error( + "Less than 2 individuals provided, therefore no pairwise distances can be calculated.\n" + ) + ) + } + + # calculate genetic distances + if (Dgen_method == "propShared") { + Dgen <- as.dist(gl.propShared(x_temp)) + } else { + if (Dgen_method == "grm") { + Dgen <- as.dist(gl.grm(x_temp, plotheatmap=FALSE, verbose = 0)) + } else { + Dgen <- gl.dist.ind(x_temp, method = Dgen_method, plot.out = FALSE, + verbose = 0) + } + } + if ((dt == "SNP" & + Dgen_method == "propShared" | + Dgen_method == "Euclidean" | + Dgen_method == "Simple" | + Dgen_method == "Absolute") | + (dt == "SilicoDArT" & Dgen_method == "Euclidean")) { + + # Reverse genetic distance matrix so that correlated values + # indicated more similar individuals as we are used to see plots in GenAleEx + Dgen <- 1 - Dgen + } + + distance <- Dgen_method + + # convert matrices to distance objects + Dgen_list[[i]] <- as.dist(Dgen) + Dgeo_list[[i]] <- as.dist(Dgeo) + } # Close for(i in 1:length(pop_list)) + pop.names <- popNames(x) + } # close if a genlight object is provided + + + #### if distances are provided #### + if (is.null(x)) { + chk.D <- function(D, name.D) { + if(!(is(D, "dist") | is.matrix(D))) + stop(error(paste0(" ", name.D, + " is neither a list, a matrix nor a distance\n"))) + if(is.matrix(D)) D <- as.dist(D) + return(list(D)) + } + + if(!is(Dgeo, "list")) { + Dgeo_list <- chk.D(D=Dgeo, name.D = "Dgeo") + } else { + Dgeo_list <- Dgeo + } + if(!is(Dgen, "list")) { + Dgen_list <- chk.D(D=Dgen, name.D = "Dgen") + } else { + Dgen_list <- Dgen + } + + # now distances are a list + if(length(Dgeo_list) != length(Dgen_list)) + stop(error( " The arguments Dgen and Dgeo should be of same length\n")) + + if(is.null(names(Dgeo_list))) pop.names <- paste0("Pop", seq_along(Dgen_list)) + + chk.D.list <- function(D.list, name.D) { + if(length(unique(sapply(D.list, class))) != 1) { + stop(error(paste0(" ", name.D, + " is a list, but its elements are of different classes. These should be either all matrices or distances\n"))) + } + + if(!(is(D.list[[1]], "dist") | is.matrix(D.list[[1]]))) { + stop(error(paste0(" ", name.D, + " is a list, but its element are neither all matrices nor distances\n"))) + } + + if(is.matrix(D.list[[1]])) D.list <- lapply(D.list, as.dist) + return(D.list) + } + + Dgeo_list <- chk.D.list(Dgeo_list, name.D = "Dgeo") + Dgen_list <- chk.D.list(Dgen_list, name.D = "Dgen") + + len.elements.Dgeo <- sapply(Dgeo_list, length) + len.elements.Dgen <- sapply(Dgen_list, length) + + if (is.character(all.equal(len.elements.Dgeo, len.elements.Dgen))) { + stop(error(" The arguments Dgen and Dgeo should have identical dimensions\n")) + } + coordstring = "Dgeo provided." + distance = "Dgen provided" + typedis = "ind" + } # Close if matrices are provided + #----------------------------------------------------------------------------# + + #### Apply transformations #### + apply.transformation <- function(D, transFUN, name.D) { + assign(name.D, value = D) + new.obj <- eval(parse(text = transFUN)) + return(new.obj) + } + + Dgen_list <- lapply(Dgen_list, apply.transformation, transFUN=Dgen_trans, name.D="Dgen") + Dgeo_list <- lapply(Dgeo_list, apply.transformation, transFUN=Dgeo_trans, name.D="Dgeo") + + lapply(Dgeo_list, function(D) { + if(sum(is.infinite(D)) > 0) { + stop( + error( + "Most likely some pairwise individual distances were zero and the transformation created missing values [e.g. log(Dgeo)]. Consider adding a suitable tranformation e.g. an offset to your Dgeo transformation if using a log transformation [e.g. Dgeo_trans='log(Dgeo+1)'] or adding some 'noise' to the coordinates.\n" + ) + ) + } + + } + ) + + convert2matrix <- function(D) { + D <- as.matrix(D) + diag(D) <- 0 + return(D) + } + + Dgen_list <- lapply(Dgen_list, convert2matrix) + Dgeo_list <- lapply(Dgeo_list, convert2matrix) + + #### Execute utils.spautocorr on a list #### + res <- list() + + for(z in seq_along(Dgeo_list)) { + + Dgeo <- Dgeo_list[[z]] + Dgen <- Dgen_list[[z]] + + sample.size <- nrow(Dgeo) + crt <- 1 / (sample.size - 1) # correction + nbins <- if (length(bins) == 1) { + bins + } else { + length(bins) - 1 + } + + splist <- + utils.spautocor(Dgen, + Dgeo, + permutation = FALSE, + bins = bins, + reps = reps) + + if (permutation) { + bssplist <- replicate(reps, + utils.spautocor( + Dgen, + Dgeo, + permutation = TRUE, + bins = bins, + reps = reps + )) + + #convert the output into a matrix + bs <- matrix( + unlist(bssplist), + nrow = reps, + ncol = nbins, + byrow = TRUE + ) + bs.l <- apply(bs, 2, quantile, probs = 0.025, na.rm = TRUE) + bs.u <- apply(bs, 2, quantile, probs = 0.975, na.rm = TRUE) + + p.one.tail <- + sapply(seq_along(splist$r.uc), function(i, r.rc, r, crt = crt) { + if (is.na(r[i])) { + NA + } else{ + if (r[i] >= 0) { + sum(r.rc[, i] >= r[i]) / length(r.rc[, i]) + } else{ + sum(r.rc[, i] <= r[i]) / length(r.rc[, i]) + } + } + }, r = splist$r.uc + crt, r.rc = bs + crt) + + } + + if (bootstrap) { + errors <- + replicate(reps, + utils.spautocor( + Dgen, + Dgeo, + bootstrap = TRUE, + bins = bins, + reps = reps + )) + errors <- + matrix(unlist(errors), + nrow = reps, + ncol = nbins, + byrow = TRUE) + err.l <- apply(errors, 2, quantile, probs = 0.025, na.rm = TRUE) + err.u <- apply(errors, 2, quantile, probs = 0.975, na.rm = TRUE) + } + + res_temp <- cbind(splist, Correction = crt, r = splist$r.uc + crt) + if (bootstrap) { + res_temp <- cbind(res_temp, L.r = err.l + crt, U.r = err.u + crt) + } + + if (permutation) { + res_temp <- cbind( + res_temp, + L.r.null.uc = bs.l, + U.r.null.uc = bs.u, + L.r.null = bs.l + crt, + U.r.null = bs.u + crt, + p.one.tail = p.one.tail + ) + } + res[[z]] <- res_temp + } + + names(res) <- pop.names + #-------- Close Execute utils.spautoCorr ------------------------------------# + +#### PRINTING OUTPUTS #### + + if (plot.out) { + + if (is.null(plot_theme)) { + plot_theme <- theme_dartR() + } + + if (is.null(plot_colors_pop)) { + plot_colors_pop <- discrete_palette + } + + if (is(plot_colors_pop, "function")) { + if(is.null(x)) n.pop <- length(Dgeo_list) else + n.pop <- nPop(x) + plot_colors_pop <- plot_colors_pop(n.pop) + } + + if (!is(plot_colors_pop, "function")) { + plot_colors_pop <- plot_colors_pop + } + + spa_multi <-data.table::rbindlist(res, use.names = TRUE, + fill = TRUE, idcol = "Population") + if(spa_multi [, max(Bin)] > 1000) { + lbls <- round(spa_multi$Bin/1000, 0) + } else { + lbls <- spa_multi$Bin + } + + p3 <- ggplot(spa_multi, aes_string("Bin", "r", col="Population")) + + geom_line(size=1) + + geom_point(size=2) + + geom_hline(yintercept = 0, col = "black", size=1) + + scale_color_manual(values = plot_colors_pop) + + scale_x_continuous(breaks = spa_multi$Bin, + labels = lbls) + + ylab("Autocorrelation (r)") + + xlab("Distance class") + + plot_theme + + if (bootstrap) { + p3 <- p3 + + geom_errorbar(aes(ymin=L.r, ymax=U.r), + width=spa_multi[, mean(tail(Bin, -1) - head(Bin, -1))]/10) + } + + if (permutation & plot.pops.together == FALSE) { + p3 <- p3 + + geom_ribbon(aes(ymin=L.r.null,ymax=U.r.null), fill = CI_color, + alpha=0.25,show.legend = FALSE) + + geom_line(aes(y = L.r.null), col = "black", linetype = "dashed") + + geom_point(aes(y = L.r.null), col = "black") + + geom_line(aes(y = U.r.null), col = "black", linetype = "dashed") + + geom_point(aes(y = U.r.null), col = "black") + + facet_wrap(~Population, nrow = length(Dgen_list), scales = "free_y") + + theme( + # strip.text.x = element_text(size = 12), + # axis.text.x = element_text( + # size = 12 + # ), + legend.position = "none") + } + + if(length(Dgen_list) == 1) { + p3 <- p3 + + scale_x_continuous(breaks = spa_multi$Bin, + labels = lbls, + sec.axis = sec_axis( + trans = ~ ., + breaks = spa_multi$Bin, + labels = spa_multi$N)) + + theme(strip.text = element_blank(), legend.position = "none") + } + suppressWarnings( + suppressMessages(print(p3)) + ) + } + + if (verbose > 0) { + cat(report(" Coordinates used from:", coordstring, "\n")) + cat(report(" Transformation of Dgeo:", Dgeo_trans, "\n")) + cat(report(" Genetic distance:", distance, "\n")) + cat(report(" Tranformation of Dgen: ", Dgen_trans, "\n")) + print(res) + } + + # SAVE INTERMEDIATES TO TEMPDIR + + # creating temp file names + if (save2tmp) { + if (plot.out) { + temp_plot <- tempfile(pattern = "Plot_") + match_call <- + paste0(names(match.call()), + "_", + as.character(match.call()), + collapse = "_") + # saving to tempdir + saveRDS(list(match_call, p3), file = temp_plot) + + if (verbose >= 2) { + cat(report(" Saving the ggplot to session tempfile\n")) + } + } + + temp_table <- tempfile(pattern = "Table_") + saveRDS(list(match_call, res), file = temp_table) + if (verbose >= 2) { + cat(report(" Saving tabulation to session tempfile\n")) + cat( + report( + " NOTE: Retrieve output files from tempdir using gl.list.reports() and gl.print.reports()\n" + ) + ) + } + } + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + return(invisible(res)) + +} diff --git a/R/gl.tree.nj.r b/R/gl.tree.nj.r index a2df4972..cbf2ca87 100644 --- a/R/gl.tree.nj.r +++ b/R/gl.tree.nj.r @@ -54,7 +54,7 @@ gl.tree.nj <- function(x, " Converting to a matrix of frequencies, locus by populations\n" )) } - t = apply(as.matrix(x), 2, tapply, pop(x), function(e) + t <-apply(as.matrix(x), 2, tapply, pop(x), function(e) mean(e) / 2) # Compute Euclidean distance if (verbose >= 2) { diff --git a/R/gl2fasta.r b/R/gl2fasta.r index dda4c1b0..95c62e8c 100644 --- a/R/gl2fasta.r +++ b/R/gl2fasta.r @@ -8,8 +8,8 @@ #' #' Four methods are employed: #' -#' Method 1 -- heterozygous positions are replaced by the standard ambiguity c -#' odes. The resultant sequence fragments are concatenated across loci to +#' Method 1 -- heterozygous positions are replaced by the standard ambiguity +#' codes. The resultant sequence fragments are concatenated across loci to #' generate a single combined sequence to be used in subsequent ML phylogenetic #' analyses. #' @@ -190,9 +190,8 @@ gl2fasta <- function(x, if (method == 1 || method == 3) { allnames <- locNames(x) - snp = as.character(x@loc.all) - trimmed <- - as.character(x@other$loc.metrics$TrimmedSequence) + snp <- as.character(x@loc.all) + trimmed <- as.character(x@other$loc.metrics$TrimmedSequence) snpmatrix <- as.matrix(x) # Create a lookup table for the ambiguity codes A T G C A A W R M) T W T K Y G R K G S C M Y S C @@ -224,9 +223,9 @@ gl2fasta <- function(x, rownames(conversion) <- colnames(conversion) # Extract alleles 1 and 2 - allelepos = x@position - allele1 = gsub("(.)/(.)", "\\1", snp, perl = T) - allele2 = gsub("(.)/(.)", "\\2", snp, perl = T) + allelepos <- x@position + allele1 <-gsub("(.)/(.)", "\\1", snp, perl = T) + allele2 <-gsub("(.)/(.)", "\\2", snp, perl = T) if (verbose >= 2) { @@ -254,16 +253,16 @@ gl2fasta <- function(x, code <- "N" } else { if (snpmatrix[i, j] == 0) { - a1 = allele1[j] - a2 = allele1[j] + a1 <-allele1[j] + a2 <-allele1[j] } if (snpmatrix[i, j] == 1) { - a1 = allele1[j] - a2 = allele2[j] + a1 <-allele1[j] + a2 <-allele2[j] } if (snpmatrix[i, j] == 2) { - a1 = allele2[j] - a2 = allele2[j] + a1 <-allele2[j] + a2 <-allele2[j] } code <- conversion[a1, a2] } @@ -372,8 +371,8 @@ gl2fasta <- function(x, end <- stringr::str_sub(trimmed, start = snpos + 1) # Extract the SNP transition bases (e.g. A and T) - state1 = gsub("(.)/(.)", "\\1", snp, perl = T) - state2 = gsub("(.)/(.)", "\\2", snp, perl = T) + state1 <-gsub("(.)/(.)", "\\1", snp, perl = T) + state2 <-gsub("(.)/(.)", "\\2", snp, perl = T) # Change the SNP state to the alternate if (snpbase == state1) { snpbase <- state2 diff --git a/R/gl2genepop.r b/R/gl2genepop.r index f2297133..b2a47310 100644 --- a/R/gl2genepop.r +++ b/R/gl2genepop.r @@ -129,8 +129,7 @@ gl2genepop <- function (x, loc_all$allele <- paste0("0",loc_all$allele) } - loci_names <- - as.character(loci_names_l[-which(duplicated(loci_names_l))]) + loci_names <- as.character(loci_names_l[-which(duplicated(loci_names_l))]) n.loci <- length(loci_names_l[-which(duplicated(loci_names_l))]) data_gpop <- data.frame(id = paste(pop_names, "_", row.names(data), ",", sep = "")) @@ -145,16 +144,17 @@ gl2genepop <- function (x, a[j] <- paste(loc_all[col_loc[hom], "allele"], loc_all[col_loc[hom], "allele"], sep = "") } else if (length(het) != 0) { - if (as.character(loc_all[col_loc[het[1]], "allele"]) < - as.character(loc_all[col_loc[het[2]], "allele"])) { + # disabling the order of alleles + # if (as.character(loc_all[col_loc[het[1]], "allele"]) < + # as.character(loc_all[col_loc[het[2]], "allele"])) { a[j] <- paste(loc_all[col_loc[het[1]], "allele"], loc_all[col_loc[het[2]], "allele"], sep = "") - } else { - a[j] <- paste(loc_all[col_loc[het[2]], "allele"], - loc_all[col_loc[het[1]], "allele"], - sep = "") - } + # } else { + # a[j] <- paste(loc_all[col_loc[het[2]], "allele"], + # loc_all[col_loc[het[1]], "allele"], + # sep = "") + # } } else { if (nchar(loc_all[1, "allele"]) == 3) { a[j] <- "000000" @@ -168,12 +168,11 @@ gl2genepop <- function (x, colnames(data_gpop) <- c("ID", as.character(loci_names)) data_gpop[,] <- apply(data_gpop, c(1, 2), as.character) - dummy <- - paste("Genepop output. Loci:", nLoc(x), "Populations:", nPop(x)) + dummy <- paste("Genepop output. Loci:", nLoc(x), "Populations:", nPop(x)) dummy[2] <- paste(locNames(x), collapse = ",") cs <- c(cumsum(table(pop(x)))) - from = c(1, (cs[-length(cs)] + 1)) - to = cs + from <-c(1, (cs[-length(cs)] + 1)) + to <-cs for (i in 1:nPop(x)) { da <- diff --git a/R/gl2geno.r b/R/gl2geno.r index a9365cac..28f13cd0 100644 --- a/R/gl2geno.r +++ b/R/gl2geno.r @@ -54,99 +54,101 @@ gl2geno <- function(x, outfilespec <- file.path(outpath, outfile) - dat <- as.matrix(x) - n = nInd(x) - L = nLoc(x) - - # Convert allelic data into absence/presence data at each locus Results are stored in the 'dat.binary' object - - if (datatype == "SilicoDArT") { - dat <- as.matrix(dat) - dat.binary <- NULL - for (j in 1:L) { - allele <- sort(unique(dat[, j])) - for (i in allele[allele >= 0]) { - dat.binary <- cbind(dat.binary, dat[, j] == i) - } - LL <- ncol(dat.binary) - ind <- which(dat[, j] < 0) - if (length(ind) != 0) { - dat.binary[ind, (LL - length(allele) + 2):LL] <- -9 - } - } - } - - if (datatype == "SNP") { - dummy_final <- NULL - for (r in 1:nrow(dat)) { - dummy <- rbind(dat[r, ], dat[r, ]) - index <- colSums(dummy, na.rm = T) == 2 - dummy[, index] <- c(0, 2) - dummy <- ifelse(is.na(dummy), -9, dummy) - dummy <- ifelse(dummy == 0, 1, dummy) - dummy_final <- rbind(dummy_final, dummy) - } - - dat.binary <- NULL - for (j in 1:L) { - allele = sort(unique(dummy_final[, j])) - for (i in allele[allele >= 0]) { - dat.binary <- cbind(dat.binary, dummy_final[, j] == i) - } - LL <- dim(dat.binary)[2] - ind <- which(dummy_final[, j] < 0) - if (length(ind) != 0) { - dat.binary[ind, (LL - length(allele) + 2):LL] <- -9 - } - } - } - - # Compute a genotype count for each allele (0,1,2 or 9 for a missing value) The results are stored in 'genotype' - - n <- nrow(dat.binary) - L <- ncol(dat) - LL <- ncol(dat.binary) - - if (datatype == "SNP") { - n = n / 2 - genotype <- matrix(NA, nrow = n, ncol = LL) - for (i in 1:n) { - genotype[i, ] <- dat.binary[2 * i - 1, ] + dat.binary[2 * i, ] - genotype[i, (genotype[i, ] < 0)] <- NA - } - if (LL == 2 * L) { - genotype <- genotype[, seq(2, LL, by = 2)] - } - } - - if (datatype == "SilicoDArT") { - genotype <- dat.binary - for (i in 1:n) { - genotype[i, (genotype[i, ] < 0)] <- NA - } - if (LL == 2 * L) { - genotype <- genotype[, seq(2, LL, by = 2)] - } - } - - genotype[is.na(genotype)] <- 9 - lst.monomorphic <- apply( - genotype, - 2, - FUN = function(x) { - length(unique(x[x != 9])) - } - ) - - if (sum(lst.monomorphic == 1) > 0) { - if (verbose > 0) { - cat(warn( - " Monomorphic alleles generated during conversion were removed. \n" - )) - } - genotype <- genotype[, lst.monomorphic > 1] - } - + # dat <- as.matrix(x) + # n <-nInd(x) + # L <-nLoc(x) + # + # # Convert allelic data into absence/presence data at each locus Results are stored in the 'dat.binary' object + # + # if (datatype == "SilicoDArT") { + # dat <- as.matrix(dat) + # dat.binary <- NULL + # for (j in 1:L) { + # allele <- sort(unique(dat[, j])) + # for (i in allele[allele >= 0]) { + # dat.binary <- cbind(dat.binary, dat[, j] == i) + # } + # LL <- ncol(dat.binary) + # ind <- which(dat[, j] < 0) + # if (length(ind) != 0) { + # dat.binary[ind, (LL - length(allele) + 2):LL] <- -9 + # } + # } + # } + # + # if (datatype == "SNP") { + # dummy_final <- NULL + # for (r in 1:nrow(dat)) { + # dummy <- rbind(dat[r, ], dat[r, ]) + # index <- colSums(dummy, na.rm = T) == 2 + # dummy[, index] <- c(0, 2) + # dummy <- ifelse(is.na(dummy), -9, dummy) + # dummy <- ifelse(dummy == 0, 1, dummy) + # dummy_final <- rbind(dummy_final, dummy) + # } + # + # dat.binary <- NULL + # for (j in 1:L) { + # allele <-sort(unique(dummy_final[, j])) + # for (i in allele[allele >= 0]) { + # dat.binary <- cbind(dat.binary, dummy_final[, j] == i) + # } + # LL <- dim(dat.binary)[2] + # ind <- which(dummy_final[, j] < 0) + # if (length(ind) != 0) { + # dat.binary[ind, (LL - length(allele) + 2):LL] <- -9 + # } + # } + # } + # + # # Compute a genotype count for each allele (0,1,2 or 9 for a missing value) The results are stored in 'genotype' + # + # n <- nrow(dat.binary) + # L <- ncol(dat) + # LL <- ncol(dat.binary) + # + # if (datatype == "SNP") { + # n <-n / 2 + # genotype <- matrix(NA, nrow = n, ncol = LL) + # for (i in 1:n) { + # genotype[i, ] <- dat.binary[2 * i - 1, ] + dat.binary[2 * i, ] + # genotype[i, (genotype[i, ] < 0)] <- NA + # } + # if (LL == 2 * L) { + # genotype <- genotype[, seq(2, LL, by = 2)] + # } + # } + # + # if (datatype == "SilicoDArT") { + # genotype <- dat.binary + # for (i in 1:n) { + # genotype[i, (genotype[i, ] < 0)] <- NA + # } + # if (LL == 2 * L) { + # genotype <- genotype[, seq(2, LL, by = 2)] + # } + # } + # + # genotype[is.na(genotype)] <- 9 + # lst.monomorphic <- apply( + # genotype, + # 2, + # FUN = function(x) { + # length(unique(x[x != 9])) + # } + # ) + # + # if (sum(lst.monomorphic == 1) > 0) { + # if (verbose > 0) { + # cat(warn( + # " Monomorphic alleles generated during conversion were removed. \n" + # )) + # } + # genotype <- genotype[, lst.monomorphic > 1] + # } + + genotype <- as.matrix(x) + genotype[is.na(genotype)] <- 9 # write lfmm write.table( genotype, diff --git a/R/gl2gi.r b/R/gl2gi.r index c3534954..f0883633 100644 --- a/R/gl2gi.r +++ b/R/gl2gi.r @@ -61,7 +61,7 @@ gl2gi <- function(x, else if (inp == 2) xx[i, ii] <- homs2[ii] } else - xx[i, ii] = "-/-" + xx[i, ii] <-"-/-" } if (probar) { setTxtProgressBar(pb, i / nrow(x2)) diff --git a/R/gl2phylip.r b/R/gl2phylip.r index cc3a4895..b2be4a9e 100644 --- a/R/gl2phylip.r +++ b/R/gl2phylip.r @@ -77,7 +77,7 @@ gl2phylip <- function(x, "Converting to a matrix of frequencies, locus by populations\n" )) } - t = apply(as.matrix(x), 2, tapply, pop(x), function(e) + t <-apply(as.matrix(x), 2, tapply, pop(x), function(e) mean(e) / 2) # Compute Euclidean distance if (verbose >= 2) { @@ -115,7 +115,7 @@ gl2phylip <- function(x, x[, sample(h, size = nLoc(x), replace = TRUE)] # Convert gl object to a matrix of allele fequencies, locus by population - t = apply(as.matrix(newx), 2, tapply, pop(x), function(e) + t <-apply(as.matrix(newx), 2, tapply, pop(x), function(e) mean(e) / 2) # Compute Euclidean distance diff --git a/R/gl2plink.r b/R/gl2plink.r index 89283190..5a89ae49 100644 --- a/R/gl2plink.r +++ b/R/gl2plink.r @@ -186,7 +186,7 @@ gl2plink <- function(x, else if (inp == 2) xx[i, ii] <- homs2[ii] } else{ - xx[i, ii] = "0/0" + xx[i, ii] <-"0/0" } } } @@ -240,8 +240,8 @@ gl2plink <- function(x, } - system_verbose = function(...) { - report = system(..., intern = T) + system_verbose <-function(...) { + report <-system(..., intern = T) message( paste0( "\n\n----------Output of function start:\n\n", diff --git a/R/gl2shp.R b/R/gl2shp.R index 7637a197..693dd7bf 100644 --- a/R/gl2shp.R +++ b/R/gl2shp.R @@ -108,7 +108,7 @@ gl2shp <- function(x, sp::coordinates(glpoints) <- c("lon", "lat") # create all sites point shp files - spdf = SpatialPointsDataFrame(glpoints, data.frame(glpoints)) + spdf <-SpatialPointsDataFrame(glpoints, data.frame(glpoints)) proj4string(spdf) <- CRS(proj4) # if (!is.null(reproj4)) spdf <- project(spdf, proj = reproj4, inv = TRUE) #now use terra diff --git a/R/gl2structure.r b/R/gl2structure.r index a7e41fbc..92ffd34e 100644 --- a/R/gl2structure.r +++ b/R/gl2structure.r @@ -57,7 +57,7 @@ gl2structure <- function(x, nInd <- nInd(x) if (is.null(indNames)) { - indNames = indNames(x) + indNames <-indNames(x) } if (length(indNames) != nInd) { diff --git a/R/gl2svdquartets.r b/R/gl2svdquartets.r index 2f9ede2c..9c27ee34 100644 --- a/R/gl2svdquartets.r +++ b/R/gl2svdquartets.r @@ -227,8 +227,8 @@ gl2svdquartets <- function(x, a[1] <- 1 b <- table(poplabels) for (i in 2:length(b)) { - b[i] = b[i] + b[i - 1] - a[i] = b[i - 1] + 1 + b[i] <-b[i] + b[i - 1] + a[i] <-b[i - 1] + 1 } plabels <- unique(poplabels) diff --git a/R/gl2vcf.r b/R/gl2vcf.r index dedfd3f1..26880923 100644 --- a/R/gl2vcf.r +++ b/R/gl2vcf.r @@ -80,10 +80,62 @@ gl2vcf <- function(x, # DO THE JOB + # assigning SNP position information + if(snp_pos == "0"){ + x$position <- rep(as.integer(0),nLoc(x)) + + }else{ + + if(snp_pos %in% names(x$other$loc.metrics)){ + + if(verbose>=2){ + cat(report(" Using the SNP position information in the field",snp_chr, "from loc.metrics.\n")) + } + + x$position <- unname(unlist(x$other$loc.metrics[snp_pos])) + + }else{ + + stop(error(" The field",snp_pos, "with the SNP position information is not present in loc.metrics.\n")) + + } + } + + # assigning chromosome information + if(is.null(x$chromosome)){ + + if(snp_chr == "0" ){ + x$chromosome <- rep(as.factor("0"),nLoc(x)) + + if(verbose>=2){ + + cat(report(" Chromosome information is not present in the slot 'chromosome'. Setting '0' as the name chromosome for all the SNPs.\n")) + + } + + }else{ + + if(snp_chr %in% names(x$other$loc.metrics)){ + + if(verbose>=2){ + cat(report(" Using the chromosome information in the field",snp_chr, "from loc.metrics.\n")) + } + + x$chromosome <- as.factor(unname(unlist(x$other$loc.metrics[snp_chr]))) + + }else{ + + stop(error(" The field",snp_chr, "with the chromosome information is not present in loc.metrics.\n")) + + } + } + } + + gl2plink( x = x, outfile = "gl_plink_temp", - outpath = tempdir(), + outpath = outpath, chr_format = chr_format, pos_cM = pos_cM, ID_dad = ID_dad, @@ -93,7 +145,7 @@ gl2vcf <- function(x, verbose = NULL ) - prefix.in_temp <- paste0(tempdir(), "/gl_plink_temp") + prefix.in_temp <- paste0(outpath, "/gl_plink_temp") prefix.out_temp <- file.path(outpath, outfile) allele_tmp <- gsub("/"," ", x$loc.all) @@ -138,8 +190,8 @@ gl2vcf <- function(x, ) } - system_verbose = function(...) { - report = system(..., intern = T) + system_verbose <-function(...) { + report <-system(..., intern = T) message( paste0( "\n\n----------Output of function start:\n\n", diff --git a/R/utils.assignment.r b/R/utils.assignment.r new file mode 100644 index 00000000..8cd0b602 --- /dev/null +++ b/R/utils.assignment.r @@ -0,0 +1,180 @@ +#' @name utils.assignment +#' @title Population assignment probabilities +#' @description +#' This function takes one individual and estimates +#' their probability of coming from individual populations +#' from multilocus genotype frequencies. +# +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param unknown Name of the individual to be assigned to a population [required]. +# @param inbreeding_par The inbreeding parameter [default 0]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details +#' This function is a re-implementation of the function multilocus_assignment +#' from package gstudio. +#' Description of the method used in this function can be found at: +#' https://dyerlab.github.io/applied_population_genetics/population-assignment.html +#' @return A \code{data.frame} consisting of assignment probabilities for each +#' population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' res <- utils.assignment(platypus.gl,unknown="T27") +#' @export + +utils.assignment <- function(x, + unknown, + # inbreeding_par = 0, + verbose = 2) { + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + if (unknown %in% indNames(x) == FALSE) { + stop(error( + paste(" Individual", unknown, "is not in the genlight object\n") + )) + } + + # DO THE JOB + + # filtering loci with all missing data by population + # x <- gl.filter.allna(x, by.pop = TRUE, verbose = 0) + unknown_pop <- gl.keep.ind(x, ind.list = unknown, verbose = 0) + unknown_pop <- data.frame(gl2alleles(unknown_pop)) + + x <- gl.drop.ind(x, ind.list = unknown, verbose = 0) + + pop_names <- popNames(x) + + pop_list <- seppop(x) + gl_alleles <- do.call(rbind, strsplit(x$loc.all, "/")) + + frequencies <- lapply(pop_list, function(y) { + freq_allele <- gl.alf(y) + freqs_gl <- + data.frame( + Allele1 = gl_alleles[, 1], + Allele2 = gl_alleles[, 2], + Frequency1 = freq_allele[, 1], + Frequency2 = freq_allele[, 2] + ) + return(freqs_gl) + }) + + ret <- data.frame(Population = pop_names, Probability = 0) + + for (popx in 1:nPop(x)) { + prob <- 1 + + if (verbose >= 2) { + cat( + report( + " Assigning individual", + unknown, + "against population", + pop_names[popx], + "\n" + ) + ) + } + + popfreq <- frequencies[[popx]] + + loc <- + as.data.frame(do.call(rbind, strsplit(unname( + unlist(unknown_pop) + ), ":"))) + colnames(loc) <- c("a1", "a2") + + df_assign <- cbind(loc, popfreq) + df_assign$hom1 <- df_assign$Frequency1 ^ 2 + df_assign$hom2 <- df_assign$Frequency2 ^ 2 + df_assign$het <- 2 * df_assign$Frequency1 * df_assign$Frequency2 + + df_assign[which(df_assign$a1 == df_assign$a2 & + df_assign$a1 == df_assign$Allele1), c("hom2", "het")] <- + 0 + + df_assign[which(df_assign$a1 == df_assign$a2 & + df_assign$a1 == df_assign$Allele2), c("hom1", "het")] <- + 0 + + df_assign[which(df_assign$a1 != df_assign$a2), c("hom1", "hom2")] <- + 0 + + df_assign$prob <- + df_assign$hom1 + df_assign$hom2 + df_assign$het + + df_assign[which(is.na(df_assign$a1)), "prob"] <- NA + + df_assign[which(df_assign$prob == 0), "prob"] <- -1 + + df_assign$prob <- df_assign$prob / nLoc(x) + + # if (inbreeding_par > 0) { + # f <- f * (1 - inbreeding_par) + # if (all_alleles[1] == all_alleles[2]) { + # f <- + # f + (popfreq$Frequency[popfreq$Allele == all_alleles[1]] * inbreeding_par) + # } + # } + # + + # assign probability + ret[popx, "Probability"] <- sum(df_assign$prob, na.rm = TRUE) + } + + ret <- ret[order(-ret$Probability),] + ret$Posterior <- ret$Probability / sum(ret$Probability) + ret$Posterior <- round(ret$Posterior, 5) + ret$Probability <- round(ret$Probability, 5) + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + return(invisible(ret)) + +} + +gl2alleles <- function (gl) { + x <- as.matrix(gl[, ]) + homs1 <- + paste(substr(gl@loc.all, 1, 1), "/", substr(gl@loc.all, 1, 1), sep = "") + hets <- gl@loc.all + homs2 <- + paste(substr(gl@loc.all, 3, 3), "/", substr(gl@loc.all, 3, 3), sep = "") + xx <- matrix(NA, ncol = ncol(x), nrow = nrow(x)) + for (i in 1:nrow(x)) { + for (ii in 1:ncol(x)) { + inp <- x[i, ii] + if (!is.na(inp)) { + if (inp == 0) + xx[i, ii] <- homs1[ii] + else if (inp == 1) + xx[i, ii] <- hets[ii] + else if (inp == 2) + xx[i, ii] <- homs2[ii] + } + else + xx[i, ii] = NA + } + } + xx <- gsub("/", ":", xx) + return(xx) +} diff --git a/R/utils.assignment_2.r b/R/utils.assignment_2.r new file mode 100644 index 00000000..86456881 --- /dev/null +++ b/R/utils.assignment_2.r @@ -0,0 +1,190 @@ +#' @name utils.assignment_2 +#' @title Population assignment probabilities +#' @description +#' This function takes one individual and estimates +#' their probability of coming from individual populations +#' from multilocus genotype frequencies. +# +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param unknown Name of the individual to be assigned to a population [required]. +# @param inbreeding_par The inbreeding parameter [default 0]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details +#' This function is a re-implementation of the function multilocus_assignment +#' from package gstudio. +#' Description of the method used in this function can be found at: +#' https://dyerlab.github.io/applied_population_genetics/population-assignment.html +#' @return A \code{data.frame} consisting of assignment probabilities for each +#' population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' res <- utils.assignment_2(platypus.gl,unknown="T27") +#' @export + +utils.assignment_2 <- function(x, + unknown, + # inbreeding_par = 0, + verbose = 2) { + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + if (unknown %in% indNames(x) == FALSE) { + stop(error( + paste(" Individual", unknown, "is not in the genlight object\n") + )) + } + + # DO THE JOB + + # filtering loci with all missing data by population + # x <- gl.filter.allna(x, by.pop = TRUE, verbose = 0) + unknown_pop <- gl.keep.ind(x, ind.list = unknown, verbose = 0) + unknown_pop <- data.frame(gl2alleles(unknown_pop)) + + x <- gl.drop.ind(x, ind.list = unknown, verbose = 0) + + pop_names <- popNames(x) + + pop_list <- seppop(x) + gl_alleles <- do.call(rbind, strsplit(x$loc.all, "/")) + + frequencies <- lapply(pop_list, function(y) { + freq_allele <- gl.alf(y) + freqs_gl <- + data.frame( + Allele1 = gl_alleles[, 1], + Allele2 = gl_alleles[, 2], + count1 = freq_allele[, 1] * (nInd(y)*2), + count2 = freq_allele[, 2] * (nInd(y)*2) + ) + return(freqs_gl) + }) + + ret <- data.frame(Population = pop_names, Likelihood = 0) + + for (popx in 1:nPop(x)) { + + # alpha_ = k in Baudouin and Lebrun (2000) + alpha_ <- 2 + # the total number of different allelic states at this locus over all + # reference populations + k <- 2 + # the total number of genes to be assigned + m <- nLoc(x) + # the total number of genes in the reference population sample + n <- nLoc(x) + + term1 <- lgamma(m+1) + term2 <- lgamma(n+alpha_) + + + if (verbose >= 2) { + cat( + report( + " Assigning individual", + unknown, + "against population", + pop_names[popx], + "\n" + ) + ) + } + + popfreq <- frequencies[[popx]] + + loc <- + as.data.frame(do.call(rbind, strsplit(unname( + unlist(unknown_pop) + ), ":"))) + colnames(loc) <- c("a1", "a2") + + df_assign <- cbind(loc, popfreq) + + df_assign$a1_count <- NA + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele1),"a1_count"] <- 2 + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele2),"a1_count"] <- 0 + df_assign[which(df_assign$a1 != df_assign$a2),"a1_count"] <- 1 + + df_assign$a2_count <- NA + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele2),"a2_count"] <- 2 + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele1),"a2_count"] <- 0 + df_assign[which(df_assign$a1 != df_assign$a2),"a2_count"] <- 1 + + term3_1 <- lgamma(( df_assign$a1_count + df_assign$count1 + (alpha_/k) )) + term3_2 <- lgamma(( df_assign$a2_count + df_assign$count2 + (alpha_/k) )) + + term3 <- sum(term3_1,term3_2,na.rm = TRUE) + + term4_1 <- lgamma(df_assign$a1_count + 1) + term4_2 <- lgamma(df_assign$a2_count + 1) + + term4 <- sum(term4_1,term4_2,na.rm = TRUE) + + term5_1 <- lgamma(df_assign$a1_count + (alpha_/k) ) + term5_2 <- lgamma(df_assign$a2_count + (alpha_/k) ) + + term5 <- sum(term5_1,term5_2,na.rm = TRUE) + + term6 <- lgamma(m+n+alpha_) + + log_L <- term1 + term2 + term3 - term4 - term5 - term6 + + # assign Likelihood + ret[popx, "Likelihood"] <- -log_L + } + + ret <- ret[order(ret$Likelihood,decreasing = TRUE),] + ret$score <- ret$Likelihood / sum(ret$Likelihood) + ret$score <- round(ret$score, 5) + ret$Likelihood <- round(ret$Likelihood, 5) + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + return(invisible(ret)) + +} + +gl2alleles <- function (gl) { + x <- as.matrix(gl[, ]) + homs1 <- + paste(substr(gl@loc.all, 1, 1), "/", substr(gl@loc.all, 1, 1), sep = "") + hets <- gl@loc.all + homs2 <- + paste(substr(gl@loc.all, 3, 3), "/", substr(gl@loc.all, 3, 3), sep = "") + xx <- matrix(NA, ncol = ncol(x), nrow = nrow(x)) + for (i in 1:nrow(x)) { + for (ii in 1:ncol(x)) { + inp <- x[i, ii] + if (!is.na(inp)) { + if (inp == 0) + xx[i, ii] <- homs1[ii] + else if (inp == 1) + xx[i, ii] <- hets[ii] + else if (inp == 2) + xx[i, ii] <- homs2[ii] + } + else + xx[i, ii] = NA + } + } + xx <- gsub("/", ":", xx) + return(xx) +} diff --git a/R/utils.assignment_3.r b/R/utils.assignment_3.r new file mode 100644 index 00000000..8f41951e --- /dev/null +++ b/R/utils.assignment_3.r @@ -0,0 +1,196 @@ +#' @name utils.assignment_3 +#' @title Population assignment probabilities +#' @description +#' This function takes one individual and estimates +#' their probability of coming from individual populations +#' from multilocus genotype frequencies. +# +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param unknown Name of the individual to be assigned to a population [required]. +# @param inbreeding_par The inbreeding parameter [default 0]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details +#' This function is a re-implementation of the function multilocus_assignment +#' from package gstudio. +#' Description of the method used in this function can be found at: +#' https://dyerlab.github.io/applied_population_genetics/population-assignment.html +#' @return A \code{data.frame} consisting of assignment probabilities for each +#' population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' res <- utils.assignment_2(platypus.gl,unknown="T27") +#' @export + +utils.assignment_3 <- function(x, + unknown, + # inbreeding_par = 0, + verbose = 2) { + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + if (unknown %in% indNames(x) == FALSE) { + stop(error( + paste(" Individual", unknown, "is not in the genlight object\n") + )) + } + + # DO THE JOB + + # filtering loci with all missing data by population + # x <- gl.filter.allna(x, by.pop = TRUE, verbose = 0) + unknown_pop <- gl.keep.ind(x, ind.list = unknown, verbose = 0) + unknown_pop <- data.frame(gl2alleles(unknown_pop)) + + x <- gl.drop.ind(x, ind.list = unknown, verbose = 0) + + pop_names <- popNames(x) + + pop_list <- seppop(x) + gl_alleles <- do.call(rbind, strsplit(x$loc.all, "/")) + + frequencies <- lapply(pop_list, function(y) { + freq_allele <- gl.alf(y) + freqs_gl <- + data.frame( + Allele1 = gl_alleles[, 1], + Allele2 = gl_alleles[, 2], + count1 = freq_allele[, 1] * (nInd(y)*2), + count2 = freq_allele[, 2] * (nInd(y)*2) + ) + return(freqs_gl) + }) + + ret <- data.frame(Population = pop_names, Likelihood = 0) + + for (popx in 1:nPop(x)) { + + # alpha_ = k in Baudouin and Lebrun (2000) + alpha_ <- 2 + # the total number of different allelic states at this locus over all + # reference populations + k <- 2 + # the total number of genes to be assigned + m <- nLoc(x) + # the total number of genes in the reference population sample + n <- nLoc(x) + + term1 <- lgamma(m+1) + term2 <- lgamma(n+alpha_) + + + if (verbose >= 2) { + cat( + report( + " Assigning individual", + unknown, + "against population", + pop_names[popx], + "\n" + ) + ) + } + + popfreq <- frequencies[[popx]] + + loc <- + as.data.frame(do.call(rbind, strsplit(unname( + unlist(unknown_pop) + ), ":"))) + colnames(loc) <- c("a1", "a2") + + df_assign <- cbind(loc, popfreq) + + df_assign$a1_count <- NA + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele1),"a1_count"] <- 2 + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele2),"a1_count"] <- 0 + df_assign[which(df_assign$a1 != df_assign$a2),"a1_count"] <- 1 + + df_assign$a2_count <- NA + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele2),"a2_count"] <- 2 + df_assign[which(df_assign$a1 == df_assign$a2 & df_assign$a1 == df_assign$Allele1),"a2_count"] <- 0 + df_assign[which(df_assign$a1 != df_assign$a2),"a2_count"] <- 1 + + term3_1 <- lgamma(( df_assign$a1_count + df_assign$count1 + (alpha_/k) )) + term3_2 <- lgamma(( df_assign$a2_count + df_assign$count2 + (alpha_/k) )) + + term3 <- sum(term3_1,term3_2,na.rm = TRUE) + + term4_1 <- lgamma(df_assign$a1_count + 1) + term4_2 <- lgamma(df_assign$a2_count + 1) + + term4 <- sum(term4_1,term4_2,na.rm = TRUE) + + term5_1 <- lgamma(df_assign$a1_count + (alpha_/k) ) + term5_2 <- lgamma(df_assign$a2_count + (alpha_/k) ) + + term5 <- sum(term5_1,term5_2,na.rm = TRUE) + + term6 <- lgamma(m+n+alpha_) + + term7_1 <- df_assign$a1_count / (nInd(pop_list[[popx]])*2) + term7_2 <- df_assign$a2_count / (nInd(pop_list[[popx]])*2) + + term7 <- sum(term7_1,term7_2,na.rm = TRUE) + + + log_L <- term1 - term4 + term7 + + # assign Likelihood + ret[popx, "Likelihood"] <- log_L + } + + ret <- ret[order(ret$Likelihood,decreasing = TRUE),] + ret$score <- ret$Likelihood / sum(ret$Likelihood) + ret$score <- round(ret$score, 5) + ret$Likelihood <- round(ret$Likelihood, 5) + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + return(invisible(ret)) + +} + +gl2alleles <- function (gl) { + x <- as.matrix(gl[, ]) + homs1 <- + paste(substr(gl@loc.all, 1, 1), "/", substr(gl@loc.all, 1, 1), sep = "") + hets <- gl@loc.all + homs2 <- + paste(substr(gl@loc.all, 3, 3), "/", substr(gl@loc.all, 3, 3), sep = "") + xx <- matrix(NA, ncol = ncol(x), nrow = nrow(x)) + for (i in 1:nrow(x)) { + for (ii in 1:ncol(x)) { + inp <- x[i, ii] + if (!is.na(inp)) { + if (inp == 0) + xx[i, ii] <- homs1[ii] + else if (inp == 1) + xx[i, ii] <- hets[ii] + else if (inp == 2) + xx[i, ii] <- homs2[ii] + } + else + xx[i, ii] = NA + } + } + xx <- gsub("/", ":", xx) + return(xx) +} diff --git a/R/utils.assignment_4.r b/R/utils.assignment_4.r new file mode 100644 index 00000000..ef14d98b --- /dev/null +++ b/R/utils.assignment_4.r @@ -0,0 +1,198 @@ +#' @name utils.assignment_4 +#' @title Population assignment probabilities +#' @description +#' This function takes one individual and estimates +#' their probability of coming from individual populations +#' from multilocus genotype frequencies. +# +#' @param x Name of the genlight object containing the SNP data [required]. +#' @param unknown Name of the individual to be assigned to a population [required]. +# @param inbreeding_par The inbreeding parameter [default 0]. +#' @param verbose Verbosity: 0, silent or fatal errors; 1, begin and end; 2, +#' progress log; 3, progress and results summary; 5, full report +#' [default 2, unless specified using gl.set.verbosity]. +#' @details +#' This function is a re-implementation of the function multilocus_assignment +#' from package gstudio. +#' Description of the method used in this function can be found at: +#' https://dyerlab.github.io/applied_population_genetics/population-assignment.html +#' @return A \code{data.frame} consisting of assignment probabilities for each +#' population. +#' @author Custodian: Luis Mijangos -- Post to +#' \url{https://groups.google.com/d/forum/dartr} +#' @examples +#' res <- utils.assignment_2(platypus.gl,unknown="T27") +#' @export + +utils.assignment_4 <- function(x, + unknown, + # inbreeding_par = 0, + verbose = 2) { + # SET VERBOSITY + verbose <- gl.check.verbosity(verbose) + + # FLAG SCRIPT START + funname <- match.call()[[1]] + utils.flag.start(func = funname, + build = "Jody", + verbosity = verbose) + + # CHECK DATATYPE + datatype <- utils.check.datatype(x, verbose = verbose) + + if (unknown %in% indNames(x) == FALSE) { + stop(error( + paste(" Individual", unknown, "is not in the genlight object\n") + )) + } + + # DO THE JOB + + # filtering loci with all missing data by population + # x <- gl.filter.allna(x, by.pop = TRUE, verbose = 0) + unknown_pop <- gl.keep.ind(x, ind.list = unknown, verbose = 0) + unknown_pop <- data.frame(gl2alleles(unknown_pop)) + + x <- gl.drop.ind(x, ind.list = unknown, verbose = 0) + + pop_names <- popNames(x) + + pop_list <- seppop(x) + gl_alleles <- do.call(rbind, strsplit(x$loc.all, "/")) + + counts <- lapply(pop_list, function(y) { + freq_allele <- gl.alf(y) + counts_gl <- + data.frame( + Allele1 = gl_alleles[, 1], + Allele2 = gl_alleles[, 2], + # the observed number of copies of the hth allele at the jth locus in a + # sample from the ith population + nhji = freq_allele[, 1] * (nInd(y)*2), + # the observed number of copies of the gth allele at the jth locus in a + # sample from the ith population + ngji = freq_allele[, 2] * (nInd(y)*2) + ) + return(counts_gl) + }) + + ret <- data.frame(Population = pop_names, Likelihood = 0) + + for (popx in 1:nPop(x)) { + + # alpha_ = k in Baudouin and Lebrun (2000) + alpha_ <- 2 + # the total number of different allelic states at this locus over all + # reference populations + k <- 2 + # the total number of genes to be assigned + m <- nLoc(x) + # the total number of genes in the reference population sample + n <- nLoc(x) + + term1 <- lgamma(m+1) + term2 <- lgamma(n+alpha_) + + + if (verbose >= 2) { + cat( + report( + " Assigning individual", + unknown, + "against population", + pop_names[popx], + "\n" + ) + ) + } + + popfreq <- counts[[popx]] + + loc <- + as.data.frame(do.call(rbind, strsplit(unname( + unlist(unknown_pop) + ), ":"))) + colnames(loc) <- c("a1", "a2") + + df_assign <- cbind(loc, popfreq) + # the number of alleles at the jth locus + kj <- 2 + # total number of copies of all alleles + nji <- nInd(pop_list[[popx]])*2 + + df_assign$hom1 <- ((df_assign$nhji + (1/kj) + 1) * (df_assign$nhji + (1/kj))) / + ((nji + 2) * (nji + 1)) + df_assign$hom2 <- ((df_assign$ngji + (1/kj) + 1) * (df_assign$ngji + (1/kj))) / + ((nji + 2) * (nji + 1)) + df_assign$het <- (2*(df_assign$nhji + (1/kj)) * (df_assign$ngji + (1/kj))) / + ((nji + 2) * (nji + 1)) + + df_assign[which(df_assign$a1 == df_assign$a2 & + df_assign$a1 == df_assign$Allele1), c("hom2", "het")] <- + 0 + + df_assign[which(df_assign$a1 == df_assign$a2 & + df_assign$a1 == df_assign$Allele2), c("hom1", "het")] <- + 0 + + df_assign[which(df_assign$a1 != df_assign$a2), c("hom1", "hom2")] <- + 0 + + df_assign$prob <- + df_assign$hom1 + df_assign$hom2 + df_assign$het + + # df_assign[which(is.na(df_assign$a1)), "prob"] <- NA + + # df_assign[which(df_assign$prob == 0), "prob"] <- -1 + + df_assign$prob <- df_assign$prob / nLoc(x) + + ret[popx, "Likelihood"] <- sum(df_assign$prob, na.rm = TRUE) + + # assign Likelihood + # ret[popx, "Likelihood"] <- prod( df_assign$prob ) + } + + ret <- ret[order(ret$Likelihood,decreasing = TRUE),] + ret$score <- ret$Likelihood / sum(ret$Likelihood) + ret$score <- round(ret$score, 5) + ret$Likelihood <- round(ret$Likelihood, 5) + + # FLAG SCRIPT END + + if (verbose >= 1) { + cat(report("Completed:", funname, "\n")) + } + + # RETURN + + return(invisible(ret)) + +} + +gl2alleles <- function (gl) { + x <- as.matrix(gl[, ]) + homs1 <- + paste(substr(gl@loc.all, 1, 1), "/", substr(gl@loc.all, 1, 1), sep = "") + hets <- gl@loc.all + homs2 <- + paste(substr(gl@loc.all, 3, 3), "/", substr(gl@loc.all, 3, 3), sep = "") + xx <- matrix(NA, ncol = ncol(x), nrow = nrow(x)) + for (i in 1:nrow(x)) { + for (ii in 1:ncol(x)) { + inp <- x[i, ii] + if (!is.na(inp)) { + if (inp == 0) + xx[i, ii] <- homs1[ii] + else if (inp == 1) + xx[i, ii] <- hets[ii] + else if (inp == 2) + xx[i, ii] <- homs2[ii] + } + else + xx[i, ii] = NA + } + } + xx <- gsub("/", ":", xx) + return(xx) +} diff --git a/R/utils.dart2genlight.r b/R/utils.dart2genlight.r index 7d4f7568..8b4f97a7 100644 --- a/R/utils.dart2genlight.r +++ b/R/utils.dart2genlight.r @@ -53,7 +53,7 @@ utils.dart2genlight <- function(dart, cat(report( "nrows not provided. Trying to guess if one row or two row format...\n" )) - gnrows = 3 - max(dart$gendata, na.rm = TRUE) + gnrows <-3 - max(dart$gendata, na.rm = TRUE) if (gnrows == 1 | gnrows == 2) { nrows <- gnrows @@ -103,7 +103,7 @@ utils.dart2genlight <- function(dart, sdata <- dart[["gendata"]] # every second line only.... - esl = seq(nrows, nrow(sdata), nrows) + esl <-seq(nrows, nrow(sdata), nrows) pos <- sraw$SnpPosition[esl] alleles <- as.character(sraw$SNP)[esl] @@ -114,7 +114,7 @@ utils.dart2genlight <- function(dart, if (nrows == 2) { for (i in 1:nind) { - isnp = paste(sdata[esl - 1, i], sdata[esl, i], sep = "/") + isnp <-paste(sdata[esl - 1, i], sdata[esl, i], sep = "/") g <- isnp g <- gsub("0/1", 2, g) g <- gsub("1/0", 0, g) @@ -127,7 +127,7 @@ utils.dart2genlight <- function(dart, } } else { for (i in 1:nind) { - isnp = sdata[esl, i] + isnp <-sdata[esl, i] g <- isnp g <- 3 - g g <- ifelse(g == 3, 0, g) @@ -182,7 +182,7 @@ utils.dart2genlight <- function(dart, stringsAsFactors = T) # is there an entry for every individual - id.col = match("id", names(ind.cov)) + id.col <-match("id", names(ind.cov)) if (is.na(id.col)) { stop(error("Fatal Error: There is no id column\n")) @@ -251,7 +251,7 @@ utils.dart2genlight <- function(dart, } } - pop.col = match("pop", names(ind.cov)) + pop.col <-match("pop", names(ind.cov)) if (is.na(pop.col)) { if (verbose >= 1) { @@ -269,8 +269,8 @@ utils.dart2genlight <- function(dart, } } - lat.col = match("lat", names(ind.cov)) - lon.col = match("lon", names(ind.cov)) + lat.col <-match("lat", names(ind.cov)) + lon.col <-match("lon", names(ind.cov)) if (verbose >= 2) { if (is.na(lat.col)) { cat( diff --git a/R/utils.ld.r b/R/utils.ld.r index 0ae017c2..1766fe5f 100644 --- a/R/utils.ld.r +++ b/R/utils.ld.r @@ -151,4 +151,27 @@ utils.read.ped <- function (file, names(map)[which] <- "snp.names" } list(genotypes = result, fam = fam, map = map) +} + +rotate.matrix <- function (x, + angle = 10, + method = "bilinear"){ + + img <- x + angle.rad <- angle * pi/180 + co.x <- matrix(rep(-(ncol(img)/2 - 0.5):(ncol(img)/2 - + 0.5), nrow(img)), nrow = nrow(img), byrow = T) + co.y <- matrix(rep(-(nrow(img)/2 - 0.5):(nrow(img)/2 - + 0.5), ncol(img)), ncol = ncol(img)) + co.xn <- round(co.x * cos(angle.rad) - co.y * sin(angle.rad)) + co.yn <- round(co.x * sin(angle.rad) + co.y * cos(angle.rad)) + co.xn2 <- co.xn + max(co.xn) + 1 + co.yn2 <- co.yn + max(co.yn) + 1 + img.rot <- numeric(max(co.yn2) * max(co.xn2)) + img.rot[(co.xn2 - 1) * max(co.yn2) + co.yn2] <- img + dim(img.rot) <- c(max(co.yn2), max(co.xn2)) + attr(img.rot, "bits.per.sample") <- attr(img, "bits.per.sample") + attr(img.rot, "samples.per.pixel") <- attr(img, "samples.per.pixel") + return(img.rot) + } \ No newline at end of file diff --git a/R/utils.outflank.diploids.r b/R/utils.outflank.diploids.r index 10a9d3a3..88cbc68e 100644 --- a/R/utils.outflank.diploids.r +++ b/R/utils.outflank.diploids.r @@ -3,22 +3,22 @@ WC_FST_Diploids_2Alleles <- function(Sample_Mat) { # has three columns (homo_p,m heterozygotes, and homo_q) and ## a row for each population - sample_sizes = rowSums(Sample_Mat) - n_ave = mean(sample_sizes) - n_pops = nrow(Sample_Mat) #r - r = n_pops - n_c = (n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / + sample_sizes <-rowSums(Sample_Mat) + n_ave <-mean(sample_sizes) + n_pops <-nrow(Sample_Mat) #r + r <-n_pops + n_c <-(n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / (n_pops - 1) - p_freqs = (Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes - p_ave = sum(sample_sizes * p_freqs) / (n_ave * n_pops) + p_freqs <-(Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes + p_ave <-sum(sample_sizes * p_freqs) / (n_ave * n_pops) - s2 = sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) + s2 <-sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) if (s2 == 0) { return(0) } - h_freqs = Sample_Mat[, 2] / sample_sizes - h_ave = sum(sample_sizes * h_freqs) / (n_ave * n_pops) + h_freqs <-Sample_Mat[, 2] / sample_sizes + h_ave <-sum(sample_sizes * h_freqs) / (n_ave * n_pops) a <- n_ave / n_c * (s2 - 1 / (n_ave - 1) * (p_ave * (1 - p_ave) - ((r - 1) / @@ -40,7 +40,7 @@ WC_FST_Diploids_2Alleles <- function(Sample_Mat) { He <- 1 - sum(p_ave ^ 2, (1 - p_ave) ^ 2) FST <- a / (a + b + c) - FSTNoCorr = aNoCorr / (aNoCorr + bNoCorr + cNoCorr) + FSTNoCorr <-aNoCorr / (aNoCorr + bNoCorr + cNoCorr) return( list( @@ -104,15 +104,15 @@ utils.outflank.MakeDiploidFSTMat <- function(SNPmat, nloci <- length(locusname) FSTmat <- matrix(NA, nrow = nloci, ncol = 8) for (i in 1:nloci) { - FSTmat[i,] = unlist(getFSTs_diploids(popname, SNPmat[, i])) + FSTmat[i,] <-unlist(getFSTs_diploids(popname, SNPmat[, i])) if (i %% 10000 == 0) { print(paste(i, "done of", nloci)) } } - outTemp = as.data.frame(FSTmat) - outTemp = cbind(locusname, outTemp) + outTemp <-as.data.frame(FSTmat) + outTemp <-cbind(locusname, outTemp) - colnames(outTemp) = c( + colnames(outTemp) <-c( "LocusName", "He", "FST", @@ -131,13 +131,13 @@ utils.outflank.MakeDiploidFSTMat <- function(SNPmat, getFSTs_diploids <- function(popNameList, SNPDataColumn) { # eliminating the missing data for this locus - popnames = unlist(as.character(popNameList)) - popNameTemp = popnames[which(SNPDataColumn != 9)] - snpDataTemp = SNPDataColumn[SNPDataColumn != 9] + popnames <-unlist(as.character(popNameList)) + popNameTemp <-popnames[which(SNPDataColumn != 9)] + snpDataTemp <-SNPDataColumn[SNPDataColumn != 9] HetCounts <- tapply(snpDataTemp, list(popNameTemp, snpDataTemp), length) - HetCounts[is.na(HetCounts)] = 0 + HetCounts[is.na(HetCounts)] <-0 # Case: all individuals are genetically identical at this locus if (dim(HetCounts)[2] == 1) { @@ -157,16 +157,16 @@ getFSTs_diploids <- function(popNameList, if (dim(HetCounts)[2] == 2) { if (paste(colnames(HetCounts), collapse = "") == "01") { - HetCounts = cbind(HetCounts, `2` = 0) + HetCounts <-cbind(HetCounts, `2` = 0) } if (paste(colnames(HetCounts), collapse = "") == "12") { - HetCounts = cbind(`0` = 0, HetCounts) + HetCounts <-cbind(`0` = 0, HetCounts) } if (paste(colnames(HetCounts), collapse = "") == "02") { - HetCounts = cbind(HetCounts[, 1], `1` = 0, HetCounts[, 2]) + HetCounts <-cbind(HetCounts[, 1], `1` = 0, HetCounts[, 2]) } } - out = WC_FST_Diploids_2Alleles(HetCounts) + out <-WC_FST_Diploids_2Alleles(HetCounts) return(out) } diff --git a/R/utils.outflank.fst.r b/R/utils.outflank.fst.r index 5c0b74a9..25a313ea 100644 --- a/R/utils.outflank.fst.r +++ b/R/utils.outflank.fst.r @@ -8,14 +8,14 @@ WC_FST_FiniteSample_Haploids_2AllelesB_MCW <- function(AllCounts) { counts1 <- AllCounts[, 1] sample_sizes <- rowSums(AllCounts) n_ave <- mean(as.numeric(sample_sizes)) - n_c = (n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / + n_c <-(n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / (n_pops - 1) - p_freqs = counts1 / sample_sizes - p_ave = sum(sample_sizes * p_freqs) / (n_ave * n_pops) + p_freqs <-counts1 / sample_sizes + p_ave <-sum(sample_sizes * p_freqs) / (n_ave * n_pops) He <- 2 * p_ave * (1 - p_ave) - s2 = sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) + s2 <-sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) T1 <- @@ -40,14 +40,14 @@ WC_FST_FiniteSample_Haploids_2AllelesB_NoSamplingCorrection <- counts1 <- AllCounts[, 1] sample_sizes <- rowSums(AllCounts) n_ave <- mean(as.numeric(sample_sizes)) - n_c = (n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / + n_c <-(n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / (n_pops - 1) - p_freqs = counts1 / sample_sizes - p_ave = sum(sample_sizes * p_freqs) / (n_ave * n_pops) + p_freqs <-counts1 / sample_sizes + p_ave <-sum(sample_sizes * p_freqs) / (n_ave * n_pops) He <- 2 * p_ave * (1 - p_ave) - s2 = sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) + s2 <-sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) T1NoCorr <- s2 @@ -90,22 +90,22 @@ WC_FST_FiniteSample_Diploids_2Alleles_NoCorr <- # Sample Mat has three columns (homo_p,m heterozygotes, and homo_q) and #a row for each population - sample_sizes = rowSums(Sample_Mat) - n_ave = mean(sample_sizes) - n_pops = nrow(Sample_Mat) #r - r = n_pops - n_c = (n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / + sample_sizes <-rowSums(Sample_Mat) + n_ave <-mean(sample_sizes) + n_pops <-nrow(Sample_Mat) #r + r <-n_pops + n_c <-(n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / (n_pops - 1) - p_freqs = (Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes - p_ave = sum(sample_sizes * p_freqs) / (n_ave * n_pops) - s2 = sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) + p_freqs <-(Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes + p_ave <-sum(sample_sizes * p_freqs) / (n_ave * n_pops) + s2 <-sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) if (s2 == 0) { return(1) } - h_freqs = Sample_Mat[, 2] / sample_sizes - h_ave = sum(sample_sizes * h_freqs) / (n_ave * n_pops) + h_freqs <-Sample_Mat[, 2] / sample_sizes + h_ave <-sum(sample_sizes * h_freqs) / (n_ave * n_pops) a <- n_ave / n_c * (s2) @@ -130,22 +130,22 @@ WC_FST_FiniteSample_Diploids_2Alleles <- function(Sample_Mat) { # Sample Mat has three columns (homo_p,m heterozygotes, and homo_q) and a #row for each population - sample_sizes = rowSums(Sample_Mat) - n_ave = mean(sample_sizes) - n_pops = nrow(Sample_Mat) #r - r = n_pops - n_c = (n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / + sample_sizes <-rowSums(Sample_Mat) + n_ave <-mean(sample_sizes) + n_pops <-nrow(Sample_Mat) #r + r <-n_pops + n_c <-(n_pops * n_ave - sum(sample_sizes ^ 2) / (n_pops * n_ave)) / (n_pops - 1) - p_freqs = (Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes - p_ave = sum(sample_sizes * p_freqs) / (n_ave * n_pops) + p_freqs <-(Sample_Mat[, 1] + Sample_Mat[, 2] / 2) / sample_sizes + p_ave <-sum(sample_sizes * p_freqs) / (n_ave * n_pops) - s2 = sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) + s2 <-sum(sample_sizes * (p_freqs - p_ave) ^ 2) / ((n_pops - 1) * n_ave) if (s2 == 0) { return(1) } - h_freqs = Sample_Mat[, 2] / sample_sizes - h_ave = sum(sample_sizes * h_freqs) / (n_ave * n_pops) + h_freqs <-Sample_Mat[, 2] / sample_sizes + h_ave <-sum(sample_sizes * h_freqs) / (n_ave * n_pops) a <- n_ave / n_c * (s2 - 1 / (n_ave - 1) * (p_ave * (1 - p_ave) - ((r - 1) / diff --git a/R/utils.outflank.likelihood.r b/R/utils.outflank.likelihood.r index 98c98db1..3adfdc60 100644 --- a/R/utils.outflank.likelihood.r +++ b/R/utils.outflank.likelihood.r @@ -11,21 +11,21 @@ EffectiveNumberSamplesMLE <- function(FstVect, # The FstVect should already have been purged of NaN values and of loci with #too low heterozygosity or MAF. - sortedFst = FstVect[order(FstVect)] + sortedFst <-FstVect[order(FstVect)] # The Minimum Fst considered in the trimmed data is the larger of the amount #specified by the user or the mean FSt over 100. This is # to prevent extremely small Fsts from causing estiamtion errors (Especially #when R interprets a small Fst as FSt=0.) - LowTrimPoint = max(Fstbar / 100, SmallestFstInTrimmedList) + LowTrimPoint <-max(Fstbar / 100, SmallestFstInTrimmedList) - trimmedFstVect = FstVect[which((FstVect >= LowTrimPoint) & + trimmedFstVect <-FstVect[which((FstVect >= LowTrimPoint) & (FstVect <= LargestFstInTrimmedList))] - trimmedFstArray = as.array(trimmedFstVect) + trimmedFstArray <-as.array(trimmedFstVect) - localNLLAllData = function(dfInferred) { - localNLLOneLocus = function(Fst) { + localNLLAllData <-function(dfInferred) { + localNLLOneLocus <-function(Fst) { negLLdfFstTrim(Fst, dfInferred, Fstbar, @@ -62,7 +62,7 @@ negLLdfFstTrim <- function(Fst, # negative log likelihood of a given locus' Fst for a given dfInferred #CHECKED AGAINST MATHEMATICA DERIVATION## - df = dfInferred + df <-dfInferred 1 / (2 * Fstbar) * ( df * Fst + df * Fstbar * log(2) - df * Fstbar * log(df) - (df - 2) * Fstbar * log(Fst) + df * Fstbar * log(Fstbar) + 2 * diff --git a/R/utils.outflank.plotter.r b/R/utils.outflank.plotter.r index f68b9f81..37b7e163 100644 --- a/R/utils.outflank.plotter.r +++ b/R/utils.outflank.plotter.r @@ -31,25 +31,25 @@ utils.outflank.plotter <- function(OFoutput, Zoom = FALSE, RightZoomFraction = 0.05, titletext = NULL) { - data = OFoutput$results[which(OFoutput$results$He > Hmin), ] + data <-OFoutput$results[which(OFoutput$results$He > Hmin), ] if (NoCorr) { - flist = data$FSTNoCorr - fbar = sum(data$T1NoCorr) / sum(data$T2NoCorr) - titletext = paste(c(titletext, "Fst without sample size correction")) + flist <-data$FSTNoCorr + fbar <-sum(data$T1NoCorr) / sum(data$T2NoCorr) + titletext <-paste(c(titletext, "Fst without sample size correction")) } if (!NoCorr) { - flist = data$FST - fbar = OFoutput$FSTbar + flist <-data$FST + fbar <-OFoutput$FSTbar - titletext = paste(c(titletext, "Fst with sample size correction")) + titletext <-paste(c(titletext, "Fst with sample size correction")) } - flist = flist[which(!is.na(flist))] - keeperlist = which(!data$OutlierFlag) + flist <-flist[which(!is.na(flist))] + keeperlist <-which(!data$OutlierFlag) if (!withOutliers) { - flist = flist[keeperlist] + flist <-flist[keeperlist] } if (Zoom) { @@ -79,16 +79,16 @@ FstDistPlotter <- function(df, FSTbar, binwidth = 0.005, titletext = NULL) { - xPlotUpperBound = ceiling(max(FSTlist) * 100) / 100 - breakslist = seq(0, xPlotUpperBound + binwidth, by = binwidth) - breaks = length(breakslist) + xPlotUpperBound <-ceiling(max(FSTlist) * 100) / 100 + breakslist <-seq(0, xPlotUpperBound + binwidth, by = binwidth) + breaks <-length(breakslist) - x = breakslist - y = rep(0, length(x)) + x <-breakslist + y <-rep(0, length(x)) for (i in 1:breaks) - y[i] = pchisq(((i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((((i - 1.5) * binwidth)) / + y[i] <-pchisq(((i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((((i - 1.5) * binwidth)) / FSTbar * df, df = df) - y = length(FSTlist) * y + y <-length(FSTlist) * y hist( FSTlist, @@ -111,29 +111,29 @@ FstDistPlotterZoom <- function(df, binwidth = 0.005, titletext = NULL, RightZoomFraction = 0.1) { - FSTlistNoNA = FSTlist[which(!is.na(FSTlist))] + FSTlistNoNA <-FSTlist[which(!is.na(FSTlist))] - xPlotUpperBound = ceiling(max(FSTlistNoNA) * 100) / 100 - xPlotLowerBound = floor(as.numeric( + xPlotUpperBound <-ceiling(max(FSTlistNoNA) * 100) / 100 + xPlotLowerBound <-floor(as.numeric( stats::quantile( FSTlistNoNA, prob = 1 - RightZoomFraction, na.rm = TRUE ) ) * 100) / 100 - flist = FSTlistNoNA[which(FSTlistNoNA > xPlotLowerBound)] + flist <-FSTlistNoNA[which(FSTlistNoNA > xPlotLowerBound)] - breakslist = seq(xPlotLowerBound, xPlotUpperBound, by = binwidth) - breaks = length(breakslist) + breakslist <-seq(xPlotLowerBound, xPlotUpperBound, by = binwidth) + breaks <-length(breakslist) - x = breakslist + x <-breakslist - y = rep(0, length(x)) + y <-rep(0, length(x)) for (i in 1:breaks) - y[i] = pchisq((xPlotLowerBound + (i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((xPlotLowerBound + (i - 1.5) * + y[i] <-pchisq((xPlotLowerBound + (i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((xPlotLowerBound + (i - 1.5) * binwidth) / FSTbar * df, df = df) - y = length(FSTlistNoNA) * y + y <-length(FSTlistNoNA) * y hist( flist, @@ -153,10 +153,10 @@ FstDistPlotterAddBadCurve <- function(df, FSTbar, binwidth = 0.005, RightZoomFraction = 0.99) { - FSTlistNoNA = FSTlist[which(!is.na(FSTlist))] + FSTlistNoNA <-FSTlist[which(!is.na(FSTlist))] - xPlotUpperBound = ceiling(max(FSTlistNoNA) * 100) / 100 - xPlotLowerBound = floor(as.numeric( + xPlotUpperBound <-ceiling(max(FSTlistNoNA) * 100) / 100 + xPlotLowerBound <-floor(as.numeric( stats::quantile( FSTlistNoNA, prob = 1 - RightZoomFraction, @@ -164,17 +164,17 @@ FstDistPlotterAddBadCurve <- function(df, ) ) * 100) / 100 - breakslist = seq(xPlotLowerBound, xPlotUpperBound, by = binwidth) - breaks = length(breakslist) + breakslist <-seq(xPlotLowerBound, xPlotUpperBound, by = binwidth) + breaks <-length(breakslist) - x = breakslist + x <-breakslist - y = rep(0, length(x)) + y <-rep(0, length(x)) for (i in 1:breaks) - y[i] = pchisq((xPlotLowerBound + (i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((xPlotLowerBound + (i - 1.5) * + y[i] <-pchisq((xPlotLowerBound + (i - 0.5) * binwidth) / FSTbar * df, df = df) - pchisq((xPlotLowerBound + (i - 1.5) * binwidth) / FSTbar * df, df = df) - y = length(FSTlistNoNA) * y + y <-length(FSTlistNoNA) * y lines(x, y, col = "red", lwd = 3) @@ -191,23 +191,23 @@ OutFLANKBadCurvePlotter <- function(badDF, Zoom = FALSE, RightZoomFraction = 0.99, titletext = NULL) { - data = OFoutput$results[which(OFoutput$results$He > Hmin), ] + data <-OFoutput$results[which(OFoutput$results$He > Hmin), ] if (NoCorr) { - flist = data$FSTNoCorr - fbar = sum(data$T1NoCorr) / sum(data$T2NoCorr) + flist <-data$FSTNoCorr + fbar <-sum(data$T1NoCorr) / sum(data$T2NoCorr) } if (!NoCorr) { - flist = data$FST - fbar = OFoutput$FSTbar + flist <-data$FST + fbar <-OFoutput$FSTbar } - flist = flist[which(!is.na(flist))] - keeperlist = which(!data$OutlierFlag) + flist <-flist[which(!is.na(flist))] + keeperlist <-which(!data$OutlierFlag) if (!withOutliers) - flist = flist[keeperlist] + flist <-flist[keeperlist] FstDistPlotterAddBadCurve(badDF, FSTlist = flist, diff --git a/R/utils.outflank.r b/R/utils.outflank.r index 687a62e2..8dcb699c 100644 --- a/R/utils.outflank.r +++ b/R/utils.outflank.r @@ -100,22 +100,22 @@ utils.outflank <- function(FstDataFrame, NumberOfSamples, qthreshold = 0.05) { # Setting up necessary columns in dataframe - Fstdata = outputDFStarterNoCorr(FstDataFrame, Hmin) + Fstdata <-outputDFStarterNoCorr(FstDataFrame, Hmin) # making working dataframe with real Fst (no NAs), storing NAs to add back #later - workingDataFrame = Fstdata[which(!is.na(Fstdata$FSTNoCorr)),] - storedDataFrameNA = Fstdata[which(is.na(Fstdata$FSTNoCorr)),] + workingDataFrame <-Fstdata[which(!is.na(Fstdata$FSTNoCorr)),] + storedDataFrameNA <-Fstdata[which(is.na(Fstdata$FSTNoCorr)),] # Finding upper and lower bounds for trimming (eliminating NAs, but not #negative FSTs) - sortedDataFrame = workingDataFrame[order(workingDataFrame$FSTNoCorr),] + sortedDataFrame <-workingDataFrame[order(workingDataFrame$FSTNoCorr),] - NLociTotal = length(sortedDataFrame$FSTNoCorr) - SmallestKeeper = ceiling(NLociTotal * LeftTrimFraction) - LargestKeeper = floor(NLociTotal * (1 - RightTrimFraction)) - LowTrimPoint = sortedDataFrame$FSTNoCorr[[SmallestKeeper]] - HighTrimPoint = sortedDataFrame$FSTNoCorr[[LargestKeeper]] + NLociTotal <-length(sortedDataFrame$FSTNoCorr) + SmallestKeeper <-ceiling(NLociTotal * LeftTrimFraction) + LargestKeeper <-floor(NLociTotal * (1 - RightTrimFraction)) + LowTrimPoint <-sortedDataFrame$FSTNoCorr[[SmallestKeeper]] + HighTrimPoint <-sortedDataFrame$FSTNoCorr[[LargestKeeper]] if (LowTrimPoint < 0) { writeLines( @@ -136,36 +136,36 @@ utils.outflank <- function(FstDataFrame, } # finding dfInferred and Fstbar iteratively - putativeNeutralListTemp = ifelse(workingDataFrame$FSTNoCorr > 0, TRUE, FALSE) + putativeNeutralListTemp <-ifelse(workingDataFrame$FSTNoCorr > 0, TRUE, FALSE) - oldOutlierFlag = rep(FALSE, NLociTotal) + oldOutlierFlag <-rep(FALSE, NLociTotal) # Note: All negative FST loci are maked as putative outliers, which will #need to be tested with the coalescent model later. In the # meantime, they are removed so as to not confuse the likelihood function. - keepGoing = TRUE - count = 0 + keepGoing <-TRUE + count <-0 # writeLines(paste(mean(workingDataFrame$FSTNoCorr[putativeNeutralListTemp]))) while (keepGoing) { - count = count + 1 + count <-count + 1 if (count > 19) { - keepGoing = FALSE + keepGoing <-FALSE writeLines(important("Exceeded iteration maximum.")) ###Try with #increased maximum value for count two lines above. } - FstbarNoCorrTemp = fstBarCalculatorNoCorr(workingDataFrame[putativeNeutralListTemp,]) + FstbarNoCorrTemp <-fstBarCalculatorNoCorr(workingDataFrame[putativeNeutralListTemp,]) - dfInferredTemp = EffectiveNumberSamplesMLE( + dfInferredTemp <-EffectiveNumberSamplesMLE( workingDataFrame$FSTNoCorr[putativeNeutralListTemp], FstbarNoCorrTemp, NumberOfSamples, LowTrimPoint, HighTrimPoint ) - workingDataFrame = pOutlierFinderChiSqNoCorr(workingDataFrame, + workingDataFrame <-pOutlierFinderChiSqNoCorr(workingDataFrame, FstbarNoCorrTemp, dfInferredTemp, qthreshold) @@ -174,7 +174,7 @@ utils.outflank <- function(FstDataFrame, #(because negative Fst estimates can't be evaluated through #### the chi-square approach on their own) if (any(workingDataFrame$OutlierFlag[workingDataFrame$FSTNoCorr < LowTrimPoint])) - workingDataFrame$OutlierFlag[workingDataFrame$FSTNoCorr < 0] = TRUE + workingDataFrame$OutlierFlag[workingDataFrame$FSTNoCorr < 0] <-TRUE #### Any loci previously marked as $OutlierFlag=TRUE remain so, even if #the new iteration doesn''t flag them as outliers @@ -182,14 +182,14 @@ utils.outflank <- function(FstDataFrame, # Resetting neutral list, and checking whether the outlier list has #stabilized - putativeNeutralListTemp = ifelse((!workingDataFrame$OutlierFlag), TRUE, FALSE) + putativeNeutralListTemp <-ifelse((!workingDataFrame$OutlierFlag), TRUE, FALSE) if (sum(putativeNeutralListTemp) == 0) { writeLines(error("No loci in neutral list...\n")) return(error("FAIL")) } if (identical(oldOutlierFlag, workingDataFrame$OutlierFlag)) - keepGoing = FALSE + keepGoing <-FALSE ###### if all in trimmed get IDed as outlier - return to user with #warning @@ -211,7 +211,7 @@ utils.outflank <- function(FstDataFrame, return(0) } - oldOutlierFlag = workingDataFrame$OutlierFlag + oldOutlierFlag <-workingDataFrame$OutlierFlag # writeLines(paste(as.character(count),' ',as.character(sum(putativeNeutralListTemp)))) } @@ -219,14 +219,14 @@ utils.outflank <- function(FstDataFrame, if (count > 19) writeLines(important("Loop iteration limit exceeded.")) - numberLowFstOutliers = sum(workingDataFrame$OutlierFlag[(workingDataFrame$FSTNoCorr < LowTrimPoint)]) - numberHighFstOutliers = sum(workingDataFrame$OutlierFlag[(workingDataFrame$FSTNoCorr > HighTrimPoint)]) + numberLowFstOutliers <-sum(workingDataFrame$OutlierFlag[(workingDataFrame$FSTNoCorr < LowTrimPoint)]) + numberHighFstOutliers <-sum(workingDataFrame$OutlierFlag[(workingDataFrame$FSTNoCorr > HighTrimPoint)]) - FSTbar = fstBarCalculator(workingDataFrame[putativeNeutralListTemp,]) + FSTbar <-fstBarCalculator(workingDataFrame[putativeNeutralListTemp,]) # merge NA list back to working list, and sort by original order\t - resultsDataFrame = rbind(workingDataFrame, storedDataFrameNA) - resultsDataFrame = resultsDataFrame[order(resultsDataFrame$indexOrder),] + resultsDataFrame <-rbind(workingDataFrame, storedDataFrameNA) + resultsDataFrame <-resultsDataFrame[order(resultsDataFrame$indexOrder),] # return new dataframe list( FSTbar = FSTbar, @@ -248,13 +248,13 @@ outputDFStarterNoCorr <- function(FstDataFrame, #the initial calculations. By default this requires that a # locus have heterozygosity equal to 10% or more. - len = length(FstDataFrame$FSTNoCorr) - indexOrder = seq(1, len) - GoodH = ifelse(FstDataFrame$He < Hmin, "lowH", "goodH") - OutlierFlag = ifelse(is.na(FstDataFrame$FSTNoCorr), NA, FALSE) - qvalues = rep(NA, len) - pvalues = rep(NA, len) - pvaluesRightTail = rep(NA, len) + len <-length(FstDataFrame$FSTNoCorr) + indexOrder <-seq(1, len) + GoodH <-ifelse(FstDataFrame$He < Hmin, "lowH", "goodH") + OutlierFlag <-ifelse(is.na(FstDataFrame$FSTNoCorr), NA, FALSE) + qvalues <-rep(NA, len) + pvalues <-rep(NA, len) + pvaluesRightTail <-rep(NA, len) cbind(FstDataFrame, indexOrder, GoodH, @@ -281,27 +281,27 @@ pOutlierFinderChiSqNoCorr <- function(DataList, #do not have meaningful results with the chi-square aprach; # however, they do carry information. - DataListGood = DataList[which(DataList$FSTNoCorr > 0),] - DataListNonPosFst = DataList[which(DataList$FSTNoCorr <= 0),] - DataListNA = DataList[which(is.na(DataList$FSTNoCorr)),] + DataListGood <-DataList[which(DataList$FSTNoCorr > 0),] + DataListNonPosFst <-DataList[which(DataList$FSTNoCorr <= 0),] + DataListNA <-DataList[which(is.na(DataList$FSTNoCorr)),] - pList = pTwoSidedFromChiSq(DataListGood$FSTNoCorr * (dfInferred) / Fstbar, dfInferred) - pListRightTail = 1 - pchisq(DataListGood$FSTNoCorr * (dfInferred) / + pList <-pTwoSidedFromChiSq(DataListGood$FSTNoCorr * (dfInferred) / Fstbar, dfInferred) + pListRightTail <-1 - pchisq(DataListGood$FSTNoCorr * (dfInferred) / Fstbar, dfInferred) # Note: Change made 13 June 2014; q-values now only calcualted on #right-tail one-sided p-values - qtemp = qvalue::qvalue(pListRightTail, + qtemp <-qvalue::qvalue(pListRightTail, fdr.level = qthreshold, pi0.method = "bootstrap") # Note: Using the bootstrap method here seems OK, but if this causes #problems remove the pi0.method='bootstrap' in the previous line # to revert to the default. - DataListGood$pvalues = pList - DataListGood$pvaluesRightTail = pListRightTail - DataListGood$qvalues = qtemp$qvalues - DataListGood$OutlierFlag = qtemp$significant + DataListGood$pvalues <-pList + DataListGood$pvaluesRightTail <-pListRightTail + DataListGood$qvalues <-qtemp$qvalues + DataListGood$OutlierFlag <-qtemp$significant rbind(DataListGood, DataListNonPosFst, DataListNA) } @@ -310,6 +310,6 @@ pTwoSidedFromChiSq <- function(x, df) { # Takes a value x, finds the two-sided p-value for comparison to a #chi-square distribution with df degrees of freedom. - pOneSided = pchisq(x, df) + pOneSided <-pchisq(x, df) ifelse(pOneSided > 0.5, (1 - pOneSided) * 2, pOneSided * 2) } diff --git a/R/utils.pa.Chao.r b/R/utils.pa.Chao.r new file mode 100644 index 00000000..c4b639de --- /dev/null +++ b/R/utils.pa.Chao.r @@ -0,0 +1,51 @@ + +utils.pa.Chao <- function(x,pop1_m,pop2_m){ + +p1_m <- as.matrix(pop1_m) +p2_m <- as.matrix(pop2_m) +p1alf_m <- colMeans(p1_m, na.rm = T) / 2 +p2alf_m <- colMeans(p2_m, na.rm = T) / 2 + +#identifying private alleles +pop1_pop2_m <- unique(unname(unlist(c(which(p2alf_m == 0 & p1alf_m != 0), + which(p2alf_m == 1 & p1alf_m != 1))))) + +pop2_pop1_m <- unique(unname(unlist(c(which(p1alf_m == 0 & p2alf_m != 0), + which(p1alf_m == 1 & p2alf_m != 1))))) + +# keeping only the loci with private alleles +pa_pop1_pop2_m <- gl.keep.loc(x,loc.list = locNames(x)[pop1_pop2_m],verbose =0) +pa_pop2_pop1_m <- gl.keep.loc(x,loc.list = locNames(x)[pop2_pop1_m],verbose =0) +# recalculating allele frequencies +pa_pop1_pop2_m <- gl.recalc.metrics(pa_pop1_pop2_m,verbose =0) +pa_pop2_pop1_m <- gl.recalc.metrics(pa_pop2_pop1_m,verbose =0) + +# calculating the number of singletons +table_pop1_pop2_m <- as.data.frame(table(pa_pop1_pop2_m$other$loc.metrics$maf*(nInd(pa_pop1_pop2_m)*2))) +colnames(table_pop1_pop2_m) <- c("number_alleles","count") +table_pop1_pop2_m$number_alleles <- as.numeric(table_pop1_pop2_m$number_alleles) +table_pop1_pop2_m$number_alleles <- ceiling(table_pop1_pop2_m$number_alleles) + +table_pop2_pop1_m <- as.data.frame(table(pa_pop2_pop1_m$other$loc.metrics$maf*(nInd(pa_pop2_pop1_m)*2))) +colnames(table_pop2_pop1_m) <- c("number_alleles","count") +table_pop2_pop1_m$number_alleles <- as.numeric(table_pop2_pop1_m$number_alleles ) +table_pop2_pop1_m$number_alleles <- ceiling(table_pop2_pop1_m$number_alleles) + +# estimating the number of un detected private alleles +# equation 2c +# Deciphering the enigma of undetected species, phylogenetic, and functional +# diversity based on Good-Turing theory + +f1_pop1_pop2 <- table_pop1_pop2_m[1,"count"] +f2_pop1_pop2 <- table_pop1_pop2_m[2,"count"] +n_pop1_pop2 <- sum(table_pop1_pop2_m$count) +pa_Chao_pop1_pop2 <- ((n_pop1_pop2-1)/n_pop1_pop2) * ((f1_pop1_pop2^2) / (2*f2_pop1_pop2)) + +f1_pop2_pop1 <- table_pop2_pop1_m[1,"count"] +f2_pop2_pop1 <- table_pop2_pop1_m[2,"count"] +n_pop2_pop1 <- sum(table_pop2_pop1_m$count) +pa_Chao_pop2_pop1 <- ((n_pop2_pop1-1)/n_pop2_pop1) * ((f1_pop2_pop1^2) / (2*f2_pop2_pop1)) + +return(list(pa_Chao_pop1_pop2,pa_Chao_pop2_pop1)) + +} \ No newline at end of file diff --git a/R/utils.read.dart.r b/R/utils.read.dart.r index d0825dae..c33eeae7 100644 --- a/R/utils.read.dart.r +++ b/R/utils.read.dart.r @@ -120,29 +120,6 @@ utils.read.dart <- function(filename, make.unique(as.character(ind.names), sep = "_") } - datas <- snpraw[, (lmet + 1):ncol(snpraw)] - - nrows = NULL - if (is.null(nrows)) { - gnrows = 3 - max(datas, na.rm = TRUE) #if max(datas==1) then two row format, if two then one row format - - if (gnrows == 1 | gnrows == 2) { - nrows <- gnrows - if (verbose >= 2) { - cat(report(paste( - " Detected", nrows, "row format.\n" - ))) - } - } else { - stop( - error( - "The DArT format must be either 1row or 2row. This does not seem to be the case here.\n" - ) - ) - } - - } - stdmetricscols <- 1:lmet # if (length(stdmetricscols) != length(stdmetrics)) { cat(paste('\nCould not find all standard metrics.\n',stdmetrics[!(stdmetrics # %in% names(snpraw) )] ,' is missing.\n Carefully check the spelling of your headers!\n')) stop() } if (!is.null(addmetrics)) { @@ -150,12 +127,6 @@ utils.read.dart <- function(filename, # find all additional metrics.\n',addmetrics[!(addmetrics %in% names(snpraw) )] ,' is missing.\n Carefully check the spelling of your # headers! or set addmetrics to NULL\n')) stop() } stdmetricscols <- c(stdmetricscols, addmetricscols) } - if (verbose >= 2) { - cat(report("Added the following locus metrics:\n")) - cat(report(paste( - paste(names(snpraw)[stdmetricscols], collapse = " "), ".\n" - ))) - } covmetrics <- snpraw[, stdmetricscols] ##### Various checks (first are there two rows per allele? we do not need cloneid any more.... covmetrics$CloneID = @@ -167,33 +138,135 @@ utils.read.dart <- function(filename, # check that there are two lines per locus... covmetrics = separate(covmetrics, AlleleID, into = c('allid','alrest'),sep = '\\|', # extra='merge') covmetrics$clone <- - (sub("\\|.*", "", covmetrics$AlleleID, perl = T)) + (sub("\\|.*", "", covmetrics$AlleleID, perl = T)) spp <- - (sub(".+-+(\\d{1,3}):.+", "\\1", covmetrics$AlleleID)) + (sub(".+-+(\\d{1,3}):.+", "\\1", covmetrics$AlleleID)) #### find uid within allelid covmetrics$uid <- paste(covmetrics$clone, spp, sep = "-") ### there should be only twos (and maybe fours) tt <- table(table(covmetrics$uid)) - if (verbose >= 2) { - cat(report( - paste( - "Number of rows per clone (should be only ", - nrows, - "s):", - names(tt), - "\n " - ) + + # Testing for SNPs with the same ID + # Jason Carling e-mail + # the marker discovery and calling pipeline which we use (called ds14) has a + # mechanism to prevent this situation from occurring. When the clusters are + # parsed, the software will not allow more than a single marker with the same + # CloneID, SNP position, and SNP variant. However, there are a number of + # scenarios I can think of which could occur after the initial marker outputs + # are produced which could violate this rule, resulting in the situation you + # have described. One example could be the combination of markers from multiple + # software outputs into a single new report file. This can occur either + # manually, or using SNP re-calling software which calls the markers based on an + # input definition file, rather than running the marker discovery de novo. + # Secondly, marker renaming is sometimes undertaken, when newly discovered + # sequences are added to our CloneID database, and these IDs are retrospectively + # incorporated into the marker report to replace temporary IDs. I have not yet + # determined where the marker name clash was introduced in this case, but I can + # answer your question by saying that unfortunately, we cannot completely + # prevent this sort of problem for the reasons indicated above due to the + # potential for processes which modify names or combine data sets after the + # initial marker discovery and output. + + if(length(tt)>1){ + # if format is two rows + if(as.numeric(names(tt[2]))==4){ + + t1 <- as.data.frame(cbind(1:nrow(covmetrics),covmetrics$uid)) + t2 <- table(t1$V2) + t3 <- names(t2[t2>2]) + t4 <- unlist(lapply(t3,function(x){ + which(t1$V2==x) + })) + t5 <- t1[t4,] + # removing SNPs with the same ID + + t5$V1 <- as.numeric(t5$V1) + covmetrics <- covmetrics[-t5$V1,] + + snpraw <- snpraw[-t5$V1,] + + cat(warn( + " There were",tt[2],"SNPs with the same ID. These SNPs will be removed. SNPs names are:",paste(unique(t5$V2),collapse = " "),"\n" + + )) + + }else{ + + t1 <- as.data.frame(cbind(1:nrow(covmetrics),covmetrics$uid)) + t2 <- table(t1$V2) + t3 <- names(t2[t2>1]) + t4 <- unlist(lapply(t3,function(x){ + which(t1$V2==x) + })) + t5 <- t1[t4,] + t5$V1 <- as.numeric(t5$V1) + # removing SNPs with the same ID + covmetrics <- covmetrics[-t5$V1,] + snpraw <- snpraw[-t5$V1,] + + cat(warn( + " There were",tt[2],"SNPs with the same ID. These SNPs will be removed. SNPs names are:",paste(unique(t5$V2),collapse = " "),"\n" + )) + + + } + + } - if (nrows != as.numeric(names(tt))) { - cat( - warn( - " Warning: The no. rows per Clone does not fit with nrow format. Most likely your data are not read in correctly!\n" + + datas <- snpraw[, (lmet + 1):ncol(snpraw)] + + nrows <-NULL + if (is.null(nrows)) { + gnrows <-3 - max(datas, na.rm = TRUE) #if max(datas==1) then two row format, if two then one row format + + if (gnrows == 1 | gnrows == 2) { + nrows <- gnrows + if (verbose >= 2) { + cat(report(paste( + " Detected", nrows, "row format.\n" + ))) + } + } else { + stop( + error( + "The DArT format must be either 1row or 2row. This does not seem to be the case here.\n" + ) ) + } + + } + + if (nrows != as.numeric(names(tt[1]))) { + cat( + warn( + " Warning: The no. rows per Clone does not fit with nrow format. Most likely your data are not read in correctly!\n" ) + ) } + + if (verbose >= 2) { + cat(report( + paste( + "Number of rows per clone (should be only ", + nrows, + "s):", + names(tt), + "\n " + ) + )) + } + + if (verbose >= 2) { + cat(report("Added the following locus metrics:\n")) + cat(report(paste( + paste(names(snpraw)[stdmetricscols], collapse = " "), ".\n" + ))) + } + nind <- ncol(datas) nsnp <- nrow(covmetrics) / nrows diff --git a/R/utils.sims.r b/R/utils.sims.r index 95fe17b2..199fab5d 100644 --- a/R/utils.sims.r +++ b/R/utils.sims.r @@ -349,9 +349,11 @@ for (int j = 0; j < loc; j++) { res$other$loc.metrics <- ref chromosome(res) <- res$other$loc.metrics$chr_name position(res) <- res$other$loc.metrics$loc_bp + res$loc.all <- rep("0/1",nLoc(res)) res$other$sim.vars <- s_vars res <- utils.reset.flags(res,verbose=0) + return(res) } diff --git a/R/utils.spautocor.R b/R/utils.spautocor.R new file mode 100644 index 00000000..ca096711 --- /dev/null +++ b/R/utils.spautocor.R @@ -0,0 +1,142 @@ +#' @name utils.spautocorr +#' @title Spatial autocorrelation coefficient calculations +#' +#' @description Carries out calculation for spatial autocorrelation coefficient +#' starting from a genetic and geogaphic distance matrix. +#' +#' @details The code of this function is based one \code{spautocorr} from the package +#' \code{PopGenReport}, which has been modified to fix a few bugs (as of +#' \code{PopGenReport v 3.0.4} and allow calculations of bootstraps estimates. +#' +#' See details from \code{gl.spatial.autoCorr} for a detailed explanation. +#' @param GD Genetic distance matrix. +#' @param GGD Geographic distance matrix. +#' +#' @inheritParams gl.spatial.autoCorr +#' @return Returns a data frame with the following columns: +#' \enumerate{ +#' \item Bin The distance classes +#' \item N The number of pairwise comparisons within each distance class +#' \item r.uc The uncorrected autocorrelation coefficient +#' } +#' if both \code{bootstap} and \code{permutation} are \code{FALSE} otherwise only +#' \code{r} estimates are returned +#' +#' @author Carlo Pacioni & Bernd Gruber +#' @references \itemize{ +#' \item Smouse PE, Peakall R. 1999. Spatial autocorrelation analysis of +#' individual multiallele and multilocus genetic structure. Heredity 82: +#' 561-573. +#' +#' \item Double, MC, et al. 2005. Dispersal, philopatry and infidelity: dissecting +#' local genetic structure in superb fairy-wrens (Malurus cyaneus). Evolution +#' 59, 625-635. +#' +#' \item Peakall, R, et al. 2003. Spatial autocorrelation analysis offers new +#' insights into gene flow in the Australian bush rat, Rattus fuscipes. +#' Evolution 57, 1182-1195. +#' +#' \item Smouse, PE, et al. 2008. A heterogeneity test for fine-scale genetic +#' structure. Molecular Ecology 17, 3389-3400. +#' +#' \item Gonzales, E, et al. 2010. The impact of landscape disturbance on spatial +#' genetic structure in the Guanacaste tree, Enterolobium +#' cyclocarpum(Fabaceae). Journal of Heredity 101, 133-143. +#' +#' \item Beck, N, et al. 2008. Social constraint and an absence of sex-biased +#' dispersal drive fine-scale genetic structure in white-winged choughs. +#' Molecular Ecology 17, 4346-4358. +#' } +#' @examples +#' # See gl.spatial.autoCorr +#' @seealso \code{\link{gl.spatial.autoCorr}} +#' @export + +utils.spautocor <- function(GD, + GGD, + permutation = FALSE, + bootstrap = FALSE, + bins = 10, + reps) { + gd <- GD + ed <- GGD + + if (permutation == TRUE) { + gdsample <- sample(nrow(gd)) + gd <- gd[gdsample, gdsample] + } + + cdmat <- function(gd) { + dimen <- nrow(gd) + sgd <- sum(gd, na.rm = TRUE) + cscd <- matrix(colSums(gd, na.rm = TRUE), dimen, dimen) + rscd <- + matrix(rowSums(gd, na.rm = TRUE), dimen, dimen, byrow = TRUE) + cd <- 0.5 * (-gd + 1 / dimen * (cscd + rscd) - 1 / dimen ^ 2 * (sgd)) + cd + } + cd <- cdmat(gd) + #remove upper triangel to speed things up.... + ed[upper.tri(ed)] <- NA + diag(ed) <- NA + + if (length(bins) == 1) { + steps <- seq_len(bins) * signif(diff(range(ed, na.rm = TRUE)) / bins, 4) + steps <- c(min(ed, na.rm = TRUE), steps) + if (steps[length(steps)] < max(ed, na.rm = TRUE)) + steps[length(steps)] <- max(ed, na.rm = TRUE) + } else { + steps <- bins + } + compute.r <- function(d, steps, ed, cd, bootstrap = FALSE) { + if (length(steps) == d) { + index <- which(ed <= steps[d] & ed >= steps[d - 1], arr.ind = TRUE) + } else { + index <- which(ed < steps[d] & ed >= steps[d - 1], arr.ind = TRUE) + } + + N <- length(index) / 2 + distance <- steps[d] + + if (bootstrap == TRUE) { + if (reps > choose(N + N - 1, N)) { + # The number of possible combination with replacement are C(n+k-1, k) + return(data.frame( + Bin = distance, + N = N, + r.uc = NA + )) + } else { + if (nrow(index) > 1) { + row.sample <- sample(nrow(index), replace = TRUE) + index <- index[row.sample,] + } + } + + } + + cx <- sum(cd[index]) + cxii <- sum(diag(cd)[index[, 1]]) + cxjj <- sum(diag(cd)[index[, 2]]) + r <- 2 * cx / (cxii + cxjj) + + + + return(data.frame(Bin = distance, N = N, r.uc = r)) + } + l.res <- + lapply( + 2:length(steps), + compute.r, + steps = steps, + ed = ed, + cd = cd, + bootstrap = bootstrap + ) + res <- do.call(rbind, l.res) + if (permutation == FALSE & bootstrap == FALSE) + return(res) + else + return(res[, "r.uc"]) + +} \ No newline at end of file diff --git a/R/zzz.r b/R/zzz.r index ef724644..fce33b0f 100644 --- a/R/zzz.r +++ b/R/zzz.r @@ -2,9 +2,10 @@ #' #' Setting theme, colors and verbosity #' @importFrom graphics axis barplot box image lines text -#' @importFrom grDevices hcl +#' @importFrom grDevices hcl col2rgb #' @importFrom methods new -#' @importFrom stats dist nobs optimize pchisq variable.names optim quantile pgamma +#' @importFrom stats dist nobs optimize pchisq variable.names optim quantile +#' pgamma #' @import ggplot2 zzz <- NULL #to create a useful named help page @@ -45,10 +46,17 @@ utils::globalVariables(c("chromosome_name","phase1","same_line","number_pops_pha `.` <- list # SET PLOTS COLORS +# Convert color names to hex RGB strings taken from function col2hex from +# package gplots +RGB_colors <- function (cname){ + colMat <- col2rgb(cname) + rgb(red = colMat[1, ]/255, green = colMat[2, ]/255, blue = colMat[3, + ]/255) +} # function to replicate defaults colors of ggplot discrete_palette <- function(n) { - hues = seq(15, 375, length = n + 1) + hues <-seq(15, 375, length = n + 1) return(hcl(h = hues, l = 65, c = 100)[1:n]) } @@ -281,25 +289,25 @@ theme_dartR <- function(base_size = 11, } ## plot method -setMethod("plot", signature(x = "genlight"), function(x, - group_pop = FALSE, - ind_labels = indNames(x), - ind_labels_size = 10, - plot_colors = four_colors, - posi = "bottom", - save2tmp = FALSE, - verbose = NULL) { - gl.smearplot( - x, - group_pop = group_pop, - ind_labels = ind_labels, - ind_labels_size = ind_labels_size, - plot_colors = plot_colors, - posi = posi, - save2tmp = save2tmp, - verbose = verbose - ) -}) +# setMethod("plot", signature(x = "genlight"), function(x, +# group_pop = TRUE, +# ind_labels = TRUE, +# ind_labels_size = 8, +# plot_colors = four_colors, +# posi = "bottom", +# save2tmp = FALSE, +# verbose = NULL) { +# gl.smearplot( +# x, +# group_pop = group_pop, +# ind_labels = ind_labels, +# ind_labels_size = ind_labels_size, +# plot_colors = plot_colors, +# posi = posi, +# save2tmp = save2tmp, +# verbose = verbose +# ) +# }) # WELCOME MESSAGE .onAttach <- function(...) { diff --git a/README.md b/README.md index 2db7c85c..cf8b0edc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ + + Main repository: [![](https://www.r-pkg.org/badges/version/dartR?color=blue)](https://cran.r-project.org/package=dartR) diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 00000000..10327515 --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,5 @@ +url: https://green-striped-gecko.github.io/dartR/ +template: + bootstrap: 5 + bootswatch: quartz + diff --git a/code_of_conduct.md b/code_of_conduct.md new file mode 100644 index 00000000..d33878e1 --- /dev/null +++ b/code_of_conduct.md @@ -0,0 +1,134 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/codemeta.json b/codemeta.json new file mode 100644 index 00000000..0843d1a2 --- /dev/null +++ b/codemeta.json @@ -0,0 +1,1031 @@ +{ + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "identifier": "dartR", + "description": "Functions are provided that facilitate the import and analysis of SNP (single nucleotide polymorphism) and silicodart (presence/absence) data. The main focus is on data generated by DarT (Diversity Arrays Technology). However, once SNP or related fragment presence/absence data from any source is imported into a genlight object many of the functions can be used. Functions are available for input and output of SNP and silicodart data, for reporting on and filtering on various criteria (e.g. CallRate, Heterozygosity, Reproducibility, maximum allele frequency). Advanced filtering is based on Linkage Disequilibrium and HWE (Hardy-Weinberg equilibrium). Other functions are available for visualization after PCoA (Principle Coordinate Analysis), or to facilitate transfer of data between genlight/genind objects and newhybrids, related, phylip, structure, faststructure packages.", + "name": "dartR: Importing and Analysing SNP and Silicodart Data Generated by\n Genome-Wide Restriction Fragment Analysis", + "codeRepository": "https://github.com/green-striped-gecko/dartR", + "license": "https://spdx.org/licenses/GPL-3.0", + "version": "2.3", + "programmingLanguage": { + "@type": "ComputerLanguage", + "name": "R", + "url": "https://r-project.org" + }, + "runtimePlatform": "R version 4.1.2 (2021-11-01)", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "author": [ + { + "@type": "Person", + "givenName": "Bernd", + "familyName": "Gruber", + "email": "bernd.gruber@canberra.edu.au" + }, + { + "@type": "Person", + "givenName": "Arthur", + "familyName": "Georges", + "email": "georges@aerg.edu.au" + }, + { + "@type": "Person", + "givenName": "Jose L.", + "familyName": "Mijangos", + "email": "Luis.MijangosAraujo@canberra.edu.au" + }, + { + "@type": "Person", + "givenName": "Carlo", + "familyName": "Pacioni", + "email": "carlo.pacioni@delwp.vic.gov.au" + } + ], + "contributor": [ + { + "@type": "Person", + "givenName": "Peter J.", + "familyName": "Unmack", + "email": "peter.mail2@unmack.net" + }, + { + "@type": "Person", + "givenName": "Oliver", + "familyName": "Berry", + "email": "oliver.berry@csiro.au" + }, + { + "@type": "Person", + "givenName": "Lindsay V.", + "familyName": "Clark", + "email": "lvclark@illinois.edu" + }, + { + "@type": "Person", + "givenName": "Floriaan", + "familyName": "Devloo-Delva", + "email": "Floriaan.Devloo-Delva@csiro.au" + }, + { + "@type": "Person", + "givenName": "Eric", + "familyName": "Archer", + "email": "eric.archer@noaa.gov" + } + ], + "maintainer": [ + { + "@type": "Person", + "givenName": "Bernd", + "familyName": "Gruber", + "email": "bernd.gruber@canberra.edu.au" + } + ], + "softwareSuggestions": [ + { + "@type": "SoftwareApplication", + "identifier": "boot", + "name": "boot", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=boot" + }, + { + "@type": "SoftwareApplication", + "identifier": "devtools", + "name": "devtools", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=devtools" + }, + { + "@type": "SoftwareApplication", + "identifier": "directlabels", + "name": "directlabels", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=directlabels" + }, + { + "@type": "SoftwareApplication", + "identifier": "dismo", + "name": "dismo", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=dismo" + }, + { + "@type": "SoftwareApplication", + "identifier": "doParallel", + "name": "doParallel", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=doParallel" + }, + { + "@type": "SoftwareApplication", + "identifier": "expm", + "name": "expm", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=expm" + }, + { + "@type": "SoftwareApplication", + "identifier": "gdistance", + "name": "gdistance", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=gdistance" + }, + { + "@type": "SoftwareApplication", + "identifier": "gganimate", + "name": "gganimate", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=gganimate" + }, + { + "@type": "SoftwareApplication", + "identifier": "ggrepel", + "name": "ggrepel", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ggrepel" + }, + { + "@type": "SoftwareApplication", + "identifier": "ggtern", + "name": "ggtern", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ggtern" + }, + { + "@type": "SoftwareApplication", + "identifier": "ggthemes", + "name": "ggthemes", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ggthemes" + }, + { + "@type": "SoftwareApplication", + "identifier": "gplots", + "name": "gplots", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=gplots" + }, + { + "@type": "SoftwareApplication", + "identifier": "HardyWeinberg", + "name": "HardyWeinberg", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=HardyWeinberg" + }, + { + "@type": "SoftwareApplication", + "identifier": "hierfstat", + "name": "hierfstat", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=hierfstat" + }, + { + "@type": "SoftwareApplication", + "identifier": "igraph", + "name": "igraph", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=igraph" + }, + { + "@type": "SoftwareApplication", + "identifier": "iterpc", + "name": "iterpc", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=iterpc" + }, + { + "@type": "SoftwareApplication", + "identifier": "knitr", + "name": "knitr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=knitr" + }, + { + "@type": "SoftwareApplication", + "identifier": "label.switching", + "name": "label.switching", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=label.switching" + }, + { + "@type": "SoftwareApplication", + "identifier": "leaflet", + "name": "leaflet", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=leaflet" + }, + { + "@type": "SoftwareApplication", + "identifier": "leaflet.minicharts", + "name": "leaflet.minicharts", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=leaflet.minicharts" + }, + { + "@type": "SoftwareApplication", + "identifier": "markdown", + "name": "markdown", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=markdown" + }, + { + "@type": "SoftwareApplication", + "identifier": "mmod", + "name": "mmod", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=mmod" + }, + { + "@type": "SoftwareApplication", + "identifier": "networkD3", + "name": "networkD3", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=networkD3" + }, + { + "@type": "SoftwareApplication", + "identifier": "parallel", + "name": "parallel" + }, + { + "@type": "SoftwareApplication", + "identifier": "pca3d", + "name": "pca3d", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=pca3d" + }, + { + "@type": "SoftwareApplication", + "identifier": "pegas", + "name": "pegas", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=pegas" + }, + { + "@type": "SoftwareApplication", + "identifier": "pheatmap", + "name": "pheatmap", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=pheatmap" + }, + { + "@type": "SoftwareApplication", + "identifier": "plotly", + "name": "plotly", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=plotly" + }, + { + "@type": "SoftwareApplication", + "identifier": "poppr", + "name": "poppr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=poppr" + }, + { + "@type": "SoftwareApplication", + "identifier": "proxy", + "name": "proxy", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=proxy" + }, + { + "@type": "SoftwareApplication", + "identifier": "purrr", + "name": "purrr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=purrr" + }, + { + "@type": "SoftwareApplication", + "identifier": "qvalue", + "name": "qvalue", + "provider": { + "@id": "https://www.bioconductor.org", + "@type": "Organization", + "name": "Bioconductor", + "url": "https://www.bioconductor.org" + }, + "sameAs": "https://bioconductor.org/packages/release/bioc/html/qvalue.html" + }, + { + "@type": "SoftwareApplication", + "identifier": "RColorBrewer", + "name": "RColorBrewer", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=RColorBrewer" + }, + { + "@type": "SoftwareApplication", + "identifier": "Rcpp", + "name": "Rcpp", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=Rcpp" + }, + { + "@type": "SoftwareApplication", + "identifier": "rgl", + "name": "rgl", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rgl" + }, + { + "@type": "SoftwareApplication", + "identifier": "rmarkdown", + "name": "rmarkdown", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rmarkdown" + }, + { + "@type": "SoftwareApplication", + "identifier": "rrBLUP", + "name": "rrBLUP", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=rrBLUP" + }, + { + "@type": "SoftwareApplication", + "identifier": "scales", + "name": "scales", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=scales" + }, + { + "@type": "SoftwareApplication", + "identifier": "seqinr", + "name": "seqinr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=seqinr" + }, + { + "@type": "SoftwareApplication", + "identifier": "shinyBS", + "name": "shinyBS", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=shinyBS" + }, + { + "@type": "SoftwareApplication", + "identifier": "shinyjs", + "name": "shinyjs", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=shinyjs" + }, + { + "@type": "SoftwareApplication", + "identifier": "shinythemes", + "name": "shinythemes", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=shinythemes" + }, + { + "@type": "SoftwareApplication", + "identifier": "shinyWidgets", + "name": "shinyWidgets", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=shinyWidgets" + }, + { + "@type": "SoftwareApplication", + "identifier": "SIBER", + "name": "SIBER", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=SIBER" + }, + { + "@type": "SoftwareApplication", + "identifier": "snpStats", + "name": "snpStats", + "provider": { + "@id": "https://www.bioconductor.org", + "@type": "Organization", + "name": "Bioconductor", + "url": "https://www.bioconductor.org" + }, + "sameAs": "https://bioconductor.org/packages/release/bioc/html/snpStats.html" + }, + { + "@type": "SoftwareApplication", + "identifier": "stringi", + "name": "stringi", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=stringi" + }, + { + "@type": "SoftwareApplication", + "identifier": "terra", + "name": "terra", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=terra" + }, + { + "@type": "SoftwareApplication", + "identifier": "testthat", + "name": "testthat", + "version": ">= 3.0.0", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=testthat" + }, + { + "@type": "SoftwareApplication", + "identifier": "tibble", + "name": "tibble", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tibble" + }, + { + "@type": "SoftwareApplication", + "identifier": "tidyverse", + "name": "tidyverse", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tidyverse" + }, + { + "@type": "SoftwareApplication", + "identifier": "vcfR", + "name": "vcfR", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=vcfR" + } + ], + "softwareRequirements": { + "1": { + "@type": "SoftwareApplication", + "identifier": "R", + "name": "R", + "version": ">= 3.5" + }, + "2": { + "@type": "SoftwareApplication", + "identifier": "adegenet", + "name": "adegenet", + "version": ">= 2.0.0", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=adegenet" + }, + "3": { + "@type": "SoftwareApplication", + "identifier": "ggplot2", + "name": "ggplot2", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ggplot2" + }, + "4": { + "@type": "SoftwareApplication", + "identifier": "dplyr", + "name": "dplyr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=dplyr" + }, + "5": { + "@type": "SoftwareApplication", + "identifier": "ape", + "name": "ape", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=ape" + }, + "6": { + "@type": "SoftwareApplication", + "identifier": "crayon", + "name": "crayon", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=crayon" + }, + "7": { + "@type": "SoftwareApplication", + "identifier": "data.table", + "name": "data.table", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=data.table" + }, + "8": { + "@type": "SoftwareApplication", + "identifier": "fields", + "name": "fields", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=fields" + }, + "9": { + "@type": "SoftwareApplication", + "identifier": "foreach", + "name": "foreach", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=foreach" + }, + "10": { + "@type": "SoftwareApplication", + "identifier": "gridExtra", + "name": "gridExtra", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=gridExtra" + }, + "11": { + "@type": "SoftwareApplication", + "identifier": "MASS", + "name": "MASS", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=MASS" + }, + "12": { + "@type": "SoftwareApplication", + "identifier": "methods", + "name": "methods" + }, + "13": { + "@type": "SoftwareApplication", + "identifier": "patchwork", + "name": "patchwork", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=patchwork" + }, + "14": { + "@type": "SoftwareApplication", + "identifier": "plyr", + "name": "plyr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=plyr" + }, + "15": { + "@type": "SoftwareApplication", + "identifier": "PopGenReport", + "name": "PopGenReport", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=PopGenReport" + }, + "16": { + "@type": "SoftwareApplication", + "identifier": "raster", + "name": "raster", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=raster" + }, + "17": { + "@type": "SoftwareApplication", + "identifier": "reshape2", + "name": "reshape2", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=reshape2" + }, + "18": { + "@type": "SoftwareApplication", + "identifier": "shiny", + "name": "shiny", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=shiny" + }, + "19": { + "@type": "SoftwareApplication", + "identifier": "SNPRelate", + "name": "SNPRelate", + "provider": { + "@id": "https://www.bioconductor.org", + "@type": "Organization", + "name": "Bioconductor", + "url": "https://www.bioconductor.org" + }, + "sameAs": "https://bioconductor.org/packages/release/bioc/html/SNPRelate.html" + }, + "20": { + "@type": "SoftwareApplication", + "identifier": "sp", + "name": "sp", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=sp" + }, + "21": { + "@type": "SoftwareApplication", + "identifier": "StAMPP", + "name": "StAMPP", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=StAMPP" + }, + "22": { + "@type": "SoftwareApplication", + "identifier": "stats", + "name": "stats" + }, + "23": { + "@type": "SoftwareApplication", + "identifier": "stringr", + "name": "stringr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=stringr" + }, + "24": { + "@type": "SoftwareApplication", + "identifier": "tidyr", + "name": "tidyr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tidyr" + }, + "25": { + "@type": "SoftwareApplication", + "identifier": "utils", + "name": "utils" + }, + "26": { + "@type": "SoftwareApplication", + "identifier": "vegan", + "name": "vegan", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Comprehensive R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=vegan" + }, + "SystemRequirements": null + }, + "fileSize": "29970.797KB", + "citation": [ + { + "@type": "ScholarlyArticle", + "datePublished": "2018", + "author": [ + { + "@type": "Person", + "givenName": "Bernd", + "familyName": "Gruber" + }, + { + "@type": "Person", + "givenName": ["Peter", "J"], + "familyName": "Unmack" + }, + { + "@type": "Person", + "givenName": ["Oliver", "F"], + "familyName": "Berry" + }, + { + "@type": "Person", + "givenName": "Arthur", + "familyName": "Georges" + } + ], + "name": " dartr: An r package to facilitate analysis of SNP data generated from reduced representation genome sequencing", + "identifier": "10.1111/1755-0998.12745", + "pagination": "691--699", + "@id": "https://doi.org/10.1111/1755-0998.12745", + "sameAs": "https://doi.org/10.1111/1755-0998.12745", + "isPartOf": { + "@type": "PublicationIssue", + "datePublished": "2018", + "isPartOf": { + "@type": ["PublicationVolume", "Periodical"], + "volumeNumber": "18", + "name": "Molecular Ecology Resources" + } + } + } + ], + "relatedLink": ["https://green-striped-gecko.github.io/dartR/", "https://CRAN.R-project.org/package=dartR"], + "readme": "https://github.com/green-striped-gecko/dartR/blob/master/README.md", + "contIntegration": ["https://github.com/green-striped-gecko/dartR/actions", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-beta.yaml", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-dev.yaml", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-dev_Arthur.yaml", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-dev_Bernd.yaml", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-dev_Luis.yaml", "https://github.com/green-striped-gecko/dartR/actions/workflows/R-CMD-check-dev_Carlo.yaml"] +} diff --git a/doc/dartRTutorials.R b/doc/dartRTutorials.R deleted file mode 100644 index 8b137891..00000000 --- a/doc/dartRTutorials.R +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/dartRTutorials.Rmd b/doc/dartRTutorials.Rmd deleted file mode 100644 index 96e97e48..00000000 --- a/doc/dartRTutorials.Rmd +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: "dartR Tutorials" -author: "Bernd Gruber, Peter Unmack, Oliver Berry, Luis Mijangos & Arthur Georges" -date: '`r Sys.Date()`' -output: - pdf_document -vignette: > - %\VignetteIndexEntry{dartR_Tutorials} - %\VignetteEngine{knitr::rmarkdown} - \usepackage[utf8]{inputenc} ---- - -Tutorials for beginner and developer in dartR can be found on our github pages - -* [Tutorials for dartR please click here http://georges.biomatix.org/dartR](http://georges.biomatix.org/dartR) \ No newline at end of file diff --git a/doc/dartRTutorials.pdf b/doc/dartRTutorials.pdf deleted file mode 100644 index 56535582..00000000 Binary files a/doc/dartRTutorials.pdf and /dev/null differ diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 00000000..bbac6e73 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,86 @@ + + + + + + + +Page not found (404) • dartR + + + + + + + + + Skip to contents + + +
+
+
+ +Content not found. Please use links in the navbar. + +
+
+ + + +
+ + + + + + + diff --git a/docs/LICENSE.html b/docs/LICENSE.html new file mode 100644 index 00000000..27bafe06 --- /dev/null +++ b/docs/LICENSE.html @@ -0,0 +1,260 @@ + +GNU General Public License • dartR + Skip to contents + + +
+
+
+ +
+ +

Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>

+

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

+
+

Preamble

+

The GNU General Public License is a free, copyleft license for software and other kinds of works.

+

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program–to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

+

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

+

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

+

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

+

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

+

For the developers’ and authors’ protection, the GPL clearly explains that there is no warranty for this free software. For both users’ and authors’ sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

+

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users’ freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

+

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

+

The precise terms and conditions for copying, distribution and modification follow.

+
+
+

TERMS AND CONDITIONS

+
+

0. Definitions

+

“This License” refers to version 3 of the GNU General Public License.

+

“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

+

“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

+

To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

+

A “covered work” means either the unmodified Program or a work based on the Program.

+

To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

+

To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

+

An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

+
+
+

1. Source Code

+

The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

+

A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

+

The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

+

The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work’s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

+

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

+

The Corresponding Source for a work in source code form is that same work.

+
+
+

2. Basic Permissions

+

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

+

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

+

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

+
+
+ +

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

+

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work’s users, your or third parties’ legal rights to forbid circumvention of technological measures.

+
+
+

4. Conveying Verbatim Copies

+

You may convey verbatim copies of the Program’s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

+

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

+
+
+

5. Conveying Modified Source Versions

+

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

+
  • +a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • +
  • +b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
  • +
  • +c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • +
  • +d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
  • +

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation’s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

+
+
+

6. Conveying Non-Source Forms

+

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

+
  • +a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • +
  • +b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • +
  • +c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • +
  • +d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • +
  • +e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
  • +

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

+

A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

+

“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

+

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

+

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

+

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

+
+
+

7. Additional Terms

+

“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

+

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

+

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

+
  • +a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • +
  • +b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • +
  • +c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • +
  • +d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • +
  • +e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • +
  • +f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
  • +

All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

+

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

+

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

+
+
+

8. Termination

+

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

+

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

+

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

+

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

+
+
+

9. Acceptance Not Required for Having Copies

+

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

+
+
+

10. Automatic Licensing of Downstream Recipients

+

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

+

An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party’s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

+

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

+
+
+

11. Patents

+

A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor’s “contributor version”.

+

A contributor’s “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

+

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor’s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

+

In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

+

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient’s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

+

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

+

A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

+

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

+
+
+

12. No Surrender of Others’ Freedom

+

If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

+
+
+

13. Use with the GNU Affero General Public License

+

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

+
+
+

14. Revised Versions of this License

+

The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

+

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

+

If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy’s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

+

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

+
+
+

15. Disclaimer of Warranty

+

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

+
+
+

16. Limitation of Liability

+

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+
+
+

17. Interpretation of Sections 15 and 16

+

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

+

END OF TERMS AND CONDITIONS

+
+
+
+

How to Apply These Terms to Your New Programs

+

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

+

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

+
<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year>  <name of author>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+

Also add information on how to contact you by electronic and paper mail.

+

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

+
<program>  Copyright (C) <year>  <name of author>
+This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type 'show c' for details.
+

The hypothetical commands show w and show c should show the appropriate parts of the General Public License. Of course, your program’s commands might be different; for a GUI interface, you would use an “about box”.

+

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.

+

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.

+
+
+ +
+ + +
+ + + + + + + diff --git a/docs/articles/dartRTutorials.html b/docs/articles/dartRTutorials.html new file mode 100644 index 00000000..17838e39 --- /dev/null +++ b/docs/articles/dartRTutorials.html @@ -0,0 +1,104 @@ + + + + + + + + +dartR Tutorials • dartR + + + + + + + + + + Skip to contents + + +
+ + + + +
+
+ + + +

Tutorials for beginner and developer in dartR can be found on our +github pages

+
+
+ + + + +
+ + + + + + + diff --git a/docs/articles/index.html b/docs/articles/index.html new file mode 100644 index 00000000..97d91e11 --- /dev/null +++ b/docs/articles/index.html @@ -0,0 +1,68 @@ + +Articles • dartR + Skip to contents + + +
+
+
+ +
+

All vignettes

+

+ +
dartR Tutorials
+
+
+
+ + +
+ + + + + + + diff --git a/docs/authors.html b/docs/authors.html new file mode 100644 index 00000000..e34f1ca1 --- /dev/null +++ b/docs/authors.html @@ -0,0 +1,119 @@ + +Authors and Citation • dartR + Skip to contents + + +
+
+
+ +
+

Authors

+ +
  • +

    Bernd Gruber. Author, maintainer. +

    +
  • +
  • +

    Arthur Georges. Author. +

    +
  • +
  • +

    Jose L. Mijangos. Author. +

    +
  • +
  • +

    Carlo Pacioni. Author. +

    +
  • +
  • +

    Peter J. Unmack. Contributor. +

    +
  • +
  • +

    Oliver Berry. Contributor. +

    +
  • +
  • +

    Lindsay V. Clark. Contributor. +

    +
  • +
  • +

    Floriaan Devloo-Delva. Contributor. +

    +
  • +
  • +

    Eric Archer. Contributor. +

    +
  • +
+ +
+

Citation

+

+ +

Gruber, B, Unmack, PJ, Berry, OF, Georges, A. dartr: An r package to facilitate analysis of SNP data generated from reduced representation genome sequencing. Mol Ecol Resour. 2018; 18: 691-699. https://doi.org/10.1111/1755-0998.12745

+
@Article{,
+  author = {Bernd Gruber and Peter J Unmack and Oliver F Berry and Arthur Georges},
+  doi = {10.1111/1755-0998.12745},
+  journal = {Molecular Ecology Resources},
+  keywords = {bioinformatics,next-generation-sequencing, phylogenomics,SNPs, population genomics, R pacakge, RADSeq},
+  pages = {691--699},
+  title = { dartr: An r package to facilitate analysis of SNP data generated from reduced representation genome sequencing},
+  volume = {18},
+  year = {2018},
+}
+
+
+ + +
+ + + + + + + diff --git a/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js b/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js new file mode 100644 index 00000000..b65b161a --- /dev/null +++ b/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.1.0 (https://getbootstrap.com/) + * Copyright 2011-2021 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=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 e},e=e=>{const i=t(e);return i&&document.querySelector(i)?i:null},i=e=>{const i=t(e);return i?document.querySelector(i):null},n=t=>{t.dispatchEvent(new Event("transitionend"))},s=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),o=t=>s(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(t):null,r=(t,e,i)=>{Object.keys(i).forEach(n=>{const o=i[n],r=e[n],a=r&&s(r)?"element":null==(l=r)?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();var l;if(!new RegExp(o).test(a))throw new TypeError(`${t.toUpperCase()}: Option "${n}" provided type "${a}" but expected type "${o}".`)})},a=t=>!(!s(t)||0===t.getClientRects().length)&&"visible"===getComputedStyle(t).getPropertyValue("visibility"),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=()=>{const{jQuery:t}=window;return t&&!document.body.hasAttribute("data-bs-no-jquery")?t: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",()=>{f.forEach(t=>t())}),f.push(e)):e()},g=t=>{"function"==typeof t&&t()},_=(t,e,i=!0)=>{if(!i)return void g(t);const s=(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 o=!1;const r=({target:i})=>{i===e&&(o=!0,e.removeEventListener("transitionend",r),g(t))};e.addEventListener("transitionend",r),setTimeout(()=>{o||n(e)},s)},b=(t,e,i,n)=>{let s=t.indexOf(e);if(-1===s)return t[!i&&n?t.length-1:0];const o=t.length;return s+=i?1:-1,n&&(s=(s+o)%o),t[Math.max(0,Math.min(s,o-1))]},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,E={};let A=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},O=/^(mouseenter|mouseleave)/i,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 k(t,e){return e&&`${e}::${A++}`||t.uidEvent||A++}function L(t){const e=k(t);return t.uidEvent=e,E[e]=E[e]||{},E[e]}function x(t,e,i=null){const n=Object.keys(t);for(let s=0,o=n.length;sfunction(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};n?n=t(n):i=t(i)}const[o,r,a]=D(e,i,n),l=L(t),c=l[a]||(l[a]={}),h=x(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=k(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(let a=o.length;a--;)if(o[a]===r)return s.delegateTarget=r,n.oneOff&&P.off(t,s.type,e,i),i.apply(r,[s]);return null}}(t,i,n):function(t,e){return function i(n){return n.delegateTarget=t,i.oneOff&&P.off(t,n.type,e),e.apply(t,[n])}}(t,i);u.delegationSelector=o?i:null,u.originalHandler=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function N(t,e,i,n,s){const o=x(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function I(t){return t=t.replace(y,""),T[t]||t}const P={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]=D(e,i,n),a=r!==e,l=L(t),c=e.startsWith(".");if(void 0!==o){if(!l||!l[r])return;return void N(t,l,r,o,s?i:null)}c&&Object.keys(l).forEach(i=>{!function(t,e,i,n){const s=e[i]||{};Object.keys(s).forEach(o=>{if(o.includes(n)){const n=s[o];N(t,e,i,n.originalHandler,n.delegationSelector)}})}(t,l,i,e.slice(1))});const h=l[r]||{};Object.keys(h).forEach(i=>{const n=i.replace(w,"");if(!a||e.includes(n)){const e=h[i];N(t,l,r,e.originalHandler,e.delegationSelector)}})},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u(),s=I(e),o=e!==s,r=C.has(s);let a,l=!0,c=!0,h=!1,d=null;return o&&n&&(a=n.Event(e,i),n(t).trigger(a),l=!a.isPropagationStopped(),c=!a.isImmediatePropagationStopped(),h=a.isDefaultPrevented()),r?(d=document.createEvent("HTMLEvents"),d.initEvent(s,l,!0)):d=new CustomEvent(e,{bubbles:l,cancelable:!0}),void 0!==i&&Object.keys(i).forEach(t=>{Object.defineProperty(d,t,{get:()=>i[t]})}),h&&d.preventDefault(),c&&t.dispatchEvent(d),d.defaultPrevented&&void 0!==a&&a.preventDefault(),d}},j=new Map;var M={set(t,e,i){j.has(t)||j.set(t,new Map);const n=j.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>j.has(t)&&j.get(t).get(e)||null,remove(t,e){if(!j.has(t))return;const i=j.get(t);i.delete(e),0===i.size&&j.delete(t)}};class H{constructor(t){(t=o(t))&&(this._element=t,M.set(this._element,this.constructor.DATA_KEY,this))}dispose(){M.remove(this._element,this.constructor.DATA_KEY),P.off(this._element,this.constructor.EVENT_KEY),Object.getOwnPropertyNames(this).forEach(t=>{this[t]=null})}_queueCallback(t,e,i=!0){_(t,e,i)}static getInstance(t){return M.get(o(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.1.0"}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}static get DATA_KEY(){return"bs."+this.NAME}static get EVENT_KEY(){return"."+this.DATA_KEY}}const B=(t,e="hide")=>{const n="click.dismiss"+t.EVENT_KEY,s=t.NAME;P.on(document,n,`[data-bs-dismiss="${s}"]`,(function(n){if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),l(this))return;const o=i(this)||this.closest("."+s);t.getOrCreateInstance(o)[e]()}))};class R extends H{static get NAME(){return"alert"}close(){if(P.trigger(this._element,"close.bs.alert").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(),P.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=R.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)}}))}}B(R,"close"),m(R);class W extends H{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=W.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}function z(t){return"true"===t||"false"!==t&&(t===Number(t).toString()?Number(t):""===t||"null"===t?null:t)}function q(t){return t.replace(/[A-Z]/g,t=>"-"+t.toLowerCase())}P.on(document,"click.bs.button.data-api",'[data-bs-toggle="button"]',t=>{t.preventDefault();const e=t.target.closest('[data-bs-toggle="button"]');W.getOrCreateInstance(e).toggle()}),m(W);const F={setDataAttribute(t,e,i){t.setAttribute("data-bs-"+q(e),i)},removeDataAttribute(t,e){t.removeAttribute("data-bs-"+q(e))},getDataAttributes(t){if(!t)return{};const e={};return Object.keys(t.dataset).filter(t=>t.startsWith("bs")).forEach(i=>{let n=i.replace(/^bs/,"");n=n.charAt(0).toLowerCase()+n.slice(1,n.length),e[n]=z(t.dataset[i])}),e},getDataAttribute:(t,e)=>z(t.getAttribute("data-bs-"+q(e))),offset(t){const e=t.getBoundingClientRect();return{top:e.top+window.pageYOffset,left:e.left+window.pageXOffset}},position:t=>({top:t.offsetTop,left:t.offsetLeft})},U={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;for(;n&&n.nodeType===Node.ELEMENT_NODE&&3!==n.nodeType;)n.matches(e)&&i.push(n),n=n.parentNode;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))}},$={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},V={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},K="next",X="prev",Y="left",Q="right",G={ArrowLeft:Q,ArrowRight:Y};class Z extends H{constructor(t,e){super(t),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._indicatorsElement=U.findOne(".carousel-indicators",this._element),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent),this._addEventListeners()}static get Default(){return $}static get NAME(){return"carousel"}next(){this._slide(K)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(X)}pause(t){t||(this._isPaused=!0),U.findOne(".carousel-item-next, .carousel-item-prev",this._element)&&(n(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}cycle(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config&&this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))}to(t){this._activeElement=U.findOne(".active.carousel-item",this._element);const e=this._getItemIndex(this._activeElement);if(t>this._items.length-1||t<0)return;if(this._isSliding)return void P.one(this._element,"slid.bs.carousel",()=>this.to(t));if(e===t)return this.pause(),void this.cycle();const i=t>e?K:X;this._slide(i,this._items[t])}_getConfig(t){return t={...$,...F.getDataAttributes(this._element),..."object"==typeof t?t:{}},r("carousel",t,V),t}_handleSwipe(){const t=Math.abs(this.touchDeltaX);if(t<=40)return;const e=t/this.touchDeltaX;this.touchDeltaX=0,e&&this._slide(e>0?Q:Y)}_addEventListeners(){this._config.keyboard&&P.on(this._element,"keydown.bs.carousel",t=>this._keydown(t)),"hover"===this._config.pause&&(P.on(this._element,"mouseenter.bs.carousel",t=>this.pause(t)),P.on(this._element,"mouseleave.bs.carousel",t=>this.cycle(t))),this._config.touch&&this._touchSupported&&this._addTouchEventListeners()}_addTouchEventListeners(){const t=t=>{!this._pointerEvent||"pen"!==t.pointerType&&"touch"!==t.pointerType?this._pointerEvent||(this.touchStartX=t.touches[0].clientX):this.touchStartX=t.clientX},e=t=>{this.touchDeltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this.touchStartX},i=t=>{!this._pointerEvent||"pen"!==t.pointerType&&"touch"!==t.pointerType||(this.touchDeltaX=t.clientX-this.touchStartX),this._handleSwipe(),"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(t=>this.cycle(t),500+this._config.interval))};U.find(".carousel-item img",this._element).forEach(t=>{P.on(t,"dragstart.bs.carousel",t=>t.preventDefault())}),this._pointerEvent?(P.on(this._element,"pointerdown.bs.carousel",e=>t(e)),P.on(this._element,"pointerup.bs.carousel",t=>i(t)),this._element.classList.add("pointer-event")):(P.on(this._element,"touchstart.bs.carousel",e=>t(e)),P.on(this._element,"touchmove.bs.carousel",t=>e(t)),P.on(this._element,"touchend.bs.carousel",t=>i(t)))}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=G[t.key];e&&(t.preventDefault(),this._slide(e))}_getItemIndex(t){return this._items=t&&t.parentNode?U.find(".carousel-item",t.parentNode):[],this._items.indexOf(t)}_getItemByOrder(t,e){const i=t===K;return b(this._items,e,i,this._config.wrap)}_triggerSlideEvent(t,e){const i=this._getItemIndex(t),n=this._getItemIndex(U.findOne(".active.carousel-item",this._element));return P.trigger(this._element,"slide.bs.carousel",{relatedTarget:t,direction:e,from:n,to:i})}_setActiveIndicatorElement(t){if(this._indicatorsElement){const e=U.findOne(".active",this._indicatorsElement);e.classList.remove("active"),e.removeAttribute("aria-current");const i=U.find("[data-bs-target]",this._indicatorsElement);for(let e=0;e{P.trigger(this._element,"slid.bs.carousel",{relatedTarget:o,direction:u,from:s,to:r})};if(this._element.classList.contains("slide")){o.classList.add(h),d(o),n.classList.add(c),o.classList.add(c);const t=()=>{o.classList.remove(c,h),o.classList.add("active"),n.classList.remove("active",h,c),this._isSliding=!1,setTimeout(f,0)};this._queueCallback(t,n,!0)}else n.classList.remove("active"),o.classList.add("active"),this._isSliding=!1,f();a&&this.cycle()}_directionToOrder(t){return[Q,Y].includes(t)?p()?t===Y?X:K:t===Y?K:X:t}_orderToDirection(t){return[K,X].includes(t)?p()?t===X?Y:Q:t===X?Q:Y:t}static carouselInterface(t,e){const i=Z.getOrCreateInstance(t,e);let{_config:n}=i;"object"==typeof e&&(n={...n,...e});const s="string"==typeof e?e:n.slide;if("number"==typeof e)i.to(e);else if("string"==typeof s){if(void 0===i[s])throw new TypeError(`No method named "${s}"`);i[s]()}else n.interval&&n.ride&&(i.pause(),i.cycle())}static jQueryInterface(t){return this.each((function(){Z.carouselInterface(this,t)}))}static dataApiClickHandler(t){const e=i(this);if(!e||!e.classList.contains("carousel"))return;const n={...F.getDataAttributes(e),...F.getDataAttributes(this)},s=this.getAttribute("data-bs-slide-to");s&&(n.interval=!1),Z.carouselInterface(e,n),s&&Z.getInstance(e).to(s),t.preventDefault()}}P.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",Z.dataApiClickHandler),P.on(window,"load.bs.carousel.data-api",()=>{const t=U.find('[data-bs-ride="carousel"]');for(let e=0,i=t.length;et===this._element);null!==s&&o.length&&(this._selector=s,this._triggerArray.push(i))}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return J}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t,e=[];if(this._config.parent){const t=U.find(".collapse .collapse",this._config.parent);e=U.find(".show, .collapsing",this._config.parent).filter(e=>!t.includes(e))}const i=U.findOne(this._selector);if(e.length){const n=e.find(t=>i!==t);if(t=n?et.getInstance(n):null,t&&t._isTransitioning)return}if(P.trigger(this._element,"show.bs.collapse").defaultPrevented)return;e.forEach(e=>{i!==e&&et.getOrCreateInstance(e,{toggle:!1}).hide(),t||M.set(e,"bs.collapse",null)});const n=this._getDimension();this._element.classList.remove("collapse"),this._element.classList.add("collapsing"),this._element.style[n]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const s="scroll"+(n[0].toUpperCase()+n.slice(1));this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove("collapsing"),this._element.classList.add("collapse","show"),this._element.style[n]="",P.trigger(this._element,"shown.bs.collapse")},this._element,!0),this._element.style[n]=this._element[s]+"px"}hide(){if(this._isTransitioning||!this._isShown())return;if(P.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=this._element.getBoundingClientRect()[t]+"px",d(this._element),this._element.classList.add("collapsing"),this._element.classList.remove("collapse","show");const e=this._triggerArray.length;for(let t=0;t{this._isTransitioning=!1,this._element.classList.remove("collapsing"),this._element.classList.add("collapse"),P.trigger(this._element,"hidden.bs.collapse")},this._element,!0)}_isShown(t=this._element){return t.classList.contains("show")}_getConfig(t){return(t={...J,...F.getDataAttributes(this._element),...t}).toggle=Boolean(t.toggle),t.parent=o(t.parent),r("collapse",t,tt),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=U.find(".collapse .collapse",this._config.parent);U.find('[data-bs-toggle="collapse"]',this._config.parent).filter(e=>!t.includes(e)).forEach(t=>{const e=i(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))})}_addAriaAndCollapsedClass(t,e){t.length&&t.forEach(t=>{e?t.classList.remove("collapsed"):t.classList.add("collapsed"),t.setAttribute("aria-expanded",e)})}static jQueryInterface(t){return this.each((function(){const e={};"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1);const i=et.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}P.on(document,"click.bs.collapse.data-api",'[data-bs-toggle="collapse"]',(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const i=e(this);U.find(i).forEach(t=>{et.getOrCreateInstance(t,{toggle:!1}).toggle()})})),m(et);var it="top",nt="bottom",st="right",ot="left",rt=[it,nt,st,ot],at=rt.reduce((function(t,e){return t.concat([e+"-start",e+"-end"])}),[]),lt=[].concat(rt,["auto"]).reduce((function(t,e){return t.concat([e,e+"-start",e+"-end"])}),[]),ct=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function ht(t){return t?(t.nodeName||"").toLowerCase():null}function dt(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function ut(t){return t instanceof dt(t).Element||t instanceof Element}function ft(t){return t instanceof dt(t).HTMLElement||t instanceof HTMLElement}function pt(t){return"undefined"!=typeof ShadowRoot&&(t instanceof dt(t).ShadowRoot||t instanceof ShadowRoot)}var mt={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];ft(s)&&ht(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}),{});ft(n)&&ht(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function gt(t){return t.split("-")[0]}var _t=Math.round;function bt(t,e){void 0===e&&(e=!1);var i=t.getBoundingClientRect(),n=1,s=1;return ft(t)&&e&&(n=i.width/t.offsetWidth||1,s=i.height/t.offsetHeight||1),{width:_t(i.width/n),height:_t(i.height/s),top:_t(i.top/s),right:_t(i.right/n),bottom:_t(i.bottom/s),left:_t(i.left/n),x:_t(i.left/n),y:_t(i.top/s)}}function vt(t){var e=bt(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 yt(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&pt(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function wt(t){return dt(t).getComputedStyle(t)}function Et(t){return["table","td","th"].indexOf(ht(t))>=0}function At(t){return((ut(t)?t.ownerDocument:t.document)||window.document).documentElement}function Tt(t){return"html"===ht(t)?t:t.assignedSlot||t.parentNode||(pt(t)?t.host:null)||At(t)}function Ot(t){return ft(t)&&"fixed"!==wt(t).position?t.offsetParent:null}function Ct(t){for(var e=dt(t),i=Ot(t);i&&Et(i)&&"static"===wt(i).position;)i=Ot(i);return i&&("html"===ht(i)||"body"===ht(i)&&"static"===wt(i).position)?e:i||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&ft(t)&&"fixed"===wt(t).position)return null;for(var i=Tt(t);ft(i)&&["html","body"].indexOf(ht(i))<0;){var n=wt(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 kt(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}var Lt=Math.max,xt=Math.min,Dt=Math.round;function St(t,e,i){return Lt(t,xt(e,i))}function Nt(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function It(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}var Pt={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=gt(i.placement),l=kt(a),c=[ot,st].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Nt("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:It(t,rt))}(s.padding,i),d=vt(o),u="y"===l?it:ot,f="y"===l?nt:st,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=Ct(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,E=St(v,w,y),A=l;i.modifiersData[n]=((e={})[A]=E,e.centerOffset=E-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)))&&yt(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},jt={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Mt(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.offsets,r=t.position,a=t.gpuAcceleration,l=t.adaptive,c=t.roundOffsets,h=!0===c?function(t){var e=t.x,i=t.y,n=window.devicePixelRatio||1;return{x:Dt(Dt(e*n)/n)||0,y:Dt(Dt(i*n)/n)||0}}(o):"function"==typeof c?c(o):o,d=h.x,u=void 0===d?0:d,f=h.y,p=void 0===f?0:f,m=o.hasOwnProperty("x"),g=o.hasOwnProperty("y"),_=ot,b=it,v=window;if(l){var y=Ct(i),w="clientHeight",E="clientWidth";y===dt(i)&&"static"!==wt(y=At(i)).position&&(w="scrollHeight",E="scrollWidth"),y=y,s===it&&(b=nt,p-=y[w]-n.height,p*=a?1:-1),s===ot&&(_=st,u-=y[E]-n.width,u*=a?1:-1)}var A,T=Object.assign({position:r},l&&jt);return a?Object.assign({},T,((A={})[b]=g?"0":"",A[_]=m?"0":"",A.transform=(v.devicePixelRatio||1)<2?"translate("+u+"px, "+p+"px)":"translate3d("+u+"px, "+p+"px, 0)",A)):Object.assign({},T,((e={})[b]=g?p+"px":"",e[_]=m?u+"px":"",e.transform="",e))}var Ht={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:gt(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,Mt(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,Mt(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:{}},Bt={passive:!0},Rt={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=dt(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,Bt)})),a&&l.addEventListener("resize",i.update,Bt),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,Bt)})),a&&l.removeEventListener("resize",i.update,Bt)}},data:{}},Wt={left:"right",right:"left",bottom:"top",top:"bottom"};function zt(t){return t.replace(/left|right|bottom|top/g,(function(t){return Wt[t]}))}var qt={start:"end",end:"start"};function Ft(t){return t.replace(/start|end/g,(function(t){return qt[t]}))}function Ut(t){var e=dt(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function $t(t){return bt(At(t)).left+Ut(t).scrollLeft}function Vt(t){var e=wt(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Kt(t,e){var i;void 0===e&&(e=[]);var n=function t(e){return["html","body","#document"].indexOf(ht(e))>=0?e.ownerDocument.body:ft(e)&&Vt(e)?e:t(Tt(e))}(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=dt(n),r=s?[o].concat(o.visualViewport||[],Vt(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(Kt(Tt(r)))}function Xt(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Yt(t,e){return"viewport"===e?Xt(function(t){var e=dt(t),i=At(t),n=e.visualViewport,s=i.clientWidth,o=i.clientHeight,r=0,a=0;return n&&(s=n.width,o=n.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(r=n.offsetLeft,a=n.offsetTop)),{width:s,height:o,x:r+$t(t),y:a}}(t)):ft(e)?function(t){var e=bt(t);return e.top=e.top+t.clientTop,e.left=e.left+t.clientLeft,e.bottom=e.top+t.clientHeight,e.right=e.left+t.clientWidth,e.width=t.clientWidth,e.height=t.clientHeight,e.x=e.left,e.y=e.top,e}(e):Xt(function(t){var e,i=At(t),n=Ut(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=Lt(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=Lt(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+$t(t),l=-n.scrollTop;return"rtl"===wt(s||i).direction&&(a+=Lt(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(At(t)))}function Qt(t){return t.split("-")[1]}function Gt(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?gt(s):null,r=s?Qt(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case it:e={x:a,y:i.y-n.height};break;case nt:e={x:a,y:i.y+i.height};break;case st:e={x:i.x+i.width,y:l};break;case ot:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?kt(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case"start":e[c]=e[c]-(i[h]/2-n[h]/2);break;case"end":e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function Zt(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.boundary,r=void 0===o?"clippingParents":o,a=i.rootBoundary,l=void 0===a?"viewport":a,c=i.elementContext,h=void 0===c?"popper":c,d=i.altBoundary,u=void 0!==d&&d,f=i.padding,p=void 0===f?0:f,m=Nt("number"!=typeof p?p:It(p,rt)),g="popper"===h?"reference":"popper",_=t.elements.reference,b=t.rects.popper,v=t.elements[u?g:h],y=function(t,e,i){var n="clippingParents"===e?function(t){var e=Kt(Tt(t)),i=["absolute","fixed"].indexOf(wt(t).position)>=0&&ft(t)?Ct(t):t;return ut(i)?e.filter((function(t){return ut(t)&&yt(t,i)&&"body"!==ht(t)})):[]}(t):[].concat(e),s=[].concat(n,[i]),o=s[0],r=s.reduce((function(e,i){var n=Yt(t,i);return e.top=Lt(n.top,e.top),e.right=xt(n.right,e.right),e.bottom=xt(n.bottom,e.bottom),e.left=Lt(n.left,e.left),e}),Yt(t,o));return r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}(ut(v)?v:v.contextElement||At(t.elements.popper),r,l),w=bt(_),E=Gt({reference:w,element:b,strategy:"absolute",placement:s}),A=Xt(Object.assign({},b,E)),T="popper"===h?A:w,O={top:y.top-T.top+m.top,bottom:T.bottom-y.bottom+m.bottom,left:y.left-T.left+m.left,right:T.right-y.right+m.right},C=t.modifiersData.offset;if("popper"===h&&C){var k=C[s];Object.keys(O).forEach((function(t){var e=[st,nt].indexOf(t)>=0?1:-1,i=[it,nt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function Jt(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?lt:l,h=Qt(n),d=h?a?at:at.filter((function(t){return Qt(t)===h})):rt,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]=Zt(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[gt(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}var te={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,_=gt(g),b=l||(_!==g&&p?function(t){if("auto"===gt(t))return[];var e=zt(t);return[Ft(t),e,Ft(e)]}(g):[zt(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat("auto"===gt(i)?Jt(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,E=new Map,A=!0,T=v[0],O=0;O=0,D=x?"width":"height",S=Zt(e,{placement:C,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),N=x?L?st:ot:L?nt:it;y[D]>w[D]&&(N=zt(N));var I=zt(N),P=[];if(o&&P.push(S[k]<=0),a&&P.push(S[N]<=0,S[I]<=0),P.every((function(t){return t}))){T=C,A=!1;break}E.set(C,P)}if(A)for(var j=function(t){var e=v.find((function(e){var i=E.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"!==j(M);M--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function ee(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 ie(t){return[it,st,nt,ot].some((function(e){return t[e]>=0}))}var ne={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=Zt(e,{elementContext:"reference"}),a=Zt(e,{altBoundary:!0}),l=ee(r,n),c=ee(a,s,o),h=ie(l),d=ie(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})}},se={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=lt.reduce((function(t,i){return t[i]=function(t,e,i){var n=gt(t),s=[ot,it].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,[ot,st].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}},oe={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=Gt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},re={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=Zt(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=gt(e.placement),b=Qt(e.placement),v=!b,y=kt(_),w="x"===y?"y":"x",E=e.modifiersData.popperOffsets,A=e.rects.reference,T=e.rects.popper,O="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,C={x:0,y:0};if(E){if(o||a){var k="y"===y?it:ot,L="y"===y?nt:st,x="y"===y?"height":"width",D=E[y],S=E[y]+g[k],N=E[y]-g[L],I=f?-T[x]/2:0,P="start"===b?A[x]:T[x],j="start"===b?-T[x]:-A[x],M=e.elements.arrow,H=f&&M?vt(M):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},R=B[k],W=B[L],z=St(0,A[x],H[x]),q=v?A[x]/2-I-z-R-O:P-z-R-O,F=v?-A[x]/2+I+z+W+O:j+z+W+O,U=e.elements.arrow&&Ct(e.elements.arrow),$=U?"y"===y?U.clientTop||0:U.clientLeft||0:0,V=e.modifiersData.offset?e.modifiersData.offset[e.placement][y]:0,K=E[y]+q-V-$,X=E[y]+F-V;if(o){var Y=St(f?xt(S,K):S,D,f?Lt(N,X):N);E[y]=Y,C[y]=Y-D}if(a){var Q="x"===y?it:ot,G="x"===y?nt:st,Z=E[w],J=Z+g[Q],tt=Z-g[G],et=St(f?xt(J,K):J,Z,f?Lt(tt,X):tt);E[w]=et,C[w]=et-Z}}e.modifiersData[n]=C}},requiresIfExists:["offset"]};function ae(t,e,i){void 0===i&&(i=!1);var n,s,o=ft(e),r=ft(e)&&function(t){var e=t.getBoundingClientRect(),i=e.width/t.offsetWidth||1,n=e.height/t.offsetHeight||1;return 1!==i||1!==n}(e),a=At(e),l=bt(t,r),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==ht(e)||Vt(a))&&(c=(n=e)!==dt(n)&&ft(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Ut(n)),ft(e)?((h=bt(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=$t(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}var le={placement:"bottom",modifiers:[],strategy:"absolute"};function ce(){for(var t=arguments.length,e=new Array(t),i=0;iP.on(t,"mouseover",h)),this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add("show"),this._element.classList.add("show"),P.trigger(this._element,"shown.bs.dropdown",t)}hide(){if(l(this._element)||!this._isShown(this._menu))return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){P.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented||("ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(t=>P.off(t,"mouseover",h)),this._popper&&this._popper.destroy(),this._menu.classList.remove("show"),this._element.classList.remove("show"),this._element.setAttribute("aria-expanded","false"),F.removeDataAttribute(this._menu,"popper"),P.trigger(this._element,"hidden.bs.dropdown",t))}_getConfig(t){if(t={...this.constructor.Default,...F.getDataAttributes(this._element),...t},r("dropdown",t,this.constructor.DefaultType),"object"==typeof t.reference&&!s(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError("dropdown".toUpperCase()+': Option "reference" provided type "object" without a required "getBoundingClientRect" method.');return t}_createPopper(t){if(void 0===pe)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=t:s(this._config.reference)?e=o(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const i=this._getPopperConfig(),n=i.modifiers.find(t=>"applyStyles"===t.name&&!1===t.enabled);this._popper=fe(e,this._menu,i),n&&F.setDataAttribute(this._menu,"popper","static")}_isShown(t=this._element){return t.classList.contains("show")}_getMenuElement(){return U.next(this._element,".dropdown-menu")[0]}_getPlacement(){const t=this._element.parentNode;if(t.classList.contains("dropend"))return ye;if(t.classList.contains("dropstart"))return we;const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?_e:ge:e?ve:be}_detectNavbar(){return null!==this._element.closest(".navbar")}_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}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return"static"===this._config.display&&(t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=U.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(a);i.length&&b(i,e,"ArrowDown"===t,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Te.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(t&&(2===t.button||"keyup"===t.type&&"Tab"!==t.key))return;const e=U.find('[data-bs-toggle="dropdown"]');for(let i=0,n=e.length;ie+t),this._setElementAttributes(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top","paddingRight",e=>e+t),this._setElementAttributes(".sticky-top","marginRight",e=>e-t)}_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)[e];t.style[e]=i(Number.parseFloat(s))+"px"})}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,"paddingRight"),this._resetElementAttributes(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top","paddingRight"),this._resetElementAttributes(".sticky-top","marginRight")}_saveInitialAttribute(t,e){const i=t.style[e];i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,t=>{const i=F.getDataAttribute(t,e);void 0===i?t.style.removeProperty(e):(F.removeDataAttribute(t,e),t.style[e]=i)})}_applyManipulationCallback(t,e){s(t)?e(t):U.find(t,this._element).forEach(e)}isOverflowing(){return this.getWidth()>0}}const Ce={className:"modal-backdrop",isVisible:!0,isAnimated:!1,rootElement:"body",clickCallback:null},ke={className:"string",isVisible:"boolean",isAnimated:"boolean",rootElement:"(element|string)",clickCallback:"(function|null)"};class Le{constructor(t){this._config=this._getConfig(t),this._isAppended=!1,this._element=null}show(t){this._config.isVisible?(this._append(),this._config.isAnimated&&d(this._getElement()),this._getElement().classList.add("show"),this._emulateAnimation(()=>{g(t)})):g(t)}hide(t){this._config.isVisible?(this._getElement().classList.remove("show"),this._emulateAnimation(()=>{this.dispose(),g(t)})):g(t)}_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}_getConfig(t){return(t={...Ce,..."object"==typeof t?t:{}}).rootElement=o(t.rootElement),r("backdrop",t,ke),t}_append(){this._isAppended||(this._config.rootElement.append(this._getElement()),P.on(this._getElement(),"mousedown.bs.backdrop",()=>{g(this._config.clickCallback)}),this._isAppended=!0)}dispose(){this._isAppended&&(P.off(this._element,"mousedown.bs.backdrop"),this._element.remove(),this._isAppended=!1)}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const xe={trapElement:null,autofocus:!0},De={trapElement:"element",autofocus:"boolean"};class Se{constructor(t){this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}activate(){const{trapElement:t,autofocus:e}=this._config;this._isActive||(e&&t.focus(),P.off(document,".bs.focustrap"),P.on(document,"focusin.bs.focustrap",t=>this._handleFocusin(t)),P.on(document,"keydown.tab.bs.focustrap",t=>this._handleKeydown(t)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,P.off(document,".bs.focustrap"))}_handleFocusin(t){const{target:e}=t,{trapElement:i}=this._config;if(e===document||e===i||i.contains(e))return;const n=U.focusableChildren(i);0===n.length?i.focus():"backward"===this._lastTabNavDirection?n[n.length-1].focus():n[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?"backward":"forward")}_getConfig(t){return t={...xe,..."object"==typeof t?t:{}},r("focustrap",t,De),t}}const Ne={backdrop:!0,keyboard:!0,focus:!0},Ie={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean"};class Pe extends H{constructor(t,e){super(t),this._config=this._getConfig(e),this._dialog=U.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollBar=new Oe}static get Default(){return Ne}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||P.trigger(this._element,"show.bs.modal",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isAnimated()&&(this._isTransitioning=!0),this._scrollBar.hide(),document.body.classList.add("modal-open"),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),P.on(this._dialog,"mousedown.dismiss.bs.modal",()=>{P.one(this._element,"mouseup.dismiss.bs.modal",t=>{t.target===this._element&&(this._ignoreBackdropClick=!0)})}),this._showBackdrop(()=>this._showElement(t)))}hide(){if(!this._isShown||this._isTransitioning)return;if(P.trigger(this._element,"hide.bs.modal").defaultPrevented)return;this._isShown=!1;const t=this._isAnimated();t&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),this._focustrap.deactivate(),this._element.classList.remove("show"),P.off(this._element,"click.dismiss.bs.modal"),P.off(this._dialog,"mousedown.dismiss.bs.modal"),this._queueCallback(()=>this._hideModal(),this._element,t)}dispose(){[window,this._dialog].forEach(t=>P.off(t,".bs.modal")),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Le({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Se({trapElement:this._element})}_getConfig(t){return t={...Ne,...F.getDataAttributes(this._element),..."object"==typeof t?t:{}},r("modal",t,Ie),t}_showElement(t){const e=this._isAnimated(),i=U.findOne(".modal-body",this._dialog);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||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,i&&(i.scrollTop=0),e&&d(this._element),this._element.classList.add("show"),this._queueCallback(()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,P.trigger(this._element,"shown.bs.modal",{relatedTarget:t})},this._dialog,e)}_setEscapeEvent(){this._isShown?P.on(this._element,"keydown.dismiss.bs.modal",t=>{this._config.keyboard&&"Escape"===t.key?(t.preventDefault(),this.hide()):this._config.keyboard||"Escape"!==t.key||this._triggerBackdropTransition()}):P.off(this._element,"keydown.dismiss.bs.modal")}_setResizeEvent(){this._isShown?P.on(window,"resize.bs.modal",()=>this._adjustDialog()):P.off(window,"resize.bs.modal")}_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("modal-open"),this._resetAdjustments(),this._scrollBar.reset(),P.trigger(this._element,"hidden.bs.modal")})}_showBackdrop(t){P.on(this._element,"click.dismiss.bs.modal",t=>{this._ignoreBackdropClick?this._ignoreBackdropClick=!1:t.target===t.currentTarget&&(!0===this._config.backdrop?this.hide():"static"===this._config.backdrop&&this._triggerBackdropTransition())}),this._backdrop.show(t)}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(P.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const{classList:t,scrollHeight:e,style:i}=this._element,n=e>document.documentElement.clientHeight;!n&&"hidden"===i.overflowY||t.contains("modal-static")||(n||(i.overflowY="hidden"),t.add("modal-static"),this._queueCallback(()=>{t.remove("modal-static"),n||this._queueCallback(()=>{i.overflowY=""},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;(!i&&t&&!p()||i&&!t&&p())&&(this._element.style.paddingLeft=e+"px"),(i&&!t&&!p()||!i&&t&&p())&&(this._element.style.paddingRight=e+"px")}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Pe.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}P.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=i(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),P.one(e,"show.bs.modal",t=>{t.defaultPrevented||P.one(e,"hidden.bs.modal",()=>{a(this)&&this.focus()})}),Pe.getOrCreateInstance(e).toggle(this)})),B(Pe),m(Pe);const je={backdrop:!0,keyboard:!0,scroll:!1},Me={backdrop:"boolean",keyboard:"boolean",scroll:"boolean"};class He extends H{constructor(t,e){super(t),this._config=this._getConfig(e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get NAME(){return"offcanvas"}static get Default(){return je}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||P.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._element.style.visibility="visible",this._backdrop.show(),this._config.scroll||(new Oe).hide(),this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add("show"),this._queueCallback(()=>{this._config.scroll||this._focustrap.activate(),P.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})},this._element,!0))}hide(){this._isShown&&(P.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.remove("show"),this._backdrop.hide(),this._queueCallback(()=>{this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._element.style.visibility="hidden",this._config.scroll||(new Oe).reset(),P.trigger(this._element,"hidden.bs.offcanvas")},this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_getConfig(t){return t={...je,...F.getDataAttributes(this._element),..."object"==typeof t?t:{}},r("offcanvas",t,Me),t}_initializeBackDrop(){return new Le({className:"offcanvas-backdrop",isVisible:this._config.backdrop,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:()=>this.hide()})}_initializeFocusTrap(){return new Se({trapElement:this._element})}_addEventListeners(){P.on(this._element,"keydown.dismiss.bs.offcanvas",t=>{this._config.keyboard&&"Escape"===t.key&&this.hide()})}static jQueryInterface(t){return this.each((function(){const e=He.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)}}))}}P.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=i(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;P.one(e,"hidden.bs.offcanvas",()=>{a(this)&&this.focus()});const n=U.findOne(".offcanvas.show");n&&n!==e&&He.getInstance(n).hide(),He.getOrCreateInstance(e).toggle(this)})),P.on(window,"load.bs.offcanvas.data-api",()=>U.find(".offcanvas.show").forEach(t=>He.getOrCreateInstance(t).show())),B(He),m(He);const Be=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Re=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/i,We=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,ze=(t,e)=>{const i=t.nodeName.toLowerCase();if(e.includes(i))return!Be.has(i)||Boolean(Re.test(t.nodeValue)||We.test(t.nodeValue));const n=e.filter(t=>t instanceof RegExp);for(let t=0,e=n.length;t{ze(t,a)||i.removeAttribute(t.nodeName)})}return n.body.innerHTML}const Fe=new Set(["sanitize","allowList","sanitizeFn"]),Ue={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(array|string|function)",container:"(string|element|boolean)",fallbackPlacements:"array",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",allowList:"object",popperConfig:"(null|object|function)"},$e={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},Ve={animation:!0,template:'',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:[0,0],container:!1,fallbackPlacements:["top","right","bottom","left"],boundary:"clippingParents",customClass:"",sanitize:!0,sanitizeFn:null,allowList:{"*":["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:[]},popperConfig:null},Ke={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"};class Xe extends H{constructor(t,e){if(void 0===pe)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this._config=this._getConfig(e),this.tip=null,this._setListeners()}static get Default(){return Ve}static get NAME(){return"tooltip"}static get Event(){return Ke}static get DefaultType(){return Ue}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(t){if(this._isEnabled)if(t){const e=this._initializeOnDelegatedTarget(t);e._activeTrigger.click=!e._activeTrigger.click,e._isWithActiveTrigger()?e._enter(null,e):e._leave(null,e)}else{if(this.getTipElement().classList.contains("show"))return void this._leave(null,this);this._enter(null,this)}}dispose(){clearTimeout(this._timeout),P.off(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this.tip&&this.tip.remove(),this._popper&&this._popper.destroy(),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=P.trigger(this._element,this.constructor.Event.SHOW),e=c(this._element),i=null===e?this._element.ownerDocument.documentElement.contains(this._element):e.contains(this._element);if(t.defaultPrevented||!i)return;const n=this.getTipElement(),s=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME);n.setAttribute("id",s),this._element.setAttribute("aria-describedby",s),this._config.animation&&n.classList.add("fade");const o="function"==typeof this._config.placement?this._config.placement.call(this,n,this._element):this._config.placement,r=this._getAttachment(o);this._addAttachmentClass(r);const{container:a}=this._config;M.set(n,this.constructor.DATA_KEY,this),this._element.ownerDocument.documentElement.contains(this.tip)||(a.append(n),P.trigger(this._element,this.constructor.Event.INSERTED)),this._popper?this._popper.update():this._popper=fe(this._element,n,this._getPopperConfig(r)),n.classList.add("show");const l=this._resolvePossibleFunction(this._config.customClass);l&&n.classList.add(...l.split(" ")),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(t=>{P.on(t,"mouseover",h)});const d=this.tip.classList.contains("fade");this._queueCallback(()=>{const t=this._hoverState;this._hoverState=null,P.trigger(this._element,this.constructor.Event.SHOWN),"out"===t&&this._leave(null,this)},this.tip,d)}hide(){if(!this._popper)return;const t=this.getTipElement();if(P.trigger(this._element,this.constructor.Event.HIDE).defaultPrevented)return;t.classList.remove("show"),"ontouchstart"in document.documentElement&&[].concat(...document.body.children).forEach(t=>P.off(t,"mouseover",h)),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1;const e=this.tip.classList.contains("fade");this._queueCallback(()=>{this._isWithActiveTrigger()||("show"!==this._hoverState&&t.remove(),this._cleanTipClass(),this._element.removeAttribute("aria-describedby"),P.trigger(this._element,this.constructor.Event.HIDDEN),this._popper&&(this._popper.destroy(),this._popper=null))},this.tip,e),this._hoverState=""}update(){null!==this._popper&&this._popper.update()}isWithContent(){return Boolean(this.getTitle())}getTipElement(){if(this.tip)return this.tip;const t=document.createElement("div");t.innerHTML=this._config.template;const e=t.children[0];return this.setContent(e),e.classList.remove("fade","show"),this.tip=e,this.tip}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".tooltip-inner")}_sanitizeAndSetContent(t,e,i){const n=U.findOne(i,t);e||!n?this.setElementContent(n,e):n.remove()}setElementContent(t,e){if(null!==t)return s(e)?(e=o(e),void(this._config.html?e.parentNode!==t&&(t.innerHTML="",t.append(e)):t.textContent=e.textContent)):void(this._config.html?(this._config.sanitize&&(e=qe(e,this._config.allowList,this._config.sanitizeFn)),t.innerHTML=e):t.textContent=e)}getTitle(){const t=this._element.getAttribute("data-bs-original-title")||this._config.title;return this._resolvePossibleFunction(t)}updateAttachment(t){return"right"===t?"end":"left"===t?"start":t}_initializeOnDelegatedTarget(t,e){return e||this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_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"function"==typeof t?t.call(this._element):t}_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:"onChange",enabled:!0,phase:"afterWrite",fn:t=>this._handlePopperPlacementChange(t)}],onFirstUpdate:t=>{t.options.placement!==t.placement&&this._handlePopperPlacementChange(t)}};return{...e,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_addAttachmentClass(t){this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(t)}`)}_getAttachment(t){return $e[t.toUpperCase()]}_setListeners(){this._config.trigger.split(" ").forEach(t=>{if("click"===t)P.on(this._element,this.constructor.Event.CLICK,this._config.selector,t=>this.toggle(t));else if("manual"!==t){const e="hover"===t?this.constructor.Event.MOUSEENTER:this.constructor.Event.FOCUSIN,i="hover"===t?this.constructor.Event.MOUSELEAVE:this.constructor.Event.FOCUSOUT;P.on(this._element,e,this._config.selector,t=>this._enter(t)),P.on(this._element,i,this._config.selector,t=>this._leave(t))}}),this._hideModalHandler=()=>{this._element&&this.hide()},P.on(this._element.closest(".modal"),"hide.bs.modal",this._hideModalHandler),this._config.selector?this._config={...this._config,trigger:"manual",selector:""}:this._fixTitle()}_fixTitle(){const t=this._element.getAttribute("title"),e=typeof this._element.getAttribute("data-bs-original-title");(t||"string"!==e)&&(this._element.setAttribute("data-bs-original-title",t||""),!t||this._element.getAttribute("aria-label")||this._element.textContent||this._element.setAttribute("aria-label",t),this._element.setAttribute("title",""))}_enter(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e.getTipElement().classList.contains("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e._config.delay&&e._config.delay.show?e._timeout=setTimeout(()=>{"show"===e._hoverState&&e.show()},e._config.delay.show):e.show())}_leave(t,e){e=this._initializeOnDelegatedTarget(t,e),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=e._element.contains(t.relatedTarget)),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e._config.delay&&e._config.delay.hide?e._timeout=setTimeout(()=>{"out"===e._hoverState&&e.hide()},e._config.delay.hide):e.hide())}_isWithActiveTrigger(){for(const t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1}_getConfig(t){const e=F.getDataAttributes(this._element);return Object.keys(e).forEach(t=>{Fe.has(t)&&delete e[t]}),(t={...this.constructor.Default,...e,..."object"==typeof t&&t?t:{}}).container=!1===t.container?document.body:o(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()),r("tooltip",t,this.constructor.DefaultType),t.sanitize&&(t.template=qe(t.template,t.allowList,t.sanitizeFn)),t}_getDelegateConfig(){const t={};for(const e in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t}_cleanTipClass(){const t=this.getTipElement(),e=new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`,"g"),i=t.getAttribute("class").match(e);null!==i&&i.length>0&&i.map(t=>t.trim()).forEach(e=>t.classList.remove(e))}_getBasicClassPrefix(){return"bs-tooltip"}_handlePopperPlacementChange(t){const{state:e}=t;e&&(this.tip=e.elements.popper,this._cleanTipClass(),this._addAttachmentClass(this._getAttachment(e.placement)))}static jQueryInterface(t){return this.each((function(){const e=Xe.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(Xe);const Ye={...Xe.Default,placement:"right",offset:[0,8],trigger:"click",content:"",template:''},Qe={...Xe.DefaultType,content:"(string|element|function)"},Ge={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"};class Ze extends Xe{static get Default(){return Ye}static get NAME(){return"popover"}static get Event(){return Ge}static get DefaultType(){return Qe}isWithContent(){return this.getTitle()||this._getContent()}setContent(t){this._sanitizeAndSetContent(t,this.getTitle(),".popover-header"),this._sanitizeAndSetContent(t,this._getContent(),".popover-body")}_getContent(){return this._resolvePossibleFunction(this._config.content)}_getBasicClassPrefix(){return"bs-popover"}static jQueryInterface(t){return this.each((function(){const e=Ze.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(Ze);const Je={offset:10,method:"auto",target:""},ti={offset:"number",method:"string",target:"(string|element)"},ei=".nav-link, .list-group-item, .dropdown-item";class ii extends H{constructor(t,e){super(t),this._scrollElement="BODY"===this._element.tagName?window:this._element,this._config=this._getConfig(e),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,P.on(this._scrollElement,"scroll.bs.scrollspy",()=>this._process()),this.refresh(),this._process()}static get Default(){return Je}static get NAME(){return"scrollspy"}refresh(){const t=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?t:this._config.method,n="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),U.find(ei,this._config.target).map(t=>{const s=e(t),o=s?U.findOne(s):null;if(o){const t=o.getBoundingClientRect();if(t.width||t.height)return[F[i](o).top+n,s]}return null}).filter(t=>t).sort((t,e)=>t[0]-e[0]).forEach(t=>{this._offsets.push(t[0]),this._targets.push(t[1])})}dispose(){P.off(this._scrollElement,".bs.scrollspy"),super.dispose()}_getConfig(t){return(t={...Je,...F.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}}).target=o(t.target)||document.documentElement,r("scrollspy",t,ti),t}_getScrollTop(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop}_getScrollHeight(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}_getOffsetHeight(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height}_process(){const t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),i=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=i){const t=this._targets[this._targets.length-1];this._activeTarget!==t&&this._activate(t)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(let e=this._offsets.length;e--;)this._activeTarget!==this._targets[e]&&t>=this._offsets[e]&&(void 0===this._offsets[e+1]||t`${e}[data-bs-target="${t}"],${e}[href="${t}"]`),i=U.findOne(e.join(","),this._config.target);i.classList.add("active"),i.classList.contains("dropdown-item")?U.findOne(".dropdown-toggle",i.closest(".dropdown")).classList.add("active"):U.parents(i,".nav, .list-group").forEach(t=>{U.prev(t,".nav-link, .list-group-item").forEach(t=>t.classList.add("active")),U.prev(t,".nav-item").forEach(t=>{U.children(t,".nav-link").forEach(t=>t.classList.add("active"))})}),P.trigger(this._scrollElement,"activate.bs.scrollspy",{relatedTarget:t})}_clear(){U.find(ei,this._config.target).filter(t=>t.classList.contains("active")).forEach(t=>t.classList.remove("active"))}static jQueryInterface(t){return this.each((function(){const e=ii.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}P.on(window,"load.bs.scrollspy.data-api",()=>{U.find('[data-bs-spy="scroll"]').forEach(t=>new ii(t))}),m(ii);class ni extends H{static get NAME(){return"tab"}show(){if(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&this._element.classList.contains("active"))return;let t;const e=i(this._element),n=this._element.closest(".nav, .list-group");if(n){const e="UL"===n.nodeName||"OL"===n.nodeName?":scope > li > .active":".active";t=U.find(e,n),t=t[t.length-1]}const s=t?P.trigger(t,"hide.bs.tab",{relatedTarget:this._element}):null;if(P.trigger(this._element,"show.bs.tab",{relatedTarget:t}).defaultPrevented||null!==s&&s.defaultPrevented)return;this._activate(this._element,n);const o=()=>{P.trigger(t,"hidden.bs.tab",{relatedTarget:this._element}),P.trigger(this._element,"shown.bs.tab",{relatedTarget:t})};e?this._activate(e,e.parentNode,o):o()}_activate(t,e,i){const n=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?U.children(e,".active"):U.find(":scope > li > .active",e))[0],s=i&&n&&n.classList.contains("fade"),o=()=>this._transitionComplete(t,n,i);n&&s?(n.classList.remove("show"),this._queueCallback(o,t,!0)):o()}_transitionComplete(t,e,i){if(e){e.classList.remove("active");const t=U.findOne(":scope > .dropdown-menu .active",e.parentNode);t&&t.classList.remove("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}t.classList.add("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d(t),t.classList.contains("fade")&&t.classList.add("show");let n=t.parentNode;if(n&&"LI"===n.nodeName&&(n=n.parentNode),n&&n.classList.contains("dropdown-menu")){const e=t.closest(".dropdown");e&&U.find(".dropdown-toggle",e).forEach(t=>t.classList.add("active")),t.setAttribute("aria-expanded",!0)}i&&i()}static jQueryInterface(t){return this.each((function(){const e=ni.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}P.on(document,"click.bs.tab.data-api",'[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||ni.getOrCreateInstance(this).show()})),m(ni);const si={animation:"boolean",autohide:"boolean",delay:"number"},oi={animation:!0,autohide:!0,delay:5e3};class ri extends H{constructor(t,e){super(t),this._config=this._getConfig(e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get DefaultType(){return si}static get Default(){return oi}static get NAME(){return"toast"}show(){P.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove("hide"),d(this._element),this._element.classList.add("show"),this._element.classList.add("showing"),this._queueCallback(()=>{this._element.classList.remove("showing"),P.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()},this._element,this._config.animation))}hide(){this._element.classList.contains("show")&&(P.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add("showing"),this._queueCallback(()=>{this._element.classList.add("hide"),this._element.classList.remove("showing"),this._element.classList.remove("show"),P.trigger(this._element,"hidden.bs.toast")},this._element,this._config.animation)))}dispose(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),super.dispose()}_getConfig(t){return t={...oi,...F.getDataAttributes(this._element),..."object"==typeof t&&t?t:{}},r("toast",t,this.constructor.DefaultType),t}_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(){P.on(this._element,"mouseover.bs.toast",t=>this._onInteraction(t,!0)),P.on(this._element,"mouseout.bs.toast",t=>this._onInteraction(t,!1)),P.on(this._element,"focusin.bs.toast",t=>this._onInteraction(t,!0)),P.on(this._element,"focusout.bs.toast",t=>this._onInteraction(t,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ri.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return B(ri),m(ri),{Alert:R,Button:W,Carousel:Z,Collapse:et,Dropdown:Te,Modal:Pe,Offcanvas:He,Popover:Ze,ScrollSpy:ii,Tab:ni,Toast:ri,Tooltip:Xe}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js.map b/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js.map new file mode 100644 index 00000000..a59a60b8 --- /dev/null +++ b/docs/deps/bootstrap-5.1.0/bootstrap.bundle.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/data.js","../../js/src/base-component.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/dom/manipulator.js","../../js/src/dom/selector-engine.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/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/math.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/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/listScrollParents.js","../../node_modules/@popperjs/core/lib/dom-utils/getScrollParent.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/getVariation.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/createPopper.js","../../node_modules/@popperjs/core/lib/utils/debounce.js","../../node_modules/@popperjs/core/lib/utils/mergeByName.js","../../node_modules/@popperjs/core/lib/utils/orderModifiers.js","../../node_modules/@popperjs/core/lib/popper-lite.js","../../node_modules/@popperjs/core/lib/popper.js","../../js/src/dropdown.js","../../js/src/util/scrollbar.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.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"],"names":["getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getSelectorFromElement","document","querySelector","getElementFromSelector","triggerTransitionEnd","dispatchEvent","Event","isElement","obj","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","toString","call","match","toLowerCase","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","getComputedStyle","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","parentNode","noop","reflow","offsetHeight","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","isRTL","dir","defineJQueryPlugin","plugin","callback","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","readyState","addEventListener","push","execute","executeAfterTransition","transitionElement","waitForTransition","emulatedDuration","transitionDuration","transitionDelay","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","getTransitionDurationFromElement","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","index","indexOf","listLength","Math","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","customEventsRegex","nativeEvents","Set","getUidEvent","uid","getEvent","findHandler","events","delegationSelector","uidEventList","i","len","event","originalHandler","normalizeParams","originalTypeEvent","delegationFn","delegation","typeEvent","getTypeEvent","has","addHandler","oneOff","wrapFn","relatedTarget","delegateTarget","this","handlers","previousFn","replace","domElements","querySelectorAll","EventHandler","off","type","apply","bootstrapDelegationHandler","bootstrapHandler","removeHandler","Boolean","on","one","inNamespace","isNamespace","elementEvent","namespace","storeElementEvent","handlerKey","removeNamespacedHandlers","slice","keyHandlers","trigger","args","isNative","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","evt","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","createEvent","initEvent","CustomEvent","cancelable","key","defineProperty","get","preventDefault","elementMap","Map","Data","set","instance","instanceMap","size","console","error","Array","from","remove","delete","BaseComponent","constructor","_element","DATA_KEY","dispose","EVENT_KEY","getOwnPropertyNames","propertyName","_queueCallback","isAnimated","[object Object]","getInstance","VERSION","Error","enableDismissTrigger","component","method","clickEvent","tagName","closest","getOrCreateInstance","Alert","close","_destroyElement","each","data","undefined","Button","toggle","setAttribute","normalizeData","val","normalizeDataKey","chr","button","Manipulator","setDataAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","dataset","filter","pureKey","charAt","getDataAttribute","offset","rect","getBoundingClientRect","top","pageYOffset","left","pageXOffset","position","offsetTop","offsetLeft","SelectorEngine","find","concat","Element","prototype","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","map","join","el","Default","interval","keyboard","slide","pause","wrap","touch","DefaultType","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","KEY_TO_DIRECTION","ArrowLeft","ArrowRight","Carousel","super","_items","_interval","_activeElement","_isPaused","_isSliding","touchTimeout","touchStartX","touchDeltaX","_config","_getConfig","_indicatorsElement","_touchSupported","navigator","maxTouchPoints","_pointerEvent","PointerEvent","_addEventListeners","_slide","nextWhenVisible","hidden","cycle","clearInterval","_updateInterval","setInterval","visibilityState","bind","to","activeIndex","_getItemIndex","order","_handleSwipe","absDeltax","abs","direction","_keydown","_addTouchEventListeners","start","pointerType","touches","clientX","move","end","clearTimeout","itemImg","e","add","_getItemByOrder","isNext","_triggerSlideEvent","eventDirectionName","targetIndex","fromIndex","_setActiveIndicatorElement","activeIndicator","indicators","parseInt","elementInterval","defaultInterval","directionOrOrder","_directionToOrder","activeElementIndex","nextElement","nextElementIndex","isCycling","directionalClassName","orderClassName","_orderToDirection","triggerSlidEvent","completeCallBack","action","ride","carouselInterface","slideIndex","dataApiClickHandler","carousels","parent","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElem","_selector","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activesData","actives","container","tempActiveData","elemActive","dimension","_getDimension","style","scrollSize","triggerArrayLength","selected","triggerArray","isOpen","bottom","right","basePlacements","variationPlacements","reduce","acc","placement","placements","modifierPhases","getNodeName","nodeName","getWindow","node","ownerDocument","defaultView","isHTMLElement","HTMLElement","isShadowRoot","applyStyles$1","enabled","phase","_ref","state","elements","styles","assign","effect","_ref2","initialStyles","popper","options","strategy","margin","arrow","reference","hasOwnProperty","attribute","requires","getBasePlacement","round","includeScale","scaleX","scaleY","width","offsetWidth","height","x","y","getLayoutRect","clientRect","rootNode","isSameNode","host","isTableElement","getDocumentElement","getParentNode","assignedSlot","getTrueOffsetParent","offsetParent","getOffsetParent","isFirefox","userAgent","currentNode","css","transform","perspective","contain","willChange","getContainingBlock","getMainAxisFromPlacement","within","mathMax","mathMin","mergePaddingObject","paddingObject","expandToHashMap","hashMap","arrow$1","_state$modifiersData$","arrowElement","popperOffsets","modifiersData","basePlacement","axis","padding","rects","toPaddingObject","arrowRect","minProp","maxProp","endDiff","startDiff","arrowOffsetParent","clientSize","clientHeight","clientWidth","centerToReference","center","axisProp","centerOffset","_options$element","requiresIfExists","unsetSides","mapToStyles","_Object$assign2","popperRect","offsets","gpuAcceleration","adaptive","roundOffsets","_ref3","dpr","devicePixelRatio","roundOffsetsByDPR","_ref3$x","_ref3$y","hasX","hasY","sideX","sideY","win","heightProp","widthProp","_Object$assign","commonStyles","computeStyles$1","_ref4","_options$gpuAccelerat","_options$adaptive","_options$roundOffsets","data-popper-placement","passive","eventListeners","_options$scroll","scroll","_options$resize","resize","scrollParents","scrollParent","update","hash","getOppositePlacement","matched","getOppositeVariationPlacement","getWindowScroll","scrollLeft","scrollTop","getWindowScrollBarX","isScrollParent","_getComputedStyle","overflow","overflowX","overflowY","listScrollParents","_element$ownerDocumen","getScrollParent","isBody","visualViewport","updatedList","rectToClientRect","getClientRectFromMixedType","clippingParent","html","getViewportRect","clientTop","clientLeft","getInnerBoundingClientRect","winScroll","scrollWidth","scrollHeight","getDocumentRect","getVariation","computeOffsets","variation","commonX","commonY","mainAxis","detectOverflow","_options","_options$placement","_options$boundary","boundary","_options$rootBoundary","rootBoundary","_options$elementConte","elementContext","_options$altBoundary","altBoundary","_options$padding","altContext","referenceElement","clippingClientRect","mainClippingParents","clippingParents","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","_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","data-popper-reference-hidden","data-popper-escaped","offset$1","_options$offset","invertDistance","skidding","distance","distanceAndSkiddingToXY","_data$state$placement","popperOffsets$1","preventOverflow$1","_options$tether","tether","_options$tetherOffset","tetherOffset","isBasePlacement","tetherOffsetValue","mainSide","altSide","additive","minLen","maxLen","arrowPaddingObject","arrowPaddingMin","arrowPaddingMax","arrowLen","minOffset","maxOffset","clientOffset","offsetModifierValue","tetherMin","tetherMax","preventedOffset","_mainSide","_altSide","_offset","_min","_max","_preventedOffset","getCompositeRect","elementOrVirtualElement","isFixed","isOffsetParentAnElement","offsetParentIsScaled","isElementScaled","DEFAULT_OPTIONS","modifiers","areValidElements","_len","arguments","_key","popperGenerator","generatorOptions","_generatorOptions","_generatorOptions$def","defaultModifiers","_generatorOptions$def2","defaultOptions","pending","orderedModifiers","effectCleanupFns","isDestroyed","setOptions","cleanupModifierEffects","merged","visited","result","modifier","dep","depModifier","orderModifiers","current","existing","m","_ref3$options","cleanupFn","forceUpdate","_state$elements","_state$orderedModifie","_state$orderedModifie2","Promise","resolve","then","destroy","onFirstUpdate","createPopper","computeStyles","applyStyles","flip","REGEXP_KEYDOWN","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","display","popperConfig","autoClose","Dropdown","_popper","_menu","_getMenuElement","_inNavbar","_detectNavbar","getParentFromElement","_createPopper","focus","_completeHide","Popper","_getPopperConfig","isDisplayStatic","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","_selectMenuItem","items","toggles","context","composedPath","isMenuTarget","isActive","stopPropagation","getToggleButton","clearMenus","dataApiKeydownHandler","ScrollBarHelper","getWidth","documentWidth","innerWidth","_disableOverFlow","_setElementAttributes","calculatedValue","_saveInitialAttribute","styleProp","scrollbarWidth","_applyManipulationCallback","_resetElementAttributes","actualValue","removeProperty","callBack","isOverflowing","className","rootElement","clickCallback","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","trapElement","autofocus","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","shiftKey","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_ignoreBackdropClick","_scrollBar","_isAnimated","_adjustDialog","_setEscapeEvent","_setResizeEvent","_showBackdrop","_showElement","_hideModal","htmlElement","handleUpdate","modalBody","_triggerBackdropTransition","_resetAdjustments","currentTarget","isModalOverflowing","isBodyOverflowing","paddingLeft","paddingRight","showEvent","Offcanvas","visibility","blur","allReadyOpen","uriAttrs","SAFE_URL_PATTERN","DATA_URL_PATTERN","allowedAttribute","attr","allowedAttributeList","attrName","nodeValue","regExp","attrRegex","sanitizeHtml","unsafeHtml","allowList","sanitizeFn","createdDocument","DOMParser","parseFromString","allowlistKeys","elName","attributeList","allowedAttributes","innerHTML","DISALLOWED_ATTRIBUTES","animation","template","title","delay","customClass","sanitize","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","*","area","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","img","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","HIDE","HIDDEN","SHOW","SHOWN","INSERTED","CLICK","FOCUSIN","FOCUSOUT","MOUSEENTER","MOUSELEAVE","Tooltip","_isEnabled","_timeout","_hoverState","_activeTrigger","tip","_setListeners","enable","disable","toggleEnabled","_initializeOnDelegatedTarget","click","_isWithActiveTrigger","_enter","_leave","getTipElement","_hideModalHandler","isWithContent","shadowRoot","isInTheDom","tipId","prefix","floor","random","getElementById","getUID","attachment","_getAttachment","_addAttachmentClass","_resolvePossibleFunction","prevHoverState","_cleanTipClass","getTitle","setContent","_sanitizeAndSetContent","content","templateElement","setElementContent","textContent","updateAttachment","_getDelegateConfig","_handlePopperPlacementChange","_getBasicClassPrefix","eventIn","eventOut","_fixTitle","originalTitleType","dataAttributes","dataAttr","basicClassPrefixRegex","tabClass","token","tClass","Popover","_getContent","SELECTOR_LINK_ITEMS","ScrollSpy","_scrollElement","_offsets","_targets","_activeTarget","_scrollHeight","_process","refresh","autoMethod","offsetMethod","offsetBase","_getScrollTop","_getScrollHeight","targetSelector","targetBCR","item","_getOffsetHeight","innerHeight","maxScroll","_activate","_clear","queries","link","listGroup","navItem","spy","Tab","listElement","itemSelector","hideEvent","complete","active","isTransitioning","_transitionComplete","dropdownChild","dropdownElement","dropdown","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","_onInteraction","isInteracting"],"mappings":"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","sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\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 getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = 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 (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\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 = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\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 // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return 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 DOMContentLoadedCallbacks.forEach(callback => callback())\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 = callback => {\n if (typeof callback === 'function') {\n callback()\n }\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 let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\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 getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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}\nconst customEventsRegex = /^(mouseenter|mouseleave)/i\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 * ------------------------------------------------------------------------\n * Private methods\n * ------------------------------------------------------------------------\n */\n\nfunction getUidEvent(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getEvent(element) {\n const uid = getUidEvent(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 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 (let i = domElements.length; i--;) {\n if (domElements[i] === target) {\n event.delegateTarget = target\n\n if (handler.oneOff) {\n // eslint-disable-next-line unicorn/consistent-destructuring\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n\n // To please ESLint\n return null\n }\n}\n\nfunction findHandler(events, handler, delegationSelector = null) {\n const uidEventList = Object.keys(events)\n\n for (let i = 0, len = uidEventList.length; i < len; i++) {\n const event = events[uidEventList[i]]\n\n if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {\n return event\n }\n }\n\n return null\n}\n\nfunction normalizeParams(originalTypeEvent, handler, delegationFn) {\n const delegation = typeof handler === 'string'\n const originalHandler = delegation ? delegationFn : handler\n\n let typeEvent = getTypeEvent(originalTypeEvent)\n const isNative = nativeEvents.has(typeEvent)\n\n if (!isNative) {\n typeEvent = originalTypeEvent\n }\n\n return [delegation, originalHandler, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n if (!handler) {\n handler = delegationFn\n delegationFn = null\n }\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 (customEventsRegex.test(originalTypeEvent)) {\n const wrapFn = 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 if (delegationFn) {\n delegationFn = wrapFn(delegationFn)\n } else {\n handler = wrapFn(handler)\n }\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const events = getEvent(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)\n\n if (previousFn) {\n previousFn.oneOff = previousFn.oneOff && oneOff\n\n return\n }\n\n const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = delegation ?\n bootstrapDelegationHandler(element, handler, delegationFn) :\n bootstrapHandler(element, handler)\n\n fn.delegationSelector = delegation ? handler : null\n fn.originalHandler = originalHandler\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, delegation)\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 Object.keys(storeElementEvent).forEach(handlerKey => {\n if (handlerKey.includes(namespace)) {\n const event = storeElementEvent[handlerKey]\n\n removeHandler(element, events, typeEvent, event.originalHandler, 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, delegationFn) {\n addHandler(element, event, handler, delegationFn, false)\n },\n\n one(element, event, handler, delegationFn) {\n addHandler(element, event, handler, delegationFn, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFn) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getEvent(element)\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof originalHandler !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!events || !events[typeEvent]) {\n return\n }\n\n removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)\n return\n }\n\n if (isNamespace) {\n Object.keys(events).forEach(elementEvent => {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n })\n }\n\n const storeElementEvent = events[typeEvent] || {}\n Object.keys(storeElementEvent).forEach(keyHandlers => {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n const event = storeElementEvent[keyHandlers]\n\n removeHandler(element, events, typeEvent, event.originalHandler, 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 const isNative = nativeEvents.has(typeEvent)\n\n let jQueryEvent\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n let evt = null\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 if (isNative) {\n evt = document.createEvent('HTMLEvents')\n evt.initEvent(typeEvent, bubbles, true)\n } else {\n evt = new CustomEvent(event, {\n bubbles,\n cancelable: true\n })\n }\n\n // merge custom information in our event\n if (typeof args !== 'undefined') {\n Object.keys(args).forEach(key => {\n Object.defineProperty(evt, key, {\n get() {\n return args[key]\n }\n })\n })\n }\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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 (v5.1.0): base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data'\nimport {\n executeAfterTransition,\n getElement\n} from './util/index'\nimport EventHandler from './dom/event-handler'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst VERSION = '5.1.0'\n\nclass BaseComponent {\n constructor(element) {\n element = getElement(element)\n\n if (!element) {\n return\n }\n\n this._element = element\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n Object.getOwnPropertyNames(this).forEach(propertyName => {\n this[propertyName] = null\n })\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n /** Static */\n\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 NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\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\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): 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'\nimport { getElementFromSelector, isDisabled } from './index'\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 = 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 (v5.1.0): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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 * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\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\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 * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nenableDismissTrigger(Alert, 'close')\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Alert to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\n\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\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\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 * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\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 * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Button to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(val) {\n if (val === 'true') {\n return true\n }\n\n if (val === 'false') {\n return false\n }\n\n if (val === Number(val).toString()) {\n return Number(val)\n }\n\n if (val === '' || val === 'null') {\n return null\n }\n\n return val\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\n Object.keys(element.dataset)\n .filter(key => key.startsWith('bs'))\n .forEach(key => {\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 offset(element) {\n const rect = element.getBoundingClientRect()\n\n return {\n top: rect.top + window.pageYOffset,\n left: rect.left + window.pageXOffset\n }\n },\n\n position(element) {\n return {\n top: element.offsetTop,\n left: element.offsetLeft\n }\n }\n}\n\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible } from '../util/index'\n\nconst NODE_TEXT = 3\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)\n .filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n\n let ancestor = element.parentNode\n\n while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {\n if (ancestor.matches(selector)) {\n parents.push(ancestor)\n }\n\n ancestor = ancestor.parentNode\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\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\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n getNextActiveElement,\n reflow,\n triggerTransitionEnd,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n slide: false,\n pause: 'hover',\n wrap: true,\n touch: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)',\n keyboard: 'boolean',\n slide: '(boolean|string)',\n pause: '(string|boolean)',\n wrap: 'boolean',\n touch: 'boolean'\n}\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\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_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 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'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ACTIVE_ITEM = '.active.carousel-item'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_INDICATOR = '[data-bs-target]'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\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(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {\n triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config && this._config.interval && !this._isPaused) {\n this._updateInterval()\n\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._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 if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const order = index > activeIndex ?\n ORDER_NEXT :\n ORDER_PREV\n\n this._slide(order, this._items[index])\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n this.touchDeltaX = 0\n\n if (!direction) {\n return\n }\n\n this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT)\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, event => this.pause(event))\n EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))\n }\n\n if (this._config.touch && this._touchSupported) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n const start = event => {\n if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) {\n this.touchStartX = event.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.touches[0].clientX\n }\n }\n\n const move = event => {\n // ensure swiping with one touch and not pinching\n this.touchDeltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this.touchStartX\n }\n\n const end = event => {\n if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) {\n this.touchDeltaX = event.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\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(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => {\n EventHandler.on(itemImg, EVENT_DRAG_START, e => e.preventDefault())\n })\n\n if (this._pointerEvent) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event))\n }\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(direction)\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode ?\n SelectorEngine.find(SELECTOR_ITEM, element.parentNode) :\n []\n\n return this._items.indexOf(element)\n }\n\n _getItemByOrder(order, activeElement) {\n const isNext = order === ORDER_NEXT\n return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))\n\n return EventHandler.trigger(this._element, EVENT_SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\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 indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement)\n\n for (let i = 0; i < indicators.length; i++) {\n if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {\n indicators[i].classList.add(CLASS_NAME_ACTIVE)\n indicators[i].setAttribute('aria-current', 'true')\n break\n }\n }\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n if (elementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = elementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n }\n\n _slide(directionOrOrder, element) {\n const order = this._directionToOrder(directionOrOrder)\n const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || this._getItemByOrder(order, activeElement)\n\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n const isNext = order === ORDER_NEXT\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n const eventDirectionName = this._orderToDirection(order)\n\n if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {\n this._isSliding = false\n return\n }\n\n if (this._isSliding) {\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n this._activeElement = nextElement\n\n const triggerSlidEvent = () => {\n EventHandler.trigger(this._element, EVENT_SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n }\n\n if (this._element.classList.contains(CLASS_NAME_SLIDE)) {\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 setTimeout(triggerSlidEvent, 0)\n }\n\n this._queueCallback(completeCallBack, activeElement, true)\n } else {\n activeElement.classList.remove(CLASS_NAME_ACTIVE)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n this._isSliding = false\n triggerSlidEvent()\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _directionToOrder(direction) {\n if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) {\n return direction\n }\n\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 (![ORDER_NEXT, ORDER_PREV].includes(order)) {\n return order\n }\n\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\n static carouselInterface(element, config) {\n const data = Carousel.getOrCreateInstance(element, config)\n\n let { _config } = data\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n }\n\n static jQueryInterface(config) {\n return this.each(function () {\n Carousel.carouselInterface(this, config)\n })\n }\n\n static dataApiClickHandler(event) {\n const target = getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n const config = {\n ...Manipulator.getDataAttributes(target),\n ...Manipulator.getDataAttributes(this)\n }\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel.carouselInterface(target, config)\n\n if (slideIndex) {\n Carousel.getInstance(target).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (let i = 0, len = carousels.length; i < len; i++) {\n Carousel.carouselInterface(carousels[i], Carousel.getInstance(carousels[i]))\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Carousel to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n getElementFromSelector,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n toggle: true,\n parent: null\n}\n\nconst DefaultType = {\n toggle: 'boolean',\n parent: '(null|element)'\n}\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_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.show, .collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._isTransitioning = false\n this._config = this._getConfig(config)\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElem => foundElem === this._element)\n\n if (selector !== null && filterElement.length) {\n this._selector = selector\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\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\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 actives = []\n let activesData\n\n if (this._config.parent) {\n const children = SelectorEngine.find(`.${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`, this._config.parent)\n actives = SelectorEngine.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)) // remove children if greater depth\n }\n\n const container = SelectorEngine.findOne(this._selector)\n if (actives.length) {\n const tempActiveData = actives.find(elem => container !== elem)\n activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null\n\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n actives.forEach(elemActive => {\n if (container !== elemActive) {\n Collapse.getOrCreateInstance(elemActive, { toggle: false }).hide()\n }\n\n if (!activesData) {\n Data.set(elemActive, DATA_KEY, null)\n }\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 const triggerArrayLength = this._triggerArray.length\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const elem = getElementFromSelector(trigger)\n\n if (elem && !this._isShown(elem)) {\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\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n typeCheckConfig(NAME, config, DefaultType)\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 = SelectorEngine.find(`.${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`, this._config.parent)\n SelectorEngine.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem))\n .forEach(element => {\n const selected = getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n })\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n triggerArray.forEach(elem => {\n if (isOpen) {\n elem.classList.remove(CLASS_NAME_COLLAPSED)\n } else {\n elem.classList.add(CLASS_NAME_COLLAPSED)\n }\n\n elem.setAttribute('aria-expanded', isOpen)\n })\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\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 * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\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 const selector = getSelectorFromElement(this)\n const selectorElements = SelectorEngine.find(selector)\n\n selectorElements.forEach(element => {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Collapse to jQuery only if jQuery is present\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}","import { isHTMLElement } from \"./instanceOf.js\";\nvar round = Math.round;\nexport default function getBoundingClientRect(element, includeScale) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n var rect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (isHTMLElement(element) && includeScale) {\n // Fallback to 1 in case both values are `0`\n scaleX = rect.width / element.offsetWidth || 1;\n scaleY = rect.height / element.offsetHeight || 1;\n }\n\n return {\n width: round(rect.width / scaleX),\n height: round(rect.height / scaleY),\n top: round(rect.top / scaleY),\n right: round(rect.right / scaleX),\n bottom: round(rect.bottom / scaleY),\n left: round(rect.left / scaleX),\n x: round(rect.left / scaleX),\n y: round(rect.top / scaleY)\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 } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.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 = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1;\n var isIE = navigator.userAgent.indexOf('Trident') !== -1;\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 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}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport default function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\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\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.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 (process.env.NODE_ENV !== \"production\") {\n if (!isHTMLElement(arrowElement)) {\n console.error(['Popper: \"arrow\" element must be an HTMLElement (not an SVGElement).', 'To use an SVG arrow, wrap it in an HTMLElement that will be used as', 'the arrow.'].join(' '));\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.error(['Popper: \"arrow\" modifier\\'s `element` must be a child of the popper', 'element.'].join(' '));\n }\n\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};","import { top, left, right, bottom } 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 { 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) {\n var x = _ref.x,\n y = _ref.y;\n var win = window;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(round(x * dpr) / dpr) || 0,\n y: round(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 offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets;\n\n var _ref3 = roundOffsets === true ? roundOffsetsByDPR(offsets) : typeof roundOffsets === 'function' ? roundOffsets(offsets) : offsets,\n _ref3$x = _ref3.x,\n x = _ref3$x === void 0 ? 0 : _ref3$x,\n _ref3$y = _ref3.y,\n y = _ref3$y === void 0 ? 0 : _ref3$y;\n\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') {\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) {\n sideY = bottom; // $FlowFixMe[prop-missing]\n\n y -= offsetParent[heightProp] - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left) {\n sideX = right; // $FlowFixMe[prop-missing]\n\n x -= offsetParent[widthProp] - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\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) < 2 ? \"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(_ref4) {\n var state = _ref4.state,\n options = _ref4.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\n if (process.env.NODE_ENV !== \"production\") {\n var transitionProperty = getComputedStyle(state.elements.popper).transitionProperty || '';\n\n if (adaptive && ['transform', 'top', 'right', 'bottom', 'left'].some(function (property) {\n return transitionProperty.indexOf(property) >= 0;\n })) {\n console.warn(['Popper: Detected CSS transitions on at least one of the following', 'CSS properties: \"transform\", \"top\", \"right\", \"bottom\", \"left\".', '\\n\\n', 'Disable the \"computeStyles\" modifier\\'s `adaptive` option to allow', 'for smooth transitions, or remove these properties from the CSS', 'transition declaration on the popper element if only transitioning', 'opacity or background-color for example.', '\\n\\n', 'We recommend using the popper element as a wrapper around an inner', 'element that can have any CSS property transitioned for animations.'].join(' '));\n }\n }\n\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration\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 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}","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}","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) {\n var rect = getBoundingClientRect(element);\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) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isHTMLElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : 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) {\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);\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));\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\";\nexport default function getViewportRect(element) {\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; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper\n // can be obscured underneath it.\n // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even\n // if it isn't open, so if this isn't available, the popper will be detected\n // to overflow the bottom of the screen too early.\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)\n // In Chrome, it returns a value very close to 0 (+/-) but contains rounding\n // errors due to floating point numbers, so we need to check precision.\n // Safari returns a number <= 0, usually < -1 when pinch-zoomed\n // Feature detection fails in mobile emulation mode in Chrome.\n // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <\n // 0.001\n // Fallback here: \"Not Safari\" userAgent\n\n if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {\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}","export default function getVariation(placement) {\n return placement.split('-')[1];\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 getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.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$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 referenceElement = state.elements.reference;\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);\n var referenceClientRect = getBoundingClientRect(referenceElement);\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\n if (process.env.NODE_ENV !== \"production\") {\n console.error(['Popper: The `allowedAutoPlacements` option did not allow any', 'placements. Ensure the `placement` option matches the variation', 'of the allowed placements.', 'For example, \"auto\" cannot be used to allow \"bottom-start\".', 'Use \"auto-start\" instead.'].join(' '));\n }\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\";\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 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 { max as mathMax, min as mathMin } 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 data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis || checkAltAxis) {\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 = popperOffsets[mainAxis] + overflow[mainSide];\n var max = popperOffsets[mainAxis] - 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 - tetherOffsetValue : minLen - arrowLen - arrowPaddingMin - tetherOffsetValue;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + tetherOffsetValue : maxLen + arrowLen + arrowPaddingMax + tetherOffsetValue;\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 = state.modifiersData.offset ? state.modifiersData.offset[state.placement][mainAxis] : 0;\n var tetherMin = popperOffsets[mainAxis] + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = popperOffsets[mainAxis] + maxOffset - offsetModifierValue;\n\n if (checkMainAxis) {\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 _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var _preventedOffset = within(tether ? mathMin(_min, tetherMin) : _min, _offset, tether ? mathMax(_max, tetherMax) : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\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\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = rect.width / element.offsetWidth || 1;\n var scaleY = 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);\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 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 getComputedStyle from \"./dom-utils/getComputedStyle.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport validateModifiers from \"./utils/validateModifiers.js\";\nimport uniqueBy from \"./utils/uniqueBy.js\";\nimport getBasePlacement from \"./utils/getBasePlacement.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nimport { auto } from \"./enums.js\";\nvar INVALID_ELEMENT_ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.';\nvar INFINITE_LOOP_ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.';\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(options) {\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 }); // Validate the provided modifiers so that the consumer will get warned\n // if one of the modifiers is invalid for any reason\n\n if (process.env.NODE_ENV !== \"production\") {\n var modifiers = uniqueBy([].concat(orderedModifiers, state.options.modifiers), function (_ref) {\n var name = _ref.name;\n return name;\n });\n validateModifiers(modifiers);\n\n if (getBasePlacement(state.options.placement) === auto) {\n var flipModifier = state.orderedModifiers.find(function (_ref2) {\n var name = _ref2.name;\n return name === 'flip';\n });\n\n if (!flipModifier) {\n console.error(['Popper: \"auto\" placements require the \"flip\" modifier be', 'present and enabled to work.'].join(' '));\n }\n }\n\n var _getComputedStyle = getComputedStyle(popper),\n marginTop = _getComputedStyle.marginTop,\n marginRight = _getComputedStyle.marginRight,\n marginBottom = _getComputedStyle.marginBottom,\n marginLeft = _getComputedStyle.marginLeft; // We no longer take into account `margins` on the popper, and it can\n // cause bugs with positioning, so we'll warn the consumer\n\n\n if ([marginTop, marginRight, marginBottom, marginLeft].some(function (margin) {\n return parseFloat(margin);\n })) {\n console.warn(['Popper: CSS \"margin\" styles cannot be used to apply padding', 'between the popper and its reference element or boundary.', 'To replicate margin, use the `offset` modifier, as well as', 'the `padding` option in the `preventOverflow` and `flip`', 'modifiers.'].join(' '));\n }\n }\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 if (process.env.NODE_ENV !== \"production\") {\n console.error(INVALID_ELEMENT_ERROR);\n }\n\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 var __debug_loops__ = 0;\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (process.env.NODE_ENV !== \"production\") {\n __debug_loops__ += 1;\n\n if (__debug_loops__ > 100) {\n console.error(INFINITE_LOOP_ERROR);\n break;\n }\n }\n\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 if (process.env.NODE_ENV !== \"production\") {\n console.error(INVALID_ELEMENT_ERROR);\n }\n\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 (_ref3) {\n var name = _ref3.name,\n _ref3$options = _ref3.options,\n options = _ref3$options === void 0 ? {} : _ref3$options,\n effect = _ref3.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 { 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 { 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 (v5.1.0): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n getElement,\n getElementFromSelector,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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 SPACE_KEY = 'Space'\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 REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)\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_NAVBAR = 'navbar'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]'\nconst SELECTOR_MENU = '.dropdown-menu'\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'\n\nconst Default = {\n offset: [0, 2],\n boundary: 'clippingParents',\n reference: 'toggle',\n display: 'dynamic',\n popperConfig: null,\n autoClose: true\n}\n\nconst DefaultType = {\n offset: '(array|string|function)',\n boundary: '(string|element)',\n reference: '(string|element|object)',\n display: 'string',\n popperConfig: '(null|object|function)',\n autoClose: '(boolean|string)'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n\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\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown(this._menu)) {\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 const parent = Dropdown.getParentFromElement(this._element)\n // Totally disable Popper for Dropdowns in Navbar\n if (this._inNavbar) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'none')\n } else {\n this._createPopper(parent)\n }\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 !parent.closest(SELECTOR_NAVBAR_NAV)) {\n [].concat(...document.body.children)\n .forEach(elem => EventHandler.on(elem, 'mouseover', noop))\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(this._menu)) {\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\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 [].concat(...document.body.children)\n .forEach(elem => EventHandler.off(elem, 'mouseover', noop))\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 = {\n ...this.constructor.Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\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(parent) {\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 = 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 const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)\n\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n\n if (isDisplayStatic) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static')\n }\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getMenuElement() {\n return SelectorEngine.next(this._element, SELECTOR_MENU)[0]\n }\n\n _getPlacement() {\n const parentDropdown = this._element.parentNode\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 // 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(`.${CLASS_NAME_NAVBAR}`) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 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\n if (this._config.display === 'static') {\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)\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\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 && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {\n return\n }\n\n const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const context = Dropdown.getInstance(toggles[i])\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n if (!context._isShown()) {\n continue\n }\n\n const relatedTarget = {\n relatedTarget: context._element\n }\n\n if (event) {\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 if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static getParentFromElement(element) {\n return getElementFromSelector(element) || element.parentNode\n }\n\n static dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName) ?\n event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&\n ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||\n event.target.closest(SELECTOR_MENU))) :\n !REGEXP_KEYDOWN.test(event.key)) {\n return\n }\n\n const isActive = this.classList.contains(CLASS_NAME_SHOW)\n\n if (!isActive && event.key === ESCAPE_KEY) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (isDisabled(this)) {\n return\n }\n\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (event.key === ESCAPE_KEY) {\n instance.hide()\n return\n }\n\n if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {\n if (!isActive) {\n instance.show()\n }\n\n instance._selectMenuItem(event)\n return\n }\n\n if (!isActive || event.key === SPACE_KEY) {\n Dropdown.clearMenus()\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\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 * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Dropdown to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine'\nimport Manipulator from '../dom/manipulator'\nimport { isElement } from './index'\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\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, 'paddingRight', 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, 'paddingRight', calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)\n }\n\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProp, 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, styleProp)\n const calculatedValue = window.getComputedStyle(element)[styleProp]\n element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, 'paddingRight')\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')\n }\n\n _saveInitialAttribute(element, styleProp) {\n const actualValue = element.style[styleProp]\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProp, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProp) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProp)\n if (typeof value === 'undefined') {\n element.style.removeProperty(styleProp)\n } else {\n Manipulator.removeDataAttribute(element, styleProp)\n element.style[styleProp] = value\n }\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n } else {\n SelectorEngine.find(selector, this._element).forEach(callBack)\n }\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'\n\nconst Default = {\n className: 'modal-backdrop',\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n isAnimated: false,\n rootElement: 'body', // give the choice to place backdrop under different elements\n clickCallback: null\n}\n\nconst DefaultType = {\n className: 'string',\n isVisible: 'boolean',\n isAnimated: 'boolean',\n rootElement: '(element|string)',\n clickCallback: '(function|null)'\n}\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nclass Backdrop {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n if (this._config.isAnimated) {\n reflow(this._getElement())\n }\n\n this._getElement().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 // Private\n\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 _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n this._config.rootElement.append(this._getElement())\n\n EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\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 _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport SelectorEngine from '../dom/selector-engine'\nimport { typeCheckConfig } from './index'\n\nconst Default = {\n trapElement: null, // The element to trap focus inside of\n autofocus: true\n}\n\nconst DefaultType = {\n trapElement: 'element',\n autofocus: 'boolean'\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\nclass FocusTrap {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n activate() {\n const { trapElement, autofocus } = this._config\n\n if (this._isActive) {\n return\n }\n\n if (autofocus) {\n 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\n _handleFocusin(event) {\n const { target } = event\n const { trapElement } = this._config\n\n if (\n target === document ||\n target === trapElement ||\n trapElement.contains(target)\n ) {\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 _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport ScrollBarHelper from './util/scrollbar'\nimport BaseComponent from './base-component'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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 Default = {\n backdrop: true,\n keyboard: true,\n focus: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n focus: 'boolean'\n}\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_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.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 SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\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._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\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\n if (this._isAnimated()) {\n this._isTransitioning = true\n }\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {\n EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {\n if (event.target === this._element) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => 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 const isAnimated = this._isAnimated()\n\n if (isAnimated) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n EventHandler.off(this._element, EVENT_CLICK_DISMISS)\n EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)\n\n this._queueCallback(() => this._hideModal(), this._element, isAnimated)\n }\n\n dispose() {\n [window, this._dialog]\n .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY))\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\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 _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const isAnimated = this._isAnimated()\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n\n if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\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 if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n if (isAnimated) {\n reflow(this._element)\n }\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, isAnimated)\n }\n\n _setEscapeEvent() {\n if (this._isShown) {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (this._config.keyboard && event.key === ESCAPE_KEY) {\n event.preventDefault()\n this.hide()\n } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {\n this._triggerBackdropTransition()\n }\n })\n } else {\n EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())\n } else {\n EventHandler.off(window, EVENT_RESIZE)\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 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 _showBackdrop(callback) {\n EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n\n if (event.target !== event.currentTarget) {\n return\n }\n\n if (this._config.backdrop === true) {\n this.hide()\n } else if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n }\n })\n\n this._backdrop.show(callback)\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 { classList, scrollHeight, style } = this._element\n const isModalOverflowing = scrollHeight > document.documentElement.clientHeight\n\n // return if the following background transition hasn't yet completed\n if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n style.overflowY = 'hidden'\n }\n\n classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n classList.remove(CLASS_NAME_STATIC)\n if (!isModalOverflowing) {\n this._queueCallback(() => {\n style.overflowY = ''\n }, this._dialog)\n }\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 && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) {\n this._element.style.paddingLeft = `${scrollbarWidth}px`\n }\n\n if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) {\n this._element.style.paddingRight = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n\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 * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = 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 const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Modal to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isDisabled,\n isVisible,\n typeCheckConfig\n} from './util/index'\nimport ScrollBarHelper from './util/scrollbar'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport SelectorEngine from './dom/selector-engine'\nimport Manipulator from './dom/manipulator'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\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 Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: 'boolean',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\nconst CLASS_NAME_SHOW = 'show'\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_HIDDEN = `hidden${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\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\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._element.style.visibility = 'visible'\n\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const completeCallBack = () => {\n if (!this._config.scroll) {\n this._focustrap.activate()\n }\n\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.remove(CLASS_NAME_SHOW)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._element.style.visibility = 'hidden'\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\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _initializeBackDrop() {\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible: this._config.backdrop,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: () => this.hide()\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 (this._config.keyboard && event.key === ESCAPE_KEY) {\n this.hide()\n }\n })\n }\n\n // Static\n\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 * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = 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 allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (allReadyOpen && allReadyOpen !== target) {\n Offcanvas.getInstance(allReadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () =>\n SelectorEngine.find(OPEN_SELECTOR).forEach(el => Offcanvas.getOrCreateInstance(el).show())\n)\n\nenableDismissTrigger(Offcanvas)\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttrs = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/i\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i\n\nconst allowedAttribute = (attr, allowedAttributeList) => {\n const attrName = attr.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attrName)) {\n if (uriAttrs.has(attrName)) {\n return Boolean(SAFE_URL_PATTERN.test(attr.nodeValue) || DATA_URL_PATTERN.test(attr.nodeValue))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter(attrRegex => attrRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, len = regExp.length; i < len; i++) {\n if (regExp[i].test(attrName)) {\n return true\n }\n }\n\n return false\n}\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\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const allowlistKeys = Object.keys(allowList)\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const el = elements[i]\n const elName = el.nodeName.toLowerCase()\n\n if (!allowlistKeys.includes(elName)) {\n el.remove()\n\n continue\n }\n\n const attributeList = [].concat(...el.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elName] || [])\n\n attributeList.forEach(attr => {\n if (!allowedAttribute(attr, allowedAttributes)) {\n el.removeAttribute(attr.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n findShadowRoot,\n getElement,\n getUID,\n isElement,\n isRTL,\n noop,\n typeCheckConfig\n} from './util/index'\nimport { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst DefaultType = {\n animation: 'boolean',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string',\n delay: '(number|object)',\n html: 'boolean',\n selector: '(string|boolean)',\n placement: '(string|function)',\n offset: '(array|string|function)',\n container: '(string|element|boolean)',\n fallbackPlacements: 'array',\n boundary: '(string|element)',\n customClass: '(string|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n allowList: 'object',\n popperConfig: '(null|object|function)'\n}\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 animation: true,\n template: '
' +\n '
' +\n '
' +\n '
',\n trigger: 'hover focus',\n title: '',\n delay: 0,\n html: false,\n selector: false,\n placement: 'top',\n offset: [0, 0],\n container: false,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n boundary: 'clippingParents',\n customClass: '',\n sanitize: true,\n sanitizeFn: null,\n allowList: DefaultAllowlist,\n popperConfig: null\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst HOVER_STATE_SHOW = 'show'\nconst HOVER_STATE_OUT = 'out'\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\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\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)\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this._config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\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(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const context = this._initializeOnDelegatedTarget(event)\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\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.tip) {\n this.tip.remove()\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\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.Event.SHOW)\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = shadowRoot === null ?\n this._element.ownerDocument.documentElement.contains(this._element) :\n shadowRoot.contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n const tip = this.getTipElement()\n const tipId = getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this._element.setAttribute('aria-describedby', tipId)\n\n if (this._config.animation) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n const placement = typeof this._config.placement === 'function' ?\n this._config.placement.call(this, tip, this._element) :\n this._config.placement\n\n const attachment = this._getAttachment(placement)\n this._addAttachmentClass(attachment)\n\n const { container } = this._config\n Data.set(tip, this.constructor.DATA_KEY, this)\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.Event.INSERTED)\n }\n\n if (this._popper) {\n this._popper.update()\n } else {\n this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n const customClass = this._resolvePossibleFunction(this._config.customClass)\n if (customClass) {\n tip.classList.add(...customClass.split(' '))\n }\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 [].concat(...document.body.children).forEach(element => {\n EventHandler.on(element, 'mouseover', noop)\n })\n }\n\n const complete = () => {\n const prevHoverState = this._hoverState\n\n this._hoverState = null\n EventHandler.trigger(this._element, this.constructor.Event.SHOWN)\n\n if (prevHoverState === HOVER_STATE_OUT) {\n this._leave(null, this)\n }\n }\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n }\n\n hide() {\n if (!this._popper) {\n return\n }\n\n const tip = this.getTipElement()\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (this._hoverState !== HOVER_STATE_SHOW) {\n tip.remove()\n }\n\n this._cleanTipClass()\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)\n\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)\n if (hideEvent.defaultPrevented) {\n return\n }\n\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 [].concat(...document.body.children)\n .forEach(element => EventHandler.off(element, 'mouseover', noop))\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.update()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n getTipElement() {\n if (this.tip) {\n return this.tip\n }\n\n const element = document.createElement('div')\n element.innerHTML = this._config.template\n\n const tip = element.children[0]\n this.setContent(tip)\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n\n this.tip = tip\n return this.tip\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)\n }\n\n _sanitizeAndSetContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!content && templateElement) {\n templateElement.remove()\n return\n }\n\n // we use append for html objects to maintain js events\n this.setElementContent(templateElement, content)\n }\n\n setElementContent(element, content) {\n if (element === null) {\n return\n }\n\n if (isElement(content)) {\n content = getElement(content)\n\n // content is a DOM node or a jQuery\n if (this._config.html) {\n if (content.parentNode !== element) {\n element.innerHTML = ''\n element.append(content)\n }\n } else {\n element.textContent = content.textContent\n }\n\n return\n }\n\n if (this._config.html) {\n if (this._config.sanitize) {\n content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)\n }\n\n element.innerHTML = content\n } else {\n element.textContent = content\n }\n }\n\n getTitle() {\n const title = this._element.getAttribute('data-bs-original-title') || this._config.title\n\n return this._resolvePossibleFunction(title)\n }\n\n updateAttachment(attachment) {\n if (attachment === 'right') {\n return 'end'\n }\n\n if (attachment === 'left') {\n return 'start'\n }\n\n return attachment\n }\n\n // Private\n\n _initializeOnDelegatedTarget(event, context) {\n return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(content) {\n return typeof content === 'function' ? content.call(this._element) : content\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: 'onChange',\n enabled: true,\n phase: 'afterWrite',\n fn: data => this._handlePopperPlacementChange(data)\n }\n ],\n onFirstUpdate: data => {\n if (data.options.placement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n }\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _addAttachmentClass(attachment) {\n this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n triggers.forEach(trigger => {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event))\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSEENTER :\n this.constructor.Event.FOCUSIN\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSELEAVE :\n this.constructor.Event.FOCUSOUT\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))\n EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))\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 if (this._config.selector) {\n this._config = {\n ...this._config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n const originalTitleType = typeof this._element.getAttribute('data-bs-original-title')\n\n if (title || originalTitleType !== 'string') {\n this._element.setAttribute('data-bs-original-title', title || '')\n if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = true\n }\n\n if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {\n context._hoverState = HOVER_STATE_SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_SHOW\n\n if (!context._config.delay || !context._config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_SHOW) {\n context.show()\n }\n }, context._config.delay.show)\n }\n\n _leave(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = context._element.contains(event.relatedTarget)\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_OUT\n\n if (!context._config.delay || !context._config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_OUT) {\n context.hide()\n }\n }, context._config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n Object.keys(dataAttributes).forEach(dataAttr => {\n if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n\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 typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const key in this._config) {\n if (this.constructor.Default[key] !== this._config[key]) {\n config[key] = this._config[key]\n }\n }\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 _cleanTipClass() {\n const tip = this.getTipElement()\n const basicClassPrefixRegex = new RegExp(`(^|\\\\s)${this._getBasicClassPrefix()}\\\\S+`, 'g')\n const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)\n if (tabClass !== null && tabClass.length > 0) {\n tabClass.map(token => token.trim())\n .forEach(tClass => tip.classList.remove(tClass))\n }\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n _handlePopperPlacementChange(popperData) {\n const { state } = popperData\n\n if (!state) {\n return\n }\n\n this.tip = state.elements.popper\n this._cleanTipClass()\n this._addAttachmentClass(this._getAttachment(state.placement))\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.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 * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Tooltip to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-popover'\n\nconst Default = {\n ...Tooltip.Default,\n placement: 'right',\n offset: [0, 8],\n trigger: 'click',\n content: '',\n template: '
' +\n '
' +\n '

' +\n '
' +\n '
'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(string|element|function)'\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE)\n this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT)\n }\n\n // Private\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.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 * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Popover to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.0): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n offset: 10,\n method: 'auto',\n target: ''\n}\n\nconst DefaultType = {\n offset: 'number',\n method: 'string',\n target: '(string|element)'\n}\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_SCROLL = `scroll${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_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_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst METHOD_OFFSET = 'offset'\nconst METHOD_POSITION = 'position'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element)\n this._scrollElement = this._element.tagName === 'BODY' ? window : this._element\n this._config = this._getConfig(config)\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process())\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window ?\n METHOD_OFFSET :\n METHOD_POSITION\n\n const offsetMethod = this._config.method === 'auto' ?\n autoMethod :\n this._config.method\n\n const offsetBase = offsetMethod === METHOD_POSITION ?\n this._getScrollTop() :\n 0\n\n this._offsets = []\n this._targets = []\n this._scrollHeight = this._getScrollHeight()\n\n const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)\n\n targets.map(element => {\n const targetSelector = getSelectorFromElement(element)\n const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n return [\n Manipulator[offsetMethod](target).top + offsetBase,\n targetSelector\n ]\n }\n }\n\n return null\n })\n .filter(item => item)\n .sort((a, b) => a[0] - b[0])\n .forEach(item => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n EventHandler.off(this._scrollElement, EVENT_KEY)\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' && config ? config : {})\n }\n\n config.target = getElement(config.target) || document.documentElement\n\n typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window ?\n this._scrollElement.pageYOffset :\n this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window ?\n window.innerHeight :\n this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n for (let i = this._offsets.length; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = SELECTOR_LINK_ITEMS.split(',')\n .map(selector => `${selector}[data-bs-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const link = SelectorEngine.findOne(queries.join(','), this._config.target)\n\n link.classList.add(CLASS_NAME_ACTIVE)\n if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n } else {\n SelectorEngine.parents(link, SELECTOR_NAV_LIST_GROUP)\n .forEach(listGroup => {\n // Set triggered links parents as active\n // With both