Repository: hughjonesd/santoku Branch: master Commit: 8933bb90a885 Files: 287 Total size: 2.2 MB Directory structure: gitextract_5kkl1r2r/ ├── .Rbuildignore ├── .github/ │ ├── .gitignore │ └── workflows/ │ ├── R-CMD-check.yaml │ └── test-coverage.yaml ├── .gitignore ├── AGENTS.md ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R/ │ ├── RcppExports.R │ ├── breaks-by-group-size.R │ ├── breaks-by-width.R │ ├── breaks-impl.R │ ├── breaks-misc.R │ ├── breaks.R │ ├── categorize.R │ ├── chop-by-group-size.R │ ├── chop-by-width.R │ ├── chop-isolates.R │ ├── chop-misc.R │ ├── chop.R │ ├── labels-datetime.R │ ├── labels-glue.R │ ├── labels-impl.R │ ├── labels-single.R │ ├── labels.R │ ├── non-standard-types-doc.R │ ├── santoku-cast.R │ ├── santoku-package.R │ ├── tab.R │ └── utils.R ├── README.Rmd ├── README.md ├── TODO.md ├── _pkgdown.yml ├── advantages.Rmd ├── codecov.yml ├── cran-comments.md ├── docs/ │ ├── 404.html │ ├── 404.md │ ├── AGENTS.html │ ├── AGENTS.md │ ├── CLAUDE.html │ ├── CLAUDE.md │ ├── LICENSE-text.html │ ├── LICENSE-text.md │ ├── LICENSE.html │ ├── LICENSE.md │ ├── TODO.html │ ├── TODO.md │ ├── articles/ │ │ ├── index.html │ │ ├── index.md │ │ ├── santoku.html │ │ ├── santoku.md │ │ ├── santoku_files/ │ │ │ ├── accessible-code-block-0.0.1/ │ │ │ │ └── empty-anchor.js │ │ │ ├── header-attrs-2.11/ │ │ │ │ └── header-attrs.js │ │ │ └── header-attrs-2.8/ │ │ │ └── header-attrs.js │ │ ├── website-articles/ │ │ │ ├── performance.html │ │ │ ├── performance.md │ │ │ └── performance_files/ │ │ │ ├── accessible-code-block-0.0.1/ │ │ │ │ └── empty-anchor.js │ │ │ ├── header-attrs-2.11/ │ │ │ │ └── header-attrs.js │ │ │ └── header-attrs-2.8/ │ │ │ └── header-attrs.js │ │ ├── whats-new-in-0-9-0.html │ │ └── whats-new-in-0-9-0.md │ ├── authors.html │ ├── authors.md │ ├── bootstrap-toc.css │ ├── bootstrap-toc.js │ ├── deps/ │ │ ├── _Courier Prime-0.4.0/ │ │ │ └── font.css │ │ ├── bootstrap-5.1.0/ │ │ │ └── font.css │ │ ├── bootstrap-5.1.3/ │ │ │ └── font.css │ │ ├── bootstrap-5.2.2/ │ │ │ └── font.css │ │ ├── bootstrap-5.3.1/ │ │ │ └── font.css │ │ ├── data-deps.txt │ │ ├── font-awesome-6.5.2/ │ │ │ └── css/ │ │ │ ├── all.css │ │ │ └── v4-shims.css │ │ └── jquery-3.6.0/ │ │ └── jquery-3.6.0.js │ ├── docsearch.css │ ├── docsearch.js │ ├── extra.css │ ├── index.html │ ├── index.md │ ├── katex-auto.js │ ├── lightswitch.js │ ├── llms.txt │ ├── news/ │ │ ├── index.html │ │ └── index.md │ ├── pkgdown.css │ ├── pkgdown.js │ ├── pkgdown.yml │ ├── reference/ │ │ ├── breaks-class.html │ │ ├── breaks-class.md │ │ ├── brk-left-right.html │ │ ├── brk-left-right.md │ │ ├── brk_default.html │ │ ├── brk_default.md │ │ ├── brk_equally.html │ │ ├── brk_evenly.html │ │ ├── brk_fn.html │ │ ├── brk_manual.html │ │ ├── brk_manual.md │ │ ├── brk_mean_sd.html │ │ ├── brk_n.html │ │ ├── brk_pretty.html │ │ ├── brk_proportions.html │ │ ├── brk_quantiles.html │ │ ├── brk_spikes.html │ │ ├── brk_width-for-datetime.html │ │ ├── brk_width-for-datetime.md │ │ ├── brk_width.Duration.html │ │ ├── brk_width.default.html │ │ ├── brk_width.html │ │ ├── chop.html │ │ ├── chop.md │ │ ├── chop_deciles.html │ │ ├── chop_equally.html │ │ ├── chop_equally.md │ │ ├── chop_evenly.html │ │ ├── chop_evenly.md │ │ ├── chop_fn.html │ │ ├── chop_fn.md │ │ ├── chop_mean_sd.html │ │ ├── chop_mean_sd.md │ │ ├── chop_n.html │ │ ├── chop_n.md │ │ ├── chop_pretty.html │ │ ├── chop_pretty.md │ │ ├── chop_proportions.html │ │ ├── chop_proportions.md │ │ ├── chop_quantiles.html │ │ ├── chop_quantiles.md │ │ ├── chop_spikes.html │ │ ├── chop_spikes.md │ │ ├── chop_width.html │ │ ├── chop_width.md │ │ ├── dissect.html │ │ ├── dissect.md │ │ ├── exactly.html │ │ ├── exactly.md │ │ ├── fillet.html │ │ ├── fillet.md │ │ ├── format.breaks.html │ │ ├── index.html │ │ ├── index.md │ │ ├── is.breaks.html │ │ ├── kiru.html │ │ ├── knife.html │ │ ├── knife.md │ │ ├── lbl_dash.html │ │ ├── lbl_dash.md │ │ ├── lbl_datetime.html │ │ ├── lbl_datetime.md │ │ ├── lbl_discrete.html │ │ ├── lbl_discrete.md │ │ ├── lbl_endpoint.html │ │ ├── lbl_endpoints.html │ │ ├── lbl_endpoints.md │ │ ├── lbl_format.html │ │ ├── lbl_format.md │ │ ├── lbl_glue.html │ │ ├── lbl_glue.md │ │ ├── lbl_intervals.html │ │ ├── lbl_intervals.md │ │ ├── lbl_manual.html │ │ ├── lbl_manual.md │ │ ├── lbl_midpoints.html │ │ ├── lbl_midpoints.md │ │ ├── lbl_seq.html │ │ ├── lbl_seq.md │ │ ├── non-standard-types.html │ │ ├── non-standard-types.md │ │ ├── percent.html │ │ ├── percent.md │ │ ├── print.breaks.html │ │ ├── santoku-cast.html │ │ ├── santoku-cast.md │ │ ├── santoku-package.html │ │ ├── santoku-package.md │ │ ├── santoku.html │ │ ├── santoku_cast_common.Date.html │ │ ├── santoku_cast_common.POSIXct.html │ │ ├── santoku_cast_common.default.html │ │ ├── santoku_cast_common.double.html │ │ ├── santoku_cast_common.hexmode.html │ │ ├── santoku_cast_common.integer64.html │ │ ├── santoku_cast_common.octmode.html │ │ ├── santoku_cast_common.ts.html │ │ ├── santoku_cast_common.zoo.html │ │ ├── sequence-labels.html │ │ ├── tab.html │ │ ├── tab_deciles.html │ │ ├── tab_dissect.html │ │ ├── tab_equally.html │ │ ├── tab_evenly.html │ │ ├── tab_fn.html │ │ ├── tab_mean_sd.html │ │ ├── tab_n.html │ │ ├── tab_pretty.html │ │ ├── tab_proportions.html │ │ ├── tab_quantiles.html │ │ ├── tab_spikes.html │ │ └── tab_width.html │ ├── search.json │ ├── sitemap.xml │ └── tutorials/ │ ├── 00-visualintroduction.html │ ├── 00-visualintroduction.md │ ├── 01-chopping-dates.html │ ├── 01-chopping-dates.md │ ├── index.html │ └── index.md ├── man/ │ ├── breaks-class.Rd │ ├── brk_default.Rd │ ├── brk_manual.Rd │ ├── brk_width-for-datetime.Rd │ ├── chop.Rd │ ├── chop_equally.Rd │ ├── chop_evenly.Rd │ ├── chop_fn.Rd │ ├── chop_mean_sd.Rd │ ├── chop_n.Rd │ ├── chop_pretty.Rd │ ├── chop_proportions.Rd │ ├── chop_quantiles.Rd │ ├── chop_spikes.Rd │ ├── chop_width.Rd │ ├── dissect.Rd │ ├── exactly.Rd │ ├── fillet.Rd │ ├── lbl_dash.Rd │ ├── lbl_datetime.Rd │ ├── lbl_discrete.Rd │ ├── lbl_endpoints.Rd │ ├── lbl_glue.Rd │ ├── lbl_intervals.Rd │ ├── lbl_manual.Rd │ ├── lbl_midpoints.Rd │ ├── lbl_seq.Rd │ ├── non-standard-types.Rd │ ├── percent.Rd │ ├── santoku-cast.Rd │ └── santoku-package.Rd ├── pkgdown/ │ └── extra.css ├── release-process.R ├── santoku.Rproj ├── src/ │ ├── .gitignore │ ├── RcppExports.cpp │ └── categorize.cpp ├── tests/ │ ├── testthat/ │ │ ├── test-Date-DateTime.R │ │ ├── test-breaks.R │ │ ├── test-categorize.R │ │ ├── test-chop.R │ │ ├── test-labels.R │ │ ├── test-nonstandard.R │ │ ├── test-tab.R │ │ └── test-zzz-systematic.R │ └── testthat.R └── vignettes/ ├── .gitignore ├── santoku.Rmd ├── tutorials/ │ ├── chopping-dates-with-santoku.Rmd │ ├── libs/ │ │ ├── Proj4Leaflet/ │ │ │ ├── proj4-compressed.js │ │ │ └── proj4leaflet.js │ │ ├── crosstalk/ │ │ │ ├── css/ │ │ │ │ └── crosstalk.css │ │ │ └── js/ │ │ │ └── crosstalk.js │ │ ├── datatables-binding/ │ │ │ └── datatables.js │ │ ├── datatables-css/ │ │ │ └── datatables-crosstalk.css │ │ ├── dt-core/ │ │ │ └── css/ │ │ │ └── jquery.dataTables.extra.css │ │ ├── header-attrs/ │ │ │ └── header-attrs.js │ │ ├── htmlwidgets/ │ │ │ └── htmlwidgets.js │ │ ├── leaflet/ │ │ │ ├── leaflet.css │ │ │ └── leaflet.js │ │ ├── leaflet-binding/ │ │ │ └── leaflet.js │ │ ├── leafletfix/ │ │ │ └── leafletfix.css │ │ ├── remark-css/ │ │ │ ├── default-fonts.css │ │ │ └── default.css │ │ └── rstudio_leaflet/ │ │ └── rstudio_leaflet.css │ ├── rsconnect/ │ │ └── documents/ │ │ └── visual-introduction.Rmd/ │ │ └── rpubs.com/ │ │ └── rpubs/ │ │ ├── Document.dcf │ │ └── Publish Document.dcf │ ├── visual-intro-styles.css │ └── visual-introduction.Rmd ├── website-articles/ │ ├── performance.Rmd │ └── performance_cache/ │ └── html/ │ ├── __packages │ ├── unnamed-chunk-1_55ef34d700344288a08acda554dff8ac.RData │ ├── unnamed-chunk-1_55ef34d700344288a08acda554dff8ac.rdb │ └── unnamed-chunk-1_55ef34d700344288a08acda554dff8ac.rdx └── whats-new-in-0-9-0.Rmd ================================================ FILE CONTENTS ================================================ ================================================ FILE: .Rbuildignore ================================================ ^.*\.Rproj$ ^\.Rproj\.user$ ^LICENSE\.md$ ^TODO\.md$ ^README\.Rmd$ ^AGENTS\.md$ ^index\.Rmd$ ^advantages\.Rmd$ ^release-process\.R$ ^\.travis\.yml$ ^appveyor\.yml$ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ ^codecov\.yml$ website-articles tutorials revdep ^doc$ ^Meta$ ^cran-comments\.md$ ^CRAN-RELEASE$ ^\.github$ ^CRAN-SUBMISSION$ ^\.positai$ ^\.claude$ ================================================ FILE: .github/.gitignore ================================================ *.html ================================================ FILE: .github/workflows/R-CMD-check.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: name: R-CMD-check.yaml permissions: read-all jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false matrix: config: - {os: macos-latest, r: 'release'} - {os: windows-latest, r: 'release'} - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes steps: - uses: actions/checkout@v4 - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck needs: check - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' ================================================ FILE: .github/workflows/test-coverage.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: name: test-coverage.yaml permissions: read-all jobs: test-coverage: runs-on: ubuntu-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::covr, any::xml2 needs: coverage - name: Test coverage run: | cov <- covr::package_coverage( quiet = FALSE, clean = FALSE, install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") ) print(cov) covr::to_cobertura(cov) shell: Rscript {0} - uses: codecov/codecov-action@v5 with: # Fail if error if not on PR, or if on PR and token is given fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} files: ./cobertura.xml plugins: noop disable_search: true token: ${{ secrets.CODECOV_TOKEN }} - name: Show testthat output if: always() run: | ## -------------------------------------------------------------------- find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true shell: bash - name: Upload test results if: failure() uses: actions/upload-artifact@v4 with: name: coverage-test-failures path: ${{ runner.temp }}/package ================================================ FILE: .gitignore ================================================ .Rproj.user .Rhistory .RData .Ruserdata inst/doc doc Meta .DS_Store revdep/** .positai ================================================ FILE: AGENTS.md ================================================ # AGENTS.md This file provides guidance to agents when working with code in this repository. ## Project Overview santoku is an R package that provides `chop()`, a versatile replacement for `base::cut()` for cutting data into intervals. The package handles numeric vectors, dates, times, and other comparable objects, with support for singleton intervals and flexible labeling. ## Common Commands ### Testing ```r # Run all tests devtools::test() # Run tests from command line R CMD check . # Run specific test file testthat::test_file("tests/testthat/test-chop.R") ``` ### Development workflow ```r # Build package devtools::build() # Install package locally devtools::install() # Check package devtools::check() # Load package for interactive development devtools::load_all() ``` ### Documentation ```r # Update documentation devtools::document() # Build website pkgdown::build_site() ``` ## Architecture ### Core Components - **Main cutting function**: `chop()` in `R/chop.R` - the primary interface that calls other functions - **Break creation**: `R/breaks*.R` files contain functions to create break points (`brk_*` functions) - **Labeling system**: `R/labels*.R` files contain labeling functions (`lbl_*` functions) - **Convenience functions**: `R/chop-*.R` files contain `chop_*` wrapper functions for common use cases - **C++ backend**: `src/categorize.cpp` provides fast interval categorization via Rcpp - **Tabulation**: `R/tab.R` provides `tab_*` functions that chop and tabulate in one step ### Key Design Patterns 1. **Function factories**: Many functions return other functions (e.g., `brk_*` functions return break-creation functions) 2. **Method dispatch**: Uses S3 methods and vctrs for handling different data types (numbers, dates, etc.) 3. **Extensible labeling**: Label functions can be combined and customized using the `lbl_*` family 4. **Performance**: Core categorization logic is implemented in C++ for speed ### File Organization - `R/chop.R` - Main `chop()` function and documentation - `R/breaks*.R` - Break point creation (`brk_default`, `brk_width`, etc.) - `R/labels*.R` - Label generation (`lbl_intervals`, `lbl_dash`, etc.) - `R/chop-*.R` - Convenience functions (`chop_quantiles`, `chop_width`, etc.) - `R/tab.R` - Tabulation functions - `R/utils.R` - Utility functions like `exactly()` and `percent()` - `src/categorize.cpp` - Fast C++ categorization implementation - `tests/testthat/` - Comprehensive test suite ## Development Notes - The package uses Rcpp for performance-critical categorization - Tests are extensive and include systematic testing in `test-zzz-systematic.R` - The package supports non-standard data types (dates, times, units) via the vctrs package - Documentation follows roxygen2 conventions with extensive examples - Uses lifecycle package for function lifecycle management ================================================ FILE: DESCRIPTION ================================================ Package: santoku Type: Package Title: A Versatile Cutting Tool Version: 1.2.1 Authors@R: c( person(given = "David", family = "Hugh-Jones", role = c("aut", "cre"), email = "davidhughjones@gmail.com"), person(given = "Daniel", family = "Possenriede", role = c("ctb"), email = "possenriede@gmail.com") ) Maintainer: David Hugh-Jones Description: A tool for cutting data into intervals. Allows singleton intervals. Always includes the whole range of data by default. Flexible labelling. Convenience functions for cutting by quantiles etc. Handles dates, times, units and other vectors. License: MIT + file LICENSE Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.3 Suggests: bench, bit64, covr, haven, Hmisc, hms, knitr, lubridate, purrr, rmarkdown, scales, stringi, testthat (>= 3.2.0), units, withr, xts, zoo Config/testthat/edition: 3 LinkingTo: Rcpp Depends: R (>= 3.5.0) Imports: Rcpp, assertthat, glue, lifecycle, rlang, vctrs URL: https://github.com/hughjonesd/santoku, https://hughjonesd.github.io/santoku/ BugReports: https://github.com/hughjonesd/santoku/issues VignetteBuilder: knitr RdMacros: lifecycle ================================================ FILE: LICENSE ================================================ YEAR: 2020 COPYRIGHT HOLDER: David Hugh-Jones ================================================ FILE: LICENSE.md ================================================ # MIT License Copyright (c) 2019 David Hugh-Jones Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand S3method(apply_format,"function") S3method(apply_format,character) S3method(apply_format,list) S3method(apply_format.character,character) S3method(apply_format.character,default) S3method(apply_format.character,numeric) S3method(apply_format.list,default) S3method(as.double,breaks) S3method(brk_width,Duration) S3method(brk_width,default) S3method(class_bounds,Date) S3method(class_bounds,POSIXct) S3method(class_bounds,default) S3method(class_bounds,difftime) S3method(class_bounds,integer64) S3method(class_bounds,numeric) S3method(class_bounds,units) S3method(class_bounds,zoo) S3method(endpoint_labels,Date) S3method(endpoint_labels,POSIXt) S3method(endpoint_labels,default) S3method(endpoint_labels,double) S3method(endpoint_labels,integer) S3method(endpoint_labels,numeric) S3method(endpoint_labels,quantileBreaks) S3method(endpoint_labels,sdBreaks) S3method(format,breaks) S3method(print,breaks) S3method(santoku_cast_common,Date) S3method(santoku_cast_common,Date.Date) S3method(santoku_cast_common,Date.POSIXct) S3method(santoku_cast_common,POSIXct) S3method(santoku_cast_common,POSIXct.Date) S3method(santoku_cast_common,POSIXct.POSIXct) S3method(santoku_cast_common,default) S3method(santoku_cast_common,default.default) S3method(santoku_cast_common,default.hexmode) S3method(santoku_cast_common,default.integer64) S3method(santoku_cast_common,default.octmode) S3method(santoku_cast_common,default.ts) S3method(santoku_cast_common,default.zoo) S3method(santoku_cast_common,double) S3method(santoku_cast_common,double.default) S3method(santoku_cast_common,double.integer64) S3method(santoku_cast_common,hexmode) S3method(santoku_cast_common,hexmode.default) S3method(santoku_cast_common,hexmode.hexmode) S3method(santoku_cast_common,integer64) S3method(santoku_cast_common,integer64.default) S3method(santoku_cast_common,integer64.double) S3method(santoku_cast_common,integer64.integer64) S3method(santoku_cast_common,octmode) S3method(santoku_cast_common,octmode.default) S3method(santoku_cast_common,octmode.octmode) S3method(santoku_cast_common,ts) S3method(santoku_cast_common,ts.default) S3method(santoku_cast_common,zoo) S3method(santoku_cast_common,zoo.default) S3method(scaled_endpoints,breaks) S3method(scaled_endpoints,default) S3method(sequence_width,Period) S3method(sequence_width,default) export(brk_default) export(brk_equally) export(brk_evenly) export(brk_fn) export(brk_manual) export(brk_mean_sd) export(brk_n) export(brk_pretty) export(brk_proportions) export(brk_quantiles) export(brk_spikes) export(brk_width) export(chop) export(chop_deciles) export(chop_equally) export(chop_evenly) export(chop_fn) export(chop_mean_sd) export(chop_n) export(chop_pretty) export(chop_proportions) export(chop_quantiles) export(chop_spikes) export(chop_width) export(dissect) export(exactly) export(fillet) export(is.breaks) export(kiru) export(lbl_dash) export(lbl_date) export(lbl_datetime) export(lbl_discrete) export(lbl_endpoint) export(lbl_endpoints) export(lbl_glue) export(lbl_intervals) export(lbl_manual) export(lbl_midpoints) export(lbl_seq) export(percent) export(santoku_cast_common.Date) export(santoku_cast_common.POSIXct) export(santoku_cast_common.default) export(santoku_cast_common.double) export(santoku_cast_common.hexmode) export(santoku_cast_common.integer64) export(santoku_cast_common.octmode) export(santoku_cast_common.ts) export(santoku_cast_common.zoo) export(tab) export(tab_deciles) export(tab_dissect) export(tab_equally) export(tab_evenly) export(tab_fn) export(tab_mean_sd) export(tab_n) export(tab_pretty) export(tab_proportions) export(tab_quantiles) export(tab_spikes) export(tab_width) import(assertthat) importFrom(Rcpp,sourceCpp) importFrom(lifecycle,deprecated) useDynLib(santoku, .registration = TRUE) ================================================ FILE: NEWS.md ================================================ # santoku 1.2.1 * Fixed a test bug. # santoku 1.2.0 * New experimental `lbl_date()` and `lbl_datetime()` functions for pretty formatting of dates and date-times. * Bugfix: extended breaks were failing on `haven::labelled` objects. * The `raw` argument to `lbl_*` functions, deprecated since 0.9.0, now throws an error. # santoku 1.1.0 * Core logic has been speeded up using raw pointers. This was vibe-coded by me and Claude Code. If it breaks, please file a bug report. * The experimental `chop_spikes()` and `dissect()` functions give common values of `x` their own singleton intervals. * On Unicode platforms, infinity will be represented as ∞ in breaks. Set `options(santoku.infinity = "Inf")` to use the old behaviour. * Singleton breaks are not labelled specially by default in `chop_quantiles(..., raw = FALSE)`. This means that e.g. if the 10th and 20th percentiles are both the same number, the label will still be `[10%, 20%]`. * When multiple quantiles are the same, santoku warns and returns the leftmost quantile interval. Before it would merge the intervals, creating labels that might be different to what the user asked for. * `chop_quantiles()` gains a `recalc_probs` argument. `recalc_probs = TRUE` recalculates probabilities using `ecdf(x)`, which may give more accurate interval labels. * `single = NULL` has been documented explicitly in `lbl_*` functions. * Bugfix: `brk_manual()` no longer warns if `close_end = TRUE` (the default). # santoku 1.0.0 * santoku is now considered stable. * `chop_quantiles()` and `brk_quantiles()` gain a new `weights` argument, letting you chop by weighted quantiles using `Hmisc::wtd.quantile()`. * `brk_quantiles()` may now return singleton breaks, producing more accurate results when `x` has duplicate elements. * Some deprecated functions have been removed, and the `raw` argument to `lbl_*` functions now always gives a deprecation warning. # santoku 0.10.0 * List arguments to `fmt` in `lbl_*` functions will be taken as arguments to `base::format`. This gives more flexibility in formatting, e.g., `units` breaks. * `chop_n()` gains a `tail` argument, to deal with a last interval containing less than `n` elements. Set `tail = "merge"` to merge it with the previous interval. This guarantees that all intervals contain at least `n` elements. * `chop_equally()` may return fewer than `groups` groups when there are duplicate elements. We now warn when this happens. * Bugfix: `chop_n()` could return intervals with fewer than `n` elements when there were duplicate elements. The new algorithm avoids this, but may be slower in this case. # santoku 0.9.1 * `endpoint_labels()` methods gain an unused `...` argument to satisfy R CMD CHECK. # santoku 0.9.0 ## Breaking changes There are important changes to `close_end`. * `close_end` is now `TRUE` by default in `chop()` and `fillet()`. In previous versions: ```r chop(1:2, 1:2) ## [1] [1, 2) {2} ## Levels: [1, 2) {2} ``` Whereas now: ```r chop(1:2, 1:2) ## [1] [1, 2] [1, 2] ## Levels: [1, 2] ``` * `close_end` is now always applied after `extend`. For example, in previous versions: ```r chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3] [2, 3] (3, 4] ## Levels: [1, 2) [2, 3] (3, 4] ``` Whereas now: ```r chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3) [3, 4] [3, 4] ## Levels: [1, 2) [2, 3) [3, 4] ``` We changed this behaviour to be more in line with user expectations. * If `breaks` has names, they will be used as labels: ```r chop(1:5, c(Low = 1, Mid = 2, High = 4)) ## [1] Low Mid Mid High High ## Levels: Low Mid High ``` Names can also be used for labels in `probs` in `chop_quantiles()` and `proportions` in `chop_proportions()`. * There is a new `raw` parameter to `chop()`. This replaces the parameter `raw` in `lbl_*` functions, which is now soft-deprecated. * `lbl_manual()` is deprecated. Just use a vector argument to `labels` instead. * A `labels` argument to `chop_quantiles()` now needs to be explicitly named. I expect these to be the last important breaking changes before we release version 1.0 and mark the package as "stable". If they cause problems for you, please file an issue. ## Other changes * New `chop_fn()`, `brk_fn()` and `tab_fn()` chop using an arbitrary function. * Added section on non-standard objects to vignette. # santoku 0.8.0 ## Breaking changes * `lbl_endpoint()` has been renamed to `lbl_endpoints()`. The old version will trigger a deprecation warning. `lbl_endpoints()` gains `first`, `last` and `single` arguments like other labelling functions. ## Other changes * New `chop_pretty()`, `brk_pretty()` and `tab_pretty()` functions use `base::pretty()` to calculate attractive breakpoints. Thanks @davidhodge931. * New `chop_proportions()`, `brk_proportions()` and `tab_proportions()` functions chop `x` into proportions of its range. * `chop_equally()` now uses `lbl_intervals(raw = TRUE)` by default, bringing it into line with `chop_evenly()`, `chop_width()` and `chop_n()`. * New `lbl_midpoints()` function labels breaks by their midpoints. * `lbl_discrete()` gains a `single` argument. * You can now chop `ts`, `xts::xts` and `zoo::zoo` objects. * `chop()` is more forgiving when mixing different types, e.g.: - `Date` objects with `POSIXct` breaks, and vice versa - `bit64::integer64` and `double`s * Bugfix: `lbl_discrete()` sometimes had ugly label formatting. # santoku 0.7.0 ## Breaking changes * In labelling functions, `first` and `last` arguments are now passed to `glue::glue()`. Variables `l` and `r` represent the left and right endpoints of the intervals. * `chop_mean_sd()` now takes a vector `sds` of standard deviations, rather than a single maximum number `sd` of standard deviations. Write e.g. `chop_mean_sd(sds = 1:3)` rather than `chop_mean_sd(sd = 3)`. The `sd` argument is deprecated. * The `groups` argument to `chop_evenly()`, deprecated in 0.4.0, has been removed. * `brk_left()` and `brk_right()`, deprecated in 0.4.0, have been removed. * `knife()`, deprecated in 0.4.0, has been removed. * `lbl_format()`, questioning since 0.4.0, has been removed. * Arguments of `lbl_dash()` and `lbl_intervals()` have been reordered for consistency with other labelling functions. ## Other changes * You can now chop many more types, including `units` from the `units` package, `difftime` objects, `package_version` objects, etc. - Character vectors will be chopped by lexicographic order, with an optional warning. - If you have problems chopping a vector type, file a bug report. * The `{glue}` package has become a hard dependency. It is used in many places to format labels. * There is a new `lbl_glue()` function using the `{glue}` package. Thanks to @dpprdan. * You can now set `labels = NULL` to return integer codes. * Arguments `first`, `last` and `single` can be used in `lbl_intervals()` and `lbl_dash()`, to override the first and last interval labels, or to label singleton intervals. * `lbl_dash()` and `lbl_discrete()` use unicode em-dash where possible. * `brk_default()` throws an error if breaks are not sorted. ## Bugfixes * Bugfix: `tab()` and friends no longer display an `x` as the variable name. * Bugfix: `lbl_endpoint()` was erroring for some types of breaks. # santoku 0.6.0 * New arguments `first` and `last` in `lbl_dash()` and `lbl_discrete()` allow you to override the first and last interval labels. * Fixes for CRAN. # santoku 0.5.0 * Negative numbers can be used in `chop_width()`. - This sets `left = FALSE` by default. - Also works for negative time intervals. # santoku 0.4.1 * Bugfix: `chop(1:4, 1)` was erroring. # santoku 0.4.0 ## Interface changes The new version has some interface changes. These are based on user experience, and are designed to make using `chop()` more intuitive and predictable. * `chop()` has two new arguments, `left` and `close_end`. - Using `left = FALSE` is simpler and more intuitive than wrapping breaks in `brk_right()`. - `brk_left()` and `brk_right()` have been kept for now, but cannot be used to wrap other break functions. - Using `close_end` is simpler than passing `close_end` into `brk_left()` or `brk_right()` (which no longer accept this argument directly). - `left = TRUE` by default, except for non-numeric objects in `chop_quantiles()` and `chop_equally()`, where `left = FALSE` works better. * `close_end` is now `FALSE` by default. - This prevents user surprises when e.g. `chop(3, 1:3)` puts `3` into a different category than `chop(3, 1:4)`. - `close_end` is `TRUE` by default for `chop_quantiles()`, `chop_n()` and similar functions. This ensures that e.g. `chop_quantiles(x, c(0, 1/3, 2/3, 1))` does what you would expect. * The `groups` argument to `chop_evenly()` has been renamed from `groups` to `intervals`. This should make it easier to remember the difference between `chop_evenly()` and `chop_equally()`. (Chop evenly into `n` equal-width *intervals*, or chop equally into `n` equal-sized *groups*.) * `knife()` has been deprecated to keep the interface slim and focused. Use `purrr::partial()` instead. ## Other changes * Date and datetime (`POSIXct`) objects can now be chopped. - `chop_width()` accepts `difftime`, `lubridate::period` or `lubridate::duration` objects - all other `chop_` functions work as well. * Many labelling functions have a new `fmt` argument. This can be a string interpreted by `sprintf()` or `format()`, or a 1-argument formatting function for break endpoints, e.g. `scales::label_percent()`. * Experimental: `lbl_discrete()` for discrete data such as integers or (most) dates. * There is a new `lbl_endpoint()` function for labelling intervals solely by their left or right endpoint. * `brk_mean_sd()` now accepts non-integer positive numbers. * Add `brk_equally()` for symmetry with `chop_equally()`. * Minor tweaks to `chop_deciles()`. * Bugfix: `lbl_format()` wasn't accepting numeric formats, even when `raw = TRUE`. Thanks to Sharla Gelfand. # santoku 0.3.0 * First CRAN release. * Changed `kut()` to `kiru()`. `kiru()` is an alternative spelling for `chop()`, for use when the tidyr package is loaded. * `lbl_sequence()` has become `lbl_manual()`. * `lbl_letters()` and friends have been replaced by `lbl_seq()`: - to replace `lbl_letters()` use `lbl_seq()` - to replace `lbl_LETTERS()` use `lbl_seq("A")` - to replace `lbl_roman()` use `lbl_seq("i")` - to replace `lbl_ROMAN()` use `lbl_seq("I")` - to replace `lbl_numerals()` use `lbl_seq("1")` - for more complex formatting use e.g. `lbl_seq("A:")`, `lbl_seq("(i)")` # santoku 0.2.0 * Added a `NEWS.md` file to track changes to the package. * Default labels when `extend = NULL` have changed, from `[-Inf, ...` and `..., Inf]` to `[min(x), ...` and `..., max(x)]`. ================================================ FILE: R/RcppExports.R ================================================ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 categorize_impl <- function(x, breaks, left) { .Call(`_santoku_categorize_impl`, x, breaks, left) } ================================================ FILE: R/breaks-by-group-size.R ================================================ #' @rdname chop_quantiles #' #' @export #' @order 2 brk_quantiles <- function (probs, ..., weights = NULL, recalc_probs = FALSE) { assert_that( is.numeric(probs), noNA(probs), all(probs >= 0), all(probs <= 1), is.null(weights) || is.numeric(weights), is.flag(recalc_probs) ) probs <- sort(probs) function (x, extend, left, close_end) { dots <- list(...) dots$x <- x if (! is.numeric(x) && ! "type" %in% names(dots)) dots$type <- 1 dots$probs <- probs dots$na.rm <- TRUE qs <- if (is.null(weights)) { do.call(stats::quantile, dots) } else { rlang::check_installed("Hmisc", reason = "to use `weights` in brk_quantiles()") dots$weights <- weights do.call(Hmisc::wtd.quantile, dots) } if (anyNA(qs)) return(empty_breaks()) # data was all NA if (anyDuplicated(qs) > 0L) { if (! recalc_probs) { warning("`x` has duplicate quantiles: break labels may be misleading") } # We use the left-most probabilities, so e.g. if 0%, 20% and 40% quantiles # are all the same number, we'll use the category [0%, 20%). # This means we always return intervals that the user asked for, though # they may be more misleading than e.g. [0%, 40%). illegal_dupes <- find_illegal_duplicates(qs) qs <- qs[! illegal_dupes] probs <- probs[! illegal_dupes] } breaks <- create_lr_breaks(qs, left) needs <- needs_extend(breaks, x, extend, left, close_end) if ((needs & LEFT) > 0) probs <- c(0, probs) if ((needs & RIGHT) > 0) probs <- c(probs, 1) breaks <- extend_and_close(breaks, x, extend, left, close_end) class(breaks) <- c("quantileBreaks", class(breaks)) if (recalc_probs) { probs <- calculate_ecdf_probs(x, breaks, weights) } attr(breaks, "scaled_endpoints") <- probs names(breaks) <- names(probs) breaks } } #' Calculate the proportions of `x` that is strictly/weakly less than #' each break #' #' @param x A numeric vector #' @param breaks A breaks object #' @param weights A vector of weights. Non-NULL weights are unimplemented #' #' @return A vector of proportions of `x` that are strictly less than #' left-closed breaks, and weakly less than right-closed breaks. #' #' @noRd calculate_ecdf_probs <- function (x, breaks, weights) { if (! is.numeric(x)) { stop("`recalc_probs = TRUE` can only be used with numeric `x`") } if (! is.null(weights)) { stop("`recalc_probs = TRUE` cannot be used with non-null `weights`") } brk_vec <- unclass_breaks(breaks) left_vec <- attr(breaks, "left") # proportion of x that is weakly less than x prop_lte_brk <- stats::ecdf(x)(brk_vec) # proportion of x that is strictly less than x prop_lt_brk <- 1 - stats::ecdf(-x)(-brk_vec) probs <- ifelse(left_vec, prop_lt_brk, prop_lte_brk) # Suppose your breaks are [a, b]. # You want to expand this? probs } #' @rdname chop_equally #' #' @export #' @order 2 brk_equally <- function (groups) { assert_that(is.count(groups)) brq <- brk_quantiles(seq(0L, groups)/groups) function (x, extend, left, close_end) { breaks <- brq(x = x, extend = extend, left = left, close_end = close_end) if (length(breaks) < groups + 1) { warning("Fewer than ", groups, " intervals created") } breaks } } #' @rdname chop_n #' @export #' @order 2 brk_n <- function (n, tail = "split") { assert_that(is.count(n), tail == "split" || tail == "merge") function (x, extend, left, close_end) { xs <- sort(x, decreasing = ! left, na.last = NA) # remove NAs if (length(xs) < 1L) return(empty_breaks()) dupes <- duplicated(xs) breaks <- xs[0] # ensures breaks has type of xs last_x <- xs[length(xs)] maybe_merge_tail <- function (breaks, tail) { if (tail == "merge" && length(breaks) > 1) { breaks <- breaks[-length(breaks)] } breaks } # Idea of the algorithm: # Loop: # if there are no dupes, just take a sequence of each nth element # starting at 1, and exit # if there are remaining dupes, then take the first element # set m to the (n+1)th element which would normally be next # if element m is a dupe: # - we need to go up, otherwise elements to the left will be in the next # interval, and this interval will be too small # - so set m to the next non-dupe (i.e. strictly larger) element # now delete the first m-1 elements # And repeat while (TRUE) { if (! any(dupes)) { breaks <- c(breaks, xs[seq(1L, length(xs), n)]) if (length(xs) %% n > 0) { breaks <- maybe_merge_tail(breaks, tail) } break } breaks <- c(breaks, xs[1]) m <- n + 1 if (length(xs) <= n || all(dupes[-seq_len(m - 1)])) { if (length(xs) < n) { breaks <- maybe_merge_tail(breaks, tail) } break } if (dupes[m]) { # the first non-dupe will be the next element that is different # we know there is one, because we checked above m <- m + match(FALSE, dupes[-(1:m)]) } discard <- seq_len(m - 1) xs <- xs[-discard] dupes <- dupes[-discard] } breaks <- c(breaks, last_x) if (! left) breaks <- rev(breaks) breaks <- create_extended_breaks(breaks, x, extend, left, close_end) breaks } } ================================================ FILE: R/breaks-by-width.R ================================================ #' Equal-width intervals for dates or datetimes #' #' `brk_width()` can be used with time interval classes from base R or the #' `lubridate` package. #' #' @param width A scalar [difftime], [Period][lubridate::Period-class] or #' [Duration][lubridate::Duration-class] object. #' #' @param start A scalar of class [Date][base::Dates] or [POSIXct][DateTimeClasses]. #' Can be omitted. #' #' @details #' If `width` is a Period, [`lubridate::add_with_rollback()`][`lubridate::m+`] #' is used to calculate the widths. This can be useful for e.g. calendar months. #' #' @examples #' #' if (requireNamespace("lubridate")) { #' year2001 <- as.Date("2001-01-01") + 0:364 #' tab_width(year2001, months(1), #' labels = lbl_discrete(" to ", fmt = "%e %b %y")) #' } #' #' @name brk_width-for-datetime NULL #' @rdname chop_width #' @export #' @order 2 brk_width <- function (width, start) UseMethod("brk_width") #' @rdname brk_width-for-datetime #' @export brk_width.Duration <- function (width, start) { loadNamespace("lubridate") width <- lubridate::make_difftime(as.numeric(width)) NextMethod() } #' @rdname chop_width #' @export #' @order 2 brk_width.default <- function (width, start) { assert_that(is.scalar(width)) sm <- missing(start) if (! sm) assert_that(is.scalar(start)) function (x, extend, left, close_end) { # finite if x has any non-NA finite elements: min_x <- quiet_min(x[is.finite(x)]) max_x <- quiet_max(x[is.finite(x)]) if (sm) { start <- if (sign(width) > 0) min_x else max_x } until <- if (sign(width) > 0) max_x else min_x if (is.finite(start) && is.finite(until)) { breaks <- sequence_width(width, start, until) } else { return(empty_breaks()) } if (sign(width) <= 0) breaks <- rev(breaks) breaks <- create_extended_breaks(breaks, x, extend, left, close_end) breaks } } #' @rdname chop_evenly #' @export #' @order 2 brk_evenly <- function(intervals) { assert_that(is.count(intervals)) function (x, extend, left, close_end) { min_x <- quiet_min(x[is.finite(x)]) max_x <- quiet_max(x[is.finite(x)]) if (sign(max_x - min_x) <= 0) return(empty_breaks()) breaks <- seq(min_x, max_x, length.out = intervals + 1L) breaks <- create_extended_breaks(breaks, x, extend, left, close_end) breaks } } #' @rdname chop_proportions #' @export #' @order 2 brk_proportions <- function(proportions) { assert_that(is.numeric(proportions), noNA(proportions), all(proportions >= 0), all(proportions <= 1)) proportions <- sort(proportions) function (x, extend, left, close_end) { min_x <- quiet_min(x[is.finite(x)]) max_x <- quiet_max(x[is.finite(x)]) range_x <- max_x - min_x if (sign(range_x) <= 0) return(empty_breaks()) breaks <- min_x + range_x * proportions breaks <- create_lr_breaks(breaks, left) scaled_endpoints <- proportions needs <- needs_extend(breaks, x, extend, left, close_end) if ((needs & LEFT) > 0) scaled_endpoints <- c(0, scaled_endpoints) if ((needs & RIGHT) > 0) scaled_endpoints <- c(scaled_endpoints, 1) breaks <- extend_and_close(breaks, x, extend, left, close_end) attr(breaks, "scaled_endpoints") <- scaled_endpoints names(breaks) <- names(scaled_endpoints) breaks } } ================================================ FILE: R/breaks-impl.R ================================================ #' Create a breaks object #' #' @param obj A sorted vector or a `breaks` object. #' @param left A logical vector, same length as `obj`. #' #' @return A breaks object #' #' @noRd #' create_breaks <- function (obj, left) { if (anyNA(obj)) stop("breaks contained NAs") stopifnot(all(obj == sort(obj))) stopifnot(is.logical(left)) stopifnot(length(left) == length(obj)) if (any(find_illegal_duplicates(obj))) { stop("breaks contained more than two consecutive equal values") } singletons <- singletons(obj) l_singletons <- c(singletons, FALSE) r_singletons <- c(FALSE, singletons) stopifnot(all(left[l_singletons])) stopifnot(all(! left[r_singletons])) break_classes <- class(obj) if (! inherits(obj, "breaks")) break_classes <- c("breaks", break_classes) structure(obj, left = left, class = break_classes) } create_extended_breaks <- function (obj, x, extend, left, close_end) { brks <- create_lr_breaks(obj = obj, left = left) extend_and_close(breaks = brks, x = x, extend = extend, left = left, close_end = close_end) } create_lr_breaks <- function (obj, left) { assert_that(is.flag(left)) left_vec <- rep(left, length(obj)) st <- singletons(obj) left_vec[which(st)] <- TRUE left_vec[which(st) + 1] <- FALSE create_breaks(obj, left_vec) } empty_breaks <- function () { create_breaks(c(-Inf, Inf), c(TRUE, FALSE)) } #' Extend `breaks` to the left or right according to `extend` parameter, #' and close end according to `close_end` parameter #' #' @param breaks,x,extend,left,close_end All passed in from `chop()` via #' a `brk_` inner function #' #' @return A `breaks` object. #' @noRd extend_and_close <- function (breaks, x, extend, left, close_end) { extend_flags <- needs_extend(breaks, x, extend, left, close_end) if ((extend_flags & LEFT) > 0) { breaks <- extend_endpoint_left(breaks, x, extend) } if ((extend_flags & RIGHT) > 0) { breaks <- extend_endpoint_right(breaks, x, extend) } breaks <- maybe_close_end(breaks, left = left, close_end = close_end) return(breaks) } NEITHER <- as.raw(0) LEFT <- as.raw(1) RIGHT <- as.raw(2) BOTH <- LEFT | RIGHT #' Reports if `breaks` will/should be extended. #' #' @param breaks A breaks object #' @param x Data #' @param extend,left,close_end Parameters passed into `chop` #' #' @return Returns LEFT or RIGHT or BOTH only if `breaks` *will*/*must* be #' extended i.e. gain an extra break, on the respective sides. #' #' @details #' If `extend` is `FALSE`, always returns `NEITHER`. If `breaks` is length #' zero, always returns `BOTH`. #' #' If extend is `NULL` then `left` and `close_end` are taken into account. #' #' To test whether `breaks` will be extended on either side, use #' `(needs & LEFT) > 0` or `(needs & RIGHT) > 0`. #' #' @noRd needs_extend <- function (breaks, x, extend, left, close_end) { if (! is.null(extend) && ! extend) return(NEITHER) if (length(breaks) < 1L) return(BOTH) needs <- NEITHER # temporarily close the breaks, to see if unextended closed breaks need # extension breaks <- maybe_close_end(breaks, left = left, close_end = close_end) left_vec <- attr(breaks, "left") res <- santoku_cast_common(x, unclass_breaks(breaks)) x <- res[[1]] breaks <- res[[2]] min_x <- quiet_min(x) max_x <- quiet_max(x) if ( isTRUE(extend) || min_x < min(breaks) || (! left_vec[1] && min_x == min(breaks)) ) { # "... and if ..." the first break is finite, or will be left-open if (is_gt_minus_inf(breaks[1]) || ! left_vec[1]) { needs <- needs | LEFT } } if ( isTRUE(extend) || max_x > max(breaks) || (left_vec[length(left_vec)] && max_x == max(breaks)) ) { # "... and if ..." the last break is finite, or will be left-closed (right-open) if (is_lt_inf(breaks[length(breaks)]) || left_vec[length(left_vec)]) { needs <- needs | RIGHT } } return(needs) } #' Close end of breaks if close_end is TRUE #' #' This never adds a break, it just changes the breaks' `left` attribute. #' It leaves everything unchanged if `close_end` is `FALSE`. #' #' @param breaks,left,close_end Passed in from a `brk_` function #' #' @return New breaks object, with the end perhaps closed #' @noRd maybe_close_end <- function (breaks, left, close_end) { if (! close_end) return(breaks) left_vec <- attr(breaks, "left") if (left) { left_vec[length(left_vec)] <- FALSE } else { left_vec[1] <- TRUE } attr(breaks, "left") <- left_vec return(breaks) } #' Extend the left endpoint of a breaks object according to user parameters #' #' This always adds a new break, which is `-Inf` if `extend` is `TRUE` #' and equal to the minimum of `x` if `extend` is `NULL`. #' #' It fixes the `left` attribute if a new singleton break is going to be #' created. #' #' @param breaks,x,extend Passed in from a `brk_` inner function #' #' @return A new breaks object #' @noRd extend_endpoint_left <- function (breaks, x, extend) { left <- attr(breaks, "left") q <- quiet_min(x) # non-finite q could be Inf if x is empty. Not appropriate for a left endpoint! extra_break <- if (is.null(extend) && is_gt_minus_inf(q)) q else class_bounds(x)[1] res <- santoku_cast_common(extra_break, unclass_breaks(breaks)) breaks <- vctrs::vec_c(res[[1]], res[[2]]) # Ensure that a new "singleton" break has the right TRUE,FALSE left-closed # pattern if (length(breaks) > 1 && breaks[1] == breaks[2]) { left[1] <- FALSE } breaks <- create_breaks(breaks, c(TRUE, left)) breaks } #' Extend the right endpoint of a breaks object according to user parameters #' #' This always adds a new break, which is `Inf` if `extend` is `TRUE` #' and equal to the maximum of `x` if `extend` is `NULL`. #' #' It fixes the `left` attribute if a new singleton break is going to be #' created. #' #' @param breaks,x,extend Passed in from a `brk_` inner function #' #' @return A new breaks object #' @noRd extend_endpoint_right <- function (breaks, x, extend) { left <- attr(breaks, "left") q <- quiet_max(x) extra_break <- if (is.null(extend) && is_lt_inf(q)) q else class_bounds(x)[2] # necessary because min() and max() may unclass things res <- santoku_cast_common(unclass_breaks(breaks), extra_break) breaks <- vctrs::vec_c(res[[1]], res[[2]]) lb <- length(breaks) # Ensure that a new "singleton" break has the right TRUE,FALSE left-closed # pattern if (lb > 1 && breaks[lb] == breaks[lb - 1]) { left[length(left)] <- TRUE } breaks <- create_breaks(breaks, c(left, FALSE)) breaks } is_lt_inf <- function (x) { x <- tryCatch(strict_as_numeric(x), error = function (...) return(TRUE) ) x < Inf } is_gt_minus_inf <- function (x) { x <- tryCatch(strict_as_numeric(x), error = function (...) return(TRUE) ) x > -Inf } #' Return the infimum and supremum of a class #' #' The default tries to cast `c(-Inf, Inf)` to the #' class. If this fails, it returns `c(min(x), max(x))` #' and emits a warning. #' #' @param x Only used for its class #' #' @return A length-two object #' @noRd class_bounds <- function (x) { UseMethod("class_bounds") } #' @export class_bounds.numeric <- function (x) c(-Inf, Inf) #' @export class_bounds.POSIXct <- function (x) { as.POSIXct(c(-Inf, Inf), origin = "1970-01-01") } #' @export class_bounds.Date <- function (x) { as.Date(c(-Inf, Inf), origin = "1970-01-01") } #' @export class_bounds.difftime <- function (x) { as.difftime(c(-Inf, Inf), units = units(x)) } #' @export class_bounds.units <- function (x) { loadNamespace("units") # note: the units() call is from namespace base, not units units::set_units(c(-Inf, Inf), units(x), mode = "standard") } #' @export class_bounds.integer64 <- function (x) { loadNamespace("bit64") bit64::lim.integer64() } #' @export class_bounds.zoo <- function (x) { loadNamespace("zoo") zoo::zoo(c(-Inf, Inf)) } #' @export class_bounds.default <- function (x) { tryCatch( vctrs::vec_cast(c(-Inf, Inf), x), error = function(...) { warning("Class '", paste(class(x), collapse = "', '"), "' has no natural endpoints corresponding to +/-Inf for `extend = TRUE`;") c(quiet_min(x), quiet_max(x)) } ) } #' Removes the "breaks" class, and all subclasses, from a break object #' #' @param breaks A breaks object #' #' @return The object, with any remaining (super)classes #' @noRd unclass_breaks <- function (breaks) { assert_that(is.breaks(breaks)) class_pos <- inherits(breaks, "breaks", which = TRUE) superclasses <- class(breaks)[-seq_len(class_pos)] class(breaks) <- if (length(superclasses) == 0 ) { NULL } else { superclasses } # this helps vec_cast_common deal with unusual types of breaks attr(breaks, "left") <- NULL breaks } #' @export as.double.breaks <- function (x, ...) { as.double(unclass_breaks(x), ...) } #' Return a sequence of width `width` #' #' @param width An object representing a width #' @param start Element to start from #' @param until Result must be just long enough to cover this element #' #' @return A sequence of breaks #' @noRd sequence_width <- function(width, start, until) { UseMethod("sequence_width") } #' @export sequence_width.default <- function (width, start, until) { breaks <- seq(start, until, width) too_short <- if (sign(width) > 0) { breaks[length(breaks)] < until } else { breaks[length(breaks)] > until } # length(breaks) == 1L captures when start == max_x if (too_short || length(breaks) == 1L) { breaks <- c(breaks, breaks[length(breaks)] + width) } breaks } #' @export sequence_width.Period <- function(width, start, until) { loadNamespace("lubridate") if (as.numeric(until - start) %% as.numeric(width) != 0 || until == start) { # extend to cover all data / ensure at least one interval until <- lubridate::add_with_rollback(until, width) } # alternative to seq, using Period arithmetic # We find the number n of widths that gets beyond seq_end # and add (width * 0:n) to start # normally this would be ceiling((seq_end - start)/width) # we calculate it roughly using a Duration n_intervals <- ceiling((until - start)/lubridate::as.duration(width)) breaks <- lubridate::add_with_rollback(start, (seq(0, n_intervals) * width)) last_break <- breaks[length(breaks)] too_short <- if (width > 0) { last_break < until } else { last_break > until } if (too_short) { breaks <- c(breaks, lubridate::add_with_rollback(last_break, width)) } breaks } ================================================ FILE: R/breaks-misc.R ================================================ #' Create a `breaks` object manually #' #' @param breaks A vector, which must be sorted. #' @param left_vec A logical vector, the same length as `breaks`. #' Specifies whether each break is left-closed or right-closed. #' #' @inherit breaks-doc return #' #' @details #' #' All breaks must be closed on exactly one side, like `..., x) [x, ...` #' (left-closed) or `..., x) [x, ...` (right-closed). #' #' For example, if `breaks = 1:3` and `left = c(TRUE, FALSE, TRUE)`, then the #' resulting intervals are \preformatted{ #' T F T #' [ 1, 2 ] ( 2, 3 ) #' } #' #' Singleton breaks are created by repeating a number in `breaks`. Singletons #' must be closed on both sides, so if there is a repeated number #' at indices `i`, `i+1`, `left[i]` *must* be `TRUE` and `left[i+1]` must be #' `FALSE`. #' #' `brk_manual()` ignores `left` and `close_end` arguments passed in #' from [chop()], since `left_vec` sets these manually. #' `extend` and `drop` arguments are respected as usual. #' #' @export #' #' @examples #' lbrks <- brk_manual(1:3, rep(TRUE, 3)) #' chop(1:3, lbrks, extend = FALSE) #' #' rbrks <- brk_manual(1:3, rep(FALSE, 3)) #' chop(1:3, rbrks, extend = FALSE) #' #' brks_singleton <- brk_manual( #' c(1, 2, 2, 3), #' c(TRUE, TRUE, FALSE, TRUE)) #' #' chop(1:3, brks_singleton, extend = FALSE) #' brk_manual <- function (breaks, left_vec) { assert_that( is.numeric(breaks), noNA(breaks), is.logical(left_vec), noNA(left_vec), length(left_vec) == length(breaks) ) function (x, extend, left, close_end) { if (! left) warning("Ignoring `left` with `brk_manual()`") if (! close_end) warning("Ignoring `close_end` with `brk_manual()`") breaks <- create_breaks(breaks, left_vec) breaks <- extend_and_close(breaks, x, extend, left = TRUE, close_end = FALSE) breaks } } #' @rdname chop_fn #' @export #' @order 2 brk_fn <- function (fn, ...) { assert_that(is.function(fn)) function (x, extend, left, close_end) { breaks <- fn(x, ...) # some functions (e.g. quantile()) return a named vector # which might create surprise labels: breaks <- unname(breaks) assert_that(is.numeric(breaks)) if (length(breaks) == 0) { return(empty_breaks()) } breaks <- create_extended_breaks(breaks, x, extend, left, close_end) breaks } } #' @rdname chop_pretty #' #' @export #' @order 2 brk_pretty <- function (n = 5, ...) { assert_that(is.count(n)) function (x, extend, left, close_end) { breaks <- base::pretty(x, n = n, ...) if (length(breaks) == 0 || is.null(breaks)) { return(empty_breaks()) } breaks <- create_extended_breaks(breaks, x, extend, left, close_end) breaks } } #' @rdname chop_mean_sd #' @export #' @order 2 #' @importFrom lifecycle deprecated brk_mean_sd <- function (sds = 1:3, sd = deprecated()) { if (lifecycle::is_present(sd)) { lifecycle::deprecate_warn( when = "0.7.0", what = "brk_mean_sd(sd)", with = "brk_mean_sd(sds = 'vector of sds')" ) assert_that(is.number(sd), sd > 0) # we start from 0 but remove the 0 # this works for e.g. sd = 0.5, whereas seq(1L, sd, 1L) would not: sds <- seq(0L, sd, 1L)[-1] if (! sd %in% sds) sds <- c(sds, sd) } assert_that(is.numeric(sds), all(sds > 0)) function (x, extend, left, close_end) { x_mean <- mean(x, na.rm = TRUE) x_sd <- stats::sd(x, na.rm = TRUE) if (is.na(x_mean) || is.na(x_sd) || x_sd == 0) { return(empty_breaks()) } # add negative sds, then scale them by mean and sd sds <- sort(sds) sds <- c(-rev(sds), 0, sds) breaks <- sds * x_sd + x_mean breaks <- create_lr_breaks(breaks, left) needs <- needs_extend(breaks, x, extend, left, close_end) if ((needs & LEFT) > 0) sds <- c(-Inf, sds) if ((needs & RIGHT) > 0) sds <- c(sds, Inf) breaks <- extend_and_close(breaks, x, extend, left, close_end) class(breaks) <- c("sdBreaks", class(breaks)) attr(breaks, "scaled_endpoints") <- sds breaks } } ================================================ FILE: R/breaks.R ================================================ #' @param breaks A numeric vector. #' @name breaks-doc #' @return A function which returns an object of class `breaks`. NULL #' Create a standard set of breaks #' #' @inherit breaks-doc params return #' @export #' #' @examples #' #' chop(1:10, c(2, 5, 8)) #' chop(1:10, brk_default(c(2, 5, 8))) #' brk_default <- function (breaks) { assert_that(noNA(breaks)) function (x, extend, left, close_end) { create_extended_breaks(breaks, x, extend, left, close_end) } } #' @rdname chop_spikes #' @export #' @order 2 brk_spikes <- function (breaks, n = NULL, prop = NULL) { assert_that( is.number(n) || is.number(prop), is.null(n) || is.null(prop), msg = "exactly one of `n` and `prop` must be a scalar numeric" ) assert_that( # it's ok for one of these to be null n >= 0 || prop >= 0 ) if (! is.function(breaks)) breaks <- brk_default(breaks) function (x, extend, left, close_end) { breaks <- breaks(x, extend, left, close_end) break_elements <- unclass_breaks(breaks) left_vec <- attr(breaks, "left") spikes <- find_spikes(x, n, prop) # We sort spikes in decreasing order so that when we add elements, # earlier elements remain in place. spikes <- sort(spikes, decreasing = TRUE) for (spike in spikes) { # We could use match() here to go faster, or even put it outside the loop. match_location <- which(spike == break_elements) n_matches <- length(match_location) # If two break elements match the spike, it's already a singleton: # we don't need to do anything. if (n_matches >= 2L) next if (n_matches == 1L) { # We turn the single matching break into a singleton and make sure # that left is c(TRUE, FALSE) break_elements <- append(break_elements, spike, after = match_location) left_vec <- append(left_vec, FALSE, after = match_location) left_vec[match_location] <- TRUE } else { # We add a singleton break at `spike` insert_location <- quiet_max(which(spike > break_elements)) if (insert_location <= 0) insert_location <- 0 break_elements <- append(break_elements, rep(spike, 2), after = insert_location) left_vec <- append(left_vec, c(TRUE, FALSE), after = insert_location) } } create_breaks(break_elements, left = left_vec) } } #' Class representing a set of intervals #' #' @param x A breaks object #' @param ... Unused #' #' @name breaks-class NULL #' @rdname breaks-class #' @export format.breaks <- function (x, ...) { if (length(x) < 2) return("Breaks object: no complete intervals") paste0("Breaks object: ", paste(lbl_intervals()(x), collapse = " ")) } #' @rdname breaks-class #' @export print.breaks <- function (x, ...) cat(format(x, ...)) #' @rdname breaks-class #' @export is.breaks <- function (x, ...) inherits(x, "breaks") on_failure(is.breaks) <- function (call, env) { paste0(deparse(call$x), " is not an object of class `breaks`") } ================================================ FILE: R/categorize.R ================================================ #' Categorize `x` according to breaks #' #' @param x A vector of data #' @param breaks A breaks object #' #' @return A set of vector codes #' @noRd categorize <- function (x, breaks) { # we first cast to the most informative common type. Then to numeric. left <- attr(breaks, "left") res <- santoku_cast_common(x, unclass_breaks(breaks)) # vec_cast won't accept e.g. characters but it also won't convert e.g. Dates # as.numeric accepts both # We want to convert things to numeric objects, but NB, not all # numeric objects will work OK in categorize_impl x <- tryCatch(strict_as_numeric(res[[1]]), error = function (...) res[[1]] ) breaks <- tryCatch(strict_as_numeric(res[[2]]), error = function (...) res[[2]] ) # we use is_bare_numeric here because e.g. large integer64 vectors will # fail in categorize_impl() codes <- if (rlang::is_bare_numeric(x) && rlang::is_bare_numeric(breaks)) { categorize_impl(x, breaks, left) } else { categorize_non_numeric(x, breaks, left) } codes } categorize_non_numeric <- function (x, breaks, left) { if (is.character(x) || is.character(breaks)) { if (getOption("santoku.warn_character", TRUE)) { warning_statement <- paste( "`x` or `breaks` is of type character, using lexical sorting.", "To turn off this warning, run:", " options(santoku.warn_character = FALSE)", collapse = "\n") warning(warning_statement) } } codes <- rep(NA_integer_, length(x)) for (j in seq_len(length(breaks) - 1)) { more_than_j <- x > breaks[j] less_than_j_plus_one <- x < breaks[j+1] equals_j <- x == breaks[j] equals_j_plus_one <- x == breaks[j+1] codes[more_than_j & less_than_j_plus_one] <- j if (left[j]) codes[equals_j] <- j if (! left[j+1]) codes[equals_j_plus_one] <- j } codes } ================================================ FILE: R/chop-by-group-size.R ================================================ #' Chop by quantiles #' #' `chop_quantiles()` chops data by quantiles. #' `chop_deciles()` is a convenience function which chops into deciles. #' #' @param probs A vector of probabilities for the quantiles. If `probs` has #' names, these will be used for labels. #' @param ... For `chop_quantiles`, passed to [chop()]. For `brk_quantiles()`, #' passed to [stats::quantile()] or [Hmisc::wtd.quantile()]. #' @param weights `NULL` or numeric vector of same length as `x`. If not #' `NULL`, [Hmisc::wtd.quantile()] is used to calculate weighted quantiles. #' @param recalc_probs Logical. Recalculate probabilities of quantiles using #' [`ecdf(x)`][stats::ecdf()]? See below. #' #' @inheritParams chop #' @inherit chop-doc params return #' #' @details #' For non-numeric `x`, `left` is set to `FALSE` by default. This works better #' for calculating "type 1" quantiles, since they round down. See #' [stats::quantile()]. #' #' By default, `chop_quantiles()` shows the requested probabilities in the #' labels. To show the numeric quantiles themselves, set `raw = TRUE`. #' #' When `x` contains duplicates, consecutive quantiles may be the same number. If #' so, interval labels may be misleading, and if `recalc_probs = FALSE` a warning is #' emitted. Set `recalc_probs = TRUE` to recalculate the probabilities of the quantiles #' using the [empirical cumulative distribution function][stats::ecdf()] of `x`. #' Doing so may give you different labels from what you expect, and will #' remove any names from `probs`, but it never changes the actual #' quantiles used for breaks. At present, `recalc_probs = TRUE` is incompatible #' with non-null `weights`. See the example below. #' #' @family chopping functions #' #' @export #' @order 1 #' #' @examples #' chop_quantiles(1:10, 1:3/4) #' #' chop_quantiles(1:10, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75)) #' #' chop(1:10, brk_quantiles(1:3/4)) #' #' chop_deciles(1:10) #' #' # to label by the quantiles themselves: #' chop_quantiles(1:10, 1:3/4, raw = TRUE) #' #' # duplicate quantiles: #' x <- c(1, 1, 1, 2, 3) #' quantile(x, 1:5/5) #' tab_quantiles(x, 1:5/5) #' tab_quantiles(x, 1:5/5, recalc_probs = TRUE) chop_quantiles <- function( x, probs, ..., labels = if (raw) lbl_intervals() else lbl_intervals(single = NULL), left = is.numeric(x), raw = FALSE, weights = NULL, recalc_probs = FALSE ) { chop(x, brk_quantiles(probs, weights = weights, recalc_probs = recalc_probs), labels = labels, ..., left = left, raw = raw) } #' @rdname chop_quantiles #' @export #' @order 1 chop_deciles <- function(x, ...) { chop_quantiles(x, 0:10/10, ...) } #' Chop equal-sized groups #' #' `chop_equally()` chops `x` into groups with an equal number of elements. #' #' @param groups Number of groups. #' @inheritParams chop #' @inherit chop-doc params return #' #' @details #' `chop_equally()` uses [brk_quantiles()] under the hood. If `x` has duplicate #' elements, you may get fewer `groups` than requested. If so, a warning will #' be emitted. See the examples. #' #' @family chopping functions #' #' @export #' @order 1 #' @examples #' chop_equally(1:10, 5) #' #' # You can't always guarantee equal-sized groups: #' dupes <- c(1, 1, 1, 2, 3, 4, 4, 4) #' quantile(dupes, 0:4/4) #' chop_equally(dupes, 4) #' # Or as many groups as you ask for: #' chop_equally(c(1, 1, 2, 2), 3) chop_equally <- function ( x, groups, ..., labels = lbl_intervals(), left = is.numeric(x), raw = TRUE ) { chop(x, brk_equally(groups), ..., labels = labels, left = left, raw = raw) } #' Chop into fixed-sized groups #' #' `chop_n()` creates intervals containing a fixed number of elements. #' #' @param n Integer. Number of elements in each interval. #' @inheritParams chop #' @param tail String. What to do if the final interval has fewer than `n` elements? #' `"split"` to keep it separate. `"merge"` to merge it with the neighbouring #' interval. #' @inherit chop-doc params return #' #' #' @details #' #' The algorithm guarantees that intervals contain no more than `n` elements, so #' long as there are no duplicates in `x` and `tail = "split"`. It also #' guarantees that intervals contain no fewer than `n` elements, except possibly #' the last interval (or first interval if `left` is `FALSE`). #' #' To ensure that all intervals contain at least `n` elements (so long as there #' are at least `n` elements in `x`!) set `tail = "merge"`. #' #' If `tail = "split"` and there are intervals containing duplicates with more #' than `n` elements, a warning is given. #' #' @export #' @order 1 #' @family chopping functions #' @examples #' chop_n(1:10, 5) #' #' chop_n(1:5, 2) #' chop_n(1:5, 2, tail = "merge") #' #' # too many duplicates #' x <- rep(1:2, each = 3) #' chop_n(x, 2) #' chop_n <- function ( x, n, ..., tail = "split" ) { res <- chop(x, brk_n(n, tail = tail), ...) if (tail == "split" && max(tabulate(res)) > n) { warning("Some intervals contain more than ", n, " elements") } res } ================================================ FILE: R/chop-by-width.R ================================================ #' Chop into fixed-width intervals #' #' `chop_width()` chops `x` into intervals of fixed `width`. #' #' @param width Width of intervals. #' @param start Starting point for intervals. By default the smallest #' finite `x` (largest if `width` is negative). #' @inheritParams chop #' @inherit chop-doc params return #' #' @details #' If `width` is negative, `chop_width()` sets `left = FALSE` and intervals will #' go downwards from `start`. #' #' @family chopping functions #' @seealso [brk_width-for-datetime] #' #' @export #' @order 1 #' #' @examples #' chop_width(1:10, 2) #' #' chop_width(1:10, 2, start = 0) #' #' chop_width(1:9, -2) #' #' chop(1:10, brk_width(2, 0)) #' chop_width <- function ( x, width, start, ..., left = sign(width) > 0 ) { chop(x, brk_width(width, start), ..., left = left) } #' Chop into equal-width intervals #' #' `chop_evenly()` chops `x` into `intervals` intervals of equal width. #' #' @param intervals Integer: number of intervals to create. #' @inheritParams chop #' @inherit chop-doc params return #' #' @family chopping functions #' #' @export #' @order 1 #' @examples #' chop_evenly(0:10, 5) #' chop_evenly <- function ( x, intervals, ... ) { chop(x, brk_evenly(intervals), ...) } #' Chop into proportions of the range of x #' #' `chop_proportions()` chops `x` into `proportions` of its range, excluding #' infinite values. #' #' By default, labels show the raw numeric endpoints. To label intervals by #' the proportions, use `raw = FALSE`. #' #' @param proportions Numeric vector between 0 and 1: proportions of x's range. #' If `proportions` has names, these will be used for labels. #' @inheritParams chop #' @inherit chop-doc params return #' #' @export #' @order 1 #' @family chopping functions #' @examples #' chop_proportions(0:10, c(0.2, 0.8)) #' chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8)) #' chop_proportions <- function ( x, proportions, ..., raw = TRUE ) { chop(x, brk_proportions(proportions), ..., raw = raw) } ================================================ FILE: R/chop-isolates.R ================================================ #' Chop common values into singleton intervals #' #' `chop_spikes()` lets you chop common values of `x` into their own #' singleton intervals. This can help make unusual values visible. #' #' This function is `r lifecycle::badge("experimental")`. #' #' @param breaks A numeric vector of cut-points or a call to a `brk_*` function. #' The resulting [`breaks`][breaks-class] object will be modified to add #' singleton breaks. #' @param n,prop Scalar. Provide either `n`, a number of values, or `prop`, #' a proportion of `length(x)`. Values of `x` which occur at least this #' often will get their own singleton break. #' @inheritParams chop #' @inherit chop-doc params return #' #' @export #' @order 1 #' @family chopping functions #' @seealso [dissect()] for a different approach. #' @examples #' x <- c(1:4, rep(5, 5), 6:10) #' chop_spikes(x, c(2, 7), n = 5) #' chop_spikes(x, c(2, 7), prop = 0.25) #' chop_spikes(x, brk_width(5), n = 5) #' #' set.seed(42) #' x <- runif(40, 0, 10) #' x <- sample(x, 200, replace = TRUE) #' tab_spikes(x, brk_width(2, 0), prop = 0.05) chop_spikes <- function ( x, breaks, ..., n = NULL, prop = NULL ) { chop(x, brk_spikes(breaks, n = n, prop = prop), ...) } #' Cut data into intervals, separating out common values #' #' Sometimes it's useful to separate out common elements of `x`. #' `dissect()` chops `x`, but puts common elements of `x` ("spikes") #' into separate categories. #' #' Unlike [chop_spikes()], `dissect()` doesn't break up #' intervals which contain a spike. As a result, unlike `chop_*` functions, #' `dissect()` does not chop `x` into disjoint intervals. See the examples. #' #' If breaks are data-dependent, their labels may be misleading after common #' elements have been removed. See the example below. To get round this, #' set `exclude_spikes` to `TRUE`. Then breaks will be calculated after #' removing spikes from the data. #' #' Levels of the result are ordered by the minimum element in each level. As #' a result, if `drop = FALSE`, empty levels will be placed last. #' #' This function is `r lifecycle::badge("experimental")`. #' #' @param x,breaks,... Passed to [chop()]. #' @inheritParams chop_spikes #' @param spike_labels [Glue][glue::glue()] string for spike labels. Use `"{l}"` #' for the spike value. #' @param exclude_spikes Logical. Exclude spikes before chopping `x`? This #' can affect the location of data-dependent breaks. #' #' @return #' `dissect()` returns the result of [chop()], but with common values put into #' separate factor levels. #' #' `tab_dissect()` returns a contingency [table()][base::table]. #' #' @seealso [chop_spikes()] for a different approach. #' @export #' @order 1 #' #' @examples #' x <- c(2, 3, 3, 3, 4) #' dissect(x, c(2, 4), n = 3) #' dissect(x, brk_width(2), prop = 0.5) #' #' set.seed(42) #' x <- runif(40, 0, 10) #' x <- sample(x, 200, replace = TRUE) #' # Compare: #' table(dissect(x, brk_width(2, 0), prop = 0.05)) #' # Versus: #' tab_spikes(x, brk_width(2, 0), prop = 0.05) #' #' # Potentially confusing data-dependent breaks: #' set.seed(42) #' x <- rnorm(99) #' x[1:9] <- x[1] #' tab_quantiles(x, 1:2/3) #' tab_dissect(x, brk_quantiles(1:2/3), n = 9) #' # Calculate quantiles excluding spikes: #' tab_dissect(x, brk_quantiles(1:2/3), n = 9, exclude_spikes = TRUE) dissect <- function (x, breaks, ..., n = NULL, prop = NULL, spike_labels = "{{{l}}}", exclude_spikes = FALSE) { assert_that( is.number(n) || is.number(prop), is.null(n) || is.null(prop), is.string(spike_labels), is.flag(exclude_spikes), msg = "exactly one of `n` and `prop` must be a scalar numeric" ) assert_that( # it's ok for one of these to be null n >= 0 || prop >= 0 ) spikes <- find_spikes(x, n, prop) x_spikes <- match(x, spikes) is_spike <- ! is.na(x_spikes) x_spikes <- x_spikes[is_spike] if (exclude_spikes) { x_not_spikes <- x[! is_spike] chopped_not_spikes <- chop(x_not_spikes, breaks, ...) chopped <- factor(rep(NA_integer_, length(x)), levels = levels(chopped_not_spikes)) chopped[! is_spike] <- chopped_not_spikes } else { chopped <- chop(x, breaks, ...) } elabels <- endpoint_labels(spikes, raw = TRUE) glue_env <- new.env() assign("l", elabels, envir = glue_env) spike_labels <- glue::glue(spike_labels, .envir = glue_env) new_levels <- c(levels(chopped), spike_labels) levels(chopped) <- new_levels chopped[is_spike] <- spike_labels[x_spikes] # We reorder the levels of chopped in order of their smallest elements. # Note that if `drop = FALSE`, empty intervals will be at the end. # The alternative would be to call `breaks` again and get the left endpoints # but this is complex. chopped <- stats::reorder(chopped, x, FUN = quiet_min) attr(chopped, "scores") <- NULL # remove leftover from reorder() chopped } #' Find common elements in `x` #' #' @param x A vector #' @param n Number of elements that counts as common. Specify exactly one of `n` #' and `prop`. #' @param prop Proportion of `length(x)` that counts as common #' #' @return The common elements, not necessarily in order. NA values are never #' considered as common. #' @noRd find_spikes <- function (x, n, prop) { n <- n %||% (length(x) * prop) unique_x <- unique(x) x_counts <- tabulate(match(x, unique_x)) spikes <- unique_x[x_counts >= n] spikes <- spikes[! is.na(spikes)] spikes } ================================================ FILE: R/chop-misc.R ================================================ #' Chop by standard deviations #' #' Intervals are measured in standard deviations on either side of the #' mean. #' #' In version 0.7.0, these functions changed to specifying `sds` as a vector. #' To chop 1, 2 and 3 standard deviations around the mean, write #' `chop_mean_sd(x, sds = 1:3)` instead of `chop_mean_sd(x, sd = 3)`. #' #' @param sds Positive numeric vector of standard deviations. #' @param sd `r lifecycle::badge("deprecated")` #' #' @inheritParams chop #' @inherit chop-doc params return #' #' @family chopping functions #' #' @export #' @order 1 #' #' @examples #' chop_mean_sd(1:10) #' #' chop(1:10, brk_mean_sd()) #' #' @importFrom lifecycle deprecated chop_mean_sd <- function ( x, sds = 1:3, ..., raw = FALSE, sd = deprecated() ) { chop(x, brk_mean_sd(sds = sds, sd = sd), ..., raw = raw) } #' Chop using pretty breakpoints #' #' `chop_pretty()` uses [base::pretty()] to calculate breakpoints #' which are 1, 2 or 5 times a power of 10. These look nice in graphs. #' #' [base::pretty()] tries to return `n+1` breakpoints, i.e. `n` intervals, but #' note that this is not guaranteed. There are methods for Date and POSIXct #' objects. #' #' For fine-grained control over [base::pretty()] parameters, use #' `chop(x, brk_pretty(...))`. #' #' @inheritParams chop #' @inherit chop-doc params return #' @param n Positive integer passed to [base::pretty()]. How many intervals to chop into? #' @param ... Passed to [chop()] by `chop_pretty()` and `tab_pretty()`; passed #' to [base::pretty()] by `brk_pretty()`. #' #' @export #' @order 1 #' #' @examples #' chop_pretty(1:10) #' #' chop(1:10, brk_pretty(n = 5, high.u.bias = 0)) #' chop_pretty <- function (x, n = 5, ...) { chop(x, brk_pretty(n = n), ...) } #' Chop using an existing function #' #' `chop_fn()` is a convenience wrapper: `chop_fn(x, foo, ...)` #' is the same as `chop(x, foo(x, ...))`. #' #' @param fn A function which returns a numeric vector of breaks. #' @param ... Further arguments to `fn` #' @inheritParams chop #' @inherit chop-doc params return #' #' @export #' @order 1 #' @family chopping functions #' @examples #' #' if (requireNamespace("scales")) { #' chop_fn(rlnorm(10), scales::breaks_log(5)) #' # same as #' # x <- rlnorm(10) #' # chop(x, scales::breaks_log(5)(x)) #' } #' chop_fn <- function ( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) { chop(x, brk_fn(fn, ...), extend = extend, left = left, close_end = close_end, raw = raw, drop = drop) } ================================================ FILE: R/chop.R ================================================ #' @name chop-doc #' @param ... Passed to [chop()]. #' @return #' `chop_*` functions return a [`factor`][base::factor] of the same length as `x`. #' #' `brk_*` functions return a [`function`] to create `breaks`. #' #' `tab_*` functions return a contingency [`table`][base::table]. NULL #' Cut data into intervals #' #' `chop()` cuts `x` into intervals. It returns a [`factor`][base::factor] of #' the same length as `x`, representing which interval contains each element of `x`. #' `kiru()` is an alias for `chop`. #' `tab()` calls `chop()` and returns a contingency [`table`][base::table] from #' the result. #' #' @param x A vector. #' @param breaks A numeric vector of cut-points, or a function to create #' cut-points from `x`. #' @param labels A character vector of labels or a function to create labels. #' @param extend Logical. If `TRUE`, always extend breaks to `+/-Inf`. If `NULL`, #' extend breaks to `min(x)` and/or `max(x)` only if necessary. If `FALSE`, never #' extend. #' @param left Logical. Left-closed or right-closed breaks? #' @param close_end Logical. Close last break at right? (If `left` is `FALSE`, #' close first break at left?) #' @param raw Logical. Use raw values in labels? #' @param drop Logical. Drop unused levels from the result? #' #' @details #' #' `x` may be a numeric vector, or more generally, any vector which can be #' compared with `<` and `==` (see [Ops][groupGeneric]). In particular #' [Date][base::Dates] and [date-time][DateTimeClasses] objects are supported. #' Character vectors are supported with a warning. #' #' ## Breaks #' #' `breaks` may be a vector or a function. #' #' If it is a vector, `breaks` gives the interval endpoints. Repeating a value #' creates a "singleton" interval, which contains only that value. #' For example `breaks = c(1, 3, 3, 5)` creates 3 intervals: #' \code{[1, 3)}, \code{{3}} and \code{(3, 5]}. #' #' If `breaks` is a function, it is called with the `x`, `extend`, `left` and #' `close_end` arguments, and should return an object of class `breaks`. #' Use `brk_*` functions to create a variety of data-dependent breaks. #' #' Names of `breaks` may be used for labels. See "Labels" below. #' #' ## Options for breaks #' #' By default, left-closed intervals are created. If `left` is `FALSE`, #' right-closed intervals are created. #' #' If `close_end` is `TRUE` the final break (or first break if `left` is `FALSE`) #' will be closed at both ends. This guarantees that all values `x` with #' `min(breaks) <= x <= max(breaks)` are included in the intervals. #' #' Before version 0.9.0, `close_end` was `FALSE` by default, and also behaved #' differently with respect to extended breaks: see "Extending intervals" below. #' #' Using [mathematical set notation][lbl_intervals()]: #' #' * If `left` is `TRUE` and `close_end` is `TRUE`, breaks will look like #' \code{[b1, b2), [b2, b3) ... [b_(n-1), b_n]}. #' * If `left` is `FALSE` and `close_end` is `TRUE`, breaks will look like #' \code{[b1, b2], (b2, b3] ... (b_(n-1), b_n]}. #' * If `left` is `TRUE` and `close_end` is `FALSE`, all breaks will look like #' \code{... [b1, b2) ...}. #' * If `left` is `FALSE` and `close_end` is `FALSE`, all breaks will look like #' \code{... (b1, b2] ...}. #' #' ## Extending intervals #' #' If `extend` is `TRUE`, intervals will be extended to \code{[-Inf, #' min(breaks))} and \code{(max(breaks), Inf]}. #' #' If `extend` is `NULL` (the default), intervals will be extended to #' \code{[min(x), min(breaks))} and \code{(max(breaks), max(x)]}, only if #' necessary, i.e. only if elements of `x` would be outside the unextended #' breaks. #' #' If `extend` is `FALSE`, intervals are never extended. #' #' Note that even when `extend = TRUE`, extended intervals will be #' dropped from the factor levels if they contain no elements and `drop = TRUE`. #' #' `close_end` is only relevant if intervals are not extended; #' extended intervals are always closed on the outside. This is a change from #' previous behaviour. Up to version 0.8.0, `close_end` was applied to the #' last user-specified interval, before any extended intervals were created. #' #' Since 1.1.0, infinity is represented as \eqn{\infty}{the infinity symbol} #' in breaks on unicode platforms. Set `options(santoku.infinity = "Inf")` #' to get the old behaviour. #' #' ## Labels #' #' `labels` may be a character vector. It should have the same length as the #' (possibly extended) number of intervals. Alternatively, `labels` may be a #' `lbl_*` function such as [lbl_dash()]. #' #' If `breaks` is a named vector, then names of `breaks` will be #' used as labels for the interval starting at the corresponding element. This #' overrides the `labels` argument (but unnamed breaks will still use `labels`). #' This feature is `r lifecycle::badge("experimental")`. #' #' If `labels` is `NULL`, then integer codes will be returned instead of a #' factor. #' #' If `raw` is `TRUE`, labels will show the actual interval endpoints, usually #' numbers. If `raw` is `FALSE` then labels may show other objects, such #' as quantiles for [chop_quantiles()] and friends, proportions of the range for #' [chop_proportions()], or standard deviations for [chop_mean_sd()]. #' #' If `raw` is `NULL` then `lbl_*` functions will use their default (usually #' `FALSE`). Otherwise, the `raw` argument to `chop()` overrides `raw` arguments #' passed into `lbl_*` functions directly. #' #' #' ## Miscellaneous #' #' `NA` values in `x`, and values which are outside the extended endpoints, #' return `NA`. #' #' `kiru()` is a synonym for `chop()`. If you load `{tidyr}`, you can use it to #' avoid confusion with `tidyr::chop()`. #' #' Note that `chop()`, like all of R, uses binary arithmetic. Thus, numbers may #' not be exactly equal to what you think they should be. There is an example #' below. #' #' @return #' `chop()` returns a [`factor`][base::factor] of the same length as `x`, #' representing the intervals containing the value of `x`. #' #' `tab()` returns a contingency [`table`][base::table]. #' #' @export #' #' @family chopping functions #' #' @seealso [base::cut()], [`non-standard-types`] for chopping objects that #' aren't numbers. #' #' @examples #' #' chop(1:7, c(2, 4, 6)) #' #' chop(1:7, c(2, 4, 6), extend = FALSE) #' #' # Repeat a number for a singleton break: #' chop(1:7, c(2, 4, 4, 6)) #' #' chop(1:7, c(2, 4, 6), left = FALSE) #' #' chop(1:7, c(2, 4, 6), close_end = FALSE) #' #' chop(1:7, brk_quantiles(c(0.25, 0.75))) #' #' # A single break is fine if `extend` is not `FALSE`: #' chop(1:7, 4) #' #' # Floating point inaccuracy: #' chop(0.3/3, c(0, 0.1, 0.1, 1), labels = c("< 0.1", "0.1", "> 0.1")) #' #' # -- Labels -- #' #' chop(1:7, c(Lowest = 1, Low = 2, Mid = 4, High = 6)) #' #' chop(1:7, c(2, 4, 6), labels = c("Lowest", "Low", "Mid", "High")) #' #' chop(1:7, c(2, 4, 6), labels = lbl_dash()) #' #' # Mixing names and other labels: #' chop(1:7, c("<2" = 1, 2, 4, ">=6" = 6), labels = lbl_dash()) #' #' # -- Non-standard types -- #' #' chop(as.Date("2001-01-01") + 1:7, as.Date("2001-01-04")) #' #' suppressWarnings(chop(LETTERS[1:7], "D")) #' #' chop <- function (x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) { assert_that( is.flag(extend) || is.null(extend), is.flag(left), is.flag(close_end), is.flag(drop), is.flag(raw) || is.null(raw) ) if (! is.function(breaks)) breaks <- brk_default(breaks) breaks <- breaks(x, extend, left, close_end) assert_that(is.breaks(breaks), length(breaks) >= 2L) codes <- categorize(x, breaks) if (is.null(labels)) return(codes) lbls <- if (is.function(labels)) { if (is.null(raw)) labels(breaks) else labels(breaks, raw = raw) } else { labels } lbls <- add_break_names(lbls, breaks) stopifnot(length(lbls) == length(breaks) - 1) real_codes <- if (drop) unique(codes[! is.na(codes)]) else TRUE if (anyDuplicated(lbls[real_codes])) { stop("Duplicate labels found: ", paste(lbls, collapse = ", ")) } result <- factor(codes, levels = seq.int(length(breaks) - 1L), labels = lbls) if (drop) result <- droplevels(result) return(result) } #' @rdname chop #' @export kiru <- chop #' Chop data precisely (for programmers) #' #' `fillet()` calls [chop()] with `extend = FALSE` and `drop = FALSE`. This #' ensures that you get only the `breaks` and `labels` you ask for. When #' programming, consider using `fillet()` instead of `chop()`. #' #' @inheritParams chop #' #' @return `fillet()` returns a [`factor`][base::factor] of the same length as #' `x`, representing the intervals containing the value of `x`. #' #' @family chopping functions #' #' @export #' #' @examples #' fillet(1:10, c(2, 5, 8)) fillet <- function ( x, breaks, labels = lbl_intervals(), left = TRUE, close_end = TRUE, raw = NULL ) { chop(x, breaks, labels, left = left, close_end = close_end, extend = FALSE, raw = raw, drop = FALSE) } ================================================ FILE: R/labels-datetime.R ================================================ #' Parse a `strftime` format string #' #' Splits a format string into literal and directive tokens. #' #' @param fmt A `strftime` format string. #' #' @return A data frame with columns `type` (`"literal"` or `"directive"`) #' and `token`. #' @noRd parse_strftime <- function(fmt) { assert_that(is.string(fmt)) chars <- strsplit(fmt, "", fixed = TRUE)[[1]] n <- length(chars) i <- 1L types <- character(0) tokens <- character(0) while (i <= n) { if (chars[i] != "%") { start <- i while (i <= n && chars[i] != "%") i <- i + 1L types <- c(types, "literal") tokens <- c(tokens, paste0(chars[start:(i - 1L)], collapse = "")) next } if (i < n && chars[i + 1L] == "%") { types <- c(types, "literal") tokens <- c(tokens, "%") i <- i + 2L next } start <- i i <- i + 1L if (i <= n && chars[i] %in% c("E", "O")) i <- i + 1L while (i <= n && grepl("[0-9]", chars[i])) i <- i + 1L if (i <= n) i <- i + 1L types <- c(types, "directive") tokens <- c(tokens, paste0(chars[start:(i - 1L)], collapse = "")) } data.frame(type = types, token = tokens, stringsAsFactors = FALSE) } #' Return relative component rank for a strftime directive #' #' Lower values are "greater" components, e.g. year before month before day. #' #' @param token A single strftime directive token (e.g. "%Y"). #' #' @return Numeric rank, or `NA_real_` if unknown. #' @noRd strftime_rank <- function(token) { code <- gsub("^%[EO]?([0-9]*)", "", token) if (code %in% c("Y", "y", "C", "G", "g")) return(1) if (code %in% c("m", "b", "B", "h")) return(2) if (code %in% c("d", "e", "j", "a", "A", "u", "w", "p", "P")) return(3) if (code %in% c("H", "I", "k", "l", "M", "S", "OS")) return(4) NA_real_ } #' Format date/time endpoints into token matrix #' #' @param x Date/time-like vector. #' @param fmt A `strftime` format string. #' @param spec Parsed strftime spec. #' #' @return A character matrix: rows are endpoints, columns are format tokens. #' @noRd format_strftime_tokens <- function(x, fmt = NULL, spec = parse_strftime(fmt)) { n <- length(x) tokens <- lapply(seq_len(nrow(spec)), function(i) { if (identical(spec$type[[i]], "literal")) { rep(spec$token[[i]], n) } else { format(x, spec$token[[i]]) } }) do.call(cbind, tokens) } #' Collapse two formatted date/time labels #' #' @param l_tokens Left endpoint tokens. #' @param r_tokens Right endpoint tokens. #' @param symbol Separator for full ranges. #' #' @return A single collapsed range label. #' @noRd collapse_datetime_label <- function( l_tokens, r_tokens, spec, symbol = "-" ) { spaced_symbol <- paste0(" ", symbol, " ") full <- paste0(paste0(l_tokens, collapse = ""), spaced_symbol, paste0(r_tokens, collapse = "")) ranks <- vapply(spec$token, strftime_rank, FUN.VALUE = numeric(1)) is_directive <- spec$type == "directive" comparable <- is_directive & !is.na(ranks) differs <- comparable & (l_tokens != r_tokens) if (!any(differs)) return(full) diff_rank <- min(ranks[differs]) collapse_rank <- diff_rank # If both day and time differ, keep month with both endpoints so that # day labels remain anchored to a month (issue #58). if (diff_rank == 3 && any(differs & ranks > diff_rank)) { collapse_rank <- 2 } higher_differs <- comparable & (ranks < diff_rank) & (l_tokens != r_tokens) active_components <- which(comparable & (ranks >= collapse_rank)) if (any(higher_differs) || length(active_components) == 0 || diff_rank == 1) { return(full) } left_end <- max(active_components) right_start <- min(active_components) left_part <- paste0(l_tokens[seq_len(left_end)], collapse = "") right_part <- paste0(r_tokens[right_start:length(r_tokens)], collapse = "") if (!nzchar(left_part) || !nzchar(right_part)) return(full) # joiner <- if (diff_rank %in% 3:4) symbol else spaced_symbol joiner <- if (ranks[right_start] == ranks[left_end]) symbol else spaced_symbol paste0(left_part, joiner, right_part) } #' @rdname lbl_datetime #' @export lbl_date <- function( fmt = "%e %b %Y", symbol = "-", unit = as.difftime(1, units = "days"), single = "{l}", first = NULL, last = NULL ) { lbl_datetime( fmt = fmt, symbol = symbol, unit = unit, single = single, first = first, last = last ) } #' Label dates and datetimes #' #' @description #' `r lifecycle::badge("experimental")` #' #' `lbl_date()` and `lbl_datetime()` produce nice labels for dates #' and datetimes. Where possible ranges are simplified, like #' like "13-14 Jul 2026" or "11:15-12:15 1 Dec 2025". #' #' @inherit label-doc #' @inherit first-last-doc #' @param symbol String: separator to use for full ranges. #' @param unit Optional interval unit for non-overlapping labels. If not `NULL`, #'. endpoints are adjusted in the style of [lbl_discrete()]. #' #' @family labelling functions #' #' @export #' #' @examples #' winter <- as.Date("2025-12-01") + 0:89 #' tab(winter, as.Date(c("2025-12-25", "2026-01-06")), #' labels = lbl_date()) #' new_year <- as.POSIXct("2025-12-31 23:00") + 0:120 * 60 #' round_midnight <- as.POSIXct(c("2025-12-31 23:59", "2026-01-01 00:05")) #' tab(new_year, round_midnight, #' labels = lbl_datetime()) #' tab(new_year, round_midnight, #' labels = lbl_datetime(unit = as.difftime(1, units = "mins"))) lbl_datetime <- function( fmt = "%H:%M:%S %b %e %Y", symbol = "-", unit = NULL, single = "{l}", first = NULL, last = NULL ) { assert_that( is.string(fmt), is.string(symbol), length(unit) <= 1L, is.string(single) || is.null(single), is.string(first) || is.null(first), is.string(last) || is.null(last) ) function(breaks, raw = FALSE) { assert_that(is.breaks(breaks)) len_breaks <- length(breaks) endpoints <- scaled_endpoints(breaks, raw = raw) pieces <- discrete_interval_endpoints( breaks = breaks, unit = unit, endpoints = endpoints ) l <- pieces$l r <- pieces$r is_singleton <- pieces$singletons too_small <- pieces$too_small l_closed <- pieces$l_closed r_closed <- pieces$r_closed if (any(too_small)) { warning("Intervals smaller than `unit` are labelled as \"--\"") } spec <- parse_strftime(fmt) l_tokens <- format_strftime_tokens(l, spec = spec) r_tokens <- format_strftime_tokens(r, spec = spec) labels <- vapply(seq_len(len_breaks - 1L), function(i) { collapse_datetime_label( l_tokens = l_tokens[i, ], r_tokens = r_tokens[i, ], spec = spec, symbol = symbol ) }, FUN.VALUE = character(1)) l <- apply(l_tokens, 1, paste0, collapse = "") r <- apply(r_tokens, 1, paste0, collapse = "") labels[too_small] <- "--" if (!is.null(single)) { labels[is_singleton] <- glue::glue(single, l = l[is_singleton], r = r[is_singleton], l_closed = l_closed[is_singleton], r_closed = r_closed[is_singleton] ) } if (!is.null(first)) { labels[1] <- glue::glue(first, l = l[1], r = r[1], l_closed = l_closed[1], r_closed = r_closed[1] ) } if (!is.null(last)) { ll <- len_breaks - 1L labels[ll] <- glue::glue(last, l = l[ll], r = r[ll], l_closed = l_closed[ll], r_closed = r_closed[ll] ) } labels } } ================================================ FILE: R/labels-glue.R ================================================ #' Label chopped intervals using the `glue` package #' #' Use `"{l}"` and `"{r}"` to show the left and right endpoints of the intervals. #' #' @inherit label-doc #' @inherit first-last-doc params #' @param label A glue string passed to [glue::glue()]. #' @param ... Further arguments passed to [glue::glue()]. #' #' @details #' #' The following variables are available in the glue string: #' #' * `l` is a character vector of left endpoints of intervals. #' * `r` is a character vector of right endpoints of intervals. #' * `l_closed` is a logical vector. Elements are `TRUE` when the left #' endpoint is closed. #' * `r_closed` is a logical vector, `TRUE` when the right endpoint is closed. #' #' Endpoints will be formatted by `fmt` before being passed to `glue()`. #' #' @family labelling functions #' #' @export #' #' @examples #' tab(1:10, c(1, 3, 3, 7), #' labels = lbl_glue("{l} to {r}", single = "Exactly {l}")) #' #' tab(1:10 * 1000, c(1, 3, 5, 7) * 1000, #' labels = lbl_glue("{l}-{r}", #' fmt = function(x) prettyNum(x, big.mark=','))) #' #' # reproducing lbl_intervals(): #' interval_left <- "{ifelse(l_closed, '[', '(')}" #' interval_right <- "{ifelse(r_closed, ']', ')')}" #' glue_string <- paste0(interval_left, "{l}", ", ", "{r}", interval_right) #' tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(glue_string, single = "{{{l}}}")) #' lbl_glue <- function ( label, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated(), ... ) { assert_that( is.string(label), is.null(fmt) || is_format(fmt), is.string(first) || is.null(first), is.string(last) || is.null(last) ) if (lifecycle::is_present(raw)) { lifecycle::deprecate_stop("0.9.0", "lbl_glue(raw)", "chop(raw)") } function (breaks, raw = FALSE) { assert_that(is.breaks(breaks)) len_breaks <- length(breaks) labels <- character(len_breaks - 1) elabels <- endpoint_labels(breaks, raw = raw, fmt = fmt) l <- elabels[-len_breaks] r <- elabels[-1] left <- attr(breaks, "left") # Breaks like [1, 2) [2, 3] have # left TRUE, TRUE, FALSE for breaks 1,2,3 # The first two TRUEs say that the left brackets are closed # The last two TRUE & FALSE say that the right brackets are open # and closed respectively. So: l_closed <- left[-len_breaks] r_closed <- ! left[-1] # check ... for anything not in glue::glue args # effectively, we move any user-supplied arguments into # an environment specifically for glue # this is mostly to make the lbl_midpoints() hack # of passing in `m` work dots <- rlang::enexprs(...) glue_env <- new.env() not_glue_args <- setdiff(names(dots), names(formals(glue::glue))) for (nm in not_glue_args) { assign(deparse(dots[[nm]]), eval(dots[[nm]], parent.frame()), glue_env ) } labels <- glue::glue(label, l = l, r = r, l_closed = l_closed, r_closed = r_closed, ..., .envir = glue_env) if (! is.null(single)) { # which breaks are singletons? singletons <- singletons(breaks) labels[singletons] <- glue::glue(single, l = l[singletons], r = r[singletons], l_closed = l_closed[singletons], r_closed = r_closed[singletons], ..., .envir = glue_env ) } if (! is.null(first)) { labels[1] <- glue::glue(first, l = l[1], r = r[1], l_closed = l_closed[1], r_closed = r_closed[1], ..., .envir = glue_env ) } if (! is.null(last)) { ll <- len_breaks - 1 labels[ll] <- glue::glue(last, l = l[ll], r = r[ll], l_closed = l_closed[ll], r_closed = r_closed[ll], ..., .envir = glue_env ) } return(labels) } } ================================================ FILE: R/labels-impl.R ================================================ #' Replaces labels with names from the breaks vector #' #' Only non-zero-char names are used. #' #' @param labels Passed in from chop, possibly via a `lbl_*` function #' @param breaks Breaks object created via a `brk_*` function. Some of #' these preserve names of a given argument (`brk_default()`, #' `brk_proportions()`, `brk_quantiles()`) #' #' @return The altered labels #' @noRd #' add_break_names <- function(labels, breaks) { if (is.null(names(breaks))) return(labels) is_named <- nzchar(names(breaks)) # These are possibly-extended breaks; last break is the rightmost endpoint # and any name is ignored: is_named[length(is_named)] <- FALSE break_names_for_labels <- names(breaks)[is_named] # length(labels) == length(breaks) - 1 is_named <- is_named[-length(is_named)] labels[is_named] <- break_names_for_labels return(labels) } #' Return formatted strings for endpoints #' #' Methods will pick up a `scaled_endpoints` #' attribute if one exists. This provides the numbers #' for when `raw = FALSE`. #' #' Different breaks subclasses may have different default formats. #' #' A `.numeric` method exists so that formatted labels can be created #' from e.g. midpoints or other things that aren't breaks #' themselves. #' #' @param breaks Either a breaks object, or a numeric vector #' @param raw Report raw numbers instead of e.g. quantiles? #' @param fmt Format string or function #' @param ... Not used #' #' @return A character vector of break endpoints. #' @noRd #' endpoint_labels <- function (breaks, raw, fmt = NULL, ...) { UseMethod("endpoint_labels") } #' @export endpoint_labels.numeric <- function (breaks, raw, fmt = NULL, ...) { endpoints <- scaled_endpoints(breaks, raw = raw) elabels <- if (! is.null(fmt)) { apply_format(fmt, endpoints) } else { unique_truncation(endpoints) } elabels[is.infinite(endpoints)] <- sub("Inf ?", symbol_infinity(), elabels[is.infinite(endpoints)]) return(elabels) } #' @export endpoint_labels.integer <- endpoint_labels.numeric #' @export endpoint_labels.double <- endpoint_labels.numeric #' @export endpoint_labels.default <- function (breaks, raw, fmt = NULL, ...) { endpoints <- scaled_endpoints(breaks, raw = raw) elabels <- if (! is.null(fmt)) { apply_format(fmt, endpoints) } else { base::format(endpoints) } return(elabels) } #' @export endpoint_labels.Date <- function (breaks, raw, fmt = NULL, ...) { elabels <- scaled_endpoints(breaks, raw = raw) # this could be a number. If so, a `fmt` for `sprintf` # will work fine: if (! inherits(elabels, "Date")) return(NextMethod()) # set default format fmt <- fmt %||% "%F" elabels_chr <- apply_format(fmt, elabels) minus_inf <- is.infinite(elabels) & elabels < as.Date("1970-01-01") plus_inf <- is.infinite(elabels) & elabels > as.Date("1970-01-01") elabels_chr[minus_inf] <- symbol_infinity(minus = TRUE) elabels_chr[plus_inf] <- symbol_infinity() elabels_chr } #' @export endpoint_labels.POSIXt <- function (breaks, raw, fmt = NULL, ...) { elabels <- scaled_endpoints(breaks, raw = raw) # same comment as endpoint_labels.Date above: if (! inherits(elabels, "POSIXt")) return(NextMethod()) # set default format fmt <- fmt %||% "%F %H:%M:%S" elabels_chr <- apply_format(fmt, elabels) minus_inf <- is.infinite(elabels) & elabels < as.POSIXct("1970-01-01") plus_inf <- is.infinite(elabels) & elabels > as.POSIXct("1970-01-01") elabels_chr[minus_inf] <- symbol_infinity(minus = TRUE) elabels_chr[plus_inf] <- symbol_infinity() elabels_chr } #' @export endpoint_labels.quantileBreaks <- function (breaks, raw, fmt = NULL, ...) { if (raw) return(NextMethod()) # set default format fmt <- fmt %||% percent elabels <- scaled_endpoints(breaks, raw = FALSE) elabels <- apply_format(fmt, elabels) return(elabels) } #' @export endpoint_labels.sdBreaks <- function (breaks, raw, fmt = NULL, ...) { if (raw) return(NextMethod()) # set default format fmt <- fmt %||% "%.3g sd" elabels <- scaled_endpoints(breaks, raw = FALSE) elabels <- apply_format(fmt, elabels) return(elabels) } #' Return numeric (or whatever) endpoints of breaks, possibly scaled #' #' @param breaks Breaks or numeric object #' @param raw Logical. If `FALSE`, return endpoints scaled as e.g. sds or #' quantiles #' #' @return Numbers, dates, etc. with no `breaks` class. #' @noRd scaled_endpoints <- function (breaks, raw) { UseMethod("scaled_endpoints") } #' @export scaled_endpoints.breaks <- function (breaks, raw) { if (raw) { unclass_breaks(breaks) } else { attr(breaks, "scaled_endpoints") %||% unclass_breaks(breaks) } } #' @export scaled_endpoints.default <- function (breaks, raw) { if (raw) { breaks } else { attr(breaks, "scaled_endpoints") %||% breaks } } #' Apply `fmt` to an object #' #' @param fmt A one-argument function, or a character string. #' @param endpoint Endpoints of a break. Various classes. #' #' @return A character vector. #' @noRd apply_format <- function (fmt, endpoint, ...) { UseMethod("apply_format") } #' @export apply_format.function <- function (fmt, endpoint, ...) { fmt(endpoint, ...) } #' @export #' @method apply_format character apply_format.character <- function (fmt, endpoint, ...) { UseMethod("apply_format.character", endpoint) } #' @export #' @method apply_format.character default apply_format.character.default <- function (fmt, endpoint, ...) { base::format(endpoint, fmt, ...) } #' @export #' @method apply_format.character numeric apply_format.character.numeric <- function (fmt, endpoint, ...) { sprintf(fmt, endpoint, ...) } #' @export #' @method apply_format.character character apply_format.character.character <- function (fmt, endpoint, ...) { sprintf(fmt, endpoint, ...) } #' @export #' @method apply_format list apply_format.list <- function (fmt, endpoint, ...) { UseMethod("apply_format.list", endpoint) } #' @export #' @method apply_format.list default apply_format.list.default <- function (fmt, endpoint, ...) { do.call(base::format, c(list(x = endpoint), fmt)) } is_format <- function (fmt) is.string(fmt) || is.function(fmt) || is.list(fmt) on_failure(is_format) <- function(call, env) { paste0(deparse(call$fmt), " is not a valid format (a string, list or function)") } #' Build interval endpoints for discrete-style labels #' #' Shared implementation for labelers that make open intervals non-overlapping #' by shifting open endpoints inward by `unit`. #' #' @param breaks A breaks object. #' @param unit Optional scalar unit. If `NULL`, no endpoint adjustment is made. #' @param endpoints Optional endpoints vector. Defaults to `unclass_breaks(breaks)`. #' @param singleton_mask Logical mask of singleton intervals. #' #' @return A list with `l`, `r`, `singletons`, `too_small`, `l_closed`, #' and `r_closed`. #' @noRd discrete_interval_endpoints <- function( breaks, unit = NULL, endpoints = NULL, singleton_mask = singletons(breaks) ) { assert_that(is.breaks(breaks)) len_breaks <- length(breaks) endpoints <- endpoints %||% unclass_breaks(breaks) left <- attr(breaks, "left") l <- endpoints[-len_breaks] r <- endpoints[-1] left_l <- left[-len_breaks] left_r <- left[-1] if (!is.null(unit)) { l[!left_l] <- l[!left_l] + unit r[left_r] <- r[left_r] - unit singleton_mask <- singleton_mask | (r == l) too_small <- r < l } else { too_small <- rep(FALSE, len_breaks - 1L) } list( l = l, r = r, singletons = singleton_mask, too_small = too_small, l_closed = left_l, r_closed = !left_r ) } #' Truncates `num` to look nice, while preserving uniqueness #' #' @param num A numeric vector. #' #' @return A character vector #' @noRd unique_truncation <- function (num) { want_unique <- ! duplicated(num) # "real" duplicates are allowed! # we keep the first of each duplicate set. for (digits in seq(4L, 22L)) { res <- formatC(num, digits = digits, width = -1L) if (anyDuplicated(res[want_unique]) == 0L) return(res) } stop("Could not format breaks to avoid duplicates") } em_dash <- function () { if (l10n_info()[["UTF-8"]]) "\u2014" else "-" } symbol_infinity <- function (minus = FALSE) { infty <- if (l10n_info()[["UTF-8"]]) "\u221e" else "Inf" infty <- getOption("santoku.infinity", infty) if (minus) paste0("-", infty) else infty } ================================================ FILE: R/labels-single.R ================================================ #' Label chopped intervals by their midpoints #' #' This uses the midpoint of each interval for #' its label. #' #' @inherit label-doc #' @inherit first-last-doc #' #' @family labelling functions #' #' @export #' #' @examples #' chop(1:10, c(2, 5, 8), lbl_midpoints()) lbl_midpoints <- function ( fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) { if (lifecycle::is_present(raw)) { lifecycle::deprecate_stop("0.9.0", "lbl_midpoints(raw)", "chop(raw)") } function (breaks, raw = FALSE) { assert_that(is.breaks(breaks)) break_nums <- scaled_endpoints(breaks, raw = raw) l_nums <- break_nums[-length(break_nums)] r_nums <- break_nums[-1] # doing this, rather than (l_nums + r_nums)/2, works for e.g. Date objects: midpoints <- l_nums + (r_nums - l_nums)/2 # we've applied raw already (anyway, midpoints is just a numeric) midpoints <- endpoint_labels(midpoints, raw = TRUE, fmt = fmt) gluer <- lbl_glue(label = "{m}", fmt = fmt, single = single, first = first, last = last, m = midpoints) labels <- gluer(breaks, raw = raw) labels } } #' Label chopped intervals by their left or right endpoints #' #' This is useful when the left endpoint unambiguously indicates the #' interval. In other cases it may give errors due to duplicate labels. #' #' `lbl_endpoint()` is `r lifecycle::badge("defunct")` and gives an #' error since santoku 1.0.0. #' #' @inherit label-doc #' @inherit first-last-doc #' @param left Flag. Use left endpoint or right endpoint? #' #' @family labelling functions #' #' @export #' #' @examples #' chop(1:10, c(2, 5, 8), lbl_endpoints(left = TRUE)) #' chop(1:10, c(2, 5, 8), lbl_endpoints(left = FALSE)) #' if (requireNamespace("lubridate")) { #' tab_width( #' as.Date("2000-01-01") + 0:365, #' months(1), #' labels = lbl_endpoints(fmt = "%b") #' ) #' } #' #' \dontrun{ #' # This gives breaks `[1, 2) [2, 3) {3}` which lead to #' # duplicate labels `"2", "3", "3"`: #' chop(1:3, 1:3, lbl_endpoints(left = FALSE)) #' } lbl_endpoints <- function ( left = TRUE, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) { assert_that(is.flag(left)) if (lifecycle::is_present(raw)) { lifecycle::deprecate_stop("0.9.0", "lbl_endpoints(raw)", "chop(raw)") } label <- if (left) "{l}" else "{r}" lbl_glue(label, fmt = fmt, single = single, first = first, last = last) } #' @rdname lbl_endpoints #' @export lbl_endpoint <- function ( fmt = NULL, raw = FALSE, left = TRUE ) { lifecycle::deprecate_stop(when = "0.8.0", what = "lbl_endpoint()", with = "lbl_endpoints()") } #' Label chopped intervals in sequence #' #' `lbl_seq()` labels intervals sequentially, using numbers or letters. #' #' @param start String. A template for the sequence. See below. #' #' @details #'`start` shows the first element of the sequence. It must contain exactly *one* #' character out of the set "a", "A", "i", "I" or "1". For later elements: #' #' * "a" will be replaced by "a", "b", "c", ... #' * "A" will be replaced by "A", "B", "C", ... #' * "i" will be replaced by lower-case Roman numerals "i", "ii", "iii", ... #' * "I" will be replaced by upper-case Roman numerals "I", "II", "III", ... #' * "1" will be replaced by numbers "1", "2", "3", ... #' #' Other characters will be retained as-is. #' #' @family labelling functions #' @inherit label-doc return #' #' @export #' #' @examples #' chop(1:10, c(2, 5, 8), lbl_seq()) #' #' chop(1:10, c(2, 5, 8), lbl_seq("i.")) #' #' chop(1:10, c(2, 5, 8), lbl_seq("(A)")) lbl_seq <- function(start = "a") { assert_that(is.string(start)) # check like contains just one of a, A, i, I, 1 match <- gregexpr("(a|A|i|I|1)", start)[[1]] if (length(match) > 1) stop("More than one a/A/i/I/1 found in `start`: ", start) if (match == -1) stop("No a/A/i/I/1 found in `start`: ", start) # replace that with the format-string and call lbl_manual appropriately key <- substr(start, match, match) fmt <- sub("(a|A|i|I|1)", "%s", start) res <- switch(key, "a" = function (breaks, raw = NULL) { if (length(breaks) > 27L) { stop("Can't use more than 26 intervals with lbl_seq(\"a\")") } sprintf(fmt, letters[seq_len(length(breaks) - 1L)]) }, "A" = function (breaks, raw = NULL) { if (length(breaks) > 27L) { stop("Can't use more than 26 intervals with lbl_seq(\"A\")") } sprintf(fmt, LETTERS[seq_len(length(breaks) - 1L)]) }, "i" = function (breaks, raw = NULL) { sprintf(fmt, tolower(utils::as.roman(seq_len(length(breaks) - 1L)))) }, "I" = function (breaks, raw = NULL) { sprintf(fmt, utils::as.roman(seq_len(length(breaks) - 1L))) }, "1" = function (breaks, raw = NULL) { sprintf(fmt, seq_len(length(breaks) - 1L)) } ) return(res) } #' Defunct: label chopped intervals in a user-defined sequence #' #' `r lifecycle::badge("defunct")` #' #' `lbl_manual()` is defunct since santoku 1.0.0. It is little used and is not #' closely related to the rest of the package. It also risks mislabelling #' intervals, e.g. if intervals are extended. Use of `lbl_manual()` will give #' an error. #' #' @param sequence A character vector of labels. #' @inherit label-doc #' #' @family labelling functions #' #' @export #' #' @keywords internal #' #' @examples #' \dontrun{ #' chop(1:10, c(2, 5, 8), lbl_manual(c("w", "x", "y", "z"))) #' # -> #' chop(1:10, c(2, 5, 8), labels = c("w", "x", "y", "z")) #' } lbl_manual <- function (sequence, fmt = "%s") { lifecycle::deprecate_stop("0.9.0", "lbl_manual()", details = "Just specify `labels = sequence` instead.") } ================================================ FILE: R/labels.R ================================================ #' @name label-doc #' @param fmt String, list or function. A format for break endpoints. #' @param raw `r lifecycle::badge("deprecated")`. Throws an error. #' Use the `raw` argument to [chop()] instead. #' @param symbol String: symbol to use for the dash. #' @param ... Arguments passed to format methods. #' #' @section Formatting endpoints: #' #' If `fmt` is not `NULL` then it is used to format the endpoints. #' #' * If `fmt` is a string, then numeric endpoints will be formatted by #' `sprintf(fmt, breaks)`; other endpoints, e.g. [Date][base::Dates] objects, #' will be formatted by `format(breaks, fmt)`. #' #' * If `fmt` is a list, then it will be used as arguments to [format]. #' #' * If `fmt` is a function, it should take a vector of numbers (or other objects #' that can be used as breaks) and return a character vector. It may be helpful #' to use functions from the `{scales}` package, e.g. [scales::label_comma()]. #' #' @return A function that creates a vector of labels. NULL #' @name first-last-doc #' @param single Glue string: label for singleton intervals. See [lbl_glue()] #' for details. If `NULL`, singleton intervals will be labelled the same way #' as other intervals. #' @param first Glue string: override label for the first category. Write e.g. #' `first = "<{r}"` to create a label like `"<18"`. See [lbl_glue()] #' for details. #' @param last String: override label for the last category. Write e.g. #' `last = ">{l}"` to create a label like `">65"`. See [lbl_glue()] #' for details. NULL #' Label chopped intervals using set notation #' #' These labels are the most exact, since they show you whether #' intervals are "closed" or "open", i.e. whether they include their endpoints. #' #' Mathematical set notation looks like this: #' #' * \code{[a, b]}: all numbers `x` where `a <= x <= b`; #' * \code{(a, b)}: all numbers where `a < x < b`; #' * \code{[a, b)}: all numbers where `a <= x < b`; #' * \code{(a, b]}: all numbers where `a < x <= b`; #' * \code{{a}}: just the number `a` exactly. #' #' @inherit label-doc #' @inherit first-last-doc #' #' @family labelling functions #' #' @export #' #' @examples #' #' tab(-10:10, c(-3, 0, 0, 3), #' labels = lbl_intervals()) #' #' tab(-10:10, c(-3, 0, 0, 3), #' labels = lbl_intervals(fmt = list(nsmall = 1))) #' #' tab_evenly(runif(20), 10, #' labels = lbl_intervals(fmt = percent)) #' lbl_intervals <- function ( fmt = NULL, single = "{{{l}}}", first = NULL, last = NULL, raw = deprecated() ) { if (lifecycle::is_present(raw)) { lifecycle::deprecate_stop("0.9.0", "lbl_intervals(raw)", "chop(raw)") } interval_glue <- "{ifelse(l_closed, '[', '(')}{l}, {r}{ifelse(r_closed, ']', ')')}" lbl_glue(label = interval_glue, single = single, fmt = fmt, first = first, last = last) } #' Label discrete data #' #' `lbl_discrete()` creates labels for discrete data, such as integers. #' For example, breaks #' `c(1, 3, 4, 6, 7)` are labelled: `"1-2", "3", "4-5", "6-7"`. #' #' @inherit label-doc #' @param unit Minimum difference between distinct values of data. #' For integers, 1. #' @inherit first-last-doc #' #' @details #' No check is done that the data are discrete-valued. If they are not, then #' these labels may be misleading. Here, discrete-valued means that if #' `x < y`, then `x <= y - unit`. #' #' Be aware that Date objects may have non-integer values. See #' [Date][base::Dates]. #' #' @family labelling functions #' #' @export #' #' @examples #' tab(1:7, c(1, 3, 5), lbl_discrete()) #' #' tab(1:7, c(3, 5), lbl_discrete(first = "<= {r}")) #' #' tab(1:7 * 1000, c(1, 3, 5) * 1000, lbl_discrete(unit = 1000)) #' #' # Misleading labels for non-integer data #' chop(2.5, c(1, 3, 5), lbl_discrete()) #' lbl_discrete <- function ( symbol = em_dash(), unit = 1L, fmt = NULL, single = NULL, first = NULL, last = NULL ) { assert_that( is.string(symbol), is.scalar(unit), is.null(fmt) || is_format(fmt), is.string(single) || is.null(single), is.string(first) || is.null(first), is.string(last) || is.null(last) ) function (breaks, raw = NULL) { assert_that(all(ceiling(as.numeric(breaks)) == floor(as.numeric(breaks))), msg = "Non-integer breaks") len_breaks <- length(breaks) pieces <- discrete_interval_endpoints( breaks = breaks, unit = unit, endpoints = unclass_breaks(breaks) ) l <- pieces$l r <- pieces$r singletons <- pieces$singletons too_small <- pieces$too_small l_closed <- pieces$l_closed r_closed <- pieces$r_closed if (any(too_small)) { warning("Intervals smaller than `unit` are labelled as \"--\"") } labels_l <- endpoint_labels(l, raw = FALSE, fmt = fmt) labels_r <- endpoint_labels(r, raw = FALSE, fmt = fmt) labels <- paste0(labels_l, symbol, labels_r) labels[singletons] <- labels_l[singletons] labels[too_small] <- "--" if (! is.null(single)) { labels[singletons] <- glue::glue(single, l = labels_l[singletons], r = labels_r[singletons], l_closed = l_closed[singletons], r_closed = r_closed[singletons]) } if (! is.null(first)) { labels[1] <- glue::glue(first, l = labels_l[1], r = labels_r[1], l_closed = l_closed[1], r_closed = r_closed[1]) } if (! is.null(last)) { ll <- len_breaks - 1 labels[ll] <- glue::glue(last, l = labels_l[ll], r = labels_r[ll], l_closed = l_closed[ll], r_closed = r_closed[ll]) } return(labels) } } #' Label chopped intervals like 1-4, 4-5, ... #' #' This label style is user-friendly, but doesn't distinguish between #' left- and right-closed intervals. It's good for continuous data #' where you don't expect points to be exactly on the breaks. #' #' If you don't want unicode output, use `lbl_dash("-")`. #' #' @inherit label-doc #' @inherit first-last-doc #' #' @family labelling functions #' #' @export #' #' @examples #' chop(1:10, c(2, 5, 8), lbl_dash()) #' #' chop(1:10, c(2, 5, 8), lbl_dash(" to ", fmt = "%.1f")) #' #' chop(1:10, c(2, 5, 8), lbl_dash(first = "<{r}")) #' #' pretty <- function (x) prettyNum(x, big.mark = ",", digits = 1) #' chop(runif(10) * 10000, c(3000, 7000), lbl_dash(" to ", fmt = pretty)) lbl_dash <- function ( symbol = em_dash(), fmt = NULL, single = "{l}", first = NULL, last = NULL, raw = deprecated() ) { if (lifecycle::is_present(raw)) { lifecycle::deprecate_stop("0.9.0", "lbl_dash(raw)", "chop(raw)") } label_glue <- paste0("{l}", symbol, "{r}") lbl_glue(label = label_glue, fmt = fmt, single = single, first = first, last = last, raw = raw) } ================================================ FILE: R/non-standard-types-doc.R ================================================ #' Tips for chopping non-standard types #' #' Santoku can handle many non-standard types. #' #' * If objects can be compared using `<`, `==` etc. then they should #' be choppable. #' * Objects which can't be converted to numeric are handled within R code, #' which may be slower. #' * Character `x` and `breaks` are chopped with a warning. #' * If `x` and `breaks` are not the same type, they should be able to #' be cast to the same type, usually using [vctrs::vec_cast_common()]. #' * Not all chopping operations make sense, for example, [chop_mean_sd()] #' on a character vector. #' * For indexed objects such as [stats::ts()] objects, indices will be dropped #' from the result. #' * If you get errors, try setting `extend = FALSE` (but also file a bug report). #' * To request support for a type, open an issue on Github. #' #' @name non-standard-types #' @seealso brk-width-for-Datetime NULL ================================================ FILE: R/santoku-cast.R ================================================ #' Hacked version of [vctrs::vec_cast_common()] #' #' @param x,y Vectors to cast #' #' @return A list of two vectors of the same class; or #' errors, if that isn't possible. #' #' This almost always defers to `vctrs::vec_cast_common()`. #' #' Often, we are more relaxed than `vctrs` because #' we have a more specific use case (comparing numeric #' values). So e.g. we're fine with comparing a `ts` #' object to a number, whereas other binary #' operations might not make sense. #' #' @noRd #' santoku_cast_common <- function (x, y) { UseMethod("santoku_cast_common") } #' Internal functions #' #' @name santoku-cast #' @param x,y Vectors to cast. #' #' @return A list. #' @keywords internal #' #' These are internal functions. Do not use. NULL # The rawNamespace below means NAMESPACE gets both the S3method() and the # export() tag. #' @export #' @rdname santoku-cast #' @method santoku_cast_common default #' @rawNamespace export(santoku_cast_common.default) santoku_cast_common.default <- function (x, y) { UseMethod("santoku_cast_common.default", object = y) } #' @export santoku_cast_common.default.default <- function (x, y) { vctrs::vec_cast_common(x, y) } # Specific default.x methods are in their sections below. # ==== double ==== # We have specific double methods just to catch bit64 objects #' @export #' @rdname santoku-cast #' @method santoku_cast_common double #' @rawNamespace export(santoku_cast_common.double) santoku_cast_common.double <- function (x, y) { UseMethod("santoku_cast_common.double", object = y) } #' @export santoku_cast_common.double.default <- function (x, y) { # almost always delegate to default santoku_cast_common.default(x, y) } #' @export santoku_cast_common.double.integer64 <- function (x, y) { loadNamespace("bit64") # we cast the integer64 to double. # See santoku_cast_common.integer64.double below for why. list(x, as.double(y)) } # ==== Date ==== # We delegate to vctrs for Date+numeric (which gives an error) # Not obvious how to interpret conversion of Date to numeric #' @export #' @rdname santoku-cast #' @method santoku_cast_common Date #' @rawNamespace export(santoku_cast_common.Date) santoku_cast_common.Date <- function (x, y) { UseMethod("santoku_cast_common.Date", object = y) } #' @export santoku_cast_common.Date.Date <- function (x, y) { list(x, y) } #' @export santoku_cast_common.Date.POSIXct <- function (x, y) { list(as.POSIXct(x), y) } # ==== POSIXct ==== # We delegate to vctrs for POSIXct/numeric, see Date above #' @export #' @rdname santoku-cast #' @method santoku_cast_common POSIXct #' @rawNamespace export(santoku_cast_common.POSIXct) santoku_cast_common.POSIXct <- function (x, y) { UseMethod("santoku_cast_common.POSIXct", object = y) } #' @export santoku_cast_common.POSIXct.POSIXct <- function (x, y) { list(x, y) } #' @export santoku_cast_common.POSIXct.Date <- function (x, y) { list(x, as.POSIXct(y)) } # ==== ts ==== #' @export #' @rdname santoku-cast #' @method santoku_cast_common ts #' @rawNamespace export(santoku_cast_common.ts) santoku_cast_common.ts <- function (x, y) { UseMethod("santoku_cast_common.ts", object = y) } #' @export santoku_cast_common.ts.default <- function (x, y) { # We recall so that we can pick up anything # unusual in y santoku_cast_common(unclass(x), y) } #' @export santoku_cast_common.default.ts <- function (x, y) { santoku_cast_common(x, unclass(y)) } # ==== zoo ==== #' @export #' @rdname santoku-cast #' @method santoku_cast_common zoo #' @rawNamespace export(santoku_cast_common.zoo) santoku_cast_common.zoo <- function (x, y) { UseMethod("santoku_cast_common.zoo", object = y) } # we don't have a zoo.zoo method because returning # two zoo objects would cause comparisons to only # work where the indices are the same. So, we always # work on the underlying data. #' @export santoku_cast_common.zoo.default <- function (x, y) { loadNamespace("zoo") santoku_cast_common(zoo::coredata(x), y) } #' @export santoku_cast_common.default.zoo <- function (x, y) { loadNamespace("zoo") santoku_cast_common(x, zoo::coredata(y)) } # ==== integer64 ==== #' @export #' @rdname santoku-cast #' @method santoku_cast_common integer64 #' @rawNamespace export(santoku_cast_common.integer64) santoku_cast_common.integer64 <- function (x, y) { UseMethod("santoku_cast_common.integer64", object = y) } #' @export santoku_cast_common.integer64.integer64 <- function (x, y) { list(x, y) } #' @export santoku_cast_common.integer64.double <- function (x, y) { loadNamespace("bit64") # we cast the integer64 to double. # This may lose precision, but if so, it gives a warning. # If we cast double to integer64, then # e.g. chop(as.integer64(1:5), 2.5) # silently converts 2.5 to 2 and gives a wrong answer list(as.double(x), y) } #' @export santoku_cast_common.integer64.default <- function (x, y) { loadNamespace("bit64") santoku_cast_common(x, bit64::as.integer64(y)) } #' @export santoku_cast_common.default.integer64 <- function (x, y) { loadNamespace("bit64") santoku_cast_common(bit64::as.integer64(x), y) } # ==== hexmode ==== #' @export #' @rdname santoku-cast #' @method santoku_cast_common hexmode #' @rawNamespace export(santoku_cast_common.hexmode) santoku_cast_common.hexmode <- function (x, y) { UseMethod("santoku_cast_common.hexmode", object = y) } #' @export santoku_cast_common.hexmode.hexmode <- function (x, y) { # if both items are hexmode, OK fine. list(x, y) } #' @export santoku_cast_common.hexmode.default <- function (x, y) { santoku_cast_common(as.numeric(x), y) } #' @export santoku_cast_common.default.hexmode <- function (x, y) { santoku_cast_common(x, as.numeric(y)) } # ==== octmode ==== #' @export #' @rdname santoku-cast #' @method santoku_cast_common octmode #' @rawNamespace export(santoku_cast_common.octmode) santoku_cast_common.octmode <- function (x, y) { UseMethod("santoku_cast_common.octmode", object = y) } #' @export santoku_cast_common.octmode.octmode <- function (x, y) { list(x, y) } #' @export santoku_cast_common.octmode.default <- function (x, y) { santoku_cast_common(as.numeric(x), y) } #' @export santoku_cast_common.default.octmode <- function (x, y) { santoku_cast_common(x, as.numeric(y)) } ================================================ FILE: R/santoku-package.R ================================================ #' @import assertthat NULL #' A versatile cutting tool for R: package overview and options #' #' santoku is a tool for cutting data into intervals. It provides #' the function [chop()], which is similar to base R's [cut()] or `Hmisc::cut2()`. #' `chop(x, breaks)` takes a vector `x` and returns a factor of the #' same length, coding which interval each element of `x` falls into. #' #' Here are some advantages of santoku: #' #' * By default, `chop()` always covers the whole range of the data, so you #' won't get unexpected `NA` values. #' #' * Unlike `cut()` or `cut2()`, `chop()` can handle single values as well as #' intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a #' separate factor level for values exactly equal to 2. #' #' * Flexible and easy labelling. #' #' * Convenience functions for creating quantile intervals, evenly-spaced #' intervals or equal-sized groups. #' #' * Convenience functions to quickly tabulate chopped data. #' #' * Can chop numbers, dates, date-times and other objects. #' #' These advantages make santoku especially useful for exploratory analysis, #' where you may not know the range of your data in advance. #' #' To get started, read the vignette: #' #' ```r #' vignette("santoku") #' ``` #' #' For more details, start with the documentation for [chop()]. #' #' # Options #' #' Santoku has two options: #' #' * `options("santoku.infinity")` sets the symbol for infinity in breaks. The default is #' `NULL`, in which case the infinity symbol is used on platforms that support it, otherwise #' `"Inf"` is used. #' #' * `options("santoku.warn_character")` warns if you try to chop a character vector. Set to #' `FALSE` to turn off this warning. "_PACKAGE" # The following block is used by usethis to automatically manage # roxygen namespace tags. Modify with care! ## usethis namespace: start #' @importFrom Rcpp sourceCpp #' @useDynLib santoku, .registration = TRUE ## usethis namespace: end NULL ================================================ FILE: R/tab.R ================================================ #' @rdname chop #' @export #' @examples #' tab(1:10, c(2, 5, 8)) #' tab <- function ( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) { default_table( chop( x = x, breaks = breaks, labels = labels, extend = extend, left = left, close_end = close_end, raw = raw, drop = drop ) ) } #' @rdname chop_width #' @export #' @order 3 #' @examples #' tab_width(1:10, 2, start = 0) #' tab_width <- function ( x, width, start, ..., left = sign(width) > 0 ) { default_table( chop_width(x = x, width = width, start = start, ..., left = left) ) } #' @rdname chop_evenly #' @export #' @order 3 tab_evenly <- function ( x, intervals, ... ) { default_table( chop_evenly(x = x, intervals = intervals, ...) ) } #' @rdname chop_proportions #' @export #' @order 3 tab_proportions <- function ( x, proportions, ..., raw = TRUE ) { default_table( chop_proportions(x = x, proportions = proportions, ..., raw = raw) ) } #' @rdname chop_n #' @export #' @order 3 #' @examples #' tab_n(1:10, 5) #' #' # fewer elements in one group #' tab_n(1:10, 4) #' tab_n <- function ( x, n, ..., tail = "split" ) { default_table(chop_n(x = x, n = n, ..., tail = tail)) } #' @rdname chop_mean_sd #' @export #' @order 3 #' @examples #' tab_mean_sd(1:10) #' tab_mean_sd <- function ( x, sds = 1:3, ..., raw = FALSE ) { default_table(chop_mean_sd(x = x, sds = sds, ..., raw = raw)) } #' @rdname chop_pretty #' @export #' @order 3 #' @examples #' tab_pretty(1:10) #' tab_pretty <- function (x, n = 5, ...) { default_table(chop_pretty(x = x, n = n, ...)) } #' @rdname chop_quantiles #' @export #' @order 3 #' @examples #' set.seed(42) #' tab_quantiles(rnorm(100), probs = 1:3/4, raw = TRUE) #' tab_quantiles <- function ( x, probs, ..., left = is.numeric(x), raw = FALSE ) { default_table( chop_quantiles(x = x, probs = probs, ..., left = left, raw = raw) ) } #' @rdname chop_quantiles #' @export #' @order 3 tab_deciles <- function (x, ...) { default_table(chop_deciles(x = x, ...)) } #' @rdname chop_equally #' @export #' @order 3 tab_equally <- function ( x, groups, ..., left = is.numeric(x), raw = TRUE ) { default_table( chop_equally(x = x, groups = groups, ..., left = left, raw = raw) ) } #' @rdname chop_fn #' @export #' @order 3 tab_fn <- function ( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) { default_table( chop_fn(x = x, fn = fn, ..., extend = extend, left = left, close_end = close_end, raw = raw, drop = drop) ) } #' @rdname chop_spikes #' @export #' @order 3 tab_spikes <- function ( x, breaks, ..., n = NULL, prop = NULL ) { default_table( chop_spikes(x = x, breaks = breaks, ..., n = n, prop = prop) ) } #' @rdname dissect #' @export #' @order 2 tab_dissect <- function ( x, breaks, ..., n = NULL, prop = NULL ) { default_table( dissect(x = x, breaks = breaks, ..., n = n, prop = prop) ) } default_table <- function (x) { table(x, useNA = "ifany", dnn = NULL) } ================================================ FILE: R/utils.R ================================================ #' Define singleton intervals explicitly #' #' `exactly()` duplicates its input. #' It lets you define singleton intervals like this: `chop(x, c(1, exactly(2), 3))`. #' This is the same as `chop(x, c(1, 2, 2, 3))` but conveys your intent more #' clearly. #' #' @param x A numeric vector. #' #' @return The same as `rep(x, each = 2)`. #' @export #' #' @examples #' chop(1:10, c(2, exactly(5), 8)) #' #' # same: #' chop(1:10, c(2, 5, 5, 8)) exactly <- function (x) rep(x, each = 2) #' Simple percentage formatter #' #' `percent()` formats `x` as a percentage. #' For a wider range of formatters, consider the [`scales` #' package](https://cran.r-project.org/package=scales). #' #' @param x Numeric values. #' #' @return `x` formatted as a percent. #' @export #' #' @examples #' percent(0.5) percent <- function (x) { paste0(unique_truncation(x * 100), "%") } #' Report singleton intervals #' #' @param breaks A breaks object #' #' @return #' A logical vector of `length(breaks) - 1`, `TRUE` #' if the corresponding interval is a singleton. #' @noRd #' #' @examples #' brk <- brk_res(brk_default(c(1, 1, 2, 3, 3, 4))) #' brk #' # Breaks object: {1} (1, 2) [2, 3) {3} (3, 4) #' #' singletons(brk) #' # TRUE FALSE FALSE TRUE FALSE singletons <- function (breaks) { duplicated(breaks)[-1] } #' Find duplicates that would be illegal breaks #' #' @param x A vector, which should be sorted #' #' @return A logical vector of length(x), TRUE if the corresponding element #' is the second duplicate in a row. #' @noRd #' #' @examples #' find_illegal_duplicates(c(1, 2, 2, 3, 3, 3, 4)) find_illegal_duplicates <- function (x) { if (length(x) == 0) return(logical(0)) dupes <- duplicated(x) # If element n is duplicated, and element n+1 is duplicated, then n + 1 # is illegal. # The first element is never a duplicated middle. c(FALSE, dupes[-length(dupes)] & dupes[-1]) } `%||%` <- function (x, y) if (is.null(x)) y else x quiet_min <- function (x) suppressWarnings(min(x, na.rm = TRUE)) quiet_max <- function (x) suppressWarnings(max(x, na.rm = TRUE)) #' Stricter `as.numeric` #' #' This converts warnings to errors, and errors if any NAs are introduced, #' but is less strict than `vctrs::vec_cast()` #' #' @param x A vector #' #' @return `as.numeric(x)`, with no new NAs #' @noRd #' strict_as_numeric <- function (x) { nas <- is.na(x) x <- tryCatch(as.numeric(x), warning = function (w) stop("Warning from as.numeric(x)") ) if (any(is.na(x) & ! nas)) stop("Could not convert some elements") x } #' Test a break #' #' @param brk_fun A call to a `brk_` function #' @param x,extend,left,close_end Passed in to `brk_fun` #' #' @return A `breaks` object. #' @noRd #' #' @examples #' brk_res(brk_default(1:3)) #' brk_res <- function ( brk_fun, x = 1:2, extend = FALSE, left = TRUE, close_end = TRUE ) { brk_fun(x, extend = extend, left = left, close_end = close_end) } ================================================ FILE: README.Rmd ================================================ --- output: github_document --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" ) ``` # santoku santoku logo [![CRAN status](https://www.r-pkg.org/badges/version/santoku)](https://CRAN.R-project.org/package=santoku) [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) [![CRAN Downloads Per Month](http://cranlogs.r-pkg.org/badges/santoku)](https://CRAN.R-project.org/package=santoku) [![R-universe](https://hughjonesd.r-universe.dev/badges/santoku)](https://hughjonesd.r-universe.dev/santoku) [![R-CMD-check](https://github.com/hughjonesd/santoku/workflows/R-CMD-check/badge.svg)](https://github.com/hughjonesd/santoku/actions) [![Codecov test coverage](https://codecov.io/gh/hughjonesd/santoku/branch/master/graph/badge.svg)](https://app.codecov.io/gh/hughjonesd/santoku?branch=master) santoku is a versatile cutting tool for R. It provides `chop()`, a replacement for `base::cut()`. ## Installation Install from [r-universe](https://r-universe.dev): ``` r install.packages("santoku", repos = c("https://hughjonesd.r-universe.dev", "https://cloud.r-project.org")) ``` Or from CRAN: ``` r install.packages("santoku") ``` Or get the development version from github: ``` r # install.packages("remotes") remotes::install_github("hughjonesd/santoku") ``` ```{r, child = 'advantages.Rmd'} ``` ## Examples ```{r} library(santoku) ``` `chop` returns a factor: ```{r} chop(1:5, c(2, 4)) ``` Include a number twice to match it exactly: ```{r} chop(1:5, c(2, 2, 4)) ``` Use names in breaks for labels: ```{r} chop(1:5, c(Low = 1, Mid = 2, High = 4)) ``` Or use `lbl_*` functions: ```{r} chop(1:5, c(2, 4), labels = lbl_dash()) ``` Chop into fixed-width intervals: ```{r} chop_width(runif(10), 0.1) ``` Or into fixed-size groups: ```{r} chop_n(1:10, 5) ``` Chop dates by calendar month, then tabulate: ```{r} library(lubridate) dates <- as.Date("2021-12-31") + 1:90 tab_width(dates, months(1), labels = lbl_discrete(fmt = "%d %b")) ``` For more information, see the [vignette](https://hughjonesd.github.io/santoku/articles/santoku.html). ================================================ FILE: README.md ================================================ # santoku santoku logo [![CRAN status](https://www.r-pkg.org/badges/version/santoku)](https://CRAN.R-project.org/package=santoku) [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) [![CRAN Downloads Per Month](http://cranlogs.r-pkg.org/badges/santoku)](https://CRAN.R-project.org/package=santoku) [![R-universe](https://hughjonesd.r-universe.dev/badges/santoku)](https://hughjonesd.r-universe.dev/santoku) [![R-CMD-check](https://github.com/hughjonesd/santoku/workflows/R-CMD-check/badge.svg)](https://github.com/hughjonesd/santoku/actions) [![Codecov test coverage](https://codecov.io/gh/hughjonesd/santoku/branch/master/graph/badge.svg)](https://app.codecov.io/gh/hughjonesd/santoku?branch=master) santoku is a versatile cutting tool for R. It provides `chop()`, a replacement for `base::cut()`. ## Installation Install from [r-universe](https://r-universe.dev): ``` r install.packages("santoku", repos = c("https://hughjonesd.r-universe.dev", "https://cloud.r-project.org")) ``` Or from CRAN: ``` r install.packages("santoku") ``` Or get the development version from github: ``` r # install.packages("remotes") remotes::install_github("hughjonesd/santoku") ``` ## Advantages Here are some advantages of santoku: - By default, `chop()` always covers the whole range of the data, so you won’t get unexpected `NA` values. - `chop()` can handle single values as well as intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a separate factor level for values exactly equal to 2. - `chop()` can handle many kinds of data, including numbers, dates and times, and [units](https://r-quantities.github.io/units/). - `chop_*` functions create intervals in many ways, using quantiles of the data, standard deviations, fixed-width intervals, equal-sized groups, or pretty intervals for use in graphs. - It’s easy to label intervals: use names for your breaks vector, or use a `lbl_*` function to create interval notation like `[1, 2)`, dash notation like `1-2`, or arbitrary styles using `glue::glue()`. - `tab_*` functions quickly chop data, then tabulate it. These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. ## Examples ``` r library(santoku) ``` `chop` returns a factor: ``` r chop(1:5, c(2, 4)) #> [1] [1, 2) [2, 4) [2, 4) [4, 5] [4, 5] #> Levels: [1, 2) [2, 4) [4, 5] ``` Include a number twice to match it exactly: ``` r chop(1:5, c(2, 2, 4)) #> [1] [1, 2) {2} (2, 4) [4, 5] [4, 5] #> Levels: [1, 2) {2} (2, 4) [4, 5] ``` Use names in breaks for labels: ``` r chop(1:5, c(Low = 1, Mid = 2, High = 4)) #> [1] Low Mid Mid High High #> Levels: Low Mid High ``` Or use `lbl_*` functions: ``` r chop(1:5, c(2, 4), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—5 4—5 #> Levels: 1—2 2—4 4—5 ``` Chop into fixed-width intervals: ``` r chop_width(runif(10), 0.1) #> [1] [0.368, 0.468) [0.268, 0.368) [0.768, 0.868] [0.568, 0.668) #> [5] [0.668, 0.768) [0.768, 0.868] [0.06801, 0.168) [0.668, 0.768) #> [9] [0.06801, 0.168) [0.468, 0.568) #> 7 Levels: [0.06801, 0.168) [0.268, 0.368) [0.368, 0.468) ... [0.768, 0.868] ``` Or into fixed-size groups: ``` r chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] ``` Chop dates by calendar month, then tabulate: ``` r library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union dates <- as.Date("2021-12-31") + 1:90 tab_width(dates, months(1), labels = lbl_discrete(fmt = "%d %b")) #> 01 Jan—31 Jan 01 Feb—28 Feb 01 Mar—31 Mar #> 31 28 31 ``` For more information, see the [vignette](https://hughjonesd.github.io/santoku/articles/santoku.html). ================================================ FILE: TODO.md ================================================ # TODO * Work on tests - tests for `left` and `close_end` arguments - tests for `brk_default` - `brk_width()` needs tests which match the guarantees in the documentation - ditto for `brk_evenly()` which now uses its own implementation to guarantee exactly `intervals` intervals - systematic tests for `brk_*` functions * Implement a simple `Infinity` class that automatically casts to any other class and is always > or < than any other element? Then replace the `class_bounds()` complexity? - The problem at the moment is that `vec_cast()` is highly unreliable and you never know if a particular class will accept `Inf`. - An infinity class would be fine, but how does that go into the existing `breaks` object which has its own underlying class? - Might be more reasonable just not to add `Inf` or `-Inf` elements. Instead, record whether the breaks have left and right "infinity" set. Then just add numeric infinity to the breaks before you call `categorize_impl` (or the R version). In particular, e.g. `integer64` doesn't like `Inf` or `-Inf` but it does have very large numbers in `bit64::lim.integer64` which look ugly and which only exist to be lower/higher than everything else anyway... - But NB this requires a new way to create the labels, and that kinda sucks.... # Thoughts on errors * On the whole, we don't want to error out if `x` is weird. `x` is data. But if e.g. `breaks` are weird, we can error out. - Exception: `x` is the wrong class or type. * In some cases we want to guarantee the set of breaks. - e.g. `brk_manual()` with `extend` set. * In other cases, e.g. `brk_evenly()` we don't need to make such a guarantee. # Questions * Is it really OK to have `left = FALSE` as the default in `chop_quantiles()`, `chop_evenly()` and friends? - the alternative is to do it only when `x` is non-numeric. - that makes the surprise rarer, but rare surprises can be worse... and it adds complexity since the functions have to be generic. - another alternative: `chop` sets `left = FALSE` for non-numeric `x`. Probably better. * Do we need `drop`? - should `drop` have a default of `! isTRUE(extend)` i.e. be `FALSE` when `extend = TRUE`? # Questions with a (provisional) answer * Should we have a flag to return characters? - No, we have `labels = NULL` for integer codes only though. * Should we put a `percent` argument into `brk_quantiles()` so it can store scaled endpoints as proportions rather than percentages (the current default)? - My sense is, not unless someone asks. - Oh, someone just did ask; more generally though. * Should `close_end = TRUE` argument come before `...` in `chop_` variants? - No. We don't want people to set it by position, so distinguish it from the initial arguments. * What to do about `tidyr::chop()` - Current answer: fuck 'em. (NB: just kidding. I am a huge tidyverse fan.) - We provide `kiru()`. So on the REPL, people can just use `kiru()` if they load santoku first. If they load santoku second, they'll have to use `tidyr::chop()`, but reading the documentation, I suspect this will be rare. - For programming, people should probably used the fully qualified name anyway. * When to extend? - I think default should be "if necessary" (`extend = NULL`); should always extend to Inf, -Inf so that these break labels are not data-dependent - Tension between wanting something predictable in your new data, vs. something readable in `tab_*`. E.g. ```r tab_size(1:9, 3, lbl_seq()) ``` should surely return labels a, b, c. But this means we aren't always extending. * Should we allow vector `labels` to be longer than necessary? + lets people do e.g. `chop(rnorm(100), -2:2, LETTERS)` - but might hide errors - overall I'm against * Is the label interface right? Problem exposed by `brk_mean_sd`: if we aren't sure whether data gets extended, then how do we know what the labels should be? - maybe label functions should have access to `x`? - or should they be informed if breaks got extended? - or could the breaks object know how to extend its labels? - current solution: labels get `extend` - I think better: `breaks` objects include suggested labels which the user can override. That way they always have the info necessary. - We could also divide labelling into two parts: 1. choosing the break numbers (these may not be the actual values, e.g they could be quantiles or std errs from 0) 2. formatting these numbers, and with dashes, set notation etc - So maybe `brk_*` functions always return break numbers; then labels decide how to format them? * Should we automatically sort breaks, or throw an error if they're unsorted? - or a warning? - currently an error * What if `breaks = c(1, 2, 2, 2, 3)`? - throw an error * For some cases e.g. `brk_quantiles`, `brk_width`, the data may not work well e.g. if it is all NA. What is an empty set of breaks? # Possible interfaces - `hist_xxx` functions for histograms/barplots? (how to treat singletons?) - `grp_xxx` for group_by? Hmmm... - New label interface to replace `lbl_sequence`: `lbl_style("1."), lbl_style("(i)"), lbl_style("A")` etc.? - Still wonder, could we drop `extend` which adds complexity and just have `only()` or `extend()` as new breaks functions? # Other ideas - Speedup categorize by only checking left intervals, add 1 if its past each interval [NO: actually no fewer checks in the end...] - Speedup by using pointers? hmm, magic... ================================================ FILE: _pkgdown.yml ================================================ destination: docs url: https://hughjonesd.github.io/santoku template: bootstrap: 5 bootswatch: darkly theme: arrow-dark bslib: link-color: "#f9ea6b" link-decoration: none navbar-dark-hover-color: "#f9ea6b" navbar-dark-active-color: "#f9ea6b" tutorials: - name: 00-visualintroduction title: A visual introduction to santoku url: https://hughjonesd.github.io/visual-introduction.html - name: 01-chopping-dates title: Chopping dates with santoku url: https://hughjonesd.github.io/chopping-dates-with-santoku.html reference: - title: Package overview - contents: - "`santoku-package`" - title: Basic chop functions desc: Cut a vector into intervals - contents: - chop - kiru - fillet - tab - title: Chopping by width desc: Cut a vector into intervals defined by width - contents: - matches("width") - ends_with("proportions") - ends_with("evenly") - title: Chopping by n desc: Cut a vector into intervals defined by number of elements - contents: - ends_with("_n") - ends_with("quantiles") - ends_with("equally") - title: Chopping and separating desc: Cut a vector into intervals, separating out common values - contents: - ends_with("spikes") - ends_with("dissect") - title: Other chop functions desc: Miscellaneous ways to cut a vector into intervals - contents: - ends_with("mean_sd") - ends_with("pretty") - ends_with("fn") - brk_default - brk_manual - title: Label functions desc: Specify how to label the chopped intervals - contents: - starts_with("lbl") - title: Miscellaneous desc: Other helper functions - contents: - -starts_with("chop|brk|lbl|santoku") - -ends_with("dissect") - -fillet ================================================ FILE: advantages.Rmd ================================================ ## Advantages Here are some advantages of santoku: * By default, `chop()` always covers the whole range of the data, so you won't get unexpected `NA` values. * `chop()` can handle single values as well as intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a separate factor level for values exactly equal to 2. * `chop()` can handle many kinds of data, including numbers, dates and times, and [units](https://r-quantities.github.io/units/). * `chop_*` functions create intervals in many ways, using quantiles of the data, standard deviations, fixed-width intervals, equal-sized groups, or pretty intervals for use in graphs. * It's easy to label intervals: use names for your breaks vector, or use a `lbl_*` function to create interval notation like `[1, 2)`, dash notation like `1-2`, or arbitrary styles using `glue::glue()`. * `tab_*` functions quickly chop data, then tabulate it. These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. ================================================ FILE: codecov.yml ================================================ comment: false coverage: status: project: default: target: auto threshold: 1% patch: default: target: auto threshold: 1% ================================================ FILE: cran-comments.md ================================================ Version 1.2.1. Fixes (I hope) a testing bug exposed on CRAN. ## Test environments * local (macOS), R 4.6.0 * github (windows, macOS, Ubuntu), devel and release * win-builder (windows), devel and release ## R CMD check results * Fine on all platforms. ## Reverse dependencies * None. ================================================ FILE: docs/404.html ================================================ Page not found (404) • santoku Skip to contents
Content not found. Please use links in the navbar.
================================================ FILE: docs/404.md ================================================ Content not found. Please use links in the navbar. # Page not found (404) ================================================ FILE: docs/AGENTS.html ================================================ AGENTS.md • santoku Skip to contents

This file provides guidance to agents when working with code in this repository.

Project Overview

santoku is an R package that provides chop(), a versatile replacement for base::cut() for cutting data into intervals. The package handles numeric vectors, dates, times, and other comparable objects, with support for singleton intervals and flexible labeling.

Common Commands

Testing

# Run all tests
devtools::test()

# Run tests from command line
R CMD check .

# Run specific test file
testthat::test_file("tests/testthat/test-chop.R")

Development workflow

# Build package
devtools::build()

# Install package locally
devtools::install()

# Check package
devtools::check()

# Load package for interactive development
devtools::load_all()

Documentation

# Update documentation
devtools::document()

# Build website
pkgdown::build_site()

Architecture

Core Components

  • Main cutting function: chop() in R/chop.R - the primary interface that calls other functions
  • Break creation: R/breaks*.R files contain functions to create break points (brk_* functions)
  • Labeling system: R/labels*.R files contain labeling functions (lbl_* functions)
  • Convenience functions: R/chop-*.R files contain chop_* wrapper functions for common use cases
  • C++ backend: src/categorize.cpp provides fast interval categorization via Rcpp
  • Tabulation: R/tab.R provides tab_* functions that chop and tabulate in one step

Key Design Patterns

  1. Function factories: Many functions return other functions (e.g., brk_* functions return break-creation functions)
  2. Method dispatch: Uses S3 methods and vctrs for handling different data types (numbers, dates, etc.)
  3. Extensible labeling: Label functions can be combined and customized using the lbl_* family
  4. Performance: Core categorization logic is implemented in C++ for speed

File Organization

  • R/chop.R - Main chop() function and documentation
  • R/breaks*.R - Break point creation (brk_default, brk_width, etc.)
  • R/labels*.R - Label generation (lbl_intervals, lbl_dash, etc.)
  • R/chop-*.R - Convenience functions (chop_quantiles, chop_width, etc.)
  • R/tab.R - Tabulation functions
  • R/utils.R - Utility functions like exactly() and percent()
  • src/categorize.cpp - Fast C++ categorization implementation
  • tests/testthat/ - Comprehensive test suite

Development Notes

  • The package uses Rcpp for performance-critical categorization
  • Tests are extensive and include systematic testing in test-zzz-systematic.R
  • The package supports non-standard data types (dates, times, units) via the vctrs package
  • Documentation follows roxygen2 conventions with extensive examples
  • Uses lifecycle package for function lifecycle management
================================================ FILE: docs/AGENTS.md ================================================ # AGENTS.md This file provides guidance to agents when working with code in this repository. ## Project Overview santoku is an R package that provides [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), a versatile replacement for [`base::cut()`](https://rdrr.io/r/base/cut.html) for cutting data into intervals. The package handles numeric vectors, dates, times, and other comparable objects, with support for singleton intervals and flexible labeling. ## Common Commands ### Testing ``` r # Run all tests devtools::test() # Run tests from command line R CMD check . # Run specific test file testthat::test_file("tests/testthat/test-chop.R") ``` ### Development workflow ``` r # Build package devtools::build() # Install package locally devtools::install() # Check package devtools::check() # Load package for interactive development devtools::load_all() ``` ### Documentation ``` r # Update documentation devtools::document() # Build website pkgdown::build_site() ``` ## Architecture ### Core Components - **Main cutting function**: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) in `R/chop.R` - the primary interface that calls other functions - **Break creation**: `R/breaks*.R` files contain functions to create break points (`brk_*` functions) - **Labeling system**: `R/labels*.R` files contain labeling functions (`lbl_*` functions) - **Convenience functions**: `R/chop-*.R` files contain `chop_*` wrapper functions for common use cases - **C++ backend**: `src/categorize.cpp` provides fast interval categorization via Rcpp - **Tabulation**: `R/tab.R` provides `tab_*` functions that chop and tabulate in one step ### Key Design Patterns 1. **Function factories**: Many functions return other functions (e.g., `brk_*` functions return break-creation functions) 2. **Method dispatch**: Uses S3 methods and vctrs for handling different data types (numbers, dates, etc.) 3. **Extensible labeling**: Label functions can be combined and customized using the `lbl_*` family 4. **Performance**: Core categorization logic is implemented in C++ for speed ### File Organization - `R/chop.R` - Main [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) function and documentation - `R/breaks*.R` - Break point creation (`brk_default`, `brk_width`, etc.) - `R/labels*.R` - Label generation (`lbl_intervals`, `lbl_dash`, etc.) - `R/chop-*.R` - Convenience functions (`chop_quantiles`, `chop_width`, etc.) - `R/tab.R` - Tabulation functions - `R/utils.R` - Utility functions like [`exactly()`](https://hughjonesd.github.io/santoku/reference/exactly.md) and [`percent()`](https://hughjonesd.github.io/santoku/reference/percent.md) - `src/categorize.cpp` - Fast C++ categorization implementation - `tests/testthat/` - Comprehensive test suite ## Development Notes - The package uses Rcpp for performance-critical categorization - Tests are extensive and include systematic testing in `test-zzz-systematic.R` - The package supports non-standard data types (dates, times, units) via the vctrs package - Documentation follows roxygen2 conventions with extensive examples - Uses lifecycle package for function lifecycle management ================================================ FILE: docs/CLAUDE.html ================================================ CLAUDE.md • santoku Skip to contents

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

santoku is an R package that provides chop(), a versatile replacement for base::cut() for cutting data into intervals. The package handles numeric vectors, dates, times, and other comparable objects, with support for singleton intervals and flexible labeling.

Common Commands

Testing

# Run all tests
devtools::test()

# Run tests from command line
R CMD check .

# Run specific test file
testthat::test_file("tests/testthat/test-chop.R")

Development workflow

# Build package
devtools::build()

# Install package locally
devtools::install()

# Check package
devtools::check()

# Load package for interactive development
devtools::load_all()

Documentation

# Update documentation
devtools::document()

# Build website
pkgdown::build_site()

Architecture

Core Components

  • Main cutting function: chop() in R/chop.R - the primary interface that calls other functions
  • Break creation: R/breaks*.R files contain functions to create break points (brk_* functions)
  • Labeling system: R/labels*.R files contain labeling functions (lbl_* functions)
  • Convenience functions: R/chop-*.R files contain chop_* wrapper functions for common use cases
  • C++ backend: src/categorize.cpp provides fast interval categorization via Rcpp
  • Tabulation: R/tab.R provides tab_* functions that chop and tabulate in one step

Key Design Patterns

  1. Function factories: Many functions return other functions (e.g., brk_* functions return break-creation functions)
  2. Method dispatch: Uses S3 methods and vctrs for handling different data types (numbers, dates, etc.)
  3. Extensible labeling: Label functions can be combined and customized using the lbl_* family
  4. Performance: Core categorization logic is implemented in C++ for speed

File Organization

  • R/chop.R - Main chop() function and documentation
  • R/breaks*.R - Break point creation (brk_default, brk_width, etc.)
  • R/labels*.R - Label generation (lbl_intervals, lbl_dash, etc.)
  • R/chop-*.R - Convenience functions (chop_quantiles, chop_width, etc.)
  • R/tab.R - Tabulation functions
  • R/utils.R - Utility functions like exactly() and percent()
  • src/categorize.cpp - Fast C++ categorization implementation
  • tests/testthat/ - Comprehensive test suite

Development Notes

  • The package uses Rcpp for performance-critical categorization
  • Tests are extensive and include systematic testing in test-zzz-systematic.R
  • The package supports non-standard data types (dates, times, units) via the vctrs package
  • Documentation follows roxygen2 conventions with extensive examples
  • Uses lifecycle package for function lifecycle management
================================================ FILE: docs/CLAUDE.md ================================================ # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview santoku is an R package that provides [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), a versatile replacement for [`base::cut()`](https://rdrr.io/r/base/cut.html) for cutting data into intervals. The package handles numeric vectors, dates, times, and other comparable objects, with support for singleton intervals and flexible labeling. ## Common Commands ### Testing ``` r # Run all tests devtools::test() # Run tests from command line R CMD check . # Run specific test file testthat::test_file("tests/testthat/test-chop.R") ``` ### Development workflow ``` r # Build package devtools::build() # Install package locally devtools::install() # Check package devtools::check() # Load package for interactive development devtools::load_all() ``` ### Documentation ``` r # Update documentation devtools::document() # Build website pkgdown::build_site() ``` ## Architecture ### Core Components - **Main cutting function**: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) in `R/chop.R` - the primary interface that calls other functions - **Break creation**: `R/breaks*.R` files contain functions to create break points (`brk_*` functions) - **Labeling system**: `R/labels*.R` files contain labeling functions (`lbl_*` functions) - **Convenience functions**: `R/chop-*.R` files contain `chop_*` wrapper functions for common use cases - **C++ backend**: `src/categorize.cpp` provides fast interval categorization via Rcpp - **Tabulation**: `R/tab.R` provides `tab_*` functions that chop and tabulate in one step ### Key Design Patterns 1. **Function factories**: Many functions return other functions (e.g., `brk_*` functions return break-creation functions) 2. **Method dispatch**: Uses S3 methods and vctrs for handling different data types (numbers, dates, etc.) 3. **Extensible labeling**: Label functions can be combined and customized using the `lbl_*` family 4. **Performance**: Core categorization logic is implemented in C++ for speed ### File Organization - `R/chop.R` - Main [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) function and documentation - `R/breaks*.R` - Break point creation (`brk_default`, `brk_width`, etc.) - `R/labels*.R` - Label generation (`lbl_intervals`, `lbl_dash`, etc.) - `R/chop-*.R` - Convenience functions (`chop_quantiles`, `chop_width`, etc.) - `R/tab.R` - Tabulation functions - `R/utils.R` - Utility functions like [`exactly()`](https://hughjonesd.github.io/santoku/reference/exactly.md) and [`percent()`](https://hughjonesd.github.io/santoku/reference/percent.md) - `src/categorize.cpp` - Fast C++ categorization implementation - `tests/testthat/` - Comprehensive test suite ## Development Notes - The package uses Rcpp for performance-critical categorization - Tests are extensive and include systematic testing in `test-zzz-systematic.R` - The package supports non-standard data types (dates, times, units) via the vctrs package - Documentation follows roxygen2 conventions with extensive examples - Uses lifecycle package for function lifecycle management ================================================ FILE: docs/LICENSE-text.html ================================================ License • santoku Skip to contents
YEAR: 2020
COPYRIGHT HOLDER: David Hugh-Jones
================================================ FILE: docs/LICENSE-text.md ================================================ # License YEAR: 2020 COPYRIGHT HOLDER: David Hugh-Jones ================================================ FILE: docs/LICENSE.html ================================================ MIT License • santoku Skip to contents

Copyright (c) 2019 David Hugh-Jones

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================ FILE: docs/LICENSE.md ================================================ # MIT License Copyright (c) 2019 David Hugh-Jones Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: docs/TODO.html ================================================ TODO • santoku Skip to contents
  • Work on tests
    • tests for left and close_end arguments
    • tests for brk_default
    • brk_width() needs tests which match the guarantees in the documentation
    • ditto for brk_evenly() which now uses its own implementation to guarantee exactly intervals intervals
    • systematic tests for brk_* functions
  • Implement a simple Infinity class that automatically casts to any other class and is always > or < than any other element? Then replace the class_bounds() complexity?
    • The problem at the moment is that vec_cast() is highly unreliable and you never know if a particular class will accept Inf.
    • An infinity class would be fine, but how does that go into the existing breaks object which has its own underlying class?
    • Might be more reasonable just not to add Inf or -Inf elements. Instead, record whether the breaks have left and right “infinity” set. Then just add numeric infinity to the breaks before you call categorize_impl (or the R version). In particular, e.g. integer64 doesn’t like Inf or -Inf but it does have very large numbers in bit64::lim.integer64 which look ugly and which only exist to be lower/higher than everything else anyway…
      • But NB this requires a new way to create the labels, and that kinda sucks….

Thoughts on errors

  • On the whole, we don’t want to error out if x is weird. x is data. But if e.g. breaks are weird, we can error out.
    • Exception: x is the wrong class or type.
  • In some cases we want to guarantee the set of breaks.
  • In other cases, e.g. brk_evenly() we don’t need to make such a guarantee.

Questions

  • Is it really OK to have left = FALSE as the default in chop_quantiles(), chop_evenly() and friends?
    • the alternative is to do it only when x is non-numeric.
    • that makes the surprise rarer, but rare surprises can be worse… and it adds complexity since the functions have to be generic.
    • another alternative: chop sets left = FALSE for non-numeric x. Probably better.
  • Do we need drop?
    • should drop have a default of ! isTRUE(extend) i.e. be FALSE when extend = TRUE?

Questions with a (provisional) answer

  • Should we have a flag to return characters?
    • No, we have labels = NULL for integer codes only though.
  • Should we put a percent argument into brk_quantiles() so it can store scaled endpoints as proportions rather than percentages (the current default)?
    • My sense is, not unless someone asks.
    • Oh, someone just did ask; more generally though.
  • Should close_end = TRUE argument come before ... in chop_ variants?
    • No. We don’t want people to set it by position, so distinguish it from the initial arguments.
  • What to do about tidyr::chop()
    • Current answer: fuck ’em. (NB: just kidding. I am a huge tidyverse fan.)
    • We provide kiru(). So on the REPL, people can just use kiru() if they load santoku first. If they load santoku second, they’ll have to use tidyr::chop(), but reading the documentation, I suspect this will be rare.
    • For programming, people should probably used the fully qualified name anyway.
  • When to extend?
    • I think default should be “if necessary” (extend = NULL); should always extend to Inf, -Inf so that these break labels are not data-dependent

    • Tension between wanting something predictable in your new data, vs. something readable in tab_*. E.g.

      tab_size(1:9, 3, lbl_seq()) 

      should surely return labels a, b, c. But this means we aren’t always extending.

  • Should we allow vector labels to be longer than necessary?
    • lets people do e.g. chop(rnorm(100), -2:2, LETTERS)
    • but might hide errors
    • overall I’m against
  • Is the label interface right? Problem exposed by brk_mean_sd: if we aren’t sure whether data gets extended, then how do we know what the labels should be?
    • maybe label functions should have access to x?
    • or should they be informed if breaks got extended?
    • or could the breaks object know how to extend its labels?
    • current solution: labels get extend
    • I think better: breaks objects include suggested labels which the user can override. That way they always have the info necessary.
    • We could also divide labelling into two parts:
      1. choosing the break numbers (these may not be the actual values, e.g they could be quantiles or std errs from 0)
      2. formatting these numbers, and with dashes, set notation etc
    • So maybe brk_* functions always return break numbers; then labels decide how to format them?
  • Should we automatically sort breaks, or throw an error if they’re unsorted?
    • or a warning?
    • currently an error
  • What if breaks = c(1, 2, 2, 2, 3)?
    • throw an error
  • For some cases e.g. brk_quantiles, brk_width, the data may not work well e.g. if it is all NA. What is an empty set of breaks?

Possible interfaces

  • hist_xxx functions for histograms/barplots? (how to treat singletons?)
  • grp_xxx for group_by? Hmmm…
  • New label interface to replace lbl_sequence: lbl_style("1."), lbl_style("(i)"), lbl_style("A") etc.?
  • Still wonder, could we drop extend which adds complexity and just have only() or extend() as new breaks functions?

Other ideas

  • Speedup categorize by only checking left intervals, add 1 if its past each interval [NO: actually no fewer checks in the end…]
  • Speedup by using pointers? hmm, magic…
================================================ FILE: docs/TODO.md ================================================ # TODO - Work on tests - tests for `left` and `close_end` arguments - tests for `brk_default` - [`brk_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) needs tests which match the guarantees in the documentation - ditto for [`brk_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) which now uses its own implementation to guarantee exactly `intervals` intervals - systematic tests for `brk_*` functions - Implement a simple `Infinity` class that automatically casts to any other class and is always \> or \< than any other element? Then replace the `class_bounds()` complexity? - The problem at the moment is that `vec_cast()` is highly unreliable and you never know if a particular class will accept `Inf`. - An infinity class would be fine, but how does that go into the existing `breaks` object which has its own underlying class? - Might be more reasonable just not to add `Inf` or `-Inf` elements. Instead, record whether the breaks have left and right “infinity” set. Then just add numeric infinity to the breaks before you call `categorize_impl` (or the R version). In particular, e.g. `integer64` doesn’t like `Inf` or `-Inf` but it does have very large numbers in [`bit64::lim.integer64`](https://bit64.r-lib.org/reference/sum.integer64.html) which look ugly and which only exist to be lower/higher than everything else anyway… - But NB this requires a new way to create the labels, and that kinda sucks…. # Thoughts on errors - On the whole, we don’t want to error out if `x` is weird. `x` is data. But if e.g. `breaks` are weird, we can error out. - Exception: `x` is the wrong class or type. - In some cases we want to guarantee the set of breaks. - e.g. [`brk_manual()`](https://hughjonesd.github.io/santoku/reference/brk_manual.md) with `extend` set. - In other cases, e.g. [`brk_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) we don’t need to make such a guarantee. # Questions - Is it really OK to have `left = FALSE` as the default in [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) and friends? - the alternative is to do it only when `x` is non-numeric. - that makes the surprise rarer, but rare surprises can be worse… and it adds complexity since the functions have to be generic. - another alternative: `chop` sets `left = FALSE` for non-numeric `x`. Probably better. - Do we need `drop`? - should `drop` have a default of `! isTRUE(extend)` i.e. be `FALSE` when `extend = TRUE`? # Questions with a (provisional) answer - Should we have a flag to return characters? - No, we have `labels = NULL` for integer codes only though. - Should we put a `percent` argument into [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) so it can store scaled endpoints as proportions rather than percentages (the current default)? - My sense is, not unless someone asks. - Oh, someone just did ask; more generally though. - Should `close_end = TRUE` argument come before `...` in `chop_` variants? - No. We don’t want people to set it by position, so distinguish it from the initial arguments. - What to do about [`tidyr::chop()`](https://tidyr.tidyverse.org/reference/chop.html) - Current answer: fuck ’em. (NB: just kidding. I am a huge tidyverse fan.) - We provide [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md). So on the REPL, people can just use [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md) if they load santoku first. If they load santoku second, they’ll have to use [`tidyr::chop()`](https://tidyr.tidyverse.org/reference/chop.html), but reading the documentation, I suspect this will be rare. - For programming, people should probably used the fully qualified name anyway. - When to extend? - I think default should be “if necessary” (`extend = NULL`); should always extend to Inf, -Inf so that these break labels are not data-dependent - Tension between wanting something predictable in your new data, vs. something readable in `tab_*`. E.g. ``` r tab_size(1:9, 3, lbl_seq()) ``` should surely return labels a, b, c. But this means we aren’t always extending. - Should we allow vector `labels` to be longer than necessary? - lets people do e.g. `chop(rnorm(100), -2:2, LETTERS)` - but might hide errors - overall I’m against - Is the label interface right? Problem exposed by `brk_mean_sd`: if we aren’t sure whether data gets extended, then how do we know what the labels should be? - maybe label functions should have access to `x`? - or should they be informed if breaks got extended? - or could the breaks object know how to extend its labels? - current solution: labels get `extend` - I think better: `breaks` objects include suggested labels which the user can override. That way they always have the info necessary. - We could also divide labelling into two parts: 1. choosing the break numbers (these may not be the actual values, e.g they could be quantiles or std errs from 0) 2. formatting these numbers, and with dashes, set notation etc - So maybe `brk_*` functions always return break numbers; then labels decide how to format them? - Should we automatically sort breaks, or throw an error if they’re unsorted? - or a warning? - currently an error - What if `breaks = c(1, 2, 2, 2, 3)`? - throw an error - For some cases e.g. `brk_quantiles`, `brk_width`, the data may not work well e.g. if it is all NA. What is an empty set of breaks? # Possible interfaces - `hist_xxx` functions for histograms/barplots? (how to treat singletons?) - `grp_xxx` for group_by? Hmmm… - New label interface to replace `lbl_sequence`: `lbl_style("1."), lbl_style("(i)"), lbl_style("A")` etc.? - Still wonder, could we drop `extend` which adds complexity and just have `only()` or `extend()` as new breaks functions? # Other ideas - Speedup categorize by only checking left intervals, add 1 if its past each interval \[NO: actually no fewer checks in the end…\] - Speedup by using pointers? hmm, magic… ================================================ FILE: docs/articles/index.html ================================================ Articles • santoku Skip to contents
================================================ FILE: docs/articles/index.md ================================================ # Articles ### All vignettes - [Performance](https://hughjonesd.github.io/santoku/articles/website-articles/performance.md): - [Introduction to santoku](https://hughjonesd.github.io/santoku/articles/santoku.md): - [What's new in santoku 0.9.0](https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.md): ================================================ FILE: docs/articles/santoku.html ================================================ Introduction to santoku • santoku Skip to contents

Introduction

Santoku is a package for cutting data into intervals. It provides chop(), a replacement for base R’s cut() function, as well as several convenience functions to cut different kinds of intervals.

To install santoku, run:

install.packages("santoku")

Basic usage

Use chop() like cut(), to cut numeric data into intervals between a set of breaks.

library(santoku)

x <- runif(10, 0, 10)
(chopped <- chop(x, breaks = 0:10))
#>  [1] [4, 5)  [8, 9)  [3, 4)  [4, 5)  [7, 8)  [9, 10] [6, 7)  [8, 9)  [1, 2) 
#> [10] [4, 5) 
#> Levels: [1, 2) [3, 4) [4, 5) [6, 7) [7, 8) [8, 9) [9, 10]
data.frame(x, chopped)
#>        x chopped
#> 1  4.978  [4, 5)
#> 2  8.970  [8, 9)
#> 3  3.392  [3, 4)
#> 4  4.677  [4, 5)
#> 5  7.057  [7, 8)
#> 6  9.708 [9, 10]
#> 7  6.714  [6, 7)
#> 8  8.377  [8, 9)
#> 9  1.086  [1, 2)
#> 10 4.495  [4, 5)

chop() returns a factor.

If data is beyond the limits of breaks, they will be extended automatically:

chopped <- chop(x, breaks = 3:7)
data.frame(x, chopped)
#>        x    chopped
#> 1  4.978     [4, 5)
#> 2  8.970 [7, 9.708]
#> 3  3.392     [3, 4)
#> 4  4.677     [4, 5)
#> 5  7.057 [7, 9.708]
#> 6  9.708 [7, 9.708]
#> 7  6.714     [6, 7)
#> 8  8.377 [7, 9.708]
#> 9  1.086 [1.086, 3)
#> 10 4.495     [4, 5)

To chop a single number into a separate category, put the number twice in breaks:

x_fives <- x
x_fives[1:5] <- 5
chopped <- chop(x_fives, c(2, 5, 5, 8))
data.frame(x_fives, chopped)
#>    x_fives    chopped
#> 1    5.000        {5}
#> 2    5.000        {5}
#> 3    5.000        {5}
#> 4    5.000        {5}
#> 5    5.000        {5}
#> 6    9.708 [8, 9.708]
#> 7    6.714     (5, 8)
#> 8    8.377 [8, 9.708]
#> 9    1.086 [1.086, 2)
#> 10   4.495     [2, 5)

To quickly produce a table of chopped data, use tab():

tab(1:10, c(2, 5, 8))
#>  [1, 2)  [2, 5)  [5, 8) [8, 10] 
#>       1       3       3       3

Chopping by width and number of elements

To chop into fixed-width intervals, starting at the minimum value, use chop_width():

chopped <- chop_width(x, 2)
data.frame(x, chopped)
#>        x        chopped
#> 1  4.978 [3.086, 5.086)
#> 2  8.970 [7.086, 9.086)
#> 3  3.392 [3.086, 5.086)
#> 4  4.677 [3.086, 5.086)
#> 5  7.057 [5.086, 7.086)
#> 6  9.708 [9.086, 11.09]
#> 7  6.714 [5.086, 7.086)
#> 8  8.377 [7.086, 9.086)
#> 9  1.086 [1.086, 3.086)
#> 10 4.495 [3.086, 5.086)

To chop into a fixed number of intervals, each with the same width, use chop_evenly():

chopped <- chop_evenly(x, intervals = 3)
data.frame(x, chopped)
#>        x        chopped
#> 1  4.978  [3.96, 6.834)
#> 2  8.970 [6.834, 9.708]
#> 3  3.392  [1.086, 3.96)
#> 4  4.677  [3.96, 6.834)
#> 5  7.057 [6.834, 9.708]
#> 6  9.708 [6.834, 9.708]
#> 7  6.714  [3.96, 6.834)
#> 8  8.377 [6.834, 9.708]
#> 9  1.086  [1.086, 3.96)
#> 10 4.495  [3.96, 6.834)

To chop into groups with a fixed number of elements, use chop_n():

chopped <- chop_n(x, 4)
table(chopped)
#> chopped
#> [1.086, 4.978)  [4.978, 8.97)  [8.97, 9.708] 
#>              4              4              2

To chop into a fixed number of groups, each with the same number of elements, use chop_equally():

chopped <- chop_equally(x, groups = 5)
table(chopped)
#> chopped
#> [1.086, 4.275) [4.275, 4.858) [4.858, 6.851) [6.851, 8.495) [8.495, 9.708] 
#>              2              2              2              2              2

To chop data up by quantiles, use chop_quantiles():

chopped <- chop_quantiles(x, c(0.25, 0.5, 0.75))
data.frame(x, chopped)
#>        x     chopped
#> 1  4.978  [25%, 50%)
#> 2  8.970 [75%, 100%]
#> 3  3.392   [0%, 25%)
#> 4  4.677  [25%, 50%)
#> 5  7.057  [50%, 75%)
#> 6  9.708 [75%, 100%]
#> 7  6.714  [50%, 75%)
#> 8  8.377 [75%, 100%]
#> 9  1.086   [0%, 25%)
#> 10 4.495   [0%, 25%)

To chop data up by proportions of the data range, use chop_proportions():

chopped <- chop_proportions(x, c(0.25, 0.5, 0.75))
data.frame(x, chopped)
#>        x        chopped
#> 1  4.978 [3.242, 5.397)
#> 2  8.970 [7.552, 9.708]
#> 3  3.392 [3.242, 5.397)
#> 4  4.677 [3.242, 5.397)
#> 5  7.057 [5.397, 7.552)
#> 6  9.708 [7.552, 9.708]
#> 7  6.714 [5.397, 7.552)
#> 8  8.377 [7.552, 9.708]
#> 9  1.086 [1.086, 3.242)
#> 10 4.495 [3.242, 5.397)

You can think of these six functions as logically arranged in a table.

Different ways to chop by size
To chop into… Sizing intervals by…
  number of elements: interval width:
a specific number of equal intervals… chop_equally() chop_evenly()
intervals of one specific size… chop_n() chop_width()
intervals of different specific sizes… chop_quantiles() chop_proportions()

Even more ways to chop

To chop data by standard deviations around the mean, use chop_mean_sd():

chopped <- chop_mean_sd(x)
data.frame(x, chopped)
#>        x        chopped
#> 1  4.978  [-1 sd, 0 sd)
#> 2  8.970   [1 sd, 2 sd)
#> 3  3.392  [-1 sd, 0 sd)
#> 4  4.677  [-1 sd, 0 sd)
#> 5  7.057   [0 sd, 1 sd)
#> 6  9.708   [1 sd, 2 sd)
#> 7  6.714   [0 sd, 1 sd)
#> 8  8.377   [0 sd, 1 sd)
#> 9  1.086 [-2 sd, -1 sd)
#> 10 4.495  [-1 sd, 0 sd)

To chop data into attractive intervals, use chop_pretty(). This selects intervals which are a multiple of 2, 5 or 10. It’s useful for producing bar plots.

chopped <- chop_pretty(x)
data.frame(x, chopped)
#>        x chopped
#> 1  4.978  [4, 6)
#> 2  8.970 [8, 10]
#> 3  3.392  [2, 4)
#> 4  4.677  [4, 6)
#> 5  7.057  [6, 8)
#> 6  9.708 [8, 10]
#> 7  6.714  [6, 8)
#> 8  8.377 [8, 10]
#> 9  1.086  [0, 2)
#> 10 4.495  [4, 6)

Isolating common values

In exploratory work, it’s sometimes useful to find common values and treat them differently. You can use dissect() to do this:

x_spike <- rnorm(100)
x_spike[1:50] <- x_spike[1]

chopped <- dissect(x_spike, -3:3, prop = 0.1)
table(chopped)
#> chopped
#> [-3, -2) [-2, -1)  [-1, 0)   [0, 1) {0.6996}   [1, 2) 
#>        2        5       15       18       50       10

prop = 0.2 will put any unique value of x into its own separate category if it makes up at least 20% of the data.

Note that unlike all the other chop_* functions, dissect() doesn’t always categorize x into ordered, connected intervals. To remind you of this, it is named differently. If you want to create separate intervals on the left and right of common elements, use chop_spikes():

chopped <- chop_spikes(x_spike, -3:3, prop = 0.1)
table(chopped)
#> chopped
#>    [-3, -2)    [-2, -1)     [-1, 0) [0, 0.6996)    {0.6996} (0.6996, 1) 
#>           2           5          15          13          50           5 
#>      [1, 2) 
#>          10

Compare this to the table before. There are two intervals on either side of the common value, instead of one interval surrounding it.

Quick tables

tab_n(), tab_width(), and friends act similarly to tab(), calling the related chop_* function and then table() on the result.

tab_n(x, 4)
#> [1.086, 4.978)  [4.978, 8.97)  [8.97, 9.708] 
#>              4              4              2
tab_width(x, 2)
#> [1.086, 3.086) [3.086, 5.086) [5.086, 7.086) [7.086, 9.086) [9.086, 11.09] 
#>              1              4              2              2              1
tab_evenly(x, 5)
#>  [1.086, 2.81)  [2.81, 4.535) [4.535, 6.259) [6.259, 7.983) [7.983, 9.708] 
#>              1              2              2              2              3
tab_mean_sd(x)
#> [-2 sd, -1 sd)  [-1 sd, 0 sd)   [0 sd, 1 sd)   [1 sd, 2 sd) 
#>              1              4              3              2

Specifying labels

By default, santoku labels intervals using mathematical notation:

  • [0, 1] means all numbers between 0 and 1 inclusive.
  • (0, 1) means all numbers strictly between 0 and 1, not including the endpoints.
  • [0, 1) means all numbers between 0 and 1, including 0 but not 1.
  • (0, 1] means all numbers between 0 and 1, including 1 but not 0.
  • {0} means just the number 0.

To override these labels, provide names to the breaks argument:

chopped <- chop(x, c(Lowest = 1, Low = 2, Higher = 5, Highest = 8))
data.frame(x, chopped)
#>        x chopped
#> 1  4.978     Low
#> 2  8.970 Highest
#> 3  3.392     Low
#> 4  4.677     Low
#> 5  7.057  Higher
#> 6  9.708 Highest
#> 7  6.714  Higher
#> 8  8.377 Highest
#> 9  1.086  Lowest
#> 10 4.495     Low

Or, you can specify factor labels with the labels argument:

chopped <- chop(x, c(2, 5, 8), labels = c("Lowest", "Low", "Higher", "Highest"))
data.frame(x, chopped)
#>        x chopped
#> 1  4.978     Low
#> 2  8.970 Highest
#> 3  3.392     Low
#> 4  4.677     Low
#> 5  7.057  Higher
#> 6  9.708 Highest
#> 7  6.714  Higher
#> 8  8.377 Highest
#> 9  1.086  Lowest
#> 10 4.495     Low

You need as many labels as there are intervals - one fewer than length(breaks) if your data doesn’t extend beyond breaks, one more than length(breaks) if it does.

To label intervals with a dash, use lbl_dash():

chopped <- chop(x, c(2, 5, 8), labels = lbl_dash())
data.frame(x, chopped)
#>        x chopped
#> 1  4.978     2—5
#> 2  8.970 8—9.708
#> 3  3.392     2—5
#> 4  4.677     2—5
#> 5  7.057     5—8
#> 6  9.708 8—9.708
#> 7  6.714     5—8
#> 8  8.377 8—9.708
#> 9  1.086 1.086—2
#> 10 4.495     2—5

To label integer data, use lbl_discrete(). It uses more informative right endpoints:

chopped  <- chop(1:10, c(2, 5, 8), labels = lbl_discrete())
chopped2 <- chop(1:10, c(2, 5, 8), labels = lbl_dash())
data.frame(x = 1:10, lbl_discrete = chopped, lbl_dash = chopped2)
#>     x lbl_discrete lbl_dash
#> 1   1            1      1—2
#> 2   2          2—4      2—5
#> 3   3          2—4      2—5
#> 4   4          2—4      2—5
#> 5   5          5—7      5—8
#> 6   6          5—7      5—8
#> 7   7          5—7      5—8
#> 8   8         8—10     8—10
#> 9   9         8—10     8—10
#> 10 10         8—10     8—10

You can customize the first or last labels:

chopped <- chop(x, c(2, 5, 8), labels = lbl_dash(first = "< 2", last = "8+"))
data.frame(x, chopped)
#>        x chopped
#> 1  4.978     2—5
#> 2  8.970      8+
#> 3  3.392     2—5
#> 4  4.677     2—5
#> 5  7.057     5—8
#> 6  9.708      8+
#> 7  6.714     5—8
#> 8  8.377      8+
#> 9  1.086     < 2
#> 10 4.495     2—5

To label intervals in order use lbl_seq():

chopped <- chop(x, c(2, 5, 8), labels = lbl_seq())
data.frame(x, chopped)
#>        x chopped
#> 1  4.978       b
#> 2  8.970       d
#> 3  3.392       b
#> 4  4.677       b
#> 5  7.057       c
#> 6  9.708       d
#> 7  6.714       c
#> 8  8.377       d
#> 9  1.086       a
#> 10 4.495       b

You can use numerals or even roman numerals:

chop(x, c(2, 5, 8), labels = lbl_seq("(1)"))
#>  [1] (2) (4) (2) (2) (3) (4) (3) (4) (1) (2)
#> Levels: (1) (2) (3) (4)
chop(x, c(2, 5, 8), labels = lbl_seq("i."))
#>  [1] ii.  iv.  ii.  ii.  iii. iv.  iii. iv.  i.   ii. 
#> Levels: i. ii. iii. iv.

Other labelling functions include:

Specifying breaks

By default, chop() extends breaks if necessary. If you don’t want that, set extend = FALSE:

chopped <- chop(x, c(3, 5, 7), extend = FALSE)
data.frame(x, chopped)
#>        x chopped
#> 1  4.978  [3, 5)
#> 2  8.970    <NA>
#> 3  3.392  [3, 5)
#> 4  4.677  [3, 5)
#> 5  7.057    <NA>
#> 6  9.708    <NA>
#> 7  6.714  [5, 7]
#> 8  8.377    <NA>
#> 9  1.086    <NA>
#> 10 4.495  [3, 5)

Data outside the range of breaks will become NA.

By default, intervals are closed on the left, i.e. they include their left endpoints. If you want right-closed intervals, set left = FALSE:

y <- 1:5
data.frame(
        y = y, 
        left_closed = chop(y, 1:5), 
        right_closed = chop(y, 1:5, left = FALSE)
      )
#>   y left_closed right_closed
#> 1 1      [1, 2)       [1, 2]
#> 2 2      [2, 3)       [1, 2]
#> 3 3      [3, 4)       (2, 3]
#> 4 4      [4, 5]       (3, 4]
#> 5 5      [4, 5]       (4, 5]

By default, the last interval is closed on both ends. If you want to keep the last interval open at the end, set close_end = FALSE:

data.frame(
  y = y,
  end_closed = chop(y, 1:5),
  end_open   = chop(y, 1:5, close_end = FALSE)
)
#>   y end_closed end_open
#> 1 1     [1, 2)   [1, 2)
#> 2 2     [2, 3)   [2, 3)
#> 3 3     [3, 4)   [3, 4)
#> 4 4     [4, 5]   [4, 5)
#> 5 5     [4, 5]      {5}

Chopping dates, times and other vectors

You can chop many kinds of vectors with santoku, including Date objects…

y2k <- as.Date("2000-01-01") + 0:10 * 7
data.frame(
  y2k = y2k,
  chopped = chop(y2k, as.Date(c("2000-02-01", "2000-03-01")))
)
#>           y2k                  chopped
#> 1  2000-01-01 [2000-01-01, 2000-02-01)
#> 2  2000-01-08 [2000-01-01, 2000-02-01)
#> 3  2000-01-15 [2000-01-01, 2000-02-01)
#> 4  2000-01-22 [2000-01-01, 2000-02-01)
#> 5  2000-01-29 [2000-01-01, 2000-02-01)
#> 6  2000-02-05 [2000-02-01, 2000-03-01)
#> 7  2000-02-12 [2000-02-01, 2000-03-01)
#> 8  2000-02-19 [2000-02-01, 2000-03-01)
#> 9  2000-02-26 [2000-02-01, 2000-03-01)
#> 10 2000-03-04 [2000-03-01, 2000-03-11]
#> 11 2000-03-11 [2000-03-01, 2000-03-11]

… and POSIXct (date-time) objects:

# hours of the 2020 Crew Dragon flight:
crew_dragon <- seq(as.POSIXct("2020-05-30 18:00", tz = "GMT"), 
                     length.out = 24, by = "hours")
liftoff <- as.POSIXct("2020-05-30 15:22", tz = "America/New_York")
dock    <- as.POSIXct("2020-05-31 10:16", tz = "America/New_York")

data.frame(
  crew_dragon = crew_dragon,
  chopped = chop(crew_dragon, c(liftoff, dock), 
                   labels = c("pre-flight", "flight", "docked"))
)
#> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent
#> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent
#>            crew_dragon    chopped
#> 1  2020-05-30 18:00:00 pre-flight
#> 2  2020-05-30 19:00:00 pre-flight
#> 3  2020-05-30 20:00:00     flight
#> 4  2020-05-30 21:00:00     flight
#> 5  2020-05-30 22:00:00     flight
#> 6  2020-05-30 23:00:00     flight
#> 7  2020-05-31 00:00:00     flight
#> 8  2020-05-31 01:00:00     flight
#> 9  2020-05-31 02:00:00     flight
#> 10 2020-05-31 03:00:00     flight
#> 11 2020-05-31 04:00:00     flight
#> 12 2020-05-31 05:00:00     flight
#> 13 2020-05-31 06:00:00     flight
#> 14 2020-05-31 07:00:00     flight
#> 15 2020-05-31 08:00:00     flight
#> 16 2020-05-31 09:00:00     flight
#> 17 2020-05-31 10:00:00     flight
#> 18 2020-05-31 11:00:00     flight
#> 19 2020-05-31 12:00:00     flight
#> 20 2020-05-31 13:00:00     flight
#> 21 2020-05-31 14:00:00     flight
#> 22 2020-05-31 15:00:00     docked
#> 23 2020-05-31 16:00:00     docked
#> 24 2020-05-31 17:00:00     docked

Note how santoku correctly handles the different timezones.

You can use chop_width() with objects from the lubridate package, to chop by irregular periods such as months:

library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union
data.frame(
  y2k = y2k,
  chopped = chop_width(y2k, months(1))
)
#>           y2k                  chopped
#> 1  2000-01-01 [2000-01-01, 2000-02-01)
#> 2  2000-01-08 [2000-01-01, 2000-02-01)
#> 3  2000-01-15 [2000-01-01, 2000-02-01)
#> 4  2000-01-22 [2000-01-01, 2000-02-01)
#> 5  2000-01-29 [2000-01-01, 2000-02-01)
#> 6  2000-02-05 [2000-02-01, 2000-03-01)
#> 7  2000-02-12 [2000-02-01, 2000-03-01)
#> 8  2000-02-19 [2000-02-01, 2000-03-01)
#> 9  2000-02-26 [2000-02-01, 2000-03-01)
#> 10 2000-03-04 [2000-03-01, 2000-04-01)
#> 11 2000-03-11 [2000-03-01, 2000-04-01)

lbl_date() produces nicely formatted dates:

data.frame(
  y2k = y2k,
  chopped = chop_width(y2k, days(28), labels = lbl_date())
)
#>           y2k              chopped
#> 1  2000-01-01        1-28 Jan 2000
#> 2  2000-01-08        1-28 Jan 2000
#> 3  2000-01-15        1-28 Jan 2000
#> 4  2000-01-22        1-28 Jan 2000
#> 5  2000-01-29 29 Jan - 25 Feb 2000
#> 6  2000-02-05 29 Jan - 25 Feb 2000
#> 7  2000-02-12 29 Jan - 25 Feb 2000
#> 8  2000-02-19 29 Jan - 25 Feb 2000
#> 9  2000-02-26 26 Feb - 24 Mar 2000
#> 10 2000-03-04 26 Feb - 24 Mar 2000
#> 11 2000-03-11 26 Feb - 24 Mar 2000

You can also chop vectors with units, using the units package:

library(units)
#> udunits database from /Users/davidhugh-jones/Library/R/arm64/4.6/library/units/share/udunits/udunits2.xml

x <- set_units(1:10 * 10, cm)
br <- set_units(1:3, ft)
data.frame(
  x = x,
  chopped = chop(x, br)
)
#>           x                    chopped
#> 1   10 [cm] [ 10.00 [cm],  30.48 [cm])
#> 2   20 [cm] [ 10.00 [cm],  30.48 [cm])
#> 3   30 [cm] [ 10.00 [cm],  30.48 [cm])
#> 4   40 [cm] [ 30.48 [cm],  60.96 [cm])
#> 5   50 [cm] [ 30.48 [cm],  60.96 [cm])
#> 6   60 [cm] [ 30.48 [cm],  60.96 [cm])
#> 7   70 [cm] [ 60.96 [cm],  91.44 [cm])
#> 8   80 [cm] [ 60.96 [cm],  91.44 [cm])
#> 9   90 [cm] [ 60.96 [cm],  91.44 [cm])
#> 10 100 [cm] [ 91.44 [cm], 100.00 [cm]]

You should be able to chop anything that has a comparison operator. You can even chop character data using lexical ordering. By default santoku emits a warning in this case, to avoid accidentally misinterpreting results:

chop(letters[1:10], c("d", "f"))
#> Warning in categorize_non_numeric(x, breaks, left): `x` or `breaks` is of type
#> character, using lexical sorting. To turn off this warning, run:
#> options(santoku.warn_character = FALSE)
#>  [1] [a, d) [a, d) [a, d) [d, f) [d, f) [f, j] [f, j] [f, j] [f, j] [f, j]
#> Levels: [a, d) [d, f) [f, j]

If you find a type of data that you can’t chop, please file an issue.

================================================ FILE: docs/articles/santoku.md ================================================ # Introduction to santoku ### Introduction Santoku is a package for cutting data into intervals. It provides [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), a replacement for base R’s [`cut()`](https://rdrr.io/r/base/cut.html) function, as well as several convenience functions to cut different kinds of intervals. To install santoku, run: ``` r install.packages("santoku") ``` ### Basic usage Use [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) like [`cut()`](https://rdrr.io/r/base/cut.html), to cut numeric data into intervals between a set of `breaks`. ``` r library(santoku) x <- runif(10, 0, 10) (chopped <- chop(x, breaks = 0:10)) #> [1] [4, 5) [8, 9) [3, 4) [4, 5) [7, 8) [9, 10] [6, 7) [8, 9) [1, 2) #> [10] [4, 5) #> Levels: [1, 2) [3, 4) [4, 5) [6, 7) [7, 8) [8, 9) [9, 10] data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 5) #> 2 8.970 [8, 9) #> 3 3.392 [3, 4) #> 4 4.677 [4, 5) #> 5 7.057 [7, 8) #> 6 9.708 [9, 10] #> 7 6.714 [6, 7) #> 8 8.377 [8, 9) #> 9 1.086 [1, 2) #> 10 4.495 [4, 5) ``` [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) returns a factor. If data is beyond the limits of `breaks`, they will be extended automatically: ``` r chopped <- chop(x, breaks = 3:7) data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 5) #> 2 8.970 [7, 9.708] #> 3 3.392 [3, 4) #> 4 4.677 [4, 5) #> 5 7.057 [7, 9.708] #> 6 9.708 [7, 9.708] #> 7 6.714 [6, 7) #> 8 8.377 [7, 9.708] #> 9 1.086 [1.086, 3) #> 10 4.495 [4, 5) ``` To chop a single number into a separate category, put the number twice in `breaks`: ``` r x_fives <- x x_fives[1:5] <- 5 chopped <- chop(x_fives, c(2, 5, 5, 8)) data.frame(x_fives, chopped) #> x_fives chopped #> 1 5.000 {5} #> 2 5.000 {5} #> 3 5.000 {5} #> 4 5.000 {5} #> 5 5.000 {5} #> 6 9.708 [8, 9.708] #> 7 6.714 (5, 8) #> 8 8.377 [8, 9.708] #> 9 1.086 [1.086, 2) #> 10 4.495 [2, 5) ``` To quickly produce a table of chopped data, use [`tab()`](https://hughjonesd.github.io/santoku/reference/chop.md): ``` r tab(1:10, c(2, 5, 8)) #> [1, 2) [2, 5) [5, 8) [8, 10] #> 1 3 3 3 ``` ### Chopping by width and number of elements To chop into fixed-width intervals, starting at the minimum value, use [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md): ``` r chopped <- chop_width(x, 2) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.086, 5.086) #> 2 8.970 [7.086, 9.086) #> 3 3.392 [3.086, 5.086) #> 4 4.677 [3.086, 5.086) #> 5 7.057 [5.086, 7.086) #> 6 9.708 [9.086, 11.09] #> 7 6.714 [5.086, 7.086) #> 8 8.377 [7.086, 9.086) #> 9 1.086 [1.086, 3.086) #> 10 4.495 [3.086, 5.086) ``` To chop into a fixed number of intervals, each with the same width, use [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md): ``` r chopped <- chop_evenly(x, intervals = 3) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.96, 6.834) #> 2 8.970 [6.834, 9.708] #> 3 3.392 [1.086, 3.96) #> 4 4.677 [3.96, 6.834) #> 5 7.057 [6.834, 9.708] #> 6 9.708 [6.834, 9.708] #> 7 6.714 [3.96, 6.834) #> 8 8.377 [6.834, 9.708] #> 9 1.086 [1.086, 3.96) #> 10 4.495 [3.96, 6.834) ``` To chop into groups with a fixed number of elements, use [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md): ``` r chopped <- chop_n(x, 4) table(chopped) #> chopped #> [1.086, 4.978) [4.978, 8.97) [8.97, 9.708] #> 4 4 2 ``` To chop into a fixed number of groups, each with the same number of elements, use [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md): ``` r chopped <- chop_equally(x, groups = 5) table(chopped) #> chopped #> [1.086, 4.275) [4.275, 4.858) [4.858, 6.851) [6.851, 8.495) [8.495, 9.708] #> 2 2 2 2 2 ``` To chop data up by quantiles, use [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md): ``` r chopped <- chop_quantiles(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) #> x chopped #> 1 4.978 [25%, 50%) #> 2 8.970 [75%, 100%] #> 3 3.392 [0%, 25%) #> 4 4.677 [25%, 50%) #> 5 7.057 [50%, 75%) #> 6 9.708 [75%, 100%] #> 7 6.714 [50%, 75%) #> 8 8.377 [75%, 100%] #> 9 1.086 [0%, 25%) #> 10 4.495 [0%, 25%) ``` To chop data up by proportions of the data range, use [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md): ``` r chopped <- chop_proportions(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.242, 5.397) #> 2 8.970 [7.552, 9.708] #> 3 3.392 [3.242, 5.397) #> 4 4.677 [3.242, 5.397) #> 5 7.057 [5.397, 7.552) #> 6 9.708 [7.552, 9.708] #> 7 6.714 [5.397, 7.552) #> 8 8.377 [7.552, 9.708] #> 9 1.086 [1.086, 3.242) #> 10 4.495 [3.242, 5.397) ``` You can think of these six functions as logically arranged in a table. | To chop into… | Sizing intervals by… | | |:---|:---|:---| |   | number of elements: | interval width: | | a specific number of equal intervals… | [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) | [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) | | intervals of one specific size… | [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) | [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) | | intervals of different specific sizes… | [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) | [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) | Different ways to chop by size {.table} ### Even more ways to chop To chop data by standard deviations around the mean, use [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md): ``` r chopped <- chop_mean_sd(x) data.frame(x, chopped) #> x chopped #> 1 4.978 [-1 sd, 0 sd) #> 2 8.970 [1 sd, 2 sd) #> 3 3.392 [-1 sd, 0 sd) #> 4 4.677 [-1 sd, 0 sd) #> 5 7.057 [0 sd, 1 sd) #> 6 9.708 [1 sd, 2 sd) #> 7 6.714 [0 sd, 1 sd) #> 8 8.377 [0 sd, 1 sd) #> 9 1.086 [-2 sd, -1 sd) #> 10 4.495 [-1 sd, 0 sd) ``` To chop data into attractive intervals, use [`chop_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md). This selects intervals which are a multiple of 2, 5 or 10. It’s useful for producing bar plots. ``` r chopped <- chop_pretty(x) data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 6) #> 2 8.970 [8, 10] #> 3 3.392 [2, 4) #> 4 4.677 [4, 6) #> 5 7.057 [6, 8) #> 6 9.708 [8, 10] #> 7 6.714 [6, 8) #> 8 8.377 [8, 10] #> 9 1.086 [0, 2) #> 10 4.495 [4, 6) ``` ### Isolating common values In exploratory work, it’s sometimes useful to find common values and treat them differently. You can use [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) to do this: ``` r x_spike <- rnorm(100) x_spike[1:50] <- x_spike[1] chopped <- dissect(x_spike, -3:3, prop = 0.1) table(chopped) #> chopped #> [-3, -2) [-2, -1) [-1, 0) [0, 1) {0.6996} [1, 2) #> 2 5 15 18 50 10 ``` `prop = 0.2` will put any unique value of `x` into its own separate category if it makes up at least 20% of the data. Note that unlike all the other `chop_*` functions, [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) doesn’t always categorize `x` into ordered, connected intervals. To remind you of this, it is named differently. If you want to create separate intervals on the left and right of common elements, use [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md): ``` r chopped <- chop_spikes(x_spike, -3:3, prop = 0.1) table(chopped) #> chopped #> [-3, -2) [-2, -1) [-1, 0) [0, 0.6996) {0.6996} (0.6996, 1) #> 2 5 15 13 50 5 #> [1, 2) #> 10 ``` Compare this to the table before. There are two intervals on either side of the common value, instead of one interval surrounding it. ### Quick tables [`tab_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`tab_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), and friends act similarly to [`tab()`](https://hughjonesd.github.io/santoku/reference/chop.md), calling the related `chop_*` function and then [`table()`](https://rdrr.io/r/base/table.html) on the result. ``` r tab_n(x, 4) #> [1.086, 4.978) [4.978, 8.97) [8.97, 9.708] #> 4 4 2 tab_width(x, 2) #> [1.086, 3.086) [3.086, 5.086) [5.086, 7.086) [7.086, 9.086) [9.086, 11.09] #> 1 4 2 2 1 tab_evenly(x, 5) #> [1.086, 2.81) [2.81, 4.535) [4.535, 6.259) [6.259, 7.983) [7.983, 9.708] #> 1 2 2 2 3 tab_mean_sd(x) #> [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) #> 1 4 3 2 ``` ### Specifying labels By default, santoku labels intervals using mathematical notation: - `[0, 1]` means all numbers between 0 and 1 inclusive. - `(0, 1)` means all numbers *strictly* between 0 and 1, not including the endpoints. - `[0, 1)` means all numbers between 0 and 1, including 0 but not 1. - `(0, 1]` means all numbers between 0 and 1, including 1 but not 0. - `{0}` means just the number 0. To override these labels, provide names to the `breaks` argument: ``` r chopped <- chop(x, c(Lowest = 1, Low = 2, Higher = 5, Highest = 8)) data.frame(x, chopped) #> x chopped #> 1 4.978 Low #> 2 8.970 Highest #> 3 3.392 Low #> 4 4.677 Low #> 5 7.057 Higher #> 6 9.708 Highest #> 7 6.714 Higher #> 8 8.377 Highest #> 9 1.086 Lowest #> 10 4.495 Low ``` Or, you can specify factor labels with the `labels` argument: ``` r chopped <- chop(x, c(2, 5, 8), labels = c("Lowest", "Low", "Higher", "Highest")) data.frame(x, chopped) #> x chopped #> 1 4.978 Low #> 2 8.970 Highest #> 3 3.392 Low #> 4 4.677 Low #> 5 7.057 Higher #> 6 9.708 Highest #> 7 6.714 Higher #> 8 8.377 Highest #> 9 1.086 Lowest #> 10 4.495 Low ``` You need as many labels as there are intervals - one fewer than `length(breaks)` if your data doesn’t extend beyond `breaks`, one more than `length(breaks)` if it does. To label intervals with a dash, use [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md): ``` r chopped <- chop(x, c(2, 5, 8), labels = lbl_dash()) data.frame(x, chopped) #> x chopped #> 1 4.978 2—5 #> 2 8.970 8—9.708 #> 3 3.392 2—5 #> 4 4.677 2—5 #> 5 7.057 5—8 #> 6 9.708 8—9.708 #> 7 6.714 5—8 #> 8 8.377 8—9.708 #> 9 1.086 1.086—2 #> 10 4.495 2—5 ``` To label integer data, use [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md). It uses more informative right endpoints: ``` r chopped <- chop(1:10, c(2, 5, 8), labels = lbl_discrete()) chopped2 <- chop(1:10, c(2, 5, 8), labels = lbl_dash()) data.frame(x = 1:10, lbl_discrete = chopped, lbl_dash = chopped2) #> x lbl_discrete lbl_dash #> 1 1 1 1—2 #> 2 2 2—4 2—5 #> 3 3 2—4 2—5 #> 4 4 2—4 2—5 #> 5 5 5—7 5—8 #> 6 6 5—7 5—8 #> 7 7 5—7 5—8 #> 8 8 8—10 8—10 #> 9 9 8—10 8—10 #> 10 10 8—10 8—10 ``` You can customize the first or last labels: ``` r chopped <- chop(x, c(2, 5, 8), labels = lbl_dash(first = "< 2", last = "8+")) data.frame(x, chopped) #> x chopped #> 1 4.978 2—5 #> 2 8.970 8+ #> 3 3.392 2—5 #> 4 4.677 2—5 #> 5 7.057 5—8 #> 6 9.708 8+ #> 7 6.714 5—8 #> 8 8.377 8+ #> 9 1.086 < 2 #> 10 4.495 2—5 ``` To label intervals in order use [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md): ``` r chopped <- chop(x, c(2, 5, 8), labels = lbl_seq()) data.frame(x, chopped) #> x chopped #> 1 4.978 b #> 2 8.970 d #> 3 3.392 b #> 4 4.677 b #> 5 7.057 c #> 6 9.708 d #> 7 6.714 c #> 8 8.377 d #> 9 1.086 a #> 10 4.495 b ``` You can use numerals or even roman numerals: ``` r chop(x, c(2, 5, 8), labels = lbl_seq("(1)")) #> [1] (2) (4) (2) (2) (3) (4) (3) (4) (1) (2) #> Levels: (1) (2) (3) (4) chop(x, c(2, 5, 8), labels = lbl_seq("i.")) #> [1] ii. iv. ii. ii. iii. iv. iii. iv. i. ii. #> Levels: i. ii. iii. iv. ``` Other labelling functions include: - [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) - use left endpoints as labels - [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md) - use interval midpoints as labels - [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) - specify labels flexibly with the [glue](https://glue.tidyverse.org/) package ### Specifying breaks By default, [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) extends `breaks` if necessary. If you don’t want that, set `extend = FALSE`: ``` r chopped <- chop(x, c(3, 5, 7), extend = FALSE) data.frame(x, chopped) #> x chopped #> 1 4.978 [3, 5) #> 2 8.970 #> 3 3.392 [3, 5) #> 4 4.677 [3, 5) #> 5 7.057 #> 6 9.708 #> 7 6.714 [5, 7] #> 8 8.377 #> 9 1.086 #> 10 4.495 [3, 5) ``` Data outside the range of `breaks` will become `NA`. By default, intervals are closed on the left, i.e. they include their left endpoints. If you want right-closed intervals, set `left = FALSE`: ``` r y <- 1:5 data.frame( y = y, left_closed = chop(y, 1:5), right_closed = chop(y, 1:5, left = FALSE) ) #> y left_closed right_closed #> 1 1 [1, 2) [1, 2] #> 2 2 [2, 3) [1, 2] #> 3 3 [3, 4) (2, 3] #> 4 4 [4, 5] (3, 4] #> 5 5 [4, 5] (4, 5] ``` By default, the last interval is closed on both ends. If you want to keep the last interval open at the end, set `close_end = FALSE`: ``` r data.frame( y = y, end_closed = chop(y, 1:5), end_open = chop(y, 1:5, close_end = FALSE) ) #> y end_closed end_open #> 1 1 [1, 2) [1, 2) #> 2 2 [2, 3) [2, 3) #> 3 3 [3, 4) [3, 4) #> 4 4 [4, 5] [4, 5) #> 5 5 [4, 5] {5} ``` ## Chopping dates, times and other vectors You can chop many kinds of vectors with santoku, including Date objects… ``` r y2k <- as.Date("2000-01-01") + 0:10 * 7 data.frame( y2k = y2k, chopped = chop(y2k, as.Date(c("2000-02-01", "2000-03-01"))) ) #> y2k chopped #> 1 2000-01-01 [2000-01-01, 2000-02-01) #> 2 2000-01-08 [2000-01-01, 2000-02-01) #> 3 2000-01-15 [2000-01-01, 2000-02-01) #> 4 2000-01-22 [2000-01-01, 2000-02-01) #> 5 2000-01-29 [2000-01-01, 2000-02-01) #> 6 2000-02-05 [2000-02-01, 2000-03-01) #> 7 2000-02-12 [2000-02-01, 2000-03-01) #> 8 2000-02-19 [2000-02-01, 2000-03-01) #> 9 2000-02-26 [2000-02-01, 2000-03-01) #> 10 2000-03-04 [2000-03-01, 2000-03-11] #> 11 2000-03-11 [2000-03-01, 2000-03-11] ``` … and POSIXct (date-time) objects: ``` r # hours of the 2020 Crew Dragon flight: crew_dragon <- seq(as.POSIXct("2020-05-30 18:00", tz = "GMT"), length.out = 24, by = "hours") liftoff <- as.POSIXct("2020-05-30 15:22", tz = "America/New_York") dock <- as.POSIXct("2020-05-31 10:16", tz = "America/New_York") data.frame( crew_dragon = crew_dragon, chopped = chop(crew_dragon, c(liftoff, dock), labels = c("pre-flight", "flight", "docked")) ) #> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent #> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent #> crew_dragon chopped #> 1 2020-05-30 18:00:00 pre-flight #> 2 2020-05-30 19:00:00 pre-flight #> 3 2020-05-30 20:00:00 flight #> 4 2020-05-30 21:00:00 flight #> 5 2020-05-30 22:00:00 flight #> 6 2020-05-30 23:00:00 flight #> 7 2020-05-31 00:00:00 flight #> 8 2020-05-31 01:00:00 flight #> 9 2020-05-31 02:00:00 flight #> 10 2020-05-31 03:00:00 flight #> 11 2020-05-31 04:00:00 flight #> 12 2020-05-31 05:00:00 flight #> 13 2020-05-31 06:00:00 flight #> 14 2020-05-31 07:00:00 flight #> 15 2020-05-31 08:00:00 flight #> 16 2020-05-31 09:00:00 flight #> 17 2020-05-31 10:00:00 flight #> 18 2020-05-31 11:00:00 flight #> 19 2020-05-31 12:00:00 flight #> 20 2020-05-31 13:00:00 flight #> 21 2020-05-31 14:00:00 flight #> 22 2020-05-31 15:00:00 docked #> 23 2020-05-31 16:00:00 docked #> 24 2020-05-31 17:00:00 docked ``` Note how santoku correctly handles the different timezones. You can use [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) with objects from the `lubridate` package, to chop by irregular periods such as months: ``` r library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union data.frame( y2k = y2k, chopped = chop_width(y2k, months(1)) ) #> y2k chopped #> 1 2000-01-01 [2000-01-01, 2000-02-01) #> 2 2000-01-08 [2000-01-01, 2000-02-01) #> 3 2000-01-15 [2000-01-01, 2000-02-01) #> 4 2000-01-22 [2000-01-01, 2000-02-01) #> 5 2000-01-29 [2000-01-01, 2000-02-01) #> 6 2000-02-05 [2000-02-01, 2000-03-01) #> 7 2000-02-12 [2000-02-01, 2000-03-01) #> 8 2000-02-19 [2000-02-01, 2000-03-01) #> 9 2000-02-26 [2000-02-01, 2000-03-01) #> 10 2000-03-04 [2000-03-01, 2000-04-01) #> 11 2000-03-11 [2000-03-01, 2000-04-01) ``` [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) produces nicely formatted dates: ``` r data.frame( y2k = y2k, chopped = chop_width(y2k, days(28), labels = lbl_date()) ) #> y2k chopped #> 1 2000-01-01 1-28 Jan 2000 #> 2 2000-01-08 1-28 Jan 2000 #> 3 2000-01-15 1-28 Jan 2000 #> 4 2000-01-22 1-28 Jan 2000 #> 5 2000-01-29 29 Jan - 25 Feb 2000 #> 6 2000-02-05 29 Jan - 25 Feb 2000 #> 7 2000-02-12 29 Jan - 25 Feb 2000 #> 8 2000-02-19 29 Jan - 25 Feb 2000 #> 9 2000-02-26 26 Feb - 24 Mar 2000 #> 10 2000-03-04 26 Feb - 24 Mar 2000 #> 11 2000-03-11 26 Feb - 24 Mar 2000 ``` You can also chop vectors with units, using the `units` package: ``` r library(units) #> udunits database from /Users/davidhugh-jones/Library/R/arm64/4.6/library/units/share/udunits/udunits2.xml x <- set_units(1:10 * 10, cm) br <- set_units(1:3, ft) data.frame( x = x, chopped = chop(x, br) ) #> x chopped #> 1 10 [cm] [ 10.00 [cm], 30.48 [cm]) #> 2 20 [cm] [ 10.00 [cm], 30.48 [cm]) #> 3 30 [cm] [ 10.00 [cm], 30.48 [cm]) #> 4 40 [cm] [ 30.48 [cm], 60.96 [cm]) #> 5 50 [cm] [ 30.48 [cm], 60.96 [cm]) #> 6 60 [cm] [ 30.48 [cm], 60.96 [cm]) #> 7 70 [cm] [ 60.96 [cm], 91.44 [cm]) #> 8 80 [cm] [ 60.96 [cm], 91.44 [cm]) #> 9 90 [cm] [ 60.96 [cm], 91.44 [cm]) #> 10 100 [cm] [ 91.44 [cm], 100.00 [cm]] ``` You should be able to chop anything that has a comparison operator. You can even chop character data using lexical ordering. By default santoku emits a warning in this case, to avoid accidentally misinterpreting results: ``` r chop(letters[1:10], c("d", "f")) #> Warning in categorize_non_numeric(x, breaks, left): `x` or `breaks` is of type #> character, using lexical sorting. To turn off this warning, run: #> options(santoku.warn_character = FALSE) #> [1] [a, d) [a, d) [a, d) [d, f) [d, f) [f, j] [f, j] [f, j] [f, j] [f, j] #> Levels: [a, d) [d, f) [f, j] ``` If you find a type of data that you can’t chop, please [file an issue](https://github.com/hughjonesd/santoku/issues). ================================================ FILE: docs/articles/santoku_files/accessible-code-block-0.0.1/empty-anchor.js ================================================ // Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> // v0.0.1 // Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. document.addEventListener('DOMContentLoaded', function() { const codeList = document.getElementsByClassName("sourceCode"); for (var i = 0; i < codeList.length; i++) { var linkList = codeList[i].getElementsByTagName('a'); for (var j = 0; j < linkList.length; j++) { if (linkList[j].innerHTML === "") { linkList[j].setAttribute('aria-hidden', 'true'); } } } }); ================================================ FILE: docs/articles/santoku_files/header-attrs-2.11/header-attrs.js ================================================ // Pandoc 2.9 adds attributes on both header and div. We remove the former (to // be compatible with the behavior of Pandoc < 2.8). document.addEventListener('DOMContentLoaded', function(e) { var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); var i, h, a; for (i = 0; i < hs.length; i++) { h = hs[i]; if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 a = h.attributes; while (a.length > 0) h.removeAttribute(a[0].name); } }); ================================================ FILE: docs/articles/santoku_files/header-attrs-2.8/header-attrs.js ================================================ // Pandoc 2.9 adds attributes on both header and div. We remove the former (to // be compatible with the behavior of Pandoc < 2.8). document.addEventListener('DOMContentLoaded', function(e) { var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); var i, h, a; for (i = 0; i < hs.length; i++) { h = hs[i]; if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 a = h.attributes; while (a.length > 0) h.removeAttribute(a[0].name); } }); ================================================ FILE: docs/articles/website-articles/performance.html ================================================ Performance • santoku Skip to contents

Speed

The core of santoku is written in C++. It is reasonably fast:


packageVersion("santoku")
#> [1] '1.2.0'
set.seed(27101975)

mb <- bench::mark(min_iterations = 100, check = FALSE,
        santoku::chop(rnorm(1e5), -2:2),
        base::cut(rnorm(1e5), -2:2),
        Hmisc::cut2(rnorm(1e5), -2:2)
      )
mb
#> # A tibble: 3 × 6
#>   expression                             min median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>                        <bch:tm> <bch:>     <dbl> <bch:byt>    <dbl>
#> 1 santoku::chop(rnorm(1e+05), -2:2)   6.34ms 6.54ms      150.   10.21MB     64.5
#> 2 base::cut(rnorm(1e+05), -2:2)       2.73ms 2.78ms      357.    2.35MB     31.5
#> 3 Hmisc::cut2(rnorm(1e+05), -2:2)     9.74ms 9.88ms      101.    19.5MB    223.
autoplot(mb, type = "violin")

Many breaks


many_breaks <- seq(-2, 2, 0.001)

mb_breaks <- bench::mark(min_iterations = 100, check = FALSE,
        santoku::chop(rnorm(1e4), many_breaks),
        base::cut(rnorm(1e4), many_breaks),
        Hmisc::cut2(rnorm(1e4), many_breaks)
      )

mb_breaks
#> # A tibble: 3 × 6
#>   expression                            min  median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>                        <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl>
#> 1 santoku::chop(rnorm(10000), many… 20.95ms 21.28ms      46.2    5.14MB     8.81
#> 2 base::cut(rnorm(10000), many_bre…  2.35ms  2.43ms     409.     1.39MB    17.7 
#> 3 Hmisc::cut2(rnorm(10000), many_b…  7.03ms   7.2ms     138.      5.7MB    32.5
autoplot(mb_breaks, type = "violin")

Various chops


x <- c(rnorm(9e4), sample(-2:2, 1e4, replace = TRUE))

mb_various <- bench::mark(min_iterations = 100, check = FALSE,
        chop(x, -2:2),
        chop_equally(x, groups = 20),
        chop_n(x, n = 2e4),
        chop_quantiles(x, c(0.05, 0.25, 0.5, 0.75, 0.95)),
        chop_evenly(x, intervals = 20),
        chop_width(x, width = 0.25),
        chop_proportions(x, proportions = c(0.05, 0.25, 0.5, 0.75, 0.95)),
        chop_mean_sd(x, sds = 1:4),
        chop_fn(x, scales::breaks_extended(10)),
        chop_pretty(x, n = 10),
        chop_spikes(x, -2:2, prop = 0.01),
        dissect(x, -2:2, prop = 0.01)
      )
      
mb_various
#> # A tibble: 12 × 6
#>    expression                           min  median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>                       <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl>
#>  1 chop(x, -2:2)                     4.23ms  4.33ms     229.     8.63MB    118. 
#>  2 chop_equally(x, groups = 20)     11.04ms  11.2ms      88.5   12.18MB     72.4
#>  3 chop_n(x, n = 20000)              7.65ms  8.11ms     124.     23.5MB    507. 
#>  4 chop_quantiles(x, c(0.05, 0.25,…  6.63ms  6.75ms     147.    12.08MB    125. 
#>  5 chop_evenly(x, intervals = 20)    5.43ms  5.57ms     178.    12.48MB    152. 
#>  6 chop_width(x, width = 0.25)       5.96ms  6.08ms     163.    12.53MB    139. 
#>  7 chop_proportions(x, proportions…  4.75ms  5.37ms     190.    12.48MB    162. 
#>  8 chop_mean_sd(x, sds = 1:4)         4.9ms     5ms     198.    11.36MB    150. 
#>  9 chop_fn(x, scales::breaks_exten…  5.33ms   5.5ms     177.    11.47MB    123. 
#> 10 chop_pretty(x, n = 10)            4.87ms  5.02ms     197.    10.58MB    142. 
#> 11 chop_spikes(x, -2:2, prop = 0.0…  8.14ms  8.41ms     119.    14.62MB    137. 
#> 12 dissect(x, -2:2, prop = 0.01)    11.42ms 11.61ms      85.6   22.27MB    257.
autoplot(mb_various, type = "violin")

================================================ FILE: docs/articles/website-articles/performance.md ================================================ # Performance ## Speed The core of santoku is written in C++. It is reasonably fast: ``` r packageVersion("santoku") #> [1] '1.2.0' set.seed(27101975) mb <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e5), -2:2), base::cut(rnorm(1e5), -2:2), Hmisc::cut2(rnorm(1e5), -2:2) ) mb #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 santoku::chop(rnorm(1e+05), -2:2) 6.34ms 6.54ms 150. 10.21MB 64.5 #> 2 base::cut(rnorm(1e+05), -2:2) 2.73ms 2.78ms 357. 2.35MB 31.5 #> 3 Hmisc::cut2(rnorm(1e+05), -2:2) 9.74ms 9.88ms 101. 19.5MB 223. ``` ``` r autoplot(mb, type = "violin") ``` ![](performance_files/figure-html/unnamed-chunk-1-1.png) ## Many breaks ``` r many_breaks <- seq(-2, 2, 0.001) mb_breaks <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e4), many_breaks), base::cut(rnorm(1e4), many_breaks), Hmisc::cut2(rnorm(1e4), many_breaks) ) mb_breaks #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 santoku::chop(rnorm(10000), many… 20.95ms 21.28ms 46.2 5.14MB 8.81 #> 2 base::cut(rnorm(10000), many_bre… 2.35ms 2.43ms 409. 1.39MB 17.7 #> 3 Hmisc::cut2(rnorm(10000), many_b… 7.03ms 7.2ms 138. 5.7MB 32.5 ``` ``` r autoplot(mb_breaks, type = "violin") ``` ![](performance_files/figure-html/unnamed-chunk-2-1.png) ## Various chops ``` r x <- c(rnorm(9e4), sample(-2:2, 1e4, replace = TRUE)) mb_various <- bench::mark(min_iterations = 100, check = FALSE, chop(x, -2:2), chop_equally(x, groups = 20), chop_n(x, n = 2e4), chop_quantiles(x, c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_evenly(x, intervals = 20), chop_width(x, width = 0.25), chop_proportions(x, proportions = c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_mean_sd(x, sds = 1:4), chop_fn(x, scales::breaks_extended(10)), chop_pretty(x, n = 10), chop_spikes(x, -2:2, prop = 0.01), dissect(x, -2:2, prop = 0.01) ) mb_various #> # A tibble: 12 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 chop(x, -2:2) 4.23ms 4.33ms 229. 8.63MB 118. #> 2 chop_equally(x, groups = 20) 11.04ms 11.2ms 88.5 12.18MB 72.4 #> 3 chop_n(x, n = 20000) 7.65ms 8.11ms 124. 23.5MB 507. #> 4 chop_quantiles(x, c(0.05, 0.25,… 6.63ms 6.75ms 147. 12.08MB 125. #> 5 chop_evenly(x, intervals = 20) 5.43ms 5.57ms 178. 12.48MB 152. #> 6 chop_width(x, width = 0.25) 5.96ms 6.08ms 163. 12.53MB 139. #> 7 chop_proportions(x, proportions… 4.75ms 5.37ms 190. 12.48MB 162. #> 8 chop_mean_sd(x, sds = 1:4) 4.9ms 5ms 198. 11.36MB 150. #> 9 chop_fn(x, scales::breaks_exten… 5.33ms 5.5ms 177. 11.47MB 123. #> 10 chop_pretty(x, n = 10) 4.87ms 5.02ms 197. 10.58MB 142. #> 11 chop_spikes(x, -2:2, prop = 0.0… 8.14ms 8.41ms 119. 14.62MB 137. #> 12 dissect(x, -2:2, prop = 0.01) 11.42ms 11.61ms 85.6 22.27MB 257. ``` ``` r autoplot(mb_various, type = "violin") ``` ![](performance_files/figure-html/unnamed-chunk-3-1.png) ================================================ FILE: docs/articles/website-articles/performance_files/accessible-code-block-0.0.1/empty-anchor.js ================================================ // Hide empty tag within highlighted CodeBlock for screen reader accessibility (see https://github.com/jgm/pandoc/issues/6352#issuecomment-626106786) --> // v0.0.1 // Written by JooYoung Seo (jooyoung@psu.edu) and Atsushi Yasumoto on June 1st, 2020. document.addEventListener('DOMContentLoaded', function() { const codeList = document.getElementsByClassName("sourceCode"); for (var i = 0; i < codeList.length; i++) { var linkList = codeList[i].getElementsByTagName('a'); for (var j = 0; j < linkList.length; j++) { if (linkList[j].innerHTML === "") { linkList[j].setAttribute('aria-hidden', 'true'); } } } }); ================================================ FILE: docs/articles/website-articles/performance_files/header-attrs-2.11/header-attrs.js ================================================ // Pandoc 2.9 adds attributes on both header and div. We remove the former (to // be compatible with the behavior of Pandoc < 2.8). document.addEventListener('DOMContentLoaded', function(e) { var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); var i, h, a; for (i = 0; i < hs.length; i++) { h = hs[i]; if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 a = h.attributes; while (a.length > 0) h.removeAttribute(a[0].name); } }); ================================================ FILE: docs/articles/website-articles/performance_files/header-attrs-2.8/header-attrs.js ================================================ // Pandoc 2.9 adds attributes on both header and div. We remove the former (to // be compatible with the behavior of Pandoc < 2.8). document.addEventListener('DOMContentLoaded', function(e) { var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); var i, h, a; for (i = 0; i < hs.length; i++) { h = hs[i]; if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 a = h.attributes; while (a.length > 0) h.removeAttribute(a[0].name); } }); ================================================ FILE: docs/articles/whats-new-in-0-9-0.html ================================================ What's new in santoku 0.9.0 • santoku Skip to contents

Santoku 0.9.0 has a few changes.

You can use break names for labels

On the command line, sometimes you’d like to quickly add labels to your breaks. Now, you can do this simply by adding names to the breaks vector:

library(santoku)

chop(1:5, c(1,3,5))
#> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5]
#> Levels: [1, 3) [3, 5]

chop(1:5, c(Low = 1, High = 3, 5))
#> [1] Low  Low  High High High
#> Levels: Low High

Break names override the labels argument, but you can still use this for unnamed breaks:


ages <- sample(12:80, 20)
tab(ages, 
      c("Under 16" = 0, 16, 25, 35, 45, 55, "65 and over" = 65), 
      labels = lbl_discrete()
    )
#>    Under 16       16—24       25—34       35—44       45—54       55—64 
#>           1           1           2           3           3           4 
#> 65 and over 
#>           6

Names can also be used for labels in chop_quantiles() and chop_proportions():

x <- rnorm(10)
chopped <- chop_quantiles(x, 
                            c("Lower tail" = 0, 0.025, "Upper tail" = 0.975)
                          )
data.frame(x, chopped)
#>             x       chopped
#> 1  -1.3888607 [2.5%, 97.5%)
#> 2  -0.2787888 [2.5%, 97.5%)
#> 3  -0.1333213 [2.5%, 97.5%)
#> 4   0.6359504 [2.5%, 97.5%)
#> 5  -0.2842529 [2.5%, 97.5%)
#> 6  -2.6564554    Lower tail
#> 7  -2.4404669 [2.5%, 97.5%)
#> 8   1.3201133    Upper tail
#> 9  -0.3066386 [2.5%, 97.5%)
#> 10 -1.7813084 [2.5%, 97.5%)

This feature is experimental for now.

close_end works differently

The close_end parameter is used to right-close the last break. This used to be applied before breaks were extended to cover items beyond the explicitly given breaks. We think this was confusing for users. So now, close_end is applied only after the breaks have been extended - i.e. to the very last break.

In 0.8.0:

chop(1:4, 2:3, close_end = TRUE)
#> [1] [1, 2) [2, 3] [2, 3] (3, 4]
#> Levels: [1, 2) [2, 3] (3, 4]

Notice how the central break [2, 3] is right-closed. (The extended break [3, 4] is right-closed too, because extended breaks are always closed at the “outer” end.)

In 0.9.0:

chop(1:4, 2:3, close_end = TRUE)
#> [1] [1, 2) [2, 3) [3, 4] [3, 4]
#> Levels: [1, 2) [2, 3) [3, 4]

Now, close_end is applied to the final, extended break [3, 4], not to the explicit break [2, 3).

close_end is TRUE by default

We think that for exploratory work, users typically want to include all the data between the lowest and highest break, inclusive. So, close_end is now TRUE by default.

In 0.8.0:

chop(1:3, 2:3)
#> [1] [1, 2) [2, 3) {3}   
#> Levels: [1, 2) [2, 3) {3}

In 0.9.0:

chop(1:3, 2:3)
#> [1] [1, 2) [2, 3] [2, 3]
#> Levels: [1, 2) [2, 3]

New raw parameter for chop()

lbl_* functions have a raw parameter to use the raw interval endpoints in labels, rather than e.g. percentiles or standard deviations. We’ve moved this into the main chop() function. This makes it easier to use:


chop_mean_sd(x)
#>  [1] [-1 sd, 0 sd)  [0 sd, 1 sd)   [0 sd, 1 sd)   [1 sd, 2 sd)   [0 sd, 1 sd)  
#>  [6] [-2 sd, -1 sd) [-2 sd, -1 sd) [1 sd, 2 sd)   [0 sd, 1 sd)   [-1 sd, 0 sd) 
#> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd)

chop_mean_sd(x, raw = TRUE)
#>  [1] [-2.03, -0.7314)  [-0.7314, 0.5674) [-0.7314, 0.5674) [0.5674, 1.866)  
#>  [5] [-0.7314, 0.5674) [-3.329, -2.03)   [-3.329, -2.03)   [0.5674, 1.866)  
#>  [9] [-0.7314, 0.5674) [-2.03, -0.7314) 
#> 4 Levels: [-3.329, -2.03) [-2.03, -0.7314) ... [0.5674, 1.866)

The raw parameter to lbl_* functions is deprecated.

Other changes

The NEWS file lists other changes, including a new chop_fn() function which creates breaks using any arbitrary function.

Feedback

We expect this to be the last release before 1.0, when we’ll stabilize the interface and move santoku from “experimental” to “stable”. So, if you have problems or suggestions regarding any of these changes, please file an issue.

================================================ FILE: docs/articles/whats-new-in-0-9-0.md ================================================ # What's new in santoku 0.9.0 Santoku 0.9.0 has a few changes. ## You can use break names for labels On the command line, sometimes you’d like to quickly add labels to your breaks. Now, you can do this simply by adding names to the `breaks` vector: ``` r library(santoku) chop(1:5, c(1,3,5)) #> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5] #> Levels: [1, 3) [3, 5] chop(1:5, c(Low = 1, High = 3, 5)) #> [1] Low Low High High High #> Levels: Low High ``` Break names override the `labels` argument, but you can still use this for unnamed breaks: ``` r ages <- sample(12:80, 20) tab(ages, c("Under 16" = 0, 16, 25, 35, 45, 55, "65 and over" = 65), labels = lbl_discrete() ) #> Under 16 16—24 25—34 35—44 45—54 55—64 #> 1 1 2 3 3 4 #> 65 and over #> 6 ``` Names can also be used for labels in [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) and [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md): ``` r x <- rnorm(10) chopped <- chop_quantiles(x, c("Lower tail" = 0, 0.025, "Upper tail" = 0.975) ) data.frame(x, chopped) #> x chopped #> 1 -1.3888607 [2.5%, 97.5%) #> 2 -0.2787888 [2.5%, 97.5%) #> 3 -0.1333213 [2.5%, 97.5%) #> 4 0.6359504 [2.5%, 97.5%) #> 5 -0.2842529 [2.5%, 97.5%) #> 6 -2.6564554 Lower tail #> 7 -2.4404669 [2.5%, 97.5%) #> 8 1.3201133 Upper tail #> 9 -0.3066386 [2.5%, 97.5%) #> 10 -1.7813084 [2.5%, 97.5%) ``` This feature is experimental for now. ## `close_end` works differently The `close_end` parameter is used to right-close the last break. This used to be applied before breaks were extended to cover items beyond the explicitly given breaks. We think this was confusing for users. So now, `close_end` is applied only after the breaks have been extended - i.e. to the very last break. In 0.8.0: ``` r chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3] [2, 3] (3, 4] #> Levels: [1, 2) [2, 3] (3, 4] ``` Notice how the central break `[2, 3]` is right-closed. (The extended break `[3, 4]` is right-closed too, because extended breaks are always closed at the “outer” end.) In 0.9.0: ``` r chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3) [3, 4] [3, 4] #> Levels: [1, 2) [2, 3) [3, 4] ``` Now, `close_end` is applied to the final, extended break `[3, 4]`, not to the explicit break `[2, 3)`. ## `close_end` is `TRUE` by default We think that for exploratory work, users typically want to include all the data between the lowest and highest break, inclusive. So, `close_end` is now `TRUE` by default. In 0.8.0: ``` r chop(1:3, 2:3) #> [1] [1, 2) [2, 3) {3} #> Levels: [1, 2) [2, 3) {3} ``` In 0.9.0: ``` r chop(1:3, 2:3) #> [1] [1, 2) [2, 3] [2, 3] #> Levels: [1, 2) [2, 3] ``` ## New `raw` parameter for `chop()` `lbl_*` functions have a `raw` parameter to use the raw interval endpoints in labels, rather than e.g. percentiles or standard deviations. We’ve moved this into the main [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) function. This makes it easier to use: ``` r chop_mean_sd(x) #> [1] [-1 sd, 0 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [0 sd, 1 sd) #> [6] [-2 sd, -1 sd) [-2 sd, -1 sd) [1 sd, 2 sd) [0 sd, 1 sd) [-1 sd, 0 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) chop_mean_sd(x, raw = TRUE) #> [1] [-2.03, -0.7314) [-0.7314, 0.5674) [-0.7314, 0.5674) [0.5674, 1.866) #> [5] [-0.7314, 0.5674) [-3.329, -2.03) [-3.329, -2.03) [0.5674, 1.866) #> [9] [-0.7314, 0.5674) [-2.03, -0.7314) #> 4 Levels: [-3.329, -2.03) [-2.03, -0.7314) ... [0.5674, 1.866) ``` The `raw` parameter to `lbl_*` functions is deprecated. ## Other changes The NEWS file lists other changes, including a new [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) function which creates breaks using any arbitrary function. ## Feedback We expect this to be the last release before 1.0, when we’ll stabilize the interface and move santoku from “experimental” to “stable”. So, if you have problems or suggestions regarding any of these changes, please [file an issue](https://github.com/hughjonesd/santoku/issues). ================================================ FILE: docs/authors.html ================================================ Authors and Citation • santoku Skip to contents

Authors

  • David Hugh-Jones. Author, maintainer.

  • Daniel Possenriede. Contributor.

Citation

Source: DESCRIPTION

Hugh-Jones D (2026). santoku: A Versatile Cutting Tool. R package version 1.2.0, https://github.com/hughjonesd/santoku.

@Manual{,
  title = {santoku: A Versatile Cutting Tool},
  author = {David Hugh-Jones},
  year = {2026},
  note = {R package version 1.2.0},
  url = {https://github.com/hughjonesd/santoku},
}
================================================ FILE: docs/authors.md ================================================ # Authors and Citation ## Authors - **David Hugh-Jones**. Author, maintainer. - **Daniel Possenriede**. Contributor. ## Citation Source: [`DESCRIPTION`](https://github.com/hughjonesd/santoku/blob/HEAD/DESCRIPTION) Hugh-Jones D (2026). *santoku: A Versatile Cutting Tool*. R package version 1.2.0, . @Manual{, title = {santoku: A Versatile Cutting Tool}, author = {David Hugh-Jones}, year = {2026}, note = {R package version 1.2.0}, url = {https://github.com/hughjonesd/santoku}, } ================================================ FILE: docs/bootstrap-toc.css ================================================ /*! * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) * Copyright 2015 Aidan Feldman * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ /* All levels of nav */ nav[data-toggle='toc'] .nav > li > a { display: block; padding: 4px 20px; font-size: 13px; font-weight: 500; color: #767676; } nav[data-toggle='toc'] .nav > li > a:hover, nav[data-toggle='toc'] .nav > li > a:focus { padding-left: 19px; color: #563d7c; text-decoration: none; background-color: transparent; border-left: 1px solid #563d7c; } nav[data-toggle='toc'] .nav > .active > a, nav[data-toggle='toc'] .nav > .active:hover > a, nav[data-toggle='toc'] .nav > .active:focus > a { padding-left: 18px; font-weight: bold; color: #563d7c; background-color: transparent; border-left: 2px solid #563d7c; } /* Nav: second level (shown on .active) */ nav[data-toggle='toc'] .nav .nav { display: none; /* Hide by default, but at >768px, show it */ padding-bottom: 10px; } nav[data-toggle='toc'] .nav .nav > li > a { padding-top: 1px; padding-bottom: 1px; padding-left: 30px; font-size: 12px; font-weight: normal; } nav[data-toggle='toc'] .nav .nav > li > a:hover, nav[data-toggle='toc'] .nav .nav > li > a:focus { padding-left: 29px; } nav[data-toggle='toc'] .nav .nav > .active > a, nav[data-toggle='toc'] .nav .nav > .active:hover > a, nav[data-toggle='toc'] .nav .nav > .active:focus > a { padding-left: 28px; font-weight: 500; } /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ nav[data-toggle='toc'] .nav > .active > ul { display: block; } ================================================ FILE: docs/bootstrap-toc.js ================================================ /*! * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) * Copyright 2015 Aidan Feldman * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ (function() { 'use strict'; window.Toc = { helpers: { // return all matching elements in the set, or their descendants findOrFilter: function($el, selector) { // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ // http://stackoverflow.com/a/12731439/358804 var $descendants = $el.find(selector); return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); }, generateUniqueIdBase: function(el) { var text = $(el).text(); var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); return anchor || el.tagName.toLowerCase(); }, generateUniqueId: function(el) { var anchorBase = this.generateUniqueIdBase(el); for (var i = 0; ; i++) { var anchor = anchorBase; if (i > 0) { // add suffix anchor += '-' + i; } // check if ID already exists if (!document.getElementById(anchor)) { return anchor; } } }, generateAnchor: function(el) { if (el.id) { return el.id; } else { var anchor = this.generateUniqueId(el); el.id = anchor; return anchor; } }, createNavList: function() { return $(''); }, createChildNavList: function($parent) { var $childList = this.createNavList(); $parent.append($childList); return $childList; }, generateNavEl: function(anchor, text) { var $a = $(''); $a.attr('href', '#' + anchor); $a.text(text); var $li = $('
  • '); $li.append($a); return $li; }, generateNavItem: function(headingEl) { var anchor = this.generateAnchor(headingEl); var $heading = $(headingEl); var text = $heading.data('toc-text') || $heading.text(); return this.generateNavEl(anchor, text); }, // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). getTopLevel: function($scope) { for (var i = 1; i <= 6; i++) { var $headings = this.findOrFilter($scope, 'h' + i); if ($headings.length > 1) { return i; } } return 1; }, // returns the elements for the top level, and the next below it getHeadings: function($scope, topLevel) { var topSelector = 'h' + topLevel; var secondaryLevel = topLevel + 1; var secondarySelector = 'h' + secondaryLevel; return this.findOrFilter($scope, topSelector + ',' + secondarySelector); }, getNavLevel: function(el) { return parseInt(el.tagName.charAt(1), 10); }, populateNav: function($topContext, topLevel, $headings) { var $context = $topContext; var $prevNav; var helpers = this; $headings.each(function(i, el) { var $newNav = helpers.generateNavItem(el); var navLevel = helpers.getNavLevel(el); // determine the proper $context if (navLevel === topLevel) { // use top level $context = $topContext; } else if ($prevNav && $context === $topContext) { // create a new level of the tree and switch to it $context = helpers.createChildNavList($prevNav); } // else use the current $context $context.append($newNav); $prevNav = $newNav; }); }, parseOps: function(arg) { var opts; if (arg.jquery) { opts = { $nav: arg }; } else { opts = arg; } opts.$scope = opts.$scope || $(document.body); return opts; } }, // accepts a jQuery object, or an options object init: function(opts) { opts = this.helpers.parseOps(opts); // ensure that the data attribute is in place for styling opts.$nav.attr('data-toggle', 'toc'); var $topContext = this.helpers.createChildNavList(opts.$nav); var topLevel = this.helpers.getTopLevel(opts.$scope); var $headings = this.helpers.getHeadings(opts.$scope, topLevel); this.helpers.populateNav($topContext, topLevel, $headings); } }; $(function() { $('nav[data-toggle="toc"]').each(function(i, el) { var $nav = $(el); Toc.init($nav); }); }); })(); ================================================ FILE: docs/deps/_Courier Prime-0.4.0/font.css ================================================ @font-face { font-family: 'Courier Prime'; font-style: normal; font-weight: 400; font-display: swap; src: url(u-450q2lgwslOqpF_6gQ8kELWwU.woff) format('woff'); } ================================================ FILE: docs/deps/bootstrap-5.1.0/font.css ================================================ @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/S6u8w4BMUTPHjxswWA.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/S6uyw4BMUTPHvxo.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/S6u9w4BMUTPHh6UVeww.woff) format('woff'); } ================================================ FILE: docs/deps/bootstrap-5.1.3/font.css ================================================ @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/S6u8w4BMUTPHjxswWA.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/S6uyw4BMUTPHvxo.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/S6u9w4BMUTPHh6UVeww.woff) format('woff'); } ================================================ FILE: docs/deps/bootstrap-5.2.2/font.css ================================================ @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/S6u8w4BMUTPHjxswWA.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/S6uyw4BMUTPHvxo.woff) format('woff'); } @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/S6u9w4BMUTPHh6UVeww.woff) format('woff'); } ================================================ FILE: docs/deps/bootstrap-5.3.1/font.css ================================================ /* latin-ext */ @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/S6u8w4BMUTPHjxsAUi-qJCY.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Lato'; font-style: italic; font-weight: 400; font-display: swap; src: url(fonts/S6u8w4BMUTPHjxsAXC-q.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* latin-ext */ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/S6uyw4BMUTPHjxAwXjeu.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 400; font-display: swap; src: url(fonts/S6uyw4BMUTPHjx4wXg.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* latin-ext */ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/S6u9w4BMUTPHh6UVSwaPGR_p.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Lato'; font-style: normal; font-weight: 700; font-display: swap; src: url(fonts/S6u9w4BMUTPHh6UVSwiPGQ.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } ================================================ FILE: docs/deps/data-deps.txt ================================================ ================================================ FILE: docs/deps/font-awesome-6.5.2/css/all.css ================================================ /*! * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2024 Fonticons, Inc. */ .fa { font-family: var(--fa-style-family, "Font Awesome 6 Free"); font-weight: var(--fa-style, 900); } .fa, .fa-classic, .fa-sharp, .fas, .fa-solid, .far, .fa-regular, .fab, .fa-brands { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; display: var(--fa-display, inline-block); font-style: normal; font-variant: normal; line-height: 1; text-rendering: auto; } .fas, .fa-classic, .fa-solid, .far, .fa-regular { font-family: 'Font Awesome 6 Free'; } .fab, .fa-brands { font-family: 'Font Awesome 6 Brands'; } .fa-1x { font-size: 1em; } .fa-2x { font-size: 2em; } .fa-3x { font-size: 3em; } .fa-4x { font-size: 4em; } .fa-5x { font-size: 5em; } .fa-6x { font-size: 6em; } .fa-7x { font-size: 7em; } .fa-8x { font-size: 8em; } .fa-9x { font-size: 9em; } .fa-10x { font-size: 10em; } .fa-2xs { font-size: 0.625em; line-height: 0.1em; vertical-align: 0.225em; } .fa-xs { font-size: 0.75em; line-height: 0.08333em; vertical-align: 0.125em; } .fa-sm { font-size: 0.875em; line-height: 0.07143em; vertical-align: 0.05357em; } .fa-lg { font-size: 1.25em; line-height: 0.05em; vertical-align: -0.075em; } .fa-xl { font-size: 1.5em; line-height: 0.04167em; vertical-align: -0.125em; } .fa-2xl { font-size: 2em; line-height: 0.03125em; vertical-align: -0.1875em; } .fa-fw { text-align: center; width: 1.25em; } .fa-ul { list-style-type: none; margin-left: var(--fa-li-margin, 2.5em); padding-left: 0; } .fa-ul > li { position: relative; } .fa-li { left: calc(var(--fa-li-width, 2em) * -1); position: absolute; text-align: center; width: var(--fa-li-width, 2em); line-height: inherit; } .fa-border { border-color: var(--fa-border-color, #eee); border-radius: var(--fa-border-radius, 0.1em); border-style: var(--fa-border-style, solid); border-width: var(--fa-border-width, 0.08em); padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); } .fa-pull-left { float: left; margin-right: var(--fa-pull-margin, 0.3em); } .fa-pull-right { float: right; margin-left: var(--fa-pull-margin, 0.3em); } .fa-beat { -webkit-animation-name: fa-beat; animation-name: fa-beat; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); animation-timing-function: var(--fa-animation-timing, ease-in-out); } .fa-bounce { -webkit-animation-name: fa-bounce; animation-name: fa-bounce; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); } .fa-fade { -webkit-animation-name: fa-fade; animation-name: fa-fade; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } .fa-beat-fade { -webkit-animation-name: fa-beat-fade; animation-name: fa-beat-fade; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); } .fa-flip { -webkit-animation-name: fa-flip; animation-name: fa-flip; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); animation-timing-function: var(--fa-animation-timing, ease-in-out); } .fa-shake { -webkit-animation-name: fa-shake; animation-name: fa-shake; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, linear); animation-timing-function: var(--fa-animation-timing, linear); } .fa-spin { -webkit-animation-name: fa-spin; animation-name: fa-spin; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 2s); animation-duration: var(--fa-animation-duration, 2s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, linear); animation-timing-function: var(--fa-animation-timing, linear); } .fa-spin-reverse { --fa-animation-direction: reverse; } .fa-pulse, .fa-spin-pulse { -webkit-animation-name: fa-spin; animation-name: fa-spin; -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); animation-timing-function: var(--fa-animation-timing, steps(8)); } @media (prefers-reduced-motion: reduce) { .fa-beat, .fa-bounce, .fa-fade, .fa-beat-fade, .fa-flip, .fa-pulse, .fa-shake, .fa-spin, .fa-spin-pulse { -webkit-animation-delay: -1ms; animation-delay: -1ms; -webkit-animation-duration: 1ms; animation-duration: 1ms; -webkit-animation-iteration-count: 1; animation-iteration-count: 1; -webkit-transition-delay: 0s; transition-delay: 0s; -webkit-transition-duration: 0s; transition-duration: 0s; } } @-webkit-keyframes fa-beat { 0%, 90% { -webkit-transform: scale(1); transform: scale(1); } 45% { -webkit-transform: scale(var(--fa-beat-scale, 1.25)); transform: scale(var(--fa-beat-scale, 1.25)); } } @keyframes fa-beat { 0%, 90% { -webkit-transform: scale(1); transform: scale(1); } 45% { -webkit-transform: scale(var(--fa-beat-scale, 1.25)); transform: scale(var(--fa-beat-scale, 1.25)); } } @-webkit-keyframes fa-bounce { 0% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } 10% { -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } 30% { -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } 50% { -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } 57% { -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } 64% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } 100% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } } @keyframes fa-bounce { 0% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } 10% { -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); } 30% { -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); } 50% { -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); } 57% { -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); } 64% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } 100% { -webkit-transform: scale(1, 1) translateY(0); transform: scale(1, 1) translateY(0); } } @-webkit-keyframes fa-fade { 50% { opacity: var(--fa-fade-opacity, 0.4); } } @keyframes fa-fade { 50% { opacity: var(--fa-fade-opacity, 0.4); } } @-webkit-keyframes fa-beat-fade { 0%, 100% { opacity: var(--fa-beat-fade-opacity, 0.4); -webkit-transform: scale(1); transform: scale(1); } 50% { opacity: 1; -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); transform: scale(var(--fa-beat-fade-scale, 1.125)); } } @keyframes fa-beat-fade { 0%, 100% { opacity: var(--fa-beat-fade-opacity, 0.4); -webkit-transform: scale(1); transform: scale(1); } 50% { opacity: 1; -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); transform: scale(var(--fa-beat-fade-scale, 1.125)); } } @-webkit-keyframes fa-flip { 50% { -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } @keyframes fa-flip { 50% { -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } } @-webkit-keyframes fa-shake { 0% { -webkit-transform: rotate(-15deg); transform: rotate(-15deg); } 4% { -webkit-transform: rotate(15deg); transform: rotate(15deg); } 8%, 24% { -webkit-transform: rotate(-18deg); transform: rotate(-18deg); } 12%, 28% { -webkit-transform: rotate(18deg); transform: rotate(18deg); } 16% { -webkit-transform: rotate(-22deg); transform: rotate(-22deg); } 20% { -webkit-transform: rotate(22deg); transform: rotate(22deg); } 32% { -webkit-transform: rotate(-12deg); transform: rotate(-12deg); } 36% { -webkit-transform: rotate(12deg); transform: rotate(12deg); } 40%, 100% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } } @keyframes fa-shake { 0% { -webkit-transform: rotate(-15deg); transform: rotate(-15deg); } 4% { -webkit-transform: rotate(15deg); transform: rotate(15deg); } 8%, 24% { -webkit-transform: rotate(-18deg); transform: rotate(-18deg); } 12%, 28% { -webkit-transform: rotate(18deg); transform: rotate(18deg); } 16% { -webkit-transform: rotate(-22deg); transform: rotate(-22deg); } 20% { -webkit-transform: rotate(22deg); transform: rotate(22deg); } 32% { -webkit-transform: rotate(-12deg); transform: rotate(-12deg); } 36% { -webkit-transform: rotate(12deg); transform: rotate(12deg); } 40%, 100% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } .fa-rotate-90 { -webkit-transform: rotate(90deg); transform: rotate(90deg); } .fa-rotate-180 { -webkit-transform: rotate(180deg); transform: rotate(180deg); } .fa-rotate-270 { -webkit-transform: rotate(270deg); transform: rotate(270deg); } .fa-flip-horizontal { -webkit-transform: scale(-1, 1); transform: scale(-1, 1); } .fa-flip-vertical { -webkit-transform: scale(1, -1); transform: scale(1, -1); } .fa-flip-both, .fa-flip-horizontal.fa-flip-vertical { -webkit-transform: scale(-1, -1); transform: scale(-1, -1); } .fa-rotate-by { -webkit-transform: rotate(var(--fa-rotate-angle, 0)); transform: rotate(var(--fa-rotate-angle, 0)); } .fa-stack { display: inline-block; height: 2em; line-height: 2em; position: relative; vertical-align: middle; width: 2.5em; } .fa-stack-1x, .fa-stack-2x { left: 0; position: absolute; text-align: center; width: 100%; z-index: var(--fa-stack-z-index, auto); } .fa-stack-1x { line-height: inherit; } .fa-stack-2x { font-size: 2em; } .fa-inverse { color: var(--fa-inverse, #fff); } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .fa-0::before { content: "\30"; } .fa-1::before { content: "\31"; } .fa-2::before { content: "\32"; } .fa-3::before { content: "\33"; } .fa-4::before { content: "\34"; } .fa-5::before { content: "\35"; } .fa-6::before { content: "\36"; } .fa-7::before { content: "\37"; } .fa-8::before { content: "\38"; } .fa-9::before { content: "\39"; } .fa-fill-drip::before { content: "\f576"; } .fa-arrows-to-circle::before { content: "\e4bd"; } .fa-circle-chevron-right::before { content: "\f138"; } .fa-chevron-circle-right::before { content: "\f138"; } .fa-at::before { content: "\40"; } .fa-trash-can::before { content: "\f2ed"; } .fa-trash-alt::before { content: "\f2ed"; } .fa-text-height::before { content: "\f034"; } .fa-user-xmark::before { content: "\f235"; } .fa-user-times::before { content: "\f235"; } .fa-stethoscope::before { content: "\f0f1"; } .fa-message::before { content: "\f27a"; } .fa-comment-alt::before { content: "\f27a"; } .fa-info::before { content: "\f129"; } .fa-down-left-and-up-right-to-center::before { content: "\f422"; } .fa-compress-alt::before { content: "\f422"; } .fa-explosion::before { content: "\e4e9"; } .fa-file-lines::before { content: "\f15c"; } .fa-file-alt::before { content: "\f15c"; } .fa-file-text::before { content: "\f15c"; } .fa-wave-square::before { content: "\f83e"; } .fa-ring::before { content: "\f70b"; } .fa-building-un::before { content: "\e4d9"; } .fa-dice-three::before { content: "\f527"; } .fa-calendar-days::before { content: "\f073"; } .fa-calendar-alt::before { content: "\f073"; } .fa-anchor-circle-check::before { content: "\e4aa"; } .fa-building-circle-arrow-right::before { content: "\e4d1"; } .fa-volleyball::before { content: "\f45f"; } .fa-volleyball-ball::before { content: "\f45f"; } .fa-arrows-up-to-line::before { content: "\e4c2"; } .fa-sort-down::before { content: "\f0dd"; } .fa-sort-desc::before { content: "\f0dd"; } .fa-circle-minus::before { content: "\f056"; } .fa-minus-circle::before { content: "\f056"; } .fa-door-open::before { content: "\f52b"; } .fa-right-from-bracket::before { content: "\f2f5"; } .fa-sign-out-alt::before { content: "\f2f5"; } .fa-atom::before { content: "\f5d2"; } .fa-soap::before { content: "\e06e"; } .fa-icons::before { content: "\f86d"; } .fa-heart-music-camera-bolt::before { content: "\f86d"; } .fa-microphone-lines-slash::before { content: "\f539"; } .fa-microphone-alt-slash::before { content: "\f539"; } .fa-bridge-circle-check::before { content: "\e4c9"; } .fa-pump-medical::before { content: "\e06a"; } .fa-fingerprint::before { content: "\f577"; } .fa-hand-point-right::before { content: "\f0a4"; } .fa-magnifying-glass-location::before { content: "\f689"; } .fa-search-location::before { content: "\f689"; } .fa-forward-step::before { content: "\f051"; } .fa-step-forward::before { content: "\f051"; } .fa-face-smile-beam::before { content: "\f5b8"; } .fa-smile-beam::before { content: "\f5b8"; } .fa-flag-checkered::before { content: "\f11e"; } .fa-football::before { content: "\f44e"; } .fa-football-ball::before { content: "\f44e"; } .fa-school-circle-exclamation::before { content: "\e56c"; } .fa-crop::before { content: "\f125"; } .fa-angles-down::before { content: "\f103"; } .fa-angle-double-down::before { content: "\f103"; } .fa-users-rectangle::before { content: "\e594"; } .fa-people-roof::before { content: "\e537"; } .fa-people-line::before { content: "\e534"; } .fa-beer-mug-empty::before { content: "\f0fc"; } .fa-beer::before { content: "\f0fc"; } .fa-diagram-predecessor::before { content: "\e477"; } .fa-arrow-up-long::before { content: "\f176"; } .fa-long-arrow-up::before { content: "\f176"; } .fa-fire-flame-simple::before { content: "\f46a"; } .fa-burn::before { content: "\f46a"; } .fa-person::before { content: "\f183"; } .fa-male::before { content: "\f183"; } .fa-laptop::before { content: "\f109"; } .fa-file-csv::before { content: "\f6dd"; } .fa-menorah::before { content: "\f676"; } .fa-truck-plane::before { content: "\e58f"; } .fa-record-vinyl::before { content: "\f8d9"; } .fa-face-grin-stars::before { content: "\f587"; } .fa-grin-stars::before { content: "\f587"; } .fa-bong::before { content: "\f55c"; } .fa-spaghetti-monster-flying::before { content: "\f67b"; } .fa-pastafarianism::before { content: "\f67b"; } .fa-arrow-down-up-across-line::before { content: "\e4af"; } .fa-spoon::before { content: "\f2e5"; } .fa-utensil-spoon::before { content: "\f2e5"; } .fa-jar-wheat::before { content: "\e517"; } .fa-envelopes-bulk::before { content: "\f674"; } .fa-mail-bulk::before { content: "\f674"; } .fa-file-circle-exclamation::before { content: "\e4eb"; } .fa-circle-h::before { content: "\f47e"; } .fa-hospital-symbol::before { content: "\f47e"; } .fa-pager::before { content: "\f815"; } .fa-address-book::before { content: "\f2b9"; } .fa-contact-book::before { content: "\f2b9"; } .fa-strikethrough::before { content: "\f0cc"; } .fa-k::before { content: "\4b"; } .fa-landmark-flag::before { content: "\e51c"; } .fa-pencil::before { content: "\f303"; } .fa-pencil-alt::before { content: "\f303"; } .fa-backward::before { content: "\f04a"; } .fa-caret-right::before { content: "\f0da"; } .fa-comments::before { content: "\f086"; } .fa-paste::before { content: "\f0ea"; } .fa-file-clipboard::before { content: "\f0ea"; } .fa-code-pull-request::before { content: "\e13c"; } .fa-clipboard-list::before { content: "\f46d"; } .fa-truck-ramp-box::before { content: "\f4de"; } .fa-truck-loading::before { content: "\f4de"; } .fa-user-check::before { content: "\f4fc"; } .fa-vial-virus::before { content: "\e597"; } .fa-sheet-plastic::before { content: "\e571"; } .fa-blog::before { content: "\f781"; } .fa-user-ninja::before { content: "\f504"; } .fa-person-arrow-up-from-line::before { content: "\e539"; } .fa-scroll-torah::before { content: "\f6a0"; } .fa-torah::before { content: "\f6a0"; } .fa-broom-ball::before { content: "\f458"; } .fa-quidditch::before { content: "\f458"; } .fa-quidditch-broom-ball::before { content: "\f458"; } .fa-toggle-off::before { content: "\f204"; } .fa-box-archive::before { content: "\f187"; } .fa-archive::before { content: "\f187"; } .fa-person-drowning::before { content: "\e545"; } .fa-arrow-down-9-1::before { content: "\f886"; } .fa-sort-numeric-desc::before { content: "\f886"; } .fa-sort-numeric-down-alt::before { content: "\f886"; } .fa-face-grin-tongue-squint::before { content: "\f58a"; } .fa-grin-tongue-squint::before { content: "\f58a"; } .fa-spray-can::before { content: "\f5bd"; } .fa-truck-monster::before { content: "\f63b"; } .fa-w::before { content: "\57"; } .fa-earth-africa::before { content: "\f57c"; } .fa-globe-africa::before { content: "\f57c"; } .fa-rainbow::before { content: "\f75b"; } .fa-circle-notch::before { content: "\f1ce"; } .fa-tablet-screen-button::before { content: "\f3fa"; } .fa-tablet-alt::before { content: "\f3fa"; } .fa-paw::before { content: "\f1b0"; } .fa-cloud::before { content: "\f0c2"; } .fa-trowel-bricks::before { content: "\e58a"; } .fa-face-flushed::before { content: "\f579"; } .fa-flushed::before { content: "\f579"; } .fa-hospital-user::before { content: "\f80d"; } .fa-tent-arrow-left-right::before { content: "\e57f"; } .fa-gavel::before { content: "\f0e3"; } .fa-legal::before { content: "\f0e3"; } .fa-binoculars::before { content: "\f1e5"; } .fa-microphone-slash::before { content: "\f131"; } .fa-box-tissue::before { content: "\e05b"; } .fa-motorcycle::before { content: "\f21c"; } .fa-bell-concierge::before { content: "\f562"; } .fa-concierge-bell::before { content: "\f562"; } .fa-pen-ruler::before { content: "\f5ae"; } .fa-pencil-ruler::before { content: "\f5ae"; } .fa-people-arrows::before { content: "\e068"; } .fa-people-arrows-left-right::before { content: "\e068"; } .fa-mars-and-venus-burst::before { content: "\e523"; } .fa-square-caret-right::before { content: "\f152"; } .fa-caret-square-right::before { content: "\f152"; } .fa-scissors::before { content: "\f0c4"; } .fa-cut::before { content: "\f0c4"; } .fa-sun-plant-wilt::before { content: "\e57a"; } .fa-toilets-portable::before { content: "\e584"; } .fa-hockey-puck::before { content: "\f453"; } .fa-table::before { content: "\f0ce"; } .fa-magnifying-glass-arrow-right::before { content: "\e521"; } .fa-tachograph-digital::before { content: "\f566"; } .fa-digital-tachograph::before { content: "\f566"; } .fa-users-slash::before { content: "\e073"; } .fa-clover::before { content: "\e139"; } .fa-reply::before { content: "\f3e5"; } .fa-mail-reply::before { content: "\f3e5"; } .fa-star-and-crescent::before { content: "\f699"; } .fa-house-fire::before { content: "\e50c"; } .fa-square-minus::before { content: "\f146"; } .fa-minus-square::before { content: "\f146"; } .fa-helicopter::before { content: "\f533"; } .fa-compass::before { content: "\f14e"; } .fa-square-caret-down::before { content: "\f150"; } .fa-caret-square-down::before { content: "\f150"; } .fa-file-circle-question::before { content: "\e4ef"; } .fa-laptop-code::before { content: "\f5fc"; } .fa-swatchbook::before { content: "\f5c3"; } .fa-prescription-bottle::before { content: "\f485"; } .fa-bars::before { content: "\f0c9"; } .fa-navicon::before { content: "\f0c9"; } .fa-people-group::before { content: "\e533"; } .fa-hourglass-end::before { content: "\f253"; } .fa-hourglass-3::before { content: "\f253"; } .fa-heart-crack::before { content: "\f7a9"; } .fa-heart-broken::before { content: "\f7a9"; } .fa-square-up-right::before { content: "\f360"; } .fa-external-link-square-alt::before { content: "\f360"; } .fa-face-kiss-beam::before { content: "\f597"; } .fa-kiss-beam::before { content: "\f597"; } .fa-film::before { content: "\f008"; } .fa-ruler-horizontal::before { content: "\f547"; } .fa-people-robbery::before { content: "\e536"; } .fa-lightbulb::before { content: "\f0eb"; } .fa-caret-left::before { content: "\f0d9"; } .fa-circle-exclamation::before { content: "\f06a"; } .fa-exclamation-circle::before { content: "\f06a"; } .fa-school-circle-xmark::before { content: "\e56d"; } .fa-arrow-right-from-bracket::before { content: "\f08b"; } .fa-sign-out::before { content: "\f08b"; } .fa-circle-chevron-down::before { content: "\f13a"; } .fa-chevron-circle-down::before { content: "\f13a"; } .fa-unlock-keyhole::before { content: "\f13e"; } .fa-unlock-alt::before { content: "\f13e"; } .fa-cloud-showers-heavy::before { content: "\f740"; } .fa-headphones-simple::before { content: "\f58f"; } .fa-headphones-alt::before { content: "\f58f"; } .fa-sitemap::before { content: "\f0e8"; } .fa-circle-dollar-to-slot::before { content: "\f4b9"; } .fa-donate::before { content: "\f4b9"; } .fa-memory::before { content: "\f538"; } .fa-road-spikes::before { content: "\e568"; } .fa-fire-burner::before { content: "\e4f1"; } .fa-flag::before { content: "\f024"; } .fa-hanukiah::before { content: "\f6e6"; } .fa-feather::before { content: "\f52d"; } .fa-volume-low::before { content: "\f027"; } .fa-volume-down::before { content: "\f027"; } .fa-comment-slash::before { content: "\f4b3"; } .fa-cloud-sun-rain::before { content: "\f743"; } .fa-compress::before { content: "\f066"; } .fa-wheat-awn::before { content: "\e2cd"; } .fa-wheat-alt::before { content: "\e2cd"; } .fa-ankh::before { content: "\f644"; } .fa-hands-holding-child::before { content: "\e4fa"; } .fa-asterisk::before { content: "\2a"; } .fa-square-check::before { content: "\f14a"; } .fa-check-square::before { content: "\f14a"; } .fa-peseta-sign::before { content: "\e221"; } .fa-heading::before { content: "\f1dc"; } .fa-header::before { content: "\f1dc"; } .fa-ghost::before { content: "\f6e2"; } .fa-list::before { content: "\f03a"; } .fa-list-squares::before { content: "\f03a"; } .fa-square-phone-flip::before { content: "\f87b"; } .fa-phone-square-alt::before { content: "\f87b"; } .fa-cart-plus::before { content: "\f217"; } .fa-gamepad::before { content: "\f11b"; } .fa-circle-dot::before { content: "\f192"; } .fa-dot-circle::before { content: "\f192"; } .fa-face-dizzy::before { content: "\f567"; } .fa-dizzy::before { content: "\f567"; } .fa-egg::before { content: "\f7fb"; } .fa-house-medical-circle-xmark::before { content: "\e513"; } .fa-campground::before { content: "\f6bb"; } .fa-folder-plus::before { content: "\f65e"; } .fa-futbol::before { content: "\f1e3"; } .fa-futbol-ball::before { content: "\f1e3"; } .fa-soccer-ball::before { content: "\f1e3"; } .fa-paintbrush::before { content: "\f1fc"; } .fa-paint-brush::before { content: "\f1fc"; } .fa-lock::before { content: "\f023"; } .fa-gas-pump::before { content: "\f52f"; } .fa-hot-tub-person::before { content: "\f593"; } .fa-hot-tub::before { content: "\f593"; } .fa-map-location::before { content: "\f59f"; } .fa-map-marked::before { content: "\f59f"; } .fa-house-flood-water::before { content: "\e50e"; } .fa-tree::before { content: "\f1bb"; } .fa-bridge-lock::before { content: "\e4cc"; } .fa-sack-dollar::before { content: "\f81d"; } .fa-pen-to-square::before { content: "\f044"; } .fa-edit::before { content: "\f044"; } .fa-car-side::before { content: "\f5e4"; } .fa-share-nodes::before { content: "\f1e0"; } .fa-share-alt::before { content: "\f1e0"; } .fa-heart-circle-minus::before { content: "\e4ff"; } .fa-hourglass-half::before { content: "\f252"; } .fa-hourglass-2::before { content: "\f252"; } .fa-microscope::before { content: "\f610"; } .fa-sink::before { content: "\e06d"; } .fa-bag-shopping::before { content: "\f290"; } .fa-shopping-bag::before { content: "\f290"; } .fa-arrow-down-z-a::before { content: "\f881"; } .fa-sort-alpha-desc::before { content: "\f881"; } .fa-sort-alpha-down-alt::before { content: "\f881"; } .fa-mitten::before { content: "\f7b5"; } .fa-person-rays::before { content: "\e54d"; } .fa-users::before { content: "\f0c0"; } .fa-eye-slash::before { content: "\f070"; } .fa-flask-vial::before { content: "\e4f3"; } .fa-hand::before { content: "\f256"; } .fa-hand-paper::before { content: "\f256"; } .fa-om::before { content: "\f679"; } .fa-worm::before { content: "\e599"; } .fa-house-circle-xmark::before { content: "\e50b"; } .fa-plug::before { content: "\f1e6"; } .fa-chevron-up::before { content: "\f077"; } .fa-hand-spock::before { content: "\f259"; } .fa-stopwatch::before { content: "\f2f2"; } .fa-face-kiss::before { content: "\f596"; } .fa-kiss::before { content: "\f596"; } .fa-bridge-circle-xmark::before { content: "\e4cb"; } .fa-face-grin-tongue::before { content: "\f589"; } .fa-grin-tongue::before { content: "\f589"; } .fa-chess-bishop::before { content: "\f43a"; } .fa-face-grin-wink::before { content: "\f58c"; } .fa-grin-wink::before { content: "\f58c"; } .fa-ear-deaf::before { content: "\f2a4"; } .fa-deaf::before { content: "\f2a4"; } .fa-deafness::before { content: "\f2a4"; } .fa-hard-of-hearing::before { content: "\f2a4"; } .fa-road-circle-check::before { content: "\e564"; } .fa-dice-five::before { content: "\f523"; } .fa-square-rss::before { content: "\f143"; } .fa-rss-square::before { content: "\f143"; } .fa-land-mine-on::before { content: "\e51b"; } .fa-i-cursor::before { content: "\f246"; } .fa-stamp::before { content: "\f5bf"; } .fa-stairs::before { content: "\e289"; } .fa-i::before { content: "\49"; } .fa-hryvnia-sign::before { content: "\f6f2"; } .fa-hryvnia::before { content: "\f6f2"; } .fa-pills::before { content: "\f484"; } .fa-face-grin-wide::before { content: "\f581"; } .fa-grin-alt::before { content: "\f581"; } .fa-tooth::before { content: "\f5c9"; } .fa-v::before { content: "\56"; } .fa-bangladeshi-taka-sign::before { content: "\e2e6"; } .fa-bicycle::before { content: "\f206"; } .fa-staff-snake::before { content: "\e579"; } .fa-rod-asclepius::before { content: "\e579"; } .fa-rod-snake::before { content: "\e579"; } .fa-staff-aesculapius::before { content: "\e579"; } .fa-head-side-cough-slash::before { content: "\e062"; } .fa-truck-medical::before { content: "\f0f9"; } .fa-ambulance::before { content: "\f0f9"; } .fa-wheat-awn-circle-exclamation::before { content: "\e598"; } .fa-snowman::before { content: "\f7d0"; } .fa-mortar-pestle::before { content: "\f5a7"; } .fa-road-barrier::before { content: "\e562"; } .fa-school::before { content: "\f549"; } .fa-igloo::before { content: "\f7ae"; } .fa-joint::before { content: "\f595"; } .fa-angle-right::before { content: "\f105"; } .fa-horse::before { content: "\f6f0"; } .fa-q::before { content: "\51"; } .fa-g::before { content: "\47"; } .fa-notes-medical::before { content: "\f481"; } .fa-temperature-half::before { content: "\f2c9"; } .fa-temperature-2::before { content: "\f2c9"; } .fa-thermometer-2::before { content: "\f2c9"; } .fa-thermometer-half::before { content: "\f2c9"; } .fa-dong-sign::before { content: "\e169"; } .fa-capsules::before { content: "\f46b"; } .fa-poo-storm::before { content: "\f75a"; } .fa-poo-bolt::before { content: "\f75a"; } .fa-face-frown-open::before { content: "\f57a"; } .fa-frown-open::before { content: "\f57a"; } .fa-hand-point-up::before { content: "\f0a6"; } .fa-money-bill::before { content: "\f0d6"; } .fa-bookmark::before { content: "\f02e"; } .fa-align-justify::before { content: "\f039"; } .fa-umbrella-beach::before { content: "\f5ca"; } .fa-helmet-un::before { content: "\e503"; } .fa-bullseye::before { content: "\f140"; } .fa-bacon::before { content: "\f7e5"; } .fa-hand-point-down::before { content: "\f0a7"; } .fa-arrow-up-from-bracket::before { content: "\e09a"; } .fa-folder::before { content: "\f07b"; } .fa-folder-blank::before { content: "\f07b"; } .fa-file-waveform::before { content: "\f478"; } .fa-file-medical-alt::before { content: "\f478"; } .fa-radiation::before { content: "\f7b9"; } .fa-chart-simple::before { content: "\e473"; } .fa-mars-stroke::before { content: "\f229"; } .fa-vial::before { content: "\f492"; } .fa-gauge::before { content: "\f624"; } .fa-dashboard::before { content: "\f624"; } .fa-gauge-med::before { content: "\f624"; } .fa-tachometer-alt-average::before { content: "\f624"; } .fa-wand-magic-sparkles::before { content: "\e2ca"; } .fa-magic-wand-sparkles::before { content: "\e2ca"; } .fa-e::before { content: "\45"; } .fa-pen-clip::before { content: "\f305"; } .fa-pen-alt::before { content: "\f305"; } .fa-bridge-circle-exclamation::before { content: "\e4ca"; } .fa-user::before { content: "\f007"; } .fa-school-circle-check::before { content: "\e56b"; } .fa-dumpster::before { content: "\f793"; } .fa-van-shuttle::before { content: "\f5b6"; } .fa-shuttle-van::before { content: "\f5b6"; } .fa-building-user::before { content: "\e4da"; } .fa-square-caret-left::before { content: "\f191"; } .fa-caret-square-left::before { content: "\f191"; } .fa-highlighter::before { content: "\f591"; } .fa-key::before { content: "\f084"; } .fa-bullhorn::before { content: "\f0a1"; } .fa-globe::before { content: "\f0ac"; } .fa-synagogue::before { content: "\f69b"; } .fa-person-half-dress::before { content: "\e548"; } .fa-road-bridge::before { content: "\e563"; } .fa-location-arrow::before { content: "\f124"; } .fa-c::before { content: "\43"; } .fa-tablet-button::before { content: "\f10a"; } .fa-building-lock::before { content: "\e4d6"; } .fa-pizza-slice::before { content: "\f818"; } .fa-money-bill-wave::before { content: "\f53a"; } .fa-chart-area::before { content: "\f1fe"; } .fa-area-chart::before { content: "\f1fe"; } .fa-house-flag::before { content: "\e50d"; } .fa-person-circle-minus::before { content: "\e540"; } .fa-ban::before { content: "\f05e"; } .fa-cancel::before { content: "\f05e"; } .fa-camera-rotate::before { content: "\e0d8"; } .fa-spray-can-sparkles::before { content: "\f5d0"; } .fa-air-freshener::before { content: "\f5d0"; } .fa-star::before { content: "\f005"; } .fa-repeat::before { content: "\f363"; } .fa-cross::before { content: "\f654"; } .fa-box::before { content: "\f466"; } .fa-venus-mars::before { content: "\f228"; } .fa-arrow-pointer::before { content: "\f245"; } .fa-mouse-pointer::before { content: "\f245"; } .fa-maximize::before { content: "\f31e"; } .fa-expand-arrows-alt::before { content: "\f31e"; } .fa-charging-station::before { content: "\f5e7"; } .fa-shapes::before { content: "\f61f"; } .fa-triangle-circle-square::before { content: "\f61f"; } .fa-shuffle::before { content: "\f074"; } .fa-random::before { content: "\f074"; } .fa-person-running::before { content: "\f70c"; } .fa-running::before { content: "\f70c"; } .fa-mobile-retro::before { content: "\e527"; } .fa-grip-lines-vertical::before { content: "\f7a5"; } .fa-spider::before { content: "\f717"; } .fa-hands-bound::before { content: "\e4f9"; } .fa-file-invoice-dollar::before { content: "\f571"; } .fa-plane-circle-exclamation::before { content: "\e556"; } .fa-x-ray::before { content: "\f497"; } .fa-spell-check::before { content: "\f891"; } .fa-slash::before { content: "\f715"; } .fa-computer-mouse::before { content: "\f8cc"; } .fa-mouse::before { content: "\f8cc"; } .fa-arrow-right-to-bracket::before { content: "\f090"; } .fa-sign-in::before { content: "\f090"; } .fa-shop-slash::before { content: "\e070"; } .fa-store-alt-slash::before { content: "\e070"; } .fa-server::before { content: "\f233"; } .fa-virus-covid-slash::before { content: "\e4a9"; } .fa-shop-lock::before { content: "\e4a5"; } .fa-hourglass-start::before { content: "\f251"; } .fa-hourglass-1::before { content: "\f251"; } .fa-blender-phone::before { content: "\f6b6"; } .fa-building-wheat::before { content: "\e4db"; } .fa-person-breastfeeding::before { content: "\e53a"; } .fa-right-to-bracket::before { content: "\f2f6"; } .fa-sign-in-alt::before { content: "\f2f6"; } .fa-venus::before { content: "\f221"; } .fa-passport::before { content: "\f5ab"; } .fa-heart-pulse::before { content: "\f21e"; } .fa-heartbeat::before { content: "\f21e"; } .fa-people-carry-box::before { content: "\f4ce"; } .fa-people-carry::before { content: "\f4ce"; } .fa-temperature-high::before { content: "\f769"; } .fa-microchip::before { content: "\f2db"; } .fa-crown::before { content: "\f521"; } .fa-weight-hanging::before { content: "\f5cd"; } .fa-xmarks-lines::before { content: "\e59a"; } .fa-file-prescription::before { content: "\f572"; } .fa-weight-scale::before { content: "\f496"; } .fa-weight::before { content: "\f496"; } .fa-user-group::before { content: "\f500"; } .fa-user-friends::before { content: "\f500"; } .fa-arrow-up-a-z::before { content: "\f15e"; } .fa-sort-alpha-up::before { content: "\f15e"; } .fa-chess-knight::before { content: "\f441"; } .fa-face-laugh-squint::before { content: "\f59b"; } .fa-laugh-squint::before { content: "\f59b"; } .fa-wheelchair::before { content: "\f193"; } .fa-circle-arrow-up::before { content: "\f0aa"; } .fa-arrow-circle-up::before { content: "\f0aa"; } .fa-toggle-on::before { content: "\f205"; } .fa-person-walking::before { content: "\f554"; } .fa-walking::before { content: "\f554"; } .fa-l::before { content: "\4c"; } .fa-fire::before { content: "\f06d"; } .fa-bed-pulse::before { content: "\f487"; } .fa-procedures::before { content: "\f487"; } .fa-shuttle-space::before { content: "\f197"; } .fa-space-shuttle::before { content: "\f197"; } .fa-face-laugh::before { content: "\f599"; } .fa-laugh::before { content: "\f599"; } .fa-folder-open::before { content: "\f07c"; } .fa-heart-circle-plus::before { content: "\e500"; } .fa-code-fork::before { content: "\e13b"; } .fa-city::before { content: "\f64f"; } .fa-microphone-lines::before { content: "\f3c9"; } .fa-microphone-alt::before { content: "\f3c9"; } .fa-pepper-hot::before { content: "\f816"; } .fa-unlock::before { content: "\f09c"; } .fa-colon-sign::before { content: "\e140"; } .fa-headset::before { content: "\f590"; } .fa-store-slash::before { content: "\e071"; } .fa-road-circle-xmark::before { content: "\e566"; } .fa-user-minus::before { content: "\f503"; } .fa-mars-stroke-up::before { content: "\f22a"; } .fa-mars-stroke-v::before { content: "\f22a"; } .fa-champagne-glasses::before { content: "\f79f"; } .fa-glass-cheers::before { content: "\f79f"; } .fa-clipboard::before { content: "\f328"; } .fa-house-circle-exclamation::before { content: "\e50a"; } .fa-file-arrow-up::before { content: "\f574"; } .fa-file-upload::before { content: "\f574"; } .fa-wifi::before { content: "\f1eb"; } .fa-wifi-3::before { content: "\f1eb"; } .fa-wifi-strong::before { content: "\f1eb"; } .fa-bath::before { content: "\f2cd"; } .fa-bathtub::before { content: "\f2cd"; } .fa-underline::before { content: "\f0cd"; } .fa-user-pen::before { content: "\f4ff"; } .fa-user-edit::before { content: "\f4ff"; } .fa-signature::before { content: "\f5b7"; } .fa-stroopwafel::before { content: "\f551"; } .fa-bold::before { content: "\f032"; } .fa-anchor-lock::before { content: "\e4ad"; } .fa-building-ngo::before { content: "\e4d7"; } .fa-manat-sign::before { content: "\e1d5"; } .fa-not-equal::before { content: "\f53e"; } .fa-border-top-left::before { content: "\f853"; } .fa-border-style::before { content: "\f853"; } .fa-map-location-dot::before { content: "\f5a0"; } .fa-map-marked-alt::before { content: "\f5a0"; } .fa-jedi::before { content: "\f669"; } .fa-square-poll-vertical::before { content: "\f681"; } .fa-poll::before { content: "\f681"; } .fa-mug-hot::before { content: "\f7b6"; } .fa-car-battery::before { content: "\f5df"; } .fa-battery-car::before { content: "\f5df"; } .fa-gift::before { content: "\f06b"; } .fa-dice-two::before { content: "\f528"; } .fa-chess-queen::before { content: "\f445"; } .fa-glasses::before { content: "\f530"; } .fa-chess-board::before { content: "\f43c"; } .fa-building-circle-check::before { content: "\e4d2"; } .fa-person-chalkboard::before { content: "\e53d"; } .fa-mars-stroke-right::before { content: "\f22b"; } .fa-mars-stroke-h::before { content: "\f22b"; } .fa-hand-back-fist::before { content: "\f255"; } .fa-hand-rock::before { content: "\f255"; } .fa-square-caret-up::before { content: "\f151"; } .fa-caret-square-up::before { content: "\f151"; } .fa-cloud-showers-water::before { content: "\e4e4"; } .fa-chart-bar::before { content: "\f080"; } .fa-bar-chart::before { content: "\f080"; } .fa-hands-bubbles::before { content: "\e05e"; } .fa-hands-wash::before { content: "\e05e"; } .fa-less-than-equal::before { content: "\f537"; } .fa-train::before { content: "\f238"; } .fa-eye-low-vision::before { content: "\f2a8"; } .fa-low-vision::before { content: "\f2a8"; } .fa-crow::before { content: "\f520"; } .fa-sailboat::before { content: "\e445"; } .fa-window-restore::before { content: "\f2d2"; } .fa-square-plus::before { content: "\f0fe"; } .fa-plus-square::before { content: "\f0fe"; } .fa-torii-gate::before { content: "\f6a1"; } .fa-frog::before { content: "\f52e"; } .fa-bucket::before { content: "\e4cf"; } .fa-image::before { content: "\f03e"; } .fa-microphone::before { content: "\f130"; } .fa-cow::before { content: "\f6c8"; } .fa-caret-up::before { content: "\f0d8"; } .fa-screwdriver::before { content: "\f54a"; } .fa-folder-closed::before { content: "\e185"; } .fa-house-tsunami::before { content: "\e515"; } .fa-square-nfi::before { content: "\e576"; } .fa-arrow-up-from-ground-water::before { content: "\e4b5"; } .fa-martini-glass::before { content: "\f57b"; } .fa-glass-martini-alt::before { content: "\f57b"; } .fa-rotate-left::before { content: "\f2ea"; } .fa-rotate-back::before { content: "\f2ea"; } .fa-rotate-backward::before { content: "\f2ea"; } .fa-undo-alt::before { content: "\f2ea"; } .fa-table-columns::before { content: "\f0db"; } .fa-columns::before { content: "\f0db"; } .fa-lemon::before { content: "\f094"; } .fa-head-side-mask::before { content: "\e063"; } .fa-handshake::before { content: "\f2b5"; } .fa-gem::before { content: "\f3a5"; } .fa-dolly::before { content: "\f472"; } .fa-dolly-box::before { content: "\f472"; } .fa-smoking::before { content: "\f48d"; } .fa-minimize::before { content: "\f78c"; } .fa-compress-arrows-alt::before { content: "\f78c"; } .fa-monument::before { content: "\f5a6"; } .fa-snowplow::before { content: "\f7d2"; } .fa-angles-right::before { content: "\f101"; } .fa-angle-double-right::before { content: "\f101"; } .fa-cannabis::before { content: "\f55f"; } .fa-circle-play::before { content: "\f144"; } .fa-play-circle::before { content: "\f144"; } .fa-tablets::before { content: "\f490"; } .fa-ethernet::before { content: "\f796"; } .fa-euro-sign::before { content: "\f153"; } .fa-eur::before { content: "\f153"; } .fa-euro::before { content: "\f153"; } .fa-chair::before { content: "\f6c0"; } .fa-circle-check::before { content: "\f058"; } .fa-check-circle::before { content: "\f058"; } .fa-circle-stop::before { content: "\f28d"; } .fa-stop-circle::before { content: "\f28d"; } .fa-compass-drafting::before { content: "\f568"; } .fa-drafting-compass::before { content: "\f568"; } .fa-plate-wheat::before { content: "\e55a"; } .fa-icicles::before { content: "\f7ad"; } .fa-person-shelter::before { content: "\e54f"; } .fa-neuter::before { content: "\f22c"; } .fa-id-badge::before { content: "\f2c1"; } .fa-marker::before { content: "\f5a1"; } .fa-face-laugh-beam::before { content: "\f59a"; } .fa-laugh-beam::before { content: "\f59a"; } .fa-helicopter-symbol::before { content: "\e502"; } .fa-universal-access::before { content: "\f29a"; } .fa-circle-chevron-up::before { content: "\f139"; } .fa-chevron-circle-up::before { content: "\f139"; } .fa-lari-sign::before { content: "\e1c8"; } .fa-volcano::before { content: "\f770"; } .fa-person-walking-dashed-line-arrow-right::before { content: "\e553"; } .fa-sterling-sign::before { content: "\f154"; } .fa-gbp::before { content: "\f154"; } .fa-pound-sign::before { content: "\f154"; } .fa-viruses::before { content: "\e076"; } .fa-square-person-confined::before { content: "\e577"; } .fa-user-tie::before { content: "\f508"; } .fa-arrow-down-long::before { content: "\f175"; } .fa-long-arrow-down::before { content: "\f175"; } .fa-tent-arrow-down-to-line::before { content: "\e57e"; } .fa-certificate::before { content: "\f0a3"; } .fa-reply-all::before { content: "\f122"; } .fa-mail-reply-all::before { content: "\f122"; } .fa-suitcase::before { content: "\f0f2"; } .fa-person-skating::before { content: "\f7c5"; } .fa-skating::before { content: "\f7c5"; } .fa-filter-circle-dollar::before { content: "\f662"; } .fa-funnel-dollar::before { content: "\f662"; } .fa-camera-retro::before { content: "\f083"; } .fa-circle-arrow-down::before { content: "\f0ab"; } .fa-arrow-circle-down::before { content: "\f0ab"; } .fa-file-import::before { content: "\f56f"; } .fa-arrow-right-to-file::before { content: "\f56f"; } .fa-square-arrow-up-right::before { content: "\f14c"; } .fa-external-link-square::before { content: "\f14c"; } .fa-box-open::before { content: "\f49e"; } .fa-scroll::before { content: "\f70e"; } .fa-spa::before { content: "\f5bb"; } .fa-location-pin-lock::before { content: "\e51f"; } .fa-pause::before { content: "\f04c"; } .fa-hill-avalanche::before { content: "\e507"; } .fa-temperature-empty::before { content: "\f2cb"; } .fa-temperature-0::before { content: "\f2cb"; } .fa-thermometer-0::before { content: "\f2cb"; } .fa-thermometer-empty::before { content: "\f2cb"; } .fa-bomb::before { content: "\f1e2"; } .fa-registered::before { content: "\f25d"; } .fa-address-card::before { content: "\f2bb"; } .fa-contact-card::before { content: "\f2bb"; } .fa-vcard::before { content: "\f2bb"; } .fa-scale-unbalanced-flip::before { content: "\f516"; } .fa-balance-scale-right::before { content: "\f516"; } .fa-subscript::before { content: "\f12c"; } .fa-diamond-turn-right::before { content: "\f5eb"; } .fa-directions::before { content: "\f5eb"; } .fa-burst::before { content: "\e4dc"; } .fa-house-laptop::before { content: "\e066"; } .fa-laptop-house::before { content: "\e066"; } .fa-face-tired::before { content: "\f5c8"; } .fa-tired::before { content: "\f5c8"; } .fa-money-bills::before { content: "\e1f3"; } .fa-smog::before { content: "\f75f"; } .fa-crutch::before { content: "\f7f7"; } .fa-cloud-arrow-up::before { content: "\f0ee"; } .fa-cloud-upload::before { content: "\f0ee"; } .fa-cloud-upload-alt::before { content: "\f0ee"; } .fa-palette::before { content: "\f53f"; } .fa-arrows-turn-right::before { content: "\e4c0"; } .fa-vest::before { content: "\e085"; } .fa-ferry::before { content: "\e4ea"; } .fa-arrows-down-to-people::before { content: "\e4b9"; } .fa-seedling::before { content: "\f4d8"; } .fa-sprout::before { content: "\f4d8"; } .fa-left-right::before { content: "\f337"; } .fa-arrows-alt-h::before { content: "\f337"; } .fa-boxes-packing::before { content: "\e4c7"; } .fa-circle-arrow-left::before { content: "\f0a8"; } .fa-arrow-circle-left::before { content: "\f0a8"; } .fa-group-arrows-rotate::before { content: "\e4f6"; } .fa-bowl-food::before { content: "\e4c6"; } .fa-candy-cane::before { content: "\f786"; } .fa-arrow-down-wide-short::before { content: "\f160"; } .fa-sort-amount-asc::before { content: "\f160"; } .fa-sort-amount-down::before { content: "\f160"; } .fa-cloud-bolt::before { content: "\f76c"; } .fa-thunderstorm::before { content: "\f76c"; } .fa-text-slash::before { content: "\f87d"; } .fa-remove-format::before { content: "\f87d"; } .fa-face-smile-wink::before { content: "\f4da"; } .fa-smile-wink::before { content: "\f4da"; } .fa-file-word::before { content: "\f1c2"; } .fa-file-powerpoint::before { content: "\f1c4"; } .fa-arrows-left-right::before { content: "\f07e"; } .fa-arrows-h::before { content: "\f07e"; } .fa-house-lock::before { content: "\e510"; } .fa-cloud-arrow-down::before { content: "\f0ed"; } .fa-cloud-download::before { content: "\f0ed"; } .fa-cloud-download-alt::before { content: "\f0ed"; } .fa-children::before { content: "\e4e1"; } .fa-chalkboard::before { content: "\f51b"; } .fa-blackboard::before { content: "\f51b"; } .fa-user-large-slash::before { content: "\f4fa"; } .fa-user-alt-slash::before { content: "\f4fa"; } .fa-envelope-open::before { content: "\f2b6"; } .fa-handshake-simple-slash::before { content: "\e05f"; } .fa-handshake-alt-slash::before { content: "\e05f"; } .fa-mattress-pillow::before { content: "\e525"; } .fa-guarani-sign::before { content: "\e19a"; } .fa-arrows-rotate::before { content: "\f021"; } .fa-refresh::before { content: "\f021"; } .fa-sync::before { content: "\f021"; } .fa-fire-extinguisher::before { content: "\f134"; } .fa-cruzeiro-sign::before { content: "\e152"; } .fa-greater-than-equal::before { content: "\f532"; } .fa-shield-halved::before { content: "\f3ed"; } .fa-shield-alt::before { content: "\f3ed"; } .fa-book-atlas::before { content: "\f558"; } .fa-atlas::before { content: "\f558"; } .fa-virus::before { content: "\e074"; } .fa-envelope-circle-check::before { content: "\e4e8"; } .fa-layer-group::before { content: "\f5fd"; } .fa-arrows-to-dot::before { content: "\e4be"; } .fa-archway::before { content: "\f557"; } .fa-heart-circle-check::before { content: "\e4fd"; } .fa-house-chimney-crack::before { content: "\f6f1"; } .fa-house-damage::before { content: "\f6f1"; } .fa-file-zipper::before { content: "\f1c6"; } .fa-file-archive::before { content: "\f1c6"; } .fa-square::before { content: "\f0c8"; } .fa-martini-glass-empty::before { content: "\f000"; } .fa-glass-martini::before { content: "\f000"; } .fa-couch::before { content: "\f4b8"; } .fa-cedi-sign::before { content: "\e0df"; } .fa-italic::before { content: "\f033"; } .fa-table-cells-column-lock::before { content: "\e678"; } .fa-church::before { content: "\f51d"; } .fa-comments-dollar::before { content: "\f653"; } .fa-democrat::before { content: "\f747"; } .fa-z::before { content: "\5a"; } .fa-person-skiing::before { content: "\f7c9"; } .fa-skiing::before { content: "\f7c9"; } .fa-road-lock::before { content: "\e567"; } .fa-a::before { content: "\41"; } .fa-temperature-arrow-down::before { content: "\e03f"; } .fa-temperature-down::before { content: "\e03f"; } .fa-feather-pointed::before { content: "\f56b"; } .fa-feather-alt::before { content: "\f56b"; } .fa-p::before { content: "\50"; } .fa-snowflake::before { content: "\f2dc"; } .fa-newspaper::before { content: "\f1ea"; } .fa-rectangle-ad::before { content: "\f641"; } .fa-ad::before { content: "\f641"; } .fa-circle-arrow-right::before { content: "\f0a9"; } .fa-arrow-circle-right::before { content: "\f0a9"; } .fa-filter-circle-xmark::before { content: "\e17b"; } .fa-locust::before { content: "\e520"; } .fa-sort::before { content: "\f0dc"; } .fa-unsorted::before { content: "\f0dc"; } .fa-list-ol::before { content: "\f0cb"; } .fa-list-1-2::before { content: "\f0cb"; } .fa-list-numeric::before { content: "\f0cb"; } .fa-person-dress-burst::before { content: "\e544"; } .fa-money-check-dollar::before { content: "\f53d"; } .fa-money-check-alt::before { content: "\f53d"; } .fa-vector-square::before { content: "\f5cb"; } .fa-bread-slice::before { content: "\f7ec"; } .fa-language::before { content: "\f1ab"; } .fa-face-kiss-wink-heart::before { content: "\f598"; } .fa-kiss-wink-heart::before { content: "\f598"; } .fa-filter::before { content: "\f0b0"; } .fa-question::before { content: "\3f"; } .fa-file-signature::before { content: "\f573"; } .fa-up-down-left-right::before { content: "\f0b2"; } .fa-arrows-alt::before { content: "\f0b2"; } .fa-house-chimney-user::before { content: "\e065"; } .fa-hand-holding-heart::before { content: "\f4be"; } .fa-puzzle-piece::before { content: "\f12e"; } .fa-money-check::before { content: "\f53c"; } .fa-star-half-stroke::before { content: "\f5c0"; } .fa-star-half-alt::before { content: "\f5c0"; } .fa-code::before { content: "\f121"; } .fa-whiskey-glass::before { content: "\f7a0"; } .fa-glass-whiskey::before { content: "\f7a0"; } .fa-building-circle-exclamation::before { content: "\e4d3"; } .fa-magnifying-glass-chart::before { content: "\e522"; } .fa-arrow-up-right-from-square::before { content: "\f08e"; } .fa-external-link::before { content: "\f08e"; } .fa-cubes-stacked::before { content: "\e4e6"; } .fa-won-sign::before { content: "\f159"; } .fa-krw::before { content: "\f159"; } .fa-won::before { content: "\f159"; } .fa-virus-covid::before { content: "\e4a8"; } .fa-austral-sign::before { content: "\e0a9"; } .fa-f::before { content: "\46"; } .fa-leaf::before { content: "\f06c"; } .fa-road::before { content: "\f018"; } .fa-taxi::before { content: "\f1ba"; } .fa-cab::before { content: "\f1ba"; } .fa-person-circle-plus::before { content: "\e541"; } .fa-chart-pie::before { content: "\f200"; } .fa-pie-chart::before { content: "\f200"; } .fa-bolt-lightning::before { content: "\e0b7"; } .fa-sack-xmark::before { content: "\e56a"; } .fa-file-excel::before { content: "\f1c3"; } .fa-file-contract::before { content: "\f56c"; } .fa-fish-fins::before { content: "\e4f2"; } .fa-building-flag::before { content: "\e4d5"; } .fa-face-grin-beam::before { content: "\f582"; } .fa-grin-beam::before { content: "\f582"; } .fa-object-ungroup::before { content: "\f248"; } .fa-poop::before { content: "\f619"; } .fa-location-pin::before { content: "\f041"; } .fa-map-marker::before { content: "\f041"; } .fa-kaaba::before { content: "\f66b"; } .fa-toilet-paper::before { content: "\f71e"; } .fa-helmet-safety::before { content: "\f807"; } .fa-hard-hat::before { content: "\f807"; } .fa-hat-hard::before { content: "\f807"; } .fa-eject::before { content: "\f052"; } .fa-circle-right::before { content: "\f35a"; } .fa-arrow-alt-circle-right::before { content: "\f35a"; } .fa-plane-circle-check::before { content: "\e555"; } .fa-face-rolling-eyes::before { content: "\f5a5"; } .fa-meh-rolling-eyes::before { content: "\f5a5"; } .fa-object-group::before { content: "\f247"; } .fa-chart-line::before { content: "\f201"; } .fa-line-chart::before { content: "\f201"; } .fa-mask-ventilator::before { content: "\e524"; } .fa-arrow-right::before { content: "\f061"; } .fa-signs-post::before { content: "\f277"; } .fa-map-signs::before { content: "\f277"; } .fa-cash-register::before { content: "\f788"; } .fa-person-circle-question::before { content: "\e542"; } .fa-h::before { content: "\48"; } .fa-tarp::before { content: "\e57b"; } .fa-screwdriver-wrench::before { content: "\f7d9"; } .fa-tools::before { content: "\f7d9"; } .fa-arrows-to-eye::before { content: "\e4bf"; } .fa-plug-circle-bolt::before { content: "\e55b"; } .fa-heart::before { content: "\f004"; } .fa-mars-and-venus::before { content: "\f224"; } .fa-house-user::before { content: "\e1b0"; } .fa-home-user::before { content: "\e1b0"; } .fa-dumpster-fire::before { content: "\f794"; } .fa-house-crack::before { content: "\e3b1"; } .fa-martini-glass-citrus::before { content: "\f561"; } .fa-cocktail::before { content: "\f561"; } .fa-face-surprise::before { content: "\f5c2"; } .fa-surprise::before { content: "\f5c2"; } .fa-bottle-water::before { content: "\e4c5"; } .fa-circle-pause::before { content: "\f28b"; } .fa-pause-circle::before { content: "\f28b"; } .fa-toilet-paper-slash::before { content: "\e072"; } .fa-apple-whole::before { content: "\f5d1"; } .fa-apple-alt::before { content: "\f5d1"; } .fa-kitchen-set::before { content: "\e51a"; } .fa-r::before { content: "\52"; } .fa-temperature-quarter::before { content: "\f2ca"; } .fa-temperature-1::before { content: "\f2ca"; } .fa-thermometer-1::before { content: "\f2ca"; } .fa-thermometer-quarter::before { content: "\f2ca"; } .fa-cube::before { content: "\f1b2"; } .fa-bitcoin-sign::before { content: "\e0b4"; } .fa-shield-dog::before { content: "\e573"; } .fa-solar-panel::before { content: "\f5ba"; } .fa-lock-open::before { content: "\f3c1"; } .fa-elevator::before { content: "\e16d"; } .fa-money-bill-transfer::before { content: "\e528"; } .fa-money-bill-trend-up::before { content: "\e529"; } .fa-house-flood-water-circle-arrow-right::before { content: "\e50f"; } .fa-square-poll-horizontal::before { content: "\f682"; } .fa-poll-h::before { content: "\f682"; } .fa-circle::before { content: "\f111"; } .fa-backward-fast::before { content: "\f049"; } .fa-fast-backward::before { content: "\f049"; } .fa-recycle::before { content: "\f1b8"; } .fa-user-astronaut::before { content: "\f4fb"; } .fa-plane-slash::before { content: "\e069"; } .fa-trademark::before { content: "\f25c"; } .fa-basketball::before { content: "\f434"; } .fa-basketball-ball::before { content: "\f434"; } .fa-satellite-dish::before { content: "\f7c0"; } .fa-circle-up::before { content: "\f35b"; } .fa-arrow-alt-circle-up::before { content: "\f35b"; } .fa-mobile-screen-button::before { content: "\f3cd"; } .fa-mobile-alt::before { content: "\f3cd"; } .fa-volume-high::before { content: "\f028"; } .fa-volume-up::before { content: "\f028"; } .fa-users-rays::before { content: "\e593"; } .fa-wallet::before { content: "\f555"; } .fa-clipboard-check::before { content: "\f46c"; } .fa-file-audio::before { content: "\f1c7"; } .fa-burger::before { content: "\f805"; } .fa-hamburger::before { content: "\f805"; } .fa-wrench::before { content: "\f0ad"; } .fa-bugs::before { content: "\e4d0"; } .fa-rupee-sign::before { content: "\f156"; } .fa-rupee::before { content: "\f156"; } .fa-file-image::before { content: "\f1c5"; } .fa-circle-question::before { content: "\f059"; } .fa-question-circle::before { content: "\f059"; } .fa-plane-departure::before { content: "\f5b0"; } .fa-handshake-slash::before { content: "\e060"; } .fa-book-bookmark::before { content: "\e0bb"; } .fa-code-branch::before { content: "\f126"; } .fa-hat-cowboy::before { content: "\f8c0"; } .fa-bridge::before { content: "\e4c8"; } .fa-phone-flip::before { content: "\f879"; } .fa-phone-alt::before { content: "\f879"; } .fa-truck-front::before { content: "\e2b7"; } .fa-cat::before { content: "\f6be"; } .fa-anchor-circle-exclamation::before { content: "\e4ab"; } .fa-truck-field::before { content: "\e58d"; } .fa-route::before { content: "\f4d7"; } .fa-clipboard-question::before { content: "\e4e3"; } .fa-panorama::before { content: "\e209"; } .fa-comment-medical::before { content: "\f7f5"; } .fa-teeth-open::before { content: "\f62f"; } .fa-file-circle-minus::before { content: "\e4ed"; } .fa-tags::before { content: "\f02c"; } .fa-wine-glass::before { content: "\f4e3"; } .fa-forward-fast::before { content: "\f050"; } .fa-fast-forward::before { content: "\f050"; } .fa-face-meh-blank::before { content: "\f5a4"; } .fa-meh-blank::before { content: "\f5a4"; } .fa-square-parking::before { content: "\f540"; } .fa-parking::before { content: "\f540"; } .fa-house-signal::before { content: "\e012"; } .fa-bars-progress::before { content: "\f828"; } .fa-tasks-alt::before { content: "\f828"; } .fa-faucet-drip::before { content: "\e006"; } .fa-cart-flatbed::before { content: "\f474"; } .fa-dolly-flatbed::before { content: "\f474"; } .fa-ban-smoking::before { content: "\f54d"; } .fa-smoking-ban::before { content: "\f54d"; } .fa-terminal::before { content: "\f120"; } .fa-mobile-button::before { content: "\f10b"; } .fa-house-medical-flag::before { content: "\e514"; } .fa-basket-shopping::before { content: "\f291"; } .fa-shopping-basket::before { content: "\f291"; } .fa-tape::before { content: "\f4db"; } .fa-bus-simple::before { content: "\f55e"; } .fa-bus-alt::before { content: "\f55e"; } .fa-eye::before { content: "\f06e"; } .fa-face-sad-cry::before { content: "\f5b3"; } .fa-sad-cry::before { content: "\f5b3"; } .fa-audio-description::before { content: "\f29e"; } .fa-person-military-to-person::before { content: "\e54c"; } .fa-file-shield::before { content: "\e4f0"; } .fa-user-slash::before { content: "\f506"; } .fa-pen::before { content: "\f304"; } .fa-tower-observation::before { content: "\e586"; } .fa-file-code::before { content: "\f1c9"; } .fa-signal::before { content: "\f012"; } .fa-signal-5::before { content: "\f012"; } .fa-signal-perfect::before { content: "\f012"; } .fa-bus::before { content: "\f207"; } .fa-heart-circle-xmark::before { content: "\e501"; } .fa-house-chimney::before { content: "\e3af"; } .fa-home-lg::before { content: "\e3af"; } .fa-window-maximize::before { content: "\f2d0"; } .fa-face-frown::before { content: "\f119"; } .fa-frown::before { content: "\f119"; } .fa-prescription::before { content: "\f5b1"; } .fa-shop::before { content: "\f54f"; } .fa-store-alt::before { content: "\f54f"; } .fa-floppy-disk::before { content: "\f0c7"; } .fa-save::before { content: "\f0c7"; } .fa-vihara::before { content: "\f6a7"; } .fa-scale-unbalanced::before { content: "\f515"; } .fa-balance-scale-left::before { content: "\f515"; } .fa-sort-up::before { content: "\f0de"; } .fa-sort-asc::before { content: "\f0de"; } .fa-comment-dots::before { content: "\f4ad"; } .fa-commenting::before { content: "\f4ad"; } .fa-plant-wilt::before { content: "\e5aa"; } .fa-diamond::before { content: "\f219"; } .fa-face-grin-squint::before { content: "\f585"; } .fa-grin-squint::before { content: "\f585"; } .fa-hand-holding-dollar::before { content: "\f4c0"; } .fa-hand-holding-usd::before { content: "\f4c0"; } .fa-bacterium::before { content: "\e05a"; } .fa-hand-pointer::before { content: "\f25a"; } .fa-drum-steelpan::before { content: "\f56a"; } .fa-hand-scissors::before { content: "\f257"; } .fa-hands-praying::before { content: "\f684"; } .fa-praying-hands::before { content: "\f684"; } .fa-arrow-rotate-right::before { content: "\f01e"; } .fa-arrow-right-rotate::before { content: "\f01e"; } .fa-arrow-rotate-forward::before { content: "\f01e"; } .fa-redo::before { content: "\f01e"; } .fa-biohazard::before { content: "\f780"; } .fa-location-crosshairs::before { content: "\f601"; } .fa-location::before { content: "\f601"; } .fa-mars-double::before { content: "\f227"; } .fa-child-dress::before { content: "\e59c"; } .fa-users-between-lines::before { content: "\e591"; } .fa-lungs-virus::before { content: "\e067"; } .fa-face-grin-tears::before { content: "\f588"; } .fa-grin-tears::before { content: "\f588"; } .fa-phone::before { content: "\f095"; } .fa-calendar-xmark::before { content: "\f273"; } .fa-calendar-times::before { content: "\f273"; } .fa-child-reaching::before { content: "\e59d"; } .fa-head-side-virus::before { content: "\e064"; } .fa-user-gear::before { content: "\f4fe"; } .fa-user-cog::before { content: "\f4fe"; } .fa-arrow-up-1-9::before { content: "\f163"; } .fa-sort-numeric-up::before { content: "\f163"; } .fa-door-closed::before { content: "\f52a"; } .fa-shield-virus::before { content: "\e06c"; } .fa-dice-six::before { content: "\f526"; } .fa-mosquito-net::before { content: "\e52c"; } .fa-bridge-water::before { content: "\e4ce"; } .fa-person-booth::before { content: "\f756"; } .fa-text-width::before { content: "\f035"; } .fa-hat-wizard::before { content: "\f6e8"; } .fa-pen-fancy::before { content: "\f5ac"; } .fa-person-digging::before { content: "\f85e"; } .fa-digging::before { content: "\f85e"; } .fa-trash::before { content: "\f1f8"; } .fa-gauge-simple::before { content: "\f629"; } .fa-gauge-simple-med::before { content: "\f629"; } .fa-tachometer-average::before { content: "\f629"; } .fa-book-medical::before { content: "\f7e6"; } .fa-poo::before { content: "\f2fe"; } .fa-quote-right::before { content: "\f10e"; } .fa-quote-right-alt::before { content: "\f10e"; } .fa-shirt::before { content: "\f553"; } .fa-t-shirt::before { content: "\f553"; } .fa-tshirt::before { content: "\f553"; } .fa-cubes::before { content: "\f1b3"; } .fa-divide::before { content: "\f529"; } .fa-tenge-sign::before { content: "\f7d7"; } .fa-tenge::before { content: "\f7d7"; } .fa-headphones::before { content: "\f025"; } .fa-hands-holding::before { content: "\f4c2"; } .fa-hands-clapping::before { content: "\e1a8"; } .fa-republican::before { content: "\f75e"; } .fa-arrow-left::before { content: "\f060"; } .fa-person-circle-xmark::before { content: "\e543"; } .fa-ruler::before { content: "\f545"; } .fa-align-left::before { content: "\f036"; } .fa-dice-d6::before { content: "\f6d1"; } .fa-restroom::before { content: "\f7bd"; } .fa-j::before { content: "\4a"; } .fa-users-viewfinder::before { content: "\e595"; } .fa-file-video::before { content: "\f1c8"; } .fa-up-right-from-square::before { content: "\f35d"; } .fa-external-link-alt::before { content: "\f35d"; } .fa-table-cells::before { content: "\f00a"; } .fa-th::before { content: "\f00a"; } .fa-file-pdf::before { content: "\f1c1"; } .fa-book-bible::before { content: "\f647"; } .fa-bible::before { content: "\f647"; } .fa-o::before { content: "\4f"; } .fa-suitcase-medical::before { content: "\f0fa"; } .fa-medkit::before { content: "\f0fa"; } .fa-user-secret::before { content: "\f21b"; } .fa-otter::before { content: "\f700"; } .fa-person-dress::before { content: "\f182"; } .fa-female::before { content: "\f182"; } .fa-comment-dollar::before { content: "\f651"; } .fa-business-time::before { content: "\f64a"; } .fa-briefcase-clock::before { content: "\f64a"; } .fa-table-cells-large::before { content: "\f009"; } .fa-th-large::before { content: "\f009"; } .fa-book-tanakh::before { content: "\f827"; } .fa-tanakh::before { content: "\f827"; } .fa-phone-volume::before { content: "\f2a0"; } .fa-volume-control-phone::before { content: "\f2a0"; } .fa-hat-cowboy-side::before { content: "\f8c1"; } .fa-clipboard-user::before { content: "\f7f3"; } .fa-child::before { content: "\f1ae"; } .fa-lira-sign::before { content: "\f195"; } .fa-satellite::before { content: "\f7bf"; } .fa-plane-lock::before { content: "\e558"; } .fa-tag::before { content: "\f02b"; } .fa-comment::before { content: "\f075"; } .fa-cake-candles::before { content: "\f1fd"; } .fa-birthday-cake::before { content: "\f1fd"; } .fa-cake::before { content: "\f1fd"; } .fa-envelope::before { content: "\f0e0"; } .fa-angles-up::before { content: "\f102"; } .fa-angle-double-up::before { content: "\f102"; } .fa-paperclip::before { content: "\f0c6"; } .fa-arrow-right-to-city::before { content: "\e4b3"; } .fa-ribbon::before { content: "\f4d6"; } .fa-lungs::before { content: "\f604"; } .fa-arrow-up-9-1::before { content: "\f887"; } .fa-sort-numeric-up-alt::before { content: "\f887"; } .fa-litecoin-sign::before { content: "\e1d3"; } .fa-border-none::before { content: "\f850"; } .fa-circle-nodes::before { content: "\e4e2"; } .fa-parachute-box::before { content: "\f4cd"; } .fa-indent::before { content: "\f03c"; } .fa-truck-field-un::before { content: "\e58e"; } .fa-hourglass::before { content: "\f254"; } .fa-hourglass-empty::before { content: "\f254"; } .fa-mountain::before { content: "\f6fc"; } .fa-user-doctor::before { content: "\f0f0"; } .fa-user-md::before { content: "\f0f0"; } .fa-circle-info::before { content: "\f05a"; } .fa-info-circle::before { content: "\f05a"; } .fa-cloud-meatball::before { content: "\f73b"; } .fa-camera::before { content: "\f030"; } .fa-camera-alt::before { content: "\f030"; } .fa-square-virus::before { content: "\e578"; } .fa-meteor::before { content: "\f753"; } .fa-car-on::before { content: "\e4dd"; } .fa-sleigh::before { content: "\f7cc"; } .fa-arrow-down-1-9::before { content: "\f162"; } .fa-sort-numeric-asc::before { content: "\f162"; } .fa-sort-numeric-down::before { content: "\f162"; } .fa-hand-holding-droplet::before { content: "\f4c1"; } .fa-hand-holding-water::before { content: "\f4c1"; } .fa-water::before { content: "\f773"; } .fa-calendar-check::before { content: "\f274"; } .fa-braille::before { content: "\f2a1"; } .fa-prescription-bottle-medical::before { content: "\f486"; } .fa-prescription-bottle-alt::before { content: "\f486"; } .fa-landmark::before { content: "\f66f"; } .fa-truck::before { content: "\f0d1"; } .fa-crosshairs::before { content: "\f05b"; } .fa-person-cane::before { content: "\e53c"; } .fa-tent::before { content: "\e57d"; } .fa-vest-patches::before { content: "\e086"; } .fa-check-double::before { content: "\f560"; } .fa-arrow-down-a-z::before { content: "\f15d"; } .fa-sort-alpha-asc::before { content: "\f15d"; } .fa-sort-alpha-down::before { content: "\f15d"; } .fa-money-bill-wheat::before { content: "\e52a"; } .fa-cookie::before { content: "\f563"; } .fa-arrow-rotate-left::before { content: "\f0e2"; } .fa-arrow-left-rotate::before { content: "\f0e2"; } .fa-arrow-rotate-back::before { content: "\f0e2"; } .fa-arrow-rotate-backward::before { content: "\f0e2"; } .fa-undo::before { content: "\f0e2"; } .fa-hard-drive::before { content: "\f0a0"; } .fa-hdd::before { content: "\f0a0"; } .fa-face-grin-squint-tears::before { content: "\f586"; } .fa-grin-squint-tears::before { content: "\f586"; } .fa-dumbbell::before { content: "\f44b"; } .fa-rectangle-list::before { content: "\f022"; } .fa-list-alt::before { content: "\f022"; } .fa-tarp-droplet::before { content: "\e57c"; } .fa-house-medical-circle-check::before { content: "\e511"; } .fa-person-skiing-nordic::before { content: "\f7ca"; } .fa-skiing-nordic::before { content: "\f7ca"; } .fa-calendar-plus::before { content: "\f271"; } .fa-plane-arrival::before { content: "\f5af"; } .fa-circle-left::before { content: "\f359"; } .fa-arrow-alt-circle-left::before { content: "\f359"; } .fa-train-subway::before { content: "\f239"; } .fa-subway::before { content: "\f239"; } .fa-chart-gantt::before { content: "\e0e4"; } .fa-indian-rupee-sign::before { content: "\e1bc"; } .fa-indian-rupee::before { content: "\e1bc"; } .fa-inr::before { content: "\e1bc"; } .fa-crop-simple::before { content: "\f565"; } .fa-crop-alt::before { content: "\f565"; } .fa-money-bill-1::before { content: "\f3d1"; } .fa-money-bill-alt::before { content: "\f3d1"; } .fa-left-long::before { content: "\f30a"; } .fa-long-arrow-alt-left::before { content: "\f30a"; } .fa-dna::before { content: "\f471"; } .fa-virus-slash::before { content: "\e075"; } .fa-minus::before { content: "\f068"; } .fa-subtract::before { content: "\f068"; } .fa-chess::before { content: "\f439"; } .fa-arrow-left-long::before { content: "\f177"; } .fa-long-arrow-left::before { content: "\f177"; } .fa-plug-circle-check::before { content: "\e55c"; } .fa-street-view::before { content: "\f21d"; } .fa-franc-sign::before { content: "\e18f"; } .fa-volume-off::before { content: "\f026"; } .fa-hands-asl-interpreting::before { content: "\f2a3"; } .fa-american-sign-language-interpreting::before { content: "\f2a3"; } .fa-asl-interpreting::before { content: "\f2a3"; } .fa-hands-american-sign-language-interpreting::before { content: "\f2a3"; } .fa-gear::before { content: "\f013"; } .fa-cog::before { content: "\f013"; } .fa-droplet-slash::before { content: "\f5c7"; } .fa-tint-slash::before { content: "\f5c7"; } .fa-mosque::before { content: "\f678"; } .fa-mosquito::before { content: "\e52b"; } .fa-star-of-david::before { content: "\f69a"; } .fa-person-military-rifle::before { content: "\e54b"; } .fa-cart-shopping::before { content: "\f07a"; } .fa-shopping-cart::before { content: "\f07a"; } .fa-vials::before { content: "\f493"; } .fa-plug-circle-plus::before { content: "\e55f"; } .fa-place-of-worship::before { content: "\f67f"; } .fa-grip-vertical::before { content: "\f58e"; } .fa-arrow-turn-up::before { content: "\f148"; } .fa-level-up::before { content: "\f148"; } .fa-u::before { content: "\55"; } .fa-square-root-variable::before { content: "\f698"; } .fa-square-root-alt::before { content: "\f698"; } .fa-clock::before { content: "\f017"; } .fa-clock-four::before { content: "\f017"; } .fa-backward-step::before { content: "\f048"; } .fa-step-backward::before { content: "\f048"; } .fa-pallet::before { content: "\f482"; } .fa-faucet::before { content: "\e005"; } .fa-baseball-bat-ball::before { content: "\f432"; } .fa-s::before { content: "\53"; } .fa-timeline::before { content: "\e29c"; } .fa-keyboard::before { content: "\f11c"; } .fa-caret-down::before { content: "\f0d7"; } .fa-house-chimney-medical::before { content: "\f7f2"; } .fa-clinic-medical::before { content: "\f7f2"; } .fa-temperature-three-quarters::before { content: "\f2c8"; } .fa-temperature-3::before { content: "\f2c8"; } .fa-thermometer-3::before { content: "\f2c8"; } .fa-thermometer-three-quarters::before { content: "\f2c8"; } .fa-mobile-screen::before { content: "\f3cf"; } .fa-mobile-android-alt::before { content: "\f3cf"; } .fa-plane-up::before { content: "\e22d"; } .fa-piggy-bank::before { content: "\f4d3"; } .fa-battery-half::before { content: "\f242"; } .fa-battery-3::before { content: "\f242"; } .fa-mountain-city::before { content: "\e52e"; } .fa-coins::before { content: "\f51e"; } .fa-khanda::before { content: "\f66d"; } .fa-sliders::before { content: "\f1de"; } .fa-sliders-h::before { content: "\f1de"; } .fa-folder-tree::before { content: "\f802"; } .fa-network-wired::before { content: "\f6ff"; } .fa-map-pin::before { content: "\f276"; } .fa-hamsa::before { content: "\f665"; } .fa-cent-sign::before { content: "\e3f5"; } .fa-flask::before { content: "\f0c3"; } .fa-person-pregnant::before { content: "\e31e"; } .fa-wand-sparkles::before { content: "\f72b"; } .fa-ellipsis-vertical::before { content: "\f142"; } .fa-ellipsis-v::before { content: "\f142"; } .fa-ticket::before { content: "\f145"; } .fa-power-off::before { content: "\f011"; } .fa-right-long::before { content: "\f30b"; } .fa-long-arrow-alt-right::before { content: "\f30b"; } .fa-flag-usa::before { content: "\f74d"; } .fa-laptop-file::before { content: "\e51d"; } .fa-tty::before { content: "\f1e4"; } .fa-teletype::before { content: "\f1e4"; } .fa-diagram-next::before { content: "\e476"; } .fa-person-rifle::before { content: "\e54e"; } .fa-house-medical-circle-exclamation::before { content: "\e512"; } .fa-closed-captioning::before { content: "\f20a"; } .fa-person-hiking::before { content: "\f6ec"; } .fa-hiking::before { content: "\f6ec"; } .fa-venus-double::before { content: "\f226"; } .fa-images::before { content: "\f302"; } .fa-calculator::before { content: "\f1ec"; } .fa-people-pulling::before { content: "\e535"; } .fa-n::before { content: "\4e"; } .fa-cable-car::before { content: "\f7da"; } .fa-tram::before { content: "\f7da"; } .fa-cloud-rain::before { content: "\f73d"; } .fa-building-circle-xmark::before { content: "\e4d4"; } .fa-ship::before { content: "\f21a"; } .fa-arrows-down-to-line::before { content: "\e4b8"; } .fa-download::before { content: "\f019"; } .fa-face-grin::before { content: "\f580"; } .fa-grin::before { content: "\f580"; } .fa-delete-left::before { content: "\f55a"; } .fa-backspace::before { content: "\f55a"; } .fa-eye-dropper::before { content: "\f1fb"; } .fa-eye-dropper-empty::before { content: "\f1fb"; } .fa-eyedropper::before { content: "\f1fb"; } .fa-file-circle-check::before { content: "\e5a0"; } .fa-forward::before { content: "\f04e"; } .fa-mobile::before { content: "\f3ce"; } .fa-mobile-android::before { content: "\f3ce"; } .fa-mobile-phone::before { content: "\f3ce"; } .fa-face-meh::before { content: "\f11a"; } .fa-meh::before { content: "\f11a"; } .fa-align-center::before { content: "\f037"; } .fa-book-skull::before { content: "\f6b7"; } .fa-book-dead::before { content: "\f6b7"; } .fa-id-card::before { content: "\f2c2"; } .fa-drivers-license::before { content: "\f2c2"; } .fa-outdent::before { content: "\f03b"; } .fa-dedent::before { content: "\f03b"; } .fa-heart-circle-exclamation::before { content: "\e4fe"; } .fa-house::before { content: "\f015"; } .fa-home::before { content: "\f015"; } .fa-home-alt::before { content: "\f015"; } .fa-home-lg-alt::before { content: "\f015"; } .fa-calendar-week::before { content: "\f784"; } .fa-laptop-medical::before { content: "\f812"; } .fa-b::before { content: "\42"; } .fa-file-medical::before { content: "\f477"; } .fa-dice-one::before { content: "\f525"; } .fa-kiwi-bird::before { content: "\f535"; } .fa-arrow-right-arrow-left::before { content: "\f0ec"; } .fa-exchange::before { content: "\f0ec"; } .fa-rotate-right::before { content: "\f2f9"; } .fa-redo-alt::before { content: "\f2f9"; } .fa-rotate-forward::before { content: "\f2f9"; } .fa-utensils::before { content: "\f2e7"; } .fa-cutlery::before { content: "\f2e7"; } .fa-arrow-up-wide-short::before { content: "\f161"; } .fa-sort-amount-up::before { content: "\f161"; } .fa-mill-sign::before { content: "\e1ed"; } .fa-bowl-rice::before { content: "\e2eb"; } .fa-skull::before { content: "\f54c"; } .fa-tower-broadcast::before { content: "\f519"; } .fa-broadcast-tower::before { content: "\f519"; } .fa-truck-pickup::before { content: "\f63c"; } .fa-up-long::before { content: "\f30c"; } .fa-long-arrow-alt-up::before { content: "\f30c"; } .fa-stop::before { content: "\f04d"; } .fa-code-merge::before { content: "\f387"; } .fa-upload::before { content: "\f093"; } .fa-hurricane::before { content: "\f751"; } .fa-mound::before { content: "\e52d"; } .fa-toilet-portable::before { content: "\e583"; } .fa-compact-disc::before { content: "\f51f"; } .fa-file-arrow-down::before { content: "\f56d"; } .fa-file-download::before { content: "\f56d"; } .fa-caravan::before { content: "\f8ff"; } .fa-shield-cat::before { content: "\e572"; } .fa-bolt::before { content: "\f0e7"; } .fa-zap::before { content: "\f0e7"; } .fa-glass-water::before { content: "\e4f4"; } .fa-oil-well::before { content: "\e532"; } .fa-vault::before { content: "\e2c5"; } .fa-mars::before { content: "\f222"; } .fa-toilet::before { content: "\f7d8"; } .fa-plane-circle-xmark::before { content: "\e557"; } .fa-yen-sign::before { content: "\f157"; } .fa-cny::before { content: "\f157"; } .fa-jpy::before { content: "\f157"; } .fa-rmb::before { content: "\f157"; } .fa-yen::before { content: "\f157"; } .fa-ruble-sign::before { content: "\f158"; } .fa-rouble::before { content: "\f158"; } .fa-rub::before { content: "\f158"; } .fa-ruble::before { content: "\f158"; } .fa-sun::before { content: "\f185"; } .fa-guitar::before { content: "\f7a6"; } .fa-face-laugh-wink::before { content: "\f59c"; } .fa-laugh-wink::before { content: "\f59c"; } .fa-horse-head::before { content: "\f7ab"; } .fa-bore-hole::before { content: "\e4c3"; } .fa-industry::before { content: "\f275"; } .fa-circle-down::before { content: "\f358"; } .fa-arrow-alt-circle-down::before { content: "\f358"; } .fa-arrows-turn-to-dots::before { content: "\e4c1"; } .fa-florin-sign::before { content: "\e184"; } .fa-arrow-down-short-wide::before { content: "\f884"; } .fa-sort-amount-desc::before { content: "\f884"; } .fa-sort-amount-down-alt::before { content: "\f884"; } .fa-less-than::before { content: "\3c"; } .fa-angle-down::before { content: "\f107"; } .fa-car-tunnel::before { content: "\e4de"; } .fa-head-side-cough::before { content: "\e061"; } .fa-grip-lines::before { content: "\f7a4"; } .fa-thumbs-down::before { content: "\f165"; } .fa-user-lock::before { content: "\f502"; } .fa-arrow-right-long::before { content: "\f178"; } .fa-long-arrow-right::before { content: "\f178"; } .fa-anchor-circle-xmark::before { content: "\e4ac"; } .fa-ellipsis::before { content: "\f141"; } .fa-ellipsis-h::before { content: "\f141"; } .fa-chess-pawn::before { content: "\f443"; } .fa-kit-medical::before { content: "\f479"; } .fa-first-aid::before { content: "\f479"; } .fa-person-through-window::before { content: "\e5a9"; } .fa-toolbox::before { content: "\f552"; } .fa-hands-holding-circle::before { content: "\e4fb"; } .fa-bug::before { content: "\f188"; } .fa-credit-card::before { content: "\f09d"; } .fa-credit-card-alt::before { content: "\f09d"; } .fa-car::before { content: "\f1b9"; } .fa-automobile::before { content: "\f1b9"; } .fa-hand-holding-hand::before { content: "\e4f7"; } .fa-book-open-reader::before { content: "\f5da"; } .fa-book-reader::before { content: "\f5da"; } .fa-mountain-sun::before { content: "\e52f"; } .fa-arrows-left-right-to-line::before { content: "\e4ba"; } .fa-dice-d20::before { content: "\f6cf"; } .fa-truck-droplet::before { content: "\e58c"; } .fa-file-circle-xmark::before { content: "\e5a1"; } .fa-temperature-arrow-up::before { content: "\e040"; } .fa-temperature-up::before { content: "\e040"; } .fa-medal::before { content: "\f5a2"; } .fa-bed::before { content: "\f236"; } .fa-square-h::before { content: "\f0fd"; } .fa-h-square::before { content: "\f0fd"; } .fa-podcast::before { content: "\f2ce"; } .fa-temperature-full::before { content: "\f2c7"; } .fa-temperature-4::before { content: "\f2c7"; } .fa-thermometer-4::before { content: "\f2c7"; } .fa-thermometer-full::before { content: "\f2c7"; } .fa-bell::before { content: "\f0f3"; } .fa-superscript::before { content: "\f12b"; } .fa-plug-circle-xmark::before { content: "\e560"; } .fa-star-of-life::before { content: "\f621"; } .fa-phone-slash::before { content: "\f3dd"; } .fa-paint-roller::before { content: "\f5aa"; } .fa-handshake-angle::before { content: "\f4c4"; } .fa-hands-helping::before { content: "\f4c4"; } .fa-location-dot::before { content: "\f3c5"; } .fa-map-marker-alt::before { content: "\f3c5"; } .fa-file::before { content: "\f15b"; } .fa-greater-than::before { content: "\3e"; } .fa-person-swimming::before { content: "\f5c4"; } .fa-swimmer::before { content: "\f5c4"; } .fa-arrow-down::before { content: "\f063"; } .fa-droplet::before { content: "\f043"; } .fa-tint::before { content: "\f043"; } .fa-eraser::before { content: "\f12d"; } .fa-earth-americas::before { content: "\f57d"; } .fa-earth::before { content: "\f57d"; } .fa-earth-america::before { content: "\f57d"; } .fa-globe-americas::before { content: "\f57d"; } .fa-person-burst::before { content: "\e53b"; } .fa-dove::before { content: "\f4ba"; } .fa-battery-empty::before { content: "\f244"; } .fa-battery-0::before { content: "\f244"; } .fa-socks::before { content: "\f696"; } .fa-inbox::before { content: "\f01c"; } .fa-section::before { content: "\e447"; } .fa-gauge-high::before { content: "\f625"; } .fa-tachometer-alt::before { content: "\f625"; } .fa-tachometer-alt-fast::before { content: "\f625"; } .fa-envelope-open-text::before { content: "\f658"; } .fa-hospital::before { content: "\f0f8"; } .fa-hospital-alt::before { content: "\f0f8"; } .fa-hospital-wide::before { content: "\f0f8"; } .fa-wine-bottle::before { content: "\f72f"; } .fa-chess-rook::before { content: "\f447"; } .fa-bars-staggered::before { content: "\f550"; } .fa-reorder::before { content: "\f550"; } .fa-stream::before { content: "\f550"; } .fa-dharmachakra::before { content: "\f655"; } .fa-hotdog::before { content: "\f80f"; } .fa-person-walking-with-cane::before { content: "\f29d"; } .fa-blind::before { content: "\f29d"; } .fa-drum::before { content: "\f569"; } .fa-ice-cream::before { content: "\f810"; } .fa-heart-circle-bolt::before { content: "\e4fc"; } .fa-fax::before { content: "\f1ac"; } .fa-paragraph::before { content: "\f1dd"; } .fa-check-to-slot::before { content: "\f772"; } .fa-vote-yea::before { content: "\f772"; } .fa-star-half::before { content: "\f089"; } .fa-boxes-stacked::before { content: "\f468"; } .fa-boxes::before { content: "\f468"; } .fa-boxes-alt::before { content: "\f468"; } .fa-link::before { content: "\f0c1"; } .fa-chain::before { content: "\f0c1"; } .fa-ear-listen::before { content: "\f2a2"; } .fa-assistive-listening-systems::before { content: "\f2a2"; } .fa-tree-city::before { content: "\e587"; } .fa-play::before { content: "\f04b"; } .fa-font::before { content: "\f031"; } .fa-table-cells-row-lock::before { content: "\e67a"; } .fa-rupiah-sign::before { content: "\e23d"; } .fa-magnifying-glass::before { content: "\f002"; } .fa-search::before { content: "\f002"; } .fa-table-tennis-paddle-ball::before { content: "\f45d"; } .fa-ping-pong-paddle-ball::before { content: "\f45d"; } .fa-table-tennis::before { content: "\f45d"; } .fa-person-dots-from-line::before { content: "\f470"; } .fa-diagnoses::before { content: "\f470"; } .fa-trash-can-arrow-up::before { content: "\f82a"; } .fa-trash-restore-alt::before { content: "\f82a"; } .fa-naira-sign::before { content: "\e1f6"; } .fa-cart-arrow-down::before { content: "\f218"; } .fa-walkie-talkie::before { content: "\f8ef"; } .fa-file-pen::before { content: "\f31c"; } .fa-file-edit::before { content: "\f31c"; } .fa-receipt::before { content: "\f543"; } .fa-square-pen::before { content: "\f14b"; } .fa-pen-square::before { content: "\f14b"; } .fa-pencil-square::before { content: "\f14b"; } .fa-suitcase-rolling::before { content: "\f5c1"; } .fa-person-circle-exclamation::before { content: "\e53f"; } .fa-chevron-down::before { content: "\f078"; } .fa-battery-full::before { content: "\f240"; } .fa-battery::before { content: "\f240"; } .fa-battery-5::before { content: "\f240"; } .fa-skull-crossbones::before { content: "\f714"; } .fa-code-compare::before { content: "\e13a"; } .fa-list-ul::before { content: "\f0ca"; } .fa-list-dots::before { content: "\f0ca"; } .fa-school-lock::before { content: "\e56f"; } .fa-tower-cell::before { content: "\e585"; } .fa-down-long::before { content: "\f309"; } .fa-long-arrow-alt-down::before { content: "\f309"; } .fa-ranking-star::before { content: "\e561"; } .fa-chess-king::before { content: "\f43f"; } .fa-person-harassing::before { content: "\e549"; } .fa-brazilian-real-sign::before { content: "\e46c"; } .fa-landmark-dome::before { content: "\f752"; } .fa-landmark-alt::before { content: "\f752"; } .fa-arrow-up::before { content: "\f062"; } .fa-tv::before { content: "\f26c"; } .fa-television::before { content: "\f26c"; } .fa-tv-alt::before { content: "\f26c"; } .fa-shrimp::before { content: "\e448"; } .fa-list-check::before { content: "\f0ae"; } .fa-tasks::before { content: "\f0ae"; } .fa-jug-detergent::before { content: "\e519"; } .fa-circle-user::before { content: "\f2bd"; } .fa-user-circle::before { content: "\f2bd"; } .fa-user-shield::before { content: "\f505"; } .fa-wind::before { content: "\f72e"; } .fa-car-burst::before { content: "\f5e1"; } .fa-car-crash::before { content: "\f5e1"; } .fa-y::before { content: "\59"; } .fa-person-snowboarding::before { content: "\f7ce"; } .fa-snowboarding::before { content: "\f7ce"; } .fa-truck-fast::before { content: "\f48b"; } .fa-shipping-fast::before { content: "\f48b"; } .fa-fish::before { content: "\f578"; } .fa-user-graduate::before { content: "\f501"; } .fa-circle-half-stroke::before { content: "\f042"; } .fa-adjust::before { content: "\f042"; } .fa-clapperboard::before { content: "\e131"; } .fa-circle-radiation::before { content: "\f7ba"; } .fa-radiation-alt::before { content: "\f7ba"; } .fa-baseball::before { content: "\f433"; } .fa-baseball-ball::before { content: "\f433"; } .fa-jet-fighter-up::before { content: "\e518"; } .fa-diagram-project::before { content: "\f542"; } .fa-project-diagram::before { content: "\f542"; } .fa-copy::before { content: "\f0c5"; } .fa-volume-xmark::before { content: "\f6a9"; } .fa-volume-mute::before { content: "\f6a9"; } .fa-volume-times::before { content: "\f6a9"; } .fa-hand-sparkles::before { content: "\e05d"; } .fa-grip::before { content: "\f58d"; } .fa-grip-horizontal::before { content: "\f58d"; } .fa-share-from-square::before { content: "\f14d"; } .fa-share-square::before { content: "\f14d"; } .fa-child-combatant::before { content: "\e4e0"; } .fa-child-rifle::before { content: "\e4e0"; } .fa-gun::before { content: "\e19b"; } .fa-square-phone::before { content: "\f098"; } .fa-phone-square::before { content: "\f098"; } .fa-plus::before { content: "\2b"; } .fa-add::before { content: "\2b"; } .fa-expand::before { content: "\f065"; } .fa-computer::before { content: "\e4e5"; } .fa-xmark::before { content: "\f00d"; } .fa-close::before { content: "\f00d"; } .fa-multiply::before { content: "\f00d"; } .fa-remove::before { content: "\f00d"; } .fa-times::before { content: "\f00d"; } .fa-arrows-up-down-left-right::before { content: "\f047"; } .fa-arrows::before { content: "\f047"; } .fa-chalkboard-user::before { content: "\f51c"; } .fa-chalkboard-teacher::before { content: "\f51c"; } .fa-peso-sign::before { content: "\e222"; } .fa-building-shield::before { content: "\e4d8"; } .fa-baby::before { content: "\f77c"; } .fa-users-line::before { content: "\e592"; } .fa-quote-left::before { content: "\f10d"; } .fa-quote-left-alt::before { content: "\f10d"; } .fa-tractor::before { content: "\f722"; } .fa-trash-arrow-up::before { content: "\f829"; } .fa-trash-restore::before { content: "\f829"; } .fa-arrow-down-up-lock::before { content: "\e4b0"; } .fa-lines-leaning::before { content: "\e51e"; } .fa-ruler-combined::before { content: "\f546"; } .fa-copyright::before { content: "\f1f9"; } .fa-equals::before { content: "\3d"; } .fa-blender::before { content: "\f517"; } .fa-teeth::before { content: "\f62e"; } .fa-shekel-sign::before { content: "\f20b"; } .fa-ils::before { content: "\f20b"; } .fa-shekel::before { content: "\f20b"; } .fa-sheqel::before { content: "\f20b"; } .fa-sheqel-sign::before { content: "\f20b"; } .fa-map::before { content: "\f279"; } .fa-rocket::before { content: "\f135"; } .fa-photo-film::before { content: "\f87c"; } .fa-photo-video::before { content: "\f87c"; } .fa-folder-minus::before { content: "\f65d"; } .fa-store::before { content: "\f54e"; } .fa-arrow-trend-up::before { content: "\e098"; } .fa-plug-circle-minus::before { content: "\e55e"; } .fa-sign-hanging::before { content: "\f4d9"; } .fa-sign::before { content: "\f4d9"; } .fa-bezier-curve::before { content: "\f55b"; } .fa-bell-slash::before { content: "\f1f6"; } .fa-tablet::before { content: "\f3fb"; } .fa-tablet-android::before { content: "\f3fb"; } .fa-school-flag::before { content: "\e56e"; } .fa-fill::before { content: "\f575"; } .fa-angle-up::before { content: "\f106"; } .fa-drumstick-bite::before { content: "\f6d7"; } .fa-holly-berry::before { content: "\f7aa"; } .fa-chevron-left::before { content: "\f053"; } .fa-bacteria::before { content: "\e059"; } .fa-hand-lizard::before { content: "\f258"; } .fa-notdef::before { content: "\e1fe"; } .fa-disease::before { content: "\f7fa"; } .fa-briefcase-medical::before { content: "\f469"; } .fa-genderless::before { content: "\f22d"; } .fa-chevron-right::before { content: "\f054"; } .fa-retweet::before { content: "\f079"; } .fa-car-rear::before { content: "\f5de"; } .fa-car-alt::before { content: "\f5de"; } .fa-pump-soap::before { content: "\e06b"; } .fa-video-slash::before { content: "\f4e2"; } .fa-battery-quarter::before { content: "\f243"; } .fa-battery-2::before { content: "\f243"; } .fa-radio::before { content: "\f8d7"; } .fa-baby-carriage::before { content: "\f77d"; } .fa-carriage-baby::before { content: "\f77d"; } .fa-traffic-light::before { content: "\f637"; } .fa-thermometer::before { content: "\f491"; } .fa-vr-cardboard::before { content: "\f729"; } .fa-hand-middle-finger::before { content: "\f806"; } .fa-percent::before { content: "\25"; } .fa-percentage::before { content: "\25"; } .fa-truck-moving::before { content: "\f4df"; } .fa-glass-water-droplet::before { content: "\e4f5"; } .fa-display::before { content: "\e163"; } .fa-face-smile::before { content: "\f118"; } .fa-smile::before { content: "\f118"; } .fa-thumbtack::before { content: "\f08d"; } .fa-thumb-tack::before { content: "\f08d"; } .fa-trophy::before { content: "\f091"; } .fa-person-praying::before { content: "\f683"; } .fa-pray::before { content: "\f683"; } .fa-hammer::before { content: "\f6e3"; } .fa-hand-peace::before { content: "\f25b"; } .fa-rotate::before { content: "\f2f1"; } .fa-sync-alt::before { content: "\f2f1"; } .fa-spinner::before { content: "\f110"; } .fa-robot::before { content: "\f544"; } .fa-peace::before { content: "\f67c"; } .fa-gears::before { content: "\f085"; } .fa-cogs::before { content: "\f085"; } .fa-warehouse::before { content: "\f494"; } .fa-arrow-up-right-dots::before { content: "\e4b7"; } .fa-splotch::before { content: "\f5bc"; } .fa-face-grin-hearts::before { content: "\f584"; } .fa-grin-hearts::before { content: "\f584"; } .fa-dice-four::before { content: "\f524"; } .fa-sim-card::before { content: "\f7c4"; } .fa-transgender::before { content: "\f225"; } .fa-transgender-alt::before { content: "\f225"; } .fa-mercury::before { content: "\f223"; } .fa-arrow-turn-down::before { content: "\f149"; } .fa-level-down::before { content: "\f149"; } .fa-person-falling-burst::before { content: "\e547"; } .fa-award::before { content: "\f559"; } .fa-ticket-simple::before { content: "\f3ff"; } .fa-ticket-alt::before { content: "\f3ff"; } .fa-building::before { content: "\f1ad"; } .fa-angles-left::before { content: "\f100"; } .fa-angle-double-left::before { content: "\f100"; } .fa-qrcode::before { content: "\f029"; } .fa-clock-rotate-left::before { content: "\f1da"; } .fa-history::before { content: "\f1da"; } .fa-face-grin-beam-sweat::before { content: "\f583"; } .fa-grin-beam-sweat::before { content: "\f583"; } .fa-file-export::before { content: "\f56e"; } .fa-arrow-right-from-file::before { content: "\f56e"; } .fa-shield::before { content: "\f132"; } .fa-shield-blank::before { content: "\f132"; } .fa-arrow-up-short-wide::before { content: "\f885"; } .fa-sort-amount-up-alt::before { content: "\f885"; } .fa-house-medical::before { content: "\e3b2"; } .fa-golf-ball-tee::before { content: "\f450"; } .fa-golf-ball::before { content: "\f450"; } .fa-circle-chevron-left::before { content: "\f137"; } .fa-chevron-circle-left::before { content: "\f137"; } .fa-house-chimney-window::before { content: "\e00d"; } .fa-pen-nib::before { content: "\f5ad"; } .fa-tent-arrow-turn-left::before { content: "\e580"; } .fa-tents::before { content: "\e582"; } .fa-wand-magic::before { content: "\f0d0"; } .fa-magic::before { content: "\f0d0"; } .fa-dog::before { content: "\f6d3"; } .fa-carrot::before { content: "\f787"; } .fa-moon::before { content: "\f186"; } .fa-wine-glass-empty::before { content: "\f5ce"; } .fa-wine-glass-alt::before { content: "\f5ce"; } .fa-cheese::before { content: "\f7ef"; } .fa-yin-yang::before { content: "\f6ad"; } .fa-music::before { content: "\f001"; } .fa-code-commit::before { content: "\f386"; } .fa-temperature-low::before { content: "\f76b"; } .fa-person-biking::before { content: "\f84a"; } .fa-biking::before { content: "\f84a"; } .fa-broom::before { content: "\f51a"; } .fa-shield-heart::before { content: "\e574"; } .fa-gopuram::before { content: "\f664"; } .fa-earth-oceania::before { content: "\e47b"; } .fa-globe-oceania::before { content: "\e47b"; } .fa-square-xmark::before { content: "\f2d3"; } .fa-times-square::before { content: "\f2d3"; } .fa-xmark-square::before { content: "\f2d3"; } .fa-hashtag::before { content: "\23"; } .fa-up-right-and-down-left-from-center::before { content: "\f424"; } .fa-expand-alt::before { content: "\f424"; } .fa-oil-can::before { content: "\f613"; } .fa-t::before { content: "\54"; } .fa-hippo::before { content: "\f6ed"; } .fa-chart-column::before { content: "\e0e3"; } .fa-infinity::before { content: "\f534"; } .fa-vial-circle-check::before { content: "\e596"; } .fa-person-arrow-down-to-line::before { content: "\e538"; } .fa-voicemail::before { content: "\f897"; } .fa-fan::before { content: "\f863"; } .fa-person-walking-luggage::before { content: "\e554"; } .fa-up-down::before { content: "\f338"; } .fa-arrows-alt-v::before { content: "\f338"; } .fa-cloud-moon-rain::before { content: "\f73c"; } .fa-calendar::before { content: "\f133"; } .fa-trailer::before { content: "\e041"; } .fa-bahai::before { content: "\f666"; } .fa-haykal::before { content: "\f666"; } .fa-sd-card::before { content: "\f7c2"; } .fa-dragon::before { content: "\f6d5"; } .fa-shoe-prints::before { content: "\f54b"; } .fa-circle-plus::before { content: "\f055"; } .fa-plus-circle::before { content: "\f055"; } .fa-face-grin-tongue-wink::before { content: "\f58b"; } .fa-grin-tongue-wink::before { content: "\f58b"; } .fa-hand-holding::before { content: "\f4bd"; } .fa-plug-circle-exclamation::before { content: "\e55d"; } .fa-link-slash::before { content: "\f127"; } .fa-chain-broken::before { content: "\f127"; } .fa-chain-slash::before { content: "\f127"; } .fa-unlink::before { content: "\f127"; } .fa-clone::before { content: "\f24d"; } .fa-person-walking-arrow-loop-left::before { content: "\e551"; } .fa-arrow-up-z-a::before { content: "\f882"; } .fa-sort-alpha-up-alt::before { content: "\f882"; } .fa-fire-flame-curved::before { content: "\f7e4"; } .fa-fire-alt::before { content: "\f7e4"; } .fa-tornado::before { content: "\f76f"; } .fa-file-circle-plus::before { content: "\e494"; } .fa-book-quran::before { content: "\f687"; } .fa-quran::before { content: "\f687"; } .fa-anchor::before { content: "\f13d"; } .fa-border-all::before { content: "\f84c"; } .fa-face-angry::before { content: "\f556"; } .fa-angry::before { content: "\f556"; } .fa-cookie-bite::before { content: "\f564"; } .fa-arrow-trend-down::before { content: "\e097"; } .fa-rss::before { content: "\f09e"; } .fa-feed::before { content: "\f09e"; } .fa-draw-polygon::before { content: "\f5ee"; } .fa-scale-balanced::before { content: "\f24e"; } .fa-balance-scale::before { content: "\f24e"; } .fa-gauge-simple-high::before { content: "\f62a"; } .fa-tachometer::before { content: "\f62a"; } .fa-tachometer-fast::before { content: "\f62a"; } .fa-shower::before { content: "\f2cc"; } .fa-desktop::before { content: "\f390"; } .fa-desktop-alt::before { content: "\f390"; } .fa-m::before { content: "\4d"; } .fa-table-list::before { content: "\f00b"; } .fa-th-list::before { content: "\f00b"; } .fa-comment-sms::before { content: "\f7cd"; } .fa-sms::before { content: "\f7cd"; } .fa-book::before { content: "\f02d"; } .fa-user-plus::before { content: "\f234"; } .fa-check::before { content: "\f00c"; } .fa-battery-three-quarters::before { content: "\f241"; } .fa-battery-4::before { content: "\f241"; } .fa-house-circle-check::before { content: "\e509"; } .fa-angle-left::before { content: "\f104"; } .fa-diagram-successor::before { content: "\e47a"; } .fa-truck-arrow-right::before { content: "\e58b"; } .fa-arrows-split-up-and-left::before { content: "\e4bc"; } .fa-hand-fist::before { content: "\f6de"; } .fa-fist-raised::before { content: "\f6de"; } .fa-cloud-moon::before { content: "\f6c3"; } .fa-briefcase::before { content: "\f0b1"; } .fa-person-falling::before { content: "\e546"; } .fa-image-portrait::before { content: "\f3e0"; } .fa-portrait::before { content: "\f3e0"; } .fa-user-tag::before { content: "\f507"; } .fa-rug::before { content: "\e569"; } .fa-earth-europe::before { content: "\f7a2"; } .fa-globe-europe::before { content: "\f7a2"; } .fa-cart-flatbed-suitcase::before { content: "\f59d"; } .fa-luggage-cart::before { content: "\f59d"; } .fa-rectangle-xmark::before { content: "\f410"; } .fa-rectangle-times::before { content: "\f410"; } .fa-times-rectangle::before { content: "\f410"; } .fa-window-close::before { content: "\f410"; } .fa-baht-sign::before { content: "\e0ac"; } .fa-book-open::before { content: "\f518"; } .fa-book-journal-whills::before { content: "\f66a"; } .fa-journal-whills::before { content: "\f66a"; } .fa-handcuffs::before { content: "\e4f8"; } .fa-triangle-exclamation::before { content: "\f071"; } .fa-exclamation-triangle::before { content: "\f071"; } .fa-warning::before { content: "\f071"; } .fa-database::before { content: "\f1c0"; } .fa-share::before { content: "\f064"; } .fa-mail-forward::before { content: "\f064"; } .fa-bottle-droplet::before { content: "\e4c4"; } .fa-mask-face::before { content: "\e1d7"; } .fa-hill-rockslide::before { content: "\e508"; } .fa-right-left::before { content: "\f362"; } .fa-exchange-alt::before { content: "\f362"; } .fa-paper-plane::before { content: "\f1d8"; } .fa-road-circle-exclamation::before { content: "\e565"; } .fa-dungeon::before { content: "\f6d9"; } .fa-align-right::before { content: "\f038"; } .fa-money-bill-1-wave::before { content: "\f53b"; } .fa-money-bill-wave-alt::before { content: "\f53b"; } .fa-life-ring::before { content: "\f1cd"; } .fa-hands::before { content: "\f2a7"; } .fa-sign-language::before { content: "\f2a7"; } .fa-signing::before { content: "\f2a7"; } .fa-calendar-day::before { content: "\f783"; } .fa-water-ladder::before { content: "\f5c5"; } .fa-ladder-water::before { content: "\f5c5"; } .fa-swimming-pool::before { content: "\f5c5"; } .fa-arrows-up-down::before { content: "\f07d"; } .fa-arrows-v::before { content: "\f07d"; } .fa-face-grimace::before { content: "\f57f"; } .fa-grimace::before { content: "\f57f"; } .fa-wheelchair-move::before { content: "\e2ce"; } .fa-wheelchair-alt::before { content: "\e2ce"; } .fa-turn-down::before { content: "\f3be"; } .fa-level-down-alt::before { content: "\f3be"; } .fa-person-walking-arrow-right::before { content: "\e552"; } .fa-square-envelope::before { content: "\f199"; } .fa-envelope-square::before { content: "\f199"; } .fa-dice::before { content: "\f522"; } .fa-bowling-ball::before { content: "\f436"; } .fa-brain::before { content: "\f5dc"; } .fa-bandage::before { content: "\f462"; } .fa-band-aid::before { content: "\f462"; } .fa-calendar-minus::before { content: "\f272"; } .fa-circle-xmark::before { content: "\f057"; } .fa-times-circle::before { content: "\f057"; } .fa-xmark-circle::before { content: "\f057"; } .fa-gifts::before { content: "\f79c"; } .fa-hotel::before { content: "\f594"; } .fa-earth-asia::before { content: "\f57e"; } .fa-globe-asia::before { content: "\f57e"; } .fa-id-card-clip::before { content: "\f47f"; } .fa-id-card-alt::before { content: "\f47f"; } .fa-magnifying-glass-plus::before { content: "\f00e"; } .fa-search-plus::before { content: "\f00e"; } .fa-thumbs-up::before { content: "\f164"; } .fa-user-clock::before { content: "\f4fd"; } .fa-hand-dots::before { content: "\f461"; } .fa-allergies::before { content: "\f461"; } .fa-file-invoice::before { content: "\f570"; } .fa-window-minimize::before { content: "\f2d1"; } .fa-mug-saucer::before { content: "\f0f4"; } .fa-coffee::before { content: "\f0f4"; } .fa-brush::before { content: "\f55d"; } .fa-mask::before { content: "\f6fa"; } .fa-magnifying-glass-minus::before { content: "\f010"; } .fa-search-minus::before { content: "\f010"; } .fa-ruler-vertical::before { content: "\f548"; } .fa-user-large::before { content: "\f406"; } .fa-user-alt::before { content: "\f406"; } .fa-train-tram::before { content: "\e5b4"; } .fa-user-nurse::before { content: "\f82f"; } .fa-syringe::before { content: "\f48e"; } .fa-cloud-sun::before { content: "\f6c4"; } .fa-stopwatch-20::before { content: "\e06f"; } .fa-square-full::before { content: "\f45c"; } .fa-magnet::before { content: "\f076"; } .fa-jar::before { content: "\e516"; } .fa-note-sticky::before { content: "\f249"; } .fa-sticky-note::before { content: "\f249"; } .fa-bug-slash::before { content: "\e490"; } .fa-arrow-up-from-water-pump::before { content: "\e4b6"; } .fa-bone::before { content: "\f5d7"; } .fa-user-injured::before { content: "\f728"; } .fa-face-sad-tear::before { content: "\f5b4"; } .fa-sad-tear::before { content: "\f5b4"; } .fa-plane::before { content: "\f072"; } .fa-tent-arrows-down::before { content: "\e581"; } .fa-exclamation::before { content: "\21"; } .fa-arrows-spin::before { content: "\e4bb"; } .fa-print::before { content: "\f02f"; } .fa-turkish-lira-sign::before { content: "\e2bb"; } .fa-try::before { content: "\e2bb"; } .fa-turkish-lira::before { content: "\e2bb"; } .fa-dollar-sign::before { content: "\24"; } .fa-dollar::before { content: "\24"; } .fa-usd::before { content: "\24"; } .fa-x::before { content: "\58"; } .fa-magnifying-glass-dollar::before { content: "\f688"; } .fa-search-dollar::before { content: "\f688"; } .fa-users-gear::before { content: "\f509"; } .fa-users-cog::before { content: "\f509"; } .fa-person-military-pointing::before { content: "\e54a"; } .fa-building-columns::before { content: "\f19c"; } .fa-bank::before { content: "\f19c"; } .fa-institution::before { content: "\f19c"; } .fa-museum::before { content: "\f19c"; } .fa-university::before { content: "\f19c"; } .fa-umbrella::before { content: "\f0e9"; } .fa-trowel::before { content: "\e589"; } .fa-d::before { content: "\44"; } .fa-stapler::before { content: "\e5af"; } .fa-masks-theater::before { content: "\f630"; } .fa-theater-masks::before { content: "\f630"; } .fa-kip-sign::before { content: "\e1c4"; } .fa-hand-point-left::before { content: "\f0a5"; } .fa-handshake-simple::before { content: "\f4c6"; } .fa-handshake-alt::before { content: "\f4c6"; } .fa-jet-fighter::before { content: "\f0fb"; } .fa-fighter-jet::before { content: "\f0fb"; } .fa-square-share-nodes::before { content: "\f1e1"; } .fa-share-alt-square::before { content: "\f1e1"; } .fa-barcode::before { content: "\f02a"; } .fa-plus-minus::before { content: "\e43c"; } .fa-video::before { content: "\f03d"; } .fa-video-camera::before { content: "\f03d"; } .fa-graduation-cap::before { content: "\f19d"; } .fa-mortar-board::before { content: "\f19d"; } .fa-hand-holding-medical::before { content: "\e05c"; } .fa-person-circle-check::before { content: "\e53e"; } .fa-turn-up::before { content: "\f3bf"; } .fa-level-up-alt::before { content: "\f3bf"; } .sr-only, .fa-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } .sr-only-focusable:not(:focus), .fa-sr-only-focusable:not(:focus) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; } :root, :host { --fa-style-family-brands: 'Font Awesome 6 Brands'; --fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands'; } @font-face { font-family: 'Font Awesome 6 Brands'; font-style: normal; font-weight: 400; font-display: block; src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } .fab, .fa-brands { font-weight: 400; } .fa-monero:before { content: "\f3d0"; } .fa-hooli:before { content: "\f427"; } .fa-yelp:before { content: "\f1e9"; } .fa-cc-visa:before { content: "\f1f0"; } .fa-lastfm:before { content: "\f202"; } .fa-shopware:before { content: "\f5b5"; } .fa-creative-commons-nc:before { content: "\f4e8"; } .fa-aws:before { content: "\f375"; } .fa-redhat:before { content: "\f7bc"; } .fa-yoast:before { content: "\f2b1"; } .fa-cloudflare:before { content: "\e07d"; } .fa-ups:before { content: "\f7e0"; } .fa-pixiv:before { content: "\e640"; } .fa-wpexplorer:before { content: "\f2de"; } .fa-dyalog:before { content: "\f399"; } .fa-bity:before { content: "\f37a"; } .fa-stackpath:before { content: "\f842"; } .fa-buysellads:before { content: "\f20d"; } .fa-first-order:before { content: "\f2b0"; } .fa-modx:before { content: "\f285"; } .fa-guilded:before { content: "\e07e"; } .fa-vnv:before { content: "\f40b"; } .fa-square-js:before { content: "\f3b9"; } .fa-js-square:before { content: "\f3b9"; } .fa-microsoft:before { content: "\f3ca"; } .fa-qq:before { content: "\f1d6"; } .fa-orcid:before { content: "\f8d2"; } .fa-java:before { content: "\f4e4"; } .fa-invision:before { content: "\f7b0"; } .fa-creative-commons-pd-alt:before { content: "\f4ed"; } .fa-centercode:before { content: "\f380"; } .fa-glide-g:before { content: "\f2a6"; } .fa-drupal:before { content: "\f1a9"; } .fa-jxl:before { content: "\e67b"; } .fa-hire-a-helper:before { content: "\f3b0"; } .fa-creative-commons-by:before { content: "\f4e7"; } .fa-unity:before { content: "\e049"; } .fa-whmcs:before { content: "\f40d"; } .fa-rocketchat:before { content: "\f3e8"; } .fa-vk:before { content: "\f189"; } .fa-untappd:before { content: "\f405"; } .fa-mailchimp:before { content: "\f59e"; } .fa-css3-alt:before { content: "\f38b"; } .fa-square-reddit:before { content: "\f1a2"; } .fa-reddit-square:before { content: "\f1a2"; } .fa-vimeo-v:before { content: "\f27d"; } .fa-contao:before { content: "\f26d"; } .fa-square-font-awesome:before { content: "\e5ad"; } .fa-deskpro:before { content: "\f38f"; } .fa-brave:before { content: "\e63c"; } .fa-sistrix:before { content: "\f3ee"; } .fa-square-instagram:before { content: "\e055"; } .fa-instagram-square:before { content: "\e055"; } .fa-battle-net:before { content: "\f835"; } .fa-the-red-yeti:before { content: "\f69d"; } .fa-square-hacker-news:before { content: "\f3af"; } .fa-hacker-news-square:before { content: "\f3af"; } .fa-edge:before { content: "\f282"; } .fa-threads:before { content: "\e618"; } .fa-napster:before { content: "\f3d2"; } .fa-square-snapchat:before { content: "\f2ad"; } .fa-snapchat-square:before { content: "\f2ad"; } .fa-google-plus-g:before { content: "\f0d5"; } .fa-artstation:before { content: "\f77a"; } .fa-markdown:before { content: "\f60f"; } .fa-sourcetree:before { content: "\f7d3"; } .fa-google-plus:before { content: "\f2b3"; } .fa-diaspora:before { content: "\f791"; } .fa-foursquare:before { content: "\f180"; } .fa-stack-overflow:before { content: "\f16c"; } .fa-github-alt:before { content: "\f113"; } .fa-phoenix-squadron:before { content: "\f511"; } .fa-pagelines:before { content: "\f18c"; } .fa-algolia:before { content: "\f36c"; } .fa-red-river:before { content: "\f3e3"; } .fa-creative-commons-sa:before { content: "\f4ef"; } .fa-safari:before { content: "\f267"; } .fa-google:before { content: "\f1a0"; } .fa-square-font-awesome-stroke:before { content: "\f35c"; } .fa-font-awesome-alt:before { content: "\f35c"; } .fa-atlassian:before { content: "\f77b"; } .fa-linkedin-in:before { content: "\f0e1"; } .fa-digital-ocean:before { content: "\f391"; } .fa-nimblr:before { content: "\f5a8"; } .fa-chromecast:before { content: "\f838"; } .fa-evernote:before { content: "\f839"; } .fa-hacker-news:before { content: "\f1d4"; } .fa-creative-commons-sampling:before { content: "\f4f0"; } .fa-adversal:before { content: "\f36a"; } .fa-creative-commons:before { content: "\f25e"; } .fa-watchman-monitoring:before { content: "\e087"; } .fa-fonticons:before { content: "\f280"; } .fa-weixin:before { content: "\f1d7"; } .fa-shirtsinbulk:before { content: "\f214"; } .fa-codepen:before { content: "\f1cb"; } .fa-git-alt:before { content: "\f841"; } .fa-lyft:before { content: "\f3c3"; } .fa-rev:before { content: "\f5b2"; } .fa-windows:before { content: "\f17a"; } .fa-wizards-of-the-coast:before { content: "\f730"; } .fa-square-viadeo:before { content: "\f2aa"; } .fa-viadeo-square:before { content: "\f2aa"; } .fa-meetup:before { content: "\f2e0"; } .fa-centos:before { content: "\f789"; } .fa-adn:before { content: "\f170"; } .fa-cloudsmith:before { content: "\f384"; } .fa-opensuse:before { content: "\e62b"; } .fa-pied-piper-alt:before { content: "\f1a8"; } .fa-square-dribbble:before { content: "\f397"; } .fa-dribbble-square:before { content: "\f397"; } .fa-codiepie:before { content: "\f284"; } .fa-node:before { content: "\f419"; } .fa-mix:before { content: "\f3cb"; } .fa-steam:before { content: "\f1b6"; } .fa-cc-apple-pay:before { content: "\f416"; } .fa-scribd:before { content: "\f28a"; } .fa-debian:before { content: "\e60b"; } .fa-openid:before { content: "\f19b"; } .fa-instalod:before { content: "\e081"; } .fa-expeditedssl:before { content: "\f23e"; } .fa-sellcast:before { content: "\f2da"; } .fa-square-twitter:before { content: "\f081"; } .fa-twitter-square:before { content: "\f081"; } .fa-r-project:before { content: "\f4f7"; } .fa-delicious:before { content: "\f1a5"; } .fa-freebsd:before { content: "\f3a4"; } .fa-vuejs:before { content: "\f41f"; } .fa-accusoft:before { content: "\f369"; } .fa-ioxhost:before { content: "\f208"; } .fa-fonticons-fi:before { content: "\f3a2"; } .fa-app-store:before { content: "\f36f"; } .fa-cc-mastercard:before { content: "\f1f1"; } .fa-itunes-note:before { content: "\f3b5"; } .fa-golang:before { content: "\e40f"; } .fa-kickstarter:before { content: "\f3bb"; } .fa-square-kickstarter:before { content: "\f3bb"; } .fa-grav:before { content: "\f2d6"; } .fa-weibo:before { content: "\f18a"; } .fa-uncharted:before { content: "\e084"; } .fa-firstdraft:before { content: "\f3a1"; } .fa-square-youtube:before { content: "\f431"; } .fa-youtube-square:before { content: "\f431"; } .fa-wikipedia-w:before { content: "\f266"; } .fa-wpressr:before { content: "\f3e4"; } .fa-rendact:before { content: "\f3e4"; } .fa-angellist:before { content: "\f209"; } .fa-galactic-republic:before { content: "\f50c"; } .fa-nfc-directional:before { content: "\e530"; } .fa-skype:before { content: "\f17e"; } .fa-joget:before { content: "\f3b7"; } .fa-fedora:before { content: "\f798"; } .fa-stripe-s:before { content: "\f42a"; } .fa-meta:before { content: "\e49b"; } .fa-laravel:before { content: "\f3bd"; } .fa-hotjar:before { content: "\f3b1"; } .fa-bluetooth-b:before { content: "\f294"; } .fa-square-letterboxd:before { content: "\e62e"; } .fa-sticker-mule:before { content: "\f3f7"; } .fa-creative-commons-zero:before { content: "\f4f3"; } .fa-hips:before { content: "\f452"; } .fa-behance:before { content: "\f1b4"; } .fa-reddit:before { content: "\f1a1"; } .fa-discord:before { content: "\f392"; } .fa-chrome:before { content: "\f268"; } .fa-app-store-ios:before { content: "\f370"; } .fa-cc-discover:before { content: "\f1f2"; } .fa-wpbeginner:before { content: "\f297"; } .fa-confluence:before { content: "\f78d"; } .fa-shoelace:before { content: "\e60c"; } .fa-mdb:before { content: "\f8ca"; } .fa-dochub:before { content: "\f394"; } .fa-accessible-icon:before { content: "\f368"; } .fa-ebay:before { content: "\f4f4"; } .fa-amazon:before { content: "\f270"; } .fa-unsplash:before { content: "\e07c"; } .fa-yarn:before { content: "\f7e3"; } .fa-square-steam:before { content: "\f1b7"; } .fa-steam-square:before { content: "\f1b7"; } .fa-500px:before { content: "\f26e"; } .fa-square-vimeo:before { content: "\f194"; } .fa-vimeo-square:before { content: "\f194"; } .fa-asymmetrik:before { content: "\f372"; } .fa-font-awesome:before { content: "\f2b4"; } .fa-font-awesome-flag:before { content: "\f2b4"; } .fa-font-awesome-logo-full:before { content: "\f2b4"; } .fa-gratipay:before { content: "\f184"; } .fa-apple:before { content: "\f179"; } .fa-hive:before { content: "\e07f"; } .fa-gitkraken:before { content: "\f3a6"; } .fa-keybase:before { content: "\f4f5"; } .fa-apple-pay:before { content: "\f415"; } .fa-padlet:before { content: "\e4a0"; } .fa-amazon-pay:before { content: "\f42c"; } .fa-square-github:before { content: "\f092"; } .fa-github-square:before { content: "\f092"; } .fa-stumbleupon:before { content: "\f1a4"; } .fa-fedex:before { content: "\f797"; } .fa-phoenix-framework:before { content: "\f3dc"; } .fa-shopify:before { content: "\e057"; } .fa-neos:before { content: "\f612"; } .fa-square-threads:before { content: "\e619"; } .fa-hackerrank:before { content: "\f5f7"; } .fa-researchgate:before { content: "\f4f8"; } .fa-swift:before { content: "\f8e1"; } .fa-angular:before { content: "\f420"; } .fa-speakap:before { content: "\f3f3"; } .fa-angrycreative:before { content: "\f36e"; } .fa-y-combinator:before { content: "\f23b"; } .fa-empire:before { content: "\f1d1"; } .fa-envira:before { content: "\f299"; } .fa-google-scholar:before { content: "\e63b"; } .fa-square-gitlab:before { content: "\e5ae"; } .fa-gitlab-square:before { content: "\e5ae"; } .fa-studiovinari:before { content: "\f3f8"; } .fa-pied-piper:before { content: "\f2ae"; } .fa-wordpress:before { content: "\f19a"; } .fa-product-hunt:before { content: "\f288"; } .fa-firefox:before { content: "\f269"; } .fa-linode:before { content: "\f2b8"; } .fa-goodreads:before { content: "\f3a8"; } .fa-square-odnoklassniki:before { content: "\f264"; } .fa-odnoklassniki-square:before { content: "\f264"; } .fa-jsfiddle:before { content: "\f1cc"; } .fa-sith:before { content: "\f512"; } .fa-themeisle:before { content: "\f2b2"; } .fa-page4:before { content: "\f3d7"; } .fa-hashnode:before { content: "\e499"; } .fa-react:before { content: "\f41b"; } .fa-cc-paypal:before { content: "\f1f4"; } .fa-squarespace:before { content: "\f5be"; } .fa-cc-stripe:before { content: "\f1f5"; } .fa-creative-commons-share:before { content: "\f4f2"; } .fa-bitcoin:before { content: "\f379"; } .fa-keycdn:before { content: "\f3ba"; } .fa-opera:before { content: "\f26a"; } .fa-itch-io:before { content: "\f83a"; } .fa-umbraco:before { content: "\f8e8"; } .fa-galactic-senate:before { content: "\f50d"; } .fa-ubuntu:before { content: "\f7df"; } .fa-draft2digital:before { content: "\f396"; } .fa-stripe:before { content: "\f429"; } .fa-houzz:before { content: "\f27c"; } .fa-gg:before { content: "\f260"; } .fa-dhl:before { content: "\f790"; } .fa-square-pinterest:before { content: "\f0d3"; } .fa-pinterest-square:before { content: "\f0d3"; } .fa-xing:before { content: "\f168"; } .fa-blackberry:before { content: "\f37b"; } .fa-creative-commons-pd:before { content: "\f4ec"; } .fa-playstation:before { content: "\f3df"; } .fa-quinscape:before { content: "\f459"; } .fa-less:before { content: "\f41d"; } .fa-blogger-b:before { content: "\f37d"; } .fa-opencart:before { content: "\f23d"; } .fa-vine:before { content: "\f1ca"; } .fa-signal-messenger:before { content: "\e663"; } .fa-paypal:before { content: "\f1ed"; } .fa-gitlab:before { content: "\f296"; } .fa-typo3:before { content: "\f42b"; } .fa-reddit-alien:before { content: "\f281"; } .fa-yahoo:before { content: "\f19e"; } .fa-dailymotion:before { content: "\e052"; } .fa-affiliatetheme:before { content: "\f36b"; } .fa-pied-piper-pp:before { content: "\f1a7"; } .fa-bootstrap:before { content: "\f836"; } .fa-odnoklassniki:before { content: "\f263"; } .fa-nfc-symbol:before { content: "\e531"; } .fa-mintbit:before { content: "\e62f"; } .fa-ethereum:before { content: "\f42e"; } .fa-speaker-deck:before { content: "\f83c"; } .fa-creative-commons-nc-eu:before { content: "\f4e9"; } .fa-patreon:before { content: "\f3d9"; } .fa-avianex:before { content: "\f374"; } .fa-ello:before { content: "\f5f1"; } .fa-gofore:before { content: "\f3a7"; } .fa-bimobject:before { content: "\f378"; } .fa-brave-reverse:before { content: "\e63d"; } .fa-facebook-f:before { content: "\f39e"; } .fa-square-google-plus:before { content: "\f0d4"; } .fa-google-plus-square:before { content: "\f0d4"; } .fa-web-awesome:before { content: "\e682"; } .fa-mandalorian:before { content: "\f50f"; } .fa-first-order-alt:before { content: "\f50a"; } .fa-osi:before { content: "\f41a"; } .fa-google-wallet:before { content: "\f1ee"; } .fa-d-and-d-beyond:before { content: "\f6ca"; } .fa-periscope:before { content: "\f3da"; } .fa-fulcrum:before { content: "\f50b"; } .fa-cloudscale:before { content: "\f383"; } .fa-forumbee:before { content: "\f211"; } .fa-mizuni:before { content: "\f3cc"; } .fa-schlix:before { content: "\f3ea"; } .fa-square-xing:before { content: "\f169"; } .fa-xing-square:before { content: "\f169"; } .fa-bandcamp:before { content: "\f2d5"; } .fa-wpforms:before { content: "\f298"; } .fa-cloudversify:before { content: "\f385"; } .fa-usps:before { content: "\f7e1"; } .fa-megaport:before { content: "\f5a3"; } .fa-magento:before { content: "\f3c4"; } .fa-spotify:before { content: "\f1bc"; } .fa-optin-monster:before { content: "\f23c"; } .fa-fly:before { content: "\f417"; } .fa-aviato:before { content: "\f421"; } .fa-itunes:before { content: "\f3b4"; } .fa-cuttlefish:before { content: "\f38c"; } .fa-blogger:before { content: "\f37c"; } .fa-flickr:before { content: "\f16e"; } .fa-viber:before { content: "\f409"; } .fa-soundcloud:before { content: "\f1be"; } .fa-digg:before { content: "\f1a6"; } .fa-tencent-weibo:before { content: "\f1d5"; } .fa-letterboxd:before { content: "\e62d"; } .fa-symfony:before { content: "\f83d"; } .fa-maxcdn:before { content: "\f136"; } .fa-etsy:before { content: "\f2d7"; } .fa-facebook-messenger:before { content: "\f39f"; } .fa-audible:before { content: "\f373"; } .fa-think-peaks:before { content: "\f731"; } .fa-bilibili:before { content: "\e3d9"; } .fa-erlang:before { content: "\f39d"; } .fa-x-twitter:before { content: "\e61b"; } .fa-cotton-bureau:before { content: "\f89e"; } .fa-dashcube:before { content: "\f210"; } .fa-42-group:before { content: "\e080"; } .fa-innosoft:before { content: "\e080"; } .fa-stack-exchange:before { content: "\f18d"; } .fa-elementor:before { content: "\f430"; } .fa-square-pied-piper:before { content: "\e01e"; } .fa-pied-piper-square:before { content: "\e01e"; } .fa-creative-commons-nd:before { content: "\f4eb"; } .fa-palfed:before { content: "\f3d8"; } .fa-superpowers:before { content: "\f2dd"; } .fa-resolving:before { content: "\f3e7"; } .fa-xbox:before { content: "\f412"; } .fa-square-web-awesome-stroke:before { content: "\e684"; } .fa-searchengin:before { content: "\f3eb"; } .fa-tiktok:before { content: "\e07b"; } .fa-square-facebook:before { content: "\f082"; } .fa-facebook-square:before { content: "\f082"; } .fa-renren:before { content: "\f18b"; } .fa-linux:before { content: "\f17c"; } .fa-glide:before { content: "\f2a5"; } .fa-linkedin:before { content: "\f08c"; } .fa-hubspot:before { content: "\f3b2"; } .fa-deploydog:before { content: "\f38e"; } .fa-twitch:before { content: "\f1e8"; } .fa-ravelry:before { content: "\f2d9"; } .fa-mixer:before { content: "\e056"; } .fa-square-lastfm:before { content: "\f203"; } .fa-lastfm-square:before { content: "\f203"; } .fa-vimeo:before { content: "\f40a"; } .fa-mendeley:before { content: "\f7b3"; } .fa-uniregistry:before { content: "\f404"; } .fa-figma:before { content: "\f799"; } .fa-creative-commons-remix:before { content: "\f4ee"; } .fa-cc-amazon-pay:before { content: "\f42d"; } .fa-dropbox:before { content: "\f16b"; } .fa-instagram:before { content: "\f16d"; } .fa-cmplid:before { content: "\e360"; } .fa-upwork:before { content: "\e641"; } .fa-facebook:before { content: "\f09a"; } .fa-gripfire:before { content: "\f3ac"; } .fa-jedi-order:before { content: "\f50e"; } .fa-uikit:before { content: "\f403"; } .fa-fort-awesome-alt:before { content: "\f3a3"; } .fa-phabricator:before { content: "\f3db"; } .fa-ussunnah:before { content: "\f407"; } .fa-earlybirds:before { content: "\f39a"; } .fa-trade-federation:before { content: "\f513"; } .fa-autoprefixer:before { content: "\f41c"; } .fa-whatsapp:before { content: "\f232"; } .fa-square-upwork:before { content: "\e67c"; } .fa-slideshare:before { content: "\f1e7"; } .fa-google-play:before { content: "\f3ab"; } .fa-viadeo:before { content: "\f2a9"; } .fa-line:before { content: "\f3c0"; } .fa-google-drive:before { content: "\f3aa"; } .fa-servicestack:before { content: "\f3ec"; } .fa-simplybuilt:before { content: "\f215"; } .fa-bitbucket:before { content: "\f171"; } .fa-imdb:before { content: "\f2d8"; } .fa-deezer:before { content: "\e077"; } .fa-raspberry-pi:before { content: "\f7bb"; } .fa-jira:before { content: "\f7b1"; } .fa-docker:before { content: "\f395"; } .fa-screenpal:before { content: "\e570"; } .fa-bluetooth:before { content: "\f293"; } .fa-gitter:before { content: "\f426"; } .fa-d-and-d:before { content: "\f38d"; } .fa-microblog:before { content: "\e01a"; } .fa-cc-diners-club:before { content: "\f24c"; } .fa-gg-circle:before { content: "\f261"; } .fa-pied-piper-hat:before { content: "\f4e5"; } .fa-kickstarter-k:before { content: "\f3bc"; } .fa-yandex:before { content: "\f413"; } .fa-readme:before { content: "\f4d5"; } .fa-html5:before { content: "\f13b"; } .fa-sellsy:before { content: "\f213"; } .fa-square-web-awesome:before { content: "\e683"; } .fa-sass:before { content: "\f41e"; } .fa-wirsindhandwerk:before { content: "\e2d0"; } .fa-wsh:before { content: "\e2d0"; } .fa-buromobelexperte:before { content: "\f37f"; } .fa-salesforce:before { content: "\f83b"; } .fa-octopus-deploy:before { content: "\e082"; } .fa-medapps:before { content: "\f3c6"; } .fa-ns8:before { content: "\f3d5"; } .fa-pinterest-p:before { content: "\f231"; } .fa-apper:before { content: "\f371"; } .fa-fort-awesome:before { content: "\f286"; } .fa-waze:before { content: "\f83f"; } .fa-bluesky:before { content: "\e671"; } .fa-cc-jcb:before { content: "\f24b"; } .fa-snapchat:before { content: "\f2ab"; } .fa-snapchat-ghost:before { content: "\f2ab"; } .fa-fantasy-flight-games:before { content: "\f6dc"; } .fa-rust:before { content: "\e07a"; } .fa-wix:before { content: "\f5cf"; } .fa-square-behance:before { content: "\f1b5"; } .fa-behance-square:before { content: "\f1b5"; } .fa-supple:before { content: "\f3f9"; } .fa-webflow:before { content: "\e65c"; } .fa-rebel:before { content: "\f1d0"; } .fa-css3:before { content: "\f13c"; } .fa-staylinked:before { content: "\f3f5"; } .fa-kaggle:before { content: "\f5fa"; } .fa-space-awesome:before { content: "\e5ac"; } .fa-deviantart:before { content: "\f1bd"; } .fa-cpanel:before { content: "\f388"; } .fa-goodreads-g:before { content: "\f3a9"; } .fa-square-git:before { content: "\f1d2"; } .fa-git-square:before { content: "\f1d2"; } .fa-square-tumblr:before { content: "\f174"; } .fa-tumblr-square:before { content: "\f174"; } .fa-trello:before { content: "\f181"; } .fa-creative-commons-nc-jp:before { content: "\f4ea"; } .fa-get-pocket:before { content: "\f265"; } .fa-perbyte:before { content: "\e083"; } .fa-grunt:before { content: "\f3ad"; } .fa-weebly:before { content: "\f5cc"; } .fa-connectdevelop:before { content: "\f20e"; } .fa-leanpub:before { content: "\f212"; } .fa-black-tie:before { content: "\f27e"; } .fa-themeco:before { content: "\f5c6"; } .fa-python:before { content: "\f3e2"; } .fa-android:before { content: "\f17b"; } .fa-bots:before { content: "\e340"; } .fa-free-code-camp:before { content: "\f2c5"; } .fa-hornbill:before { content: "\f592"; } .fa-js:before { content: "\f3b8"; } .fa-ideal:before { content: "\e013"; } .fa-git:before { content: "\f1d3"; } .fa-dev:before { content: "\f6cc"; } .fa-sketch:before { content: "\f7c6"; } .fa-yandex-international:before { content: "\f414"; } .fa-cc-amex:before { content: "\f1f3"; } .fa-uber:before { content: "\f402"; } .fa-github:before { content: "\f09b"; } .fa-php:before { content: "\f457"; } .fa-alipay:before { content: "\f642"; } .fa-youtube:before { content: "\f167"; } .fa-skyatlas:before { content: "\f216"; } .fa-firefox-browser:before { content: "\e007"; } .fa-replyd:before { content: "\f3e6"; } .fa-suse:before { content: "\f7d6"; } .fa-jenkins:before { content: "\f3b6"; } .fa-twitter:before { content: "\f099"; } .fa-rockrms:before { content: "\f3e9"; } .fa-pinterest:before { content: "\f0d2"; } .fa-buffer:before { content: "\f837"; } .fa-npm:before { content: "\f3d4"; } .fa-yammer:before { content: "\f840"; } .fa-btc:before { content: "\f15a"; } .fa-dribbble:before { content: "\f17d"; } .fa-stumbleupon-circle:before { content: "\f1a3"; } .fa-internet-explorer:before { content: "\f26b"; } .fa-stubber:before { content: "\e5c7"; } .fa-telegram:before { content: "\f2c6"; } .fa-telegram-plane:before { content: "\f2c6"; } .fa-old-republic:before { content: "\f510"; } .fa-odysee:before { content: "\e5c6"; } .fa-square-whatsapp:before { content: "\f40c"; } .fa-whatsapp-square:before { content: "\f40c"; } .fa-node-js:before { content: "\f3d3"; } .fa-edge-legacy:before { content: "\e078"; } .fa-slack:before { content: "\f198"; } .fa-slack-hash:before { content: "\f198"; } .fa-medrt:before { content: "\f3c8"; } .fa-usb:before { content: "\f287"; } .fa-tumblr:before { content: "\f173"; } .fa-vaadin:before { content: "\f408"; } .fa-quora:before { content: "\f2c4"; } .fa-square-x-twitter:before { content: "\e61a"; } .fa-reacteurope:before { content: "\f75d"; } .fa-medium:before { content: "\f23a"; } .fa-medium-m:before { content: "\f23a"; } .fa-amilia:before { content: "\f36d"; } .fa-mixcloud:before { content: "\f289"; } .fa-flipboard:before { content: "\f44d"; } .fa-viacoin:before { content: "\f237"; } .fa-critical-role:before { content: "\f6c9"; } .fa-sitrox:before { content: "\e44a"; } .fa-discourse:before { content: "\f393"; } .fa-joomla:before { content: "\f1aa"; } .fa-mastodon:before { content: "\f4f6"; } .fa-airbnb:before { content: "\f834"; } .fa-wolf-pack-battalion:before { content: "\f514"; } .fa-buy-n-large:before { content: "\f8a6"; } .fa-gulp:before { content: "\f3ae"; } .fa-creative-commons-sampling-plus:before { content: "\f4f1"; } .fa-strava:before { content: "\f428"; } .fa-ember:before { content: "\f423"; } .fa-canadian-maple-leaf:before { content: "\f785"; } .fa-teamspeak:before { content: "\f4f9"; } .fa-pushed:before { content: "\f3e1"; } .fa-wordpress-simple:before { content: "\f411"; } .fa-nutritionix:before { content: "\f3d6"; } .fa-wodu:before { content: "\e088"; } .fa-google-pay:before { content: "\e079"; } .fa-intercom:before { content: "\f7af"; } .fa-zhihu:before { content: "\f63f"; } .fa-korvue:before { content: "\f42f"; } .fa-pix:before { content: "\e43a"; } .fa-steam-symbol:before { content: "\f3f6"; } :root, :host { --fa-style-family-classic: 'Font Awesome 6 Free'; --fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; } @font-face { font-family: 'Font Awesome 6 Free'; font-style: normal; font-weight: 400; font-display: block; src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } .far, .fa-regular { font-weight: 400; } :root, :host { --fa-style-family-classic: 'Font Awesome 6 Free'; --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; } @font-face { font-family: 'Font Awesome 6 Free'; font-style: normal; font-weight: 900; font-display: block; src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } .fas, .fa-solid { font-weight: 900; } @font-face { font-family: 'Font Awesome 5 Brands'; font-display: block; font-weight: 400; src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } @font-face { font-family: 'Font Awesome 5 Free'; font-display: block; font-weight: 900; src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } @font-face { font-family: 'Font Awesome 5 Free'; font-display: block; font-weight: 400; src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } @font-face { font-family: 'FontAwesome'; font-display: block; src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); } @font-face { font-family: 'FontAwesome'; font-display: block; src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); } @font-face { font-family: 'FontAwesome'; font-display: block; src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); } @font-face { font-family: 'FontAwesome'; font-display: block; src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype"); } ================================================ FILE: docs/deps/font-awesome-6.5.2/css/v4-shims.css ================================================ /*! * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2024 Fonticons, Inc. */ .fa.fa-glass:before { content: "\f000"; } .fa.fa-envelope-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-envelope-o:before { content: "\f0e0"; } .fa.fa-star-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-star-o:before { content: "\f005"; } .fa.fa-remove:before { content: "\f00d"; } .fa.fa-close:before { content: "\f00d"; } .fa.fa-gear:before { content: "\f013"; } .fa.fa-trash-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-trash-o:before { content: "\f2ed"; } .fa.fa-home:before { content: "\f015"; } .fa.fa-file-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-o:before { content: "\f15b"; } .fa.fa-clock-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-clock-o:before { content: "\f017"; } .fa.fa-arrow-circle-o-down { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-arrow-circle-o-down:before { content: "\f358"; } .fa.fa-arrow-circle-o-up { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-arrow-circle-o-up:before { content: "\f35b"; } .fa.fa-play-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-play-circle-o:before { content: "\f144"; } .fa.fa-repeat:before { content: "\f01e"; } .fa.fa-rotate-right:before { content: "\f01e"; } .fa.fa-refresh:before { content: "\f021"; } .fa.fa-list-alt { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-list-alt:before { content: "\f022"; } .fa.fa-dedent:before { content: "\f03b"; } .fa.fa-video-camera:before { content: "\f03d"; } .fa.fa-picture-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-picture-o:before { content: "\f03e"; } .fa.fa-photo { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-photo:before { content: "\f03e"; } .fa.fa-image { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-image:before { content: "\f03e"; } .fa.fa-map-marker:before { content: "\f3c5"; } .fa.fa-pencil-square-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-pencil-square-o:before { content: "\f044"; } .fa.fa-edit { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-edit:before { content: "\f044"; } .fa.fa-share-square-o:before { content: "\f14d"; } .fa.fa-check-square-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-check-square-o:before { content: "\f14a"; } .fa.fa-arrows:before { content: "\f0b2"; } .fa.fa-times-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-times-circle-o:before { content: "\f057"; } .fa.fa-check-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-check-circle-o:before { content: "\f058"; } .fa.fa-mail-forward:before { content: "\f064"; } .fa.fa-expand:before { content: "\f424"; } .fa.fa-compress:before { content: "\f422"; } .fa.fa-eye { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-eye-slash { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-warning:before { content: "\f071"; } .fa.fa-calendar:before { content: "\f073"; } .fa.fa-arrows-v:before { content: "\f338"; } .fa.fa-arrows-h:before { content: "\f337"; } .fa.fa-bar-chart:before { content: "\e0e3"; } .fa.fa-bar-chart-o:before { content: "\e0e3"; } .fa.fa-twitter-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-twitter-square:before { content: "\f081"; } .fa.fa-facebook-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-facebook-square:before { content: "\f082"; } .fa.fa-gears:before { content: "\f085"; } .fa.fa-thumbs-o-up { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-thumbs-o-up:before { content: "\f164"; } .fa.fa-thumbs-o-down { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-thumbs-o-down:before { content: "\f165"; } .fa.fa-heart-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-heart-o:before { content: "\f004"; } .fa.fa-sign-out:before { content: "\f2f5"; } .fa.fa-linkedin-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-linkedin-square:before { content: "\f08c"; } .fa.fa-thumb-tack:before { content: "\f08d"; } .fa.fa-external-link:before { content: "\f35d"; } .fa.fa-sign-in:before { content: "\f2f6"; } .fa.fa-github-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-github-square:before { content: "\f092"; } .fa.fa-lemon-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-lemon-o:before { content: "\f094"; } .fa.fa-square-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-square-o:before { content: "\f0c8"; } .fa.fa-bookmark-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-bookmark-o:before { content: "\f02e"; } .fa.fa-twitter { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-facebook { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-facebook:before { content: "\f39e"; } .fa.fa-facebook-f { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-facebook-f:before { content: "\f39e"; } .fa.fa-github { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-credit-card { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-feed:before { content: "\f09e"; } .fa.fa-hdd-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hdd-o:before { content: "\f0a0"; } .fa.fa-hand-o-right { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-o-right:before { content: "\f0a4"; } .fa.fa-hand-o-left { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-o-left:before { content: "\f0a5"; } .fa.fa-hand-o-up { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-o-up:before { content: "\f0a6"; } .fa.fa-hand-o-down { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-o-down:before { content: "\f0a7"; } .fa.fa-globe:before { content: "\f57d"; } .fa.fa-tasks:before { content: "\f828"; } .fa.fa-arrows-alt:before { content: "\f31e"; } .fa.fa-group:before { content: "\f0c0"; } .fa.fa-chain:before { content: "\f0c1"; } .fa.fa-cut:before { content: "\f0c4"; } .fa.fa-files-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-files-o:before { content: "\f0c5"; } .fa.fa-floppy-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-floppy-o:before { content: "\f0c7"; } .fa.fa-save { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-save:before { content: "\f0c7"; } .fa.fa-navicon:before { content: "\f0c9"; } .fa.fa-reorder:before { content: "\f0c9"; } .fa.fa-magic:before { content: "\e2ca"; } .fa.fa-pinterest { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pinterest-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pinterest-square:before { content: "\f0d3"; } .fa.fa-google-plus-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-plus-square:before { content: "\f0d4"; } .fa.fa-google-plus { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-plus:before { content: "\f0d5"; } .fa.fa-money:before { content: "\f3d1"; } .fa.fa-unsorted:before { content: "\f0dc"; } .fa.fa-sort-desc:before { content: "\f0dd"; } .fa.fa-sort-asc:before { content: "\f0de"; } .fa.fa-linkedin { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-linkedin:before { content: "\f0e1"; } .fa.fa-rotate-left:before { content: "\f0e2"; } .fa.fa-legal:before { content: "\f0e3"; } .fa.fa-tachometer:before { content: "\f625"; } .fa.fa-dashboard:before { content: "\f625"; } .fa.fa-comment-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-comment-o:before { content: "\f075"; } .fa.fa-comments-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-comments-o:before { content: "\f086"; } .fa.fa-flash:before { content: "\f0e7"; } .fa.fa-clipboard:before { content: "\f0ea"; } .fa.fa-lightbulb-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-lightbulb-o:before { content: "\f0eb"; } .fa.fa-exchange:before { content: "\f362"; } .fa.fa-cloud-download:before { content: "\f0ed"; } .fa.fa-cloud-upload:before { content: "\f0ee"; } .fa.fa-bell-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-bell-o:before { content: "\f0f3"; } .fa.fa-cutlery:before { content: "\f2e7"; } .fa.fa-file-text-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-text-o:before { content: "\f15c"; } .fa.fa-building-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-building-o:before { content: "\f1ad"; } .fa.fa-hospital-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hospital-o:before { content: "\f0f8"; } .fa.fa-tablet:before { content: "\f3fa"; } .fa.fa-mobile:before { content: "\f3cd"; } .fa.fa-mobile-phone:before { content: "\f3cd"; } .fa.fa-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-circle-o:before { content: "\f111"; } .fa.fa-mail-reply:before { content: "\f3e5"; } .fa.fa-github-alt { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-folder-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-folder-o:before { content: "\f07b"; } .fa.fa-folder-open-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-folder-open-o:before { content: "\f07c"; } .fa.fa-smile-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-smile-o:before { content: "\f118"; } .fa.fa-frown-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-frown-o:before { content: "\f119"; } .fa.fa-meh-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-meh-o:before { content: "\f11a"; } .fa.fa-keyboard-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-keyboard-o:before { content: "\f11c"; } .fa.fa-flag-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-flag-o:before { content: "\f024"; } .fa.fa-mail-reply-all:before { content: "\f122"; } .fa.fa-star-half-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-star-half-o:before { content: "\f5c0"; } .fa.fa-star-half-empty { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-star-half-empty:before { content: "\f5c0"; } .fa.fa-star-half-full { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-star-half-full:before { content: "\f5c0"; } .fa.fa-code-fork:before { content: "\f126"; } .fa.fa-chain-broken:before { content: "\f127"; } .fa.fa-unlink:before { content: "\f127"; } .fa.fa-calendar-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-calendar-o:before { content: "\f133"; } .fa.fa-maxcdn { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-html5 { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-css3 { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-unlock-alt:before { content: "\f09c"; } .fa.fa-minus-square-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-minus-square-o:before { content: "\f146"; } .fa.fa-level-up:before { content: "\f3bf"; } .fa.fa-level-down:before { content: "\f3be"; } .fa.fa-pencil-square:before { content: "\f14b"; } .fa.fa-external-link-square:before { content: "\f360"; } .fa.fa-compass { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-caret-square-o-down { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-caret-square-o-down:before { content: "\f150"; } .fa.fa-toggle-down { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-toggle-down:before { content: "\f150"; } .fa.fa-caret-square-o-up { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-caret-square-o-up:before { content: "\f151"; } .fa.fa-toggle-up { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-toggle-up:before { content: "\f151"; } .fa.fa-caret-square-o-right { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-caret-square-o-right:before { content: "\f152"; } .fa.fa-toggle-right { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-toggle-right:before { content: "\f152"; } .fa.fa-eur:before { content: "\f153"; } .fa.fa-euro:before { content: "\f153"; } .fa.fa-gbp:before { content: "\f154"; } .fa.fa-usd:before { content: "\24"; } .fa.fa-dollar:before { content: "\24"; } .fa.fa-inr:before { content: "\e1bc"; } .fa.fa-rupee:before { content: "\e1bc"; } .fa.fa-jpy:before { content: "\f157"; } .fa.fa-cny:before { content: "\f157"; } .fa.fa-rmb:before { content: "\f157"; } .fa.fa-yen:before { content: "\f157"; } .fa.fa-rub:before { content: "\f158"; } .fa.fa-ruble:before { content: "\f158"; } .fa.fa-rouble:before { content: "\f158"; } .fa.fa-krw:before { content: "\f159"; } .fa.fa-won:before { content: "\f159"; } .fa.fa-btc { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bitcoin { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bitcoin:before { content: "\f15a"; } .fa.fa-file-text:before { content: "\f15c"; } .fa.fa-sort-alpha-asc:before { content: "\f15d"; } .fa.fa-sort-alpha-desc:before { content: "\f881"; } .fa.fa-sort-amount-asc:before { content: "\f884"; } .fa.fa-sort-amount-desc:before { content: "\f160"; } .fa.fa-sort-numeric-asc:before { content: "\f162"; } .fa.fa-sort-numeric-desc:before { content: "\f886"; } .fa.fa-youtube-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-youtube-square:before { content: "\f431"; } .fa.fa-youtube { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-xing { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-xing-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-xing-square:before { content: "\f169"; } .fa.fa-youtube-play { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-youtube-play:before { content: "\f167"; } .fa.fa-dropbox { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-stack-overflow { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-instagram { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-flickr { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-adn { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bitbucket { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bitbucket-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bitbucket-square:before { content: "\f171"; } .fa.fa-tumblr { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-tumblr-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-tumblr-square:before { content: "\f174"; } .fa.fa-long-arrow-down:before { content: "\f309"; } .fa.fa-long-arrow-up:before { content: "\f30c"; } .fa.fa-long-arrow-left:before { content: "\f30a"; } .fa.fa-long-arrow-right:before { content: "\f30b"; } .fa.fa-apple { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-windows { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-android { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-linux { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-dribbble { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-skype { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-foursquare { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-trello { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gratipay { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gittip { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gittip:before { content: "\f184"; } .fa.fa-sun-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-sun-o:before { content: "\f185"; } .fa.fa-moon-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-moon-o:before { content: "\f186"; } .fa.fa-vk { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-weibo { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-renren { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pagelines { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-stack-exchange { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-arrow-circle-o-right { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-arrow-circle-o-right:before { content: "\f35a"; } .fa.fa-arrow-circle-o-left { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-arrow-circle-o-left:before { content: "\f359"; } .fa.fa-caret-square-o-left { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-caret-square-o-left:before { content: "\f191"; } .fa.fa-toggle-left { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-toggle-left:before { content: "\f191"; } .fa.fa-dot-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-dot-circle-o:before { content: "\f192"; } .fa.fa-vimeo-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-vimeo-square:before { content: "\f194"; } .fa.fa-try:before { content: "\e2bb"; } .fa.fa-turkish-lira:before { content: "\e2bb"; } .fa.fa-plus-square-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-plus-square-o:before { content: "\f0fe"; } .fa.fa-slack { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wordpress { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-openid { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-institution:before { content: "\f19c"; } .fa.fa-bank:before { content: "\f19c"; } .fa.fa-mortar-board:before { content: "\f19d"; } .fa.fa-yahoo { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-reddit { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-reddit-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-reddit-square:before { content: "\f1a2"; } .fa.fa-stumbleupon-circle { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-stumbleupon { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-delicious { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-digg { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pied-piper-pp { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pied-piper-alt { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-drupal { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-joomla { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-behance { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-behance-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-behance-square:before { content: "\f1b5"; } .fa.fa-steam { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-steam-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-steam-square:before { content: "\f1b7"; } .fa.fa-automobile:before { content: "\f1b9"; } .fa.fa-cab:before { content: "\f1ba"; } .fa.fa-spotify { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-deviantart { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-soundcloud { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-file-pdf-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-pdf-o:before { content: "\f1c1"; } .fa.fa-file-word-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-word-o:before { content: "\f1c2"; } .fa.fa-file-excel-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-excel-o:before { content: "\f1c3"; } .fa.fa-file-powerpoint-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-powerpoint-o:before { content: "\f1c4"; } .fa.fa-file-image-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-image-o:before { content: "\f1c5"; } .fa.fa-file-photo-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-photo-o:before { content: "\f1c5"; } .fa.fa-file-picture-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-picture-o:before { content: "\f1c5"; } .fa.fa-file-archive-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-archive-o:before { content: "\f1c6"; } .fa.fa-file-zip-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-zip-o:before { content: "\f1c6"; } .fa.fa-file-audio-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-audio-o:before { content: "\f1c7"; } .fa.fa-file-sound-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-sound-o:before { content: "\f1c7"; } .fa.fa-file-video-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-video-o:before { content: "\f1c8"; } .fa.fa-file-movie-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-movie-o:before { content: "\f1c8"; } .fa.fa-file-code-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-file-code-o:before { content: "\f1c9"; } .fa.fa-vine { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-codepen { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-jsfiddle { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-life-bouy:before { content: "\f1cd"; } .fa.fa-life-buoy:before { content: "\f1cd"; } .fa.fa-life-saver:before { content: "\f1cd"; } .fa.fa-support:before { content: "\f1cd"; } .fa.fa-circle-o-notch:before { content: "\f1ce"; } .fa.fa-rebel { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-ra { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-ra:before { content: "\f1d0"; } .fa.fa-resistance { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-resistance:before { content: "\f1d0"; } .fa.fa-empire { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-ge { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-ge:before { content: "\f1d1"; } .fa.fa-git-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-git-square:before { content: "\f1d2"; } .fa.fa-git { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-hacker-news { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-y-combinator-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-y-combinator-square:before { content: "\f1d4"; } .fa.fa-yc-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-yc-square:before { content: "\f1d4"; } .fa.fa-tencent-weibo { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-qq { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-weixin { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wechat { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wechat:before { content: "\f1d7"; } .fa.fa-send:before { content: "\f1d8"; } .fa.fa-paper-plane-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-paper-plane-o:before { content: "\f1d8"; } .fa.fa-send-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-send-o:before { content: "\f1d8"; } .fa.fa-circle-thin { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-circle-thin:before { content: "\f111"; } .fa.fa-header:before { content: "\f1dc"; } .fa.fa-futbol-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-futbol-o:before { content: "\f1e3"; } .fa.fa-soccer-ball-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-soccer-ball-o:before { content: "\f1e3"; } .fa.fa-slideshare { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-twitch { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-yelp { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-newspaper-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-newspaper-o:before { content: "\f1ea"; } .fa.fa-paypal { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-wallet { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-visa { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-mastercard { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-discover { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-amex { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-paypal { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-stripe { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bell-slash-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-bell-slash-o:before { content: "\f1f6"; } .fa.fa-trash:before { content: "\f2ed"; } .fa.fa-copyright { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-eyedropper:before { content: "\f1fb"; } .fa.fa-area-chart:before { content: "\f1fe"; } .fa.fa-pie-chart:before { content: "\f200"; } .fa.fa-line-chart:before { content: "\f201"; } .fa.fa-lastfm { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-lastfm-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-lastfm-square:before { content: "\f203"; } .fa.fa-ioxhost { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-angellist { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-cc:before { content: "\f20a"; } .fa.fa-ils:before { content: "\f20b"; } .fa.fa-shekel:before { content: "\f20b"; } .fa.fa-sheqel:before { content: "\f20b"; } .fa.fa-buysellads { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-connectdevelop { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-dashcube { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-forumbee { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-leanpub { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-sellsy { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-shirtsinbulk { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-simplybuilt { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-skyatlas { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-diamond { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-diamond:before { content: "\f3a5"; } .fa.fa-transgender:before { content: "\f224"; } .fa.fa-intersex:before { content: "\f224"; } .fa.fa-transgender-alt:before { content: "\f225"; } .fa.fa-facebook-official { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-facebook-official:before { content: "\f09a"; } .fa.fa-pinterest-p { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-whatsapp { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-hotel:before { content: "\f236"; } .fa.fa-viacoin { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-medium { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-y-combinator { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-yc { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-yc:before { content: "\f23b"; } .fa.fa-optin-monster { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-opencart { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-expeditedssl { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-battery-4:before { content: "\f240"; } .fa.fa-battery:before { content: "\f240"; } .fa.fa-battery-3:before { content: "\f241"; } .fa.fa-battery-2:before { content: "\f242"; } .fa.fa-battery-1:before { content: "\f243"; } .fa.fa-battery-0:before { content: "\f244"; } .fa.fa-object-group { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-object-ungroup { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-sticky-note-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-sticky-note-o:before { content: "\f249"; } .fa.fa-cc-jcb { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-cc-diners-club { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-clone { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hourglass-o:before { content: "\f254"; } .fa.fa-hourglass-1:before { content: "\f251"; } .fa.fa-hourglass-2:before { content: "\f252"; } .fa.fa-hourglass-3:before { content: "\f253"; } .fa.fa-hand-rock-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-rock-o:before { content: "\f255"; } .fa.fa-hand-grab-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-grab-o:before { content: "\f255"; } .fa.fa-hand-paper-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-paper-o:before { content: "\f256"; } .fa.fa-hand-stop-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-stop-o:before { content: "\f256"; } .fa.fa-hand-scissors-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-scissors-o:before { content: "\f257"; } .fa.fa-hand-lizard-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-lizard-o:before { content: "\f258"; } .fa.fa-hand-spock-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-spock-o:before { content: "\f259"; } .fa.fa-hand-pointer-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-pointer-o:before { content: "\f25a"; } .fa.fa-hand-peace-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-hand-peace-o:before { content: "\f25b"; } .fa.fa-registered { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-creative-commons { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gg { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gg-circle { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-odnoklassniki { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-odnoklassniki-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-odnoklassniki-square:before { content: "\f264"; } .fa.fa-get-pocket { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wikipedia-w { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-safari { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-chrome { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-firefox { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-opera { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-internet-explorer { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-television:before { content: "\f26c"; } .fa.fa-contao { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-500px { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-amazon { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-calendar-plus-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-calendar-plus-o:before { content: "\f271"; } .fa.fa-calendar-minus-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-calendar-minus-o:before { content: "\f272"; } .fa.fa-calendar-times-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-calendar-times-o:before { content: "\f273"; } .fa.fa-calendar-check-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-calendar-check-o:before { content: "\f274"; } .fa.fa-map-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-map-o:before { content: "\f279"; } .fa.fa-commenting:before { content: "\f4ad"; } .fa.fa-commenting-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-commenting-o:before { content: "\f4ad"; } .fa.fa-houzz { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-vimeo { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-vimeo:before { content: "\f27d"; } .fa.fa-black-tie { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-fonticons { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-reddit-alien { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-edge { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-credit-card-alt:before { content: "\f09d"; } .fa.fa-codiepie { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-modx { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-fort-awesome { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-usb { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-product-hunt { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-mixcloud { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-scribd { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-pause-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-pause-circle-o:before { content: "\f28b"; } .fa.fa-stop-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-stop-circle-o:before { content: "\f28d"; } .fa.fa-bluetooth { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-bluetooth-b { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-gitlab { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wpbeginner { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wpforms { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-envira { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wheelchair-alt { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wheelchair-alt:before { content: "\f368"; } .fa.fa-question-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-question-circle-o:before { content: "\f059"; } .fa.fa-volume-control-phone:before { content: "\f2a0"; } .fa.fa-asl-interpreting:before { content: "\f2a3"; } .fa.fa-deafness:before { content: "\f2a4"; } .fa.fa-hard-of-hearing:before { content: "\f2a4"; } .fa.fa-glide { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-glide-g { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-signing:before { content: "\f2a7"; } .fa.fa-viadeo { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-viadeo-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-viadeo-square:before { content: "\f2aa"; } .fa.fa-snapchat { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-snapchat-ghost { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-snapchat-ghost:before { content: "\f2ab"; } .fa.fa-snapchat-square { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-snapchat-square:before { content: "\f2ad"; } .fa.fa-pied-piper { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-first-order { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-yoast { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-themeisle { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-plus-official { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-plus-official:before { content: "\f2b3"; } .fa.fa-google-plus-circle { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-google-plus-circle:before { content: "\f2b3"; } .fa.fa-font-awesome { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-fa { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-fa:before { content: "\f2b4"; } .fa.fa-handshake-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-handshake-o:before { content: "\f2b5"; } .fa.fa-envelope-open-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-envelope-open-o:before { content: "\f2b6"; } .fa.fa-linode { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-address-book-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-address-book-o:before { content: "\f2b9"; } .fa.fa-vcard:before { content: "\f2bb"; } .fa.fa-address-card-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-address-card-o:before { content: "\f2bb"; } .fa.fa-vcard-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-vcard-o:before { content: "\f2bb"; } .fa.fa-user-circle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-user-circle-o:before { content: "\f2bd"; } .fa.fa-user-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-user-o:before { content: "\f007"; } .fa.fa-id-badge { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-drivers-license:before { content: "\f2c2"; } .fa.fa-id-card-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-id-card-o:before { content: "\f2c2"; } .fa.fa-drivers-license-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-drivers-license-o:before { content: "\f2c2"; } .fa.fa-quora { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-free-code-camp { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-telegram { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-thermometer-4:before { content: "\f2c7"; } .fa.fa-thermometer:before { content: "\f2c7"; } .fa.fa-thermometer-3:before { content: "\f2c8"; } .fa.fa-thermometer-2:before { content: "\f2c9"; } .fa.fa-thermometer-1:before { content: "\f2ca"; } .fa.fa-thermometer-0:before { content: "\f2cb"; } .fa.fa-bathtub:before { content: "\f2cd"; } .fa.fa-s15:before { content: "\f2cd"; } .fa.fa-window-maximize { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-window-restore { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-times-rectangle:before { content: "\f410"; } .fa.fa-window-close-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-window-close-o:before { content: "\f410"; } .fa.fa-times-rectangle-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-times-rectangle-o:before { content: "\f410"; } .fa.fa-bandcamp { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-grav { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-etsy { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-imdb { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-ravelry { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-eercast { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-eercast:before { content: "\f2da"; } .fa.fa-snowflake-o { font-family: 'Font Awesome 6 Free'; font-weight: 400; } .fa.fa-snowflake-o:before { content: "\f2dc"; } .fa.fa-superpowers { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-wpexplorer { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } .fa.fa-meetup { font-family: 'Font Awesome 6 Brands'; font-weight: 400; } ================================================ FILE: docs/deps/jquery-3.6.0/jquery-3.6.0.js ================================================ /*! * jQuery JavaScript Library v3.6.0 * https://jquery.com/ * * Includes Sizzle.js * https://sizzlejs.com/ * * Copyright OpenJS Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2021-03-02T17:08Z */ ( function( global, factory ) { "use strict"; if ( typeof module === "object" && typeof module.exports === "object" ) { // For CommonJS and CommonJS-like environments where a proper `window` // is present, execute the factory and get jQuery. // For environments that do not have a `window` with a `document` // (such as Node.js), expose a factory as module.exports. // This accentuates the need for the creation of a real `window`. // e.g. var jQuery = require("jquery")(window); // See ticket #14549 for more info. module.exports = global.document ? factory( global, true ) : function( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }; } else { factory( global ); } // Pass this if window is not defined yet } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 // throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode // arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common // enough that all such attempts are guarded in a try block. "use strict"; var arr = []; var getProto = Object.getPrototypeOf; var slice = arr.slice; var flat = arr.flat ? function( array ) { return arr.flat.call( array ); } : function( array ) { return arr.concat.apply( [], array ); }; var push = arr.push; var indexOf = arr.indexOf; var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var fnToString = hasOwn.toString; var ObjectFunctionString = fnToString.call( Object ); var support = {}; var isFunction = function isFunction( obj ) { // Support: Chrome <=57, Firefox <=52 // In some browsers, typeof returns "function" for HTML elements // (i.e., `typeof document.createElement( "object" ) === "function"`). // We don't want to classify *any* DOM node as a function. // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 // Plus for old WebKit, typeof returns "function" for HTML collections // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) return typeof obj === "function" && typeof obj.nodeType !== "number" && typeof obj.item !== "function"; }; var isWindow = function isWindow( obj ) { return obj != null && obj === obj.window; }; var document = window.document; var preservedScriptAttributes = { type: true, src: true, nonce: true, noModule: true }; function DOMEval( code, node, doc ) { doc = doc || document; var i, val, script = doc.createElement( "script" ); script.text = code; if ( node ) { for ( i in preservedScriptAttributes ) { // Support: Firefox 64+, Edge 18+ // Some browsers don't support the "nonce" property on scripts. // On the other hand, just using `getAttribute` is not enough as // the `nonce` attribute is reset to an empty string whenever it // becomes browsing-context connected. // See https://github.com/whatwg/html/issues/2369 // See https://html.spec.whatwg.org/#nonce-attributes // The `node.getAttribute` check was added for the sake of // `jQuery.globalEval` so that it can fake a nonce-containing node // via an object. val = node[ i ] || node.getAttribute && node.getAttribute( i ); if ( val ) { script.setAttribute( i, val ); } } } doc.head.appendChild( script ).parentNode.removeChild( script ); } function toType( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android <=2.3 only (functionish RegExp) return typeof obj === "object" || typeof obj === "function" ? class2type[ toString.call( obj ) ] || "object" : typeof obj; } /* global Symbol */ // Defining this global in .eslintrc.json would create a danger of using the global // unguarded in another place, it seems safer to define global only for this module var version = "3.6.0", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // The default length of a jQuery object is 0 length: 0, toArray: function() { return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { // Return all the elements in a clean array if ( num == null ) { return slice.call( this ); } // Return just the one element from the set return num < 0 ? this[ num + this.length ] : this[ num ]; }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. each: function( callback ) { return jQuery.each( this, callback ); }, map: function( callback ) { return this.pushStack( jQuery.map( this, function( elem, i ) { return callback.call( elem, i, elem ); } ) ); }, slice: function() { return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, even: function() { return this.pushStack( jQuery.grep( this, function( _elem, i ) { return ( i + 1 ) % 2; } ) ); }, odd: function() { return this.pushStack( jQuery.grep( this, function( _elem, i ) { return i % 2; } ) ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { return this.prevObject || this.constructor(); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: arr.sort, splice: arr.splice }; jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !isFunction( target ) ) { target = {}; } // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( ( options = arguments[ i ] ) != null ) { // Extend the base object for ( name in options ) { copy = options[ name ]; // Prevent Object.prototype pollution // Prevent never-ending loop if ( name === "__proto__" || target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = Array.isArray( copy ) ) ) ) { src = target[ name ]; // Ensure proper type for the source value if ( copyIsArray && !Array.isArray( src ) ) { clone = []; } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { clone = {}; } else { clone = src; } copyIsArray = false; // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend( { // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module isReady: true, error: function( msg ) { throw new Error( msg ); }, noop: function() {}, isPlainObject: function( obj ) { var proto, Ctor; // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects if ( !obj || toString.call( obj ) !== "[object Object]" ) { return false; } proto = getProto( obj ); // Objects with no prototype (e.g., `Object.create( null )`) are plain if ( !proto ) { return true; } // Objects with prototype are plain iff they were constructed by a global Object function Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; }, isEmptyObject: function( obj ) { var name; for ( name in obj ) { return false; } return true; }, // Evaluates a script in a provided context; falls back to the global one // if not specified. globalEval: function( code, options, doc ) { DOMEval( code, { nonce: options && options.nonce }, doc ); }, each: function( obj, callback ) { var length, i = 0; if ( isArrayLike( obj ) ) { length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } else { for ( i in obj ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } return obj; }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { return arr == null ? -1 : indexOf.call( arr, elem, i ); }, // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } first.length = i; return first; }, grep: function( elems, callback, invert ) { var callbackInverse, matches = [], i = 0, length = elems.length, callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { callbackInverse = !callback( elems[ i ], i ); if ( callbackInverse !== callbackExpect ) { matches.push( elems[ i ] ); } } return matches; }, // arg is for internal usage only map: function( elems, callback, arg ) { var length, value, i = 0, ret = []; // Go through the array, translating each of the items to their new values if ( isArrayLike( elems ) ) { length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret.push( value ); } } } // Flatten any nested arrays return flat( ret ); }, // A global GUID counter for objects guid: 1, // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support } ); if ( typeof Symbol === "function" ) { jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; } // Populate the class2type map jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), function( _i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); } ); function isArrayLike( obj ) { // Support: real iOS 8.2 only (not reproducible in simulator) // `in` check used to prevent JIT error (gh-2145) // hasOwn isn't used here due to false negatives // regarding Nodelist length in IE var length = !!obj && "length" in obj && obj.length, type = toType( obj ); if ( isFunction( obj ) || isWindow( obj ) ) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } var Sizzle = /*! * Sizzle CSS Selector Engine v2.3.6 * https://sizzlejs.com/ * * Copyright JS Foundation and other contributors * Released under the MIT license * https://js.foundation/ * * Date: 2021-02-16 */ ( function( window ) { var i, support, Expr, getText, isXML, tokenize, compile, select, outermostContext, sortInput, hasDuplicate, // Local document vars setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data expando = "sizzle" + 1 * new Date(), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), nonnativeSelectorCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; }, // Instance methods hasOwn = ( {} ).hasOwnProperty, arr = [], pop = arr.pop, pushNative = arr.push, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native // https://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; for ( ; i < len; i++ ) { if ( list[ i ] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + "ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] // or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) ".*" + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rwhitespace = new RegExp( whitespace + "+", "g" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rdescend = new RegExp( whitespace + "|>" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + identifier + ")" ), "CLASS": new RegExp( "^\\.(" + identifier + ")" ), "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rhtml = /HTML$/i, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, // CSS escapes // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), funescape = function( escape, nonHex ) { var high = "0x" + escape.slice( 1 ) - 0x10000; return nonHex ? // Strip the backslash prefix from a non-hex escape sequence nonHex : // Replace a hexadecimal escape sequence with the encoded Unicode code point // Support: IE <=11+ // For values outside the Basic Multilingual Plane (BMP), manually construct a // surrogate pair high < 0 ? String.fromCharCode( high + 0x10000 ) : String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, // CSS string/identifier serialization // https://drafts.csswg.org/cssom/#common-serializing-idioms rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, fcssescape = function( ch, asCodePoint ) { if ( asCodePoint ) { // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER if ( ch === "\0" ) { return "\uFFFD"; } // Control characters and (dependent upon position) numbers get escaped as code points return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; } // Other potentially-special ASCII characters get backslash-escaped return "\\" + ch; }, // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); }, inDisabledFieldset = addCombinator( function( elem ) { return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; }, { dir: "parentNode", next: "legend" } ); // Optimize for push.apply( _, NodeList ) try { push.apply( ( arr = slice.call( preferredDoc.childNodes ) ), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply // eslint-disable-next-line no-unused-expressions arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? // Leverage slice if possible function( target, els ) { pushNative.apply( target, slice.call( els ) ); } : // Support: IE<9 // Otherwise append directly function( target, els ) { var j = target.length, i = 0; // Can't trust NodeList.length while ( ( target[ j++ ] = els[ i++ ] ) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document nodeType = context ? context.nodeType : 9; results = results || []; // Return early from calls with invalid selector or context if ( typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { return results; } // Try to shortcut find operations (as opposed to filters) in HTML documents if ( !seed ) { setDocument( context ); context = context || document; if ( documentIsHTML ) { // If the selector is sufficiently simple, try using a "get*By*" DOM method // (excepting DocumentFragment context, where the methods don't exist) if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { // ID selector if ( ( m = match[ 1 ] ) ) { // Document context if ( nodeType === 9 ) { if ( ( elem = context.getElementById( m ) ) ) { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } // Element context } else { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID if ( newContext && ( elem = newContext.getElementById( m ) ) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Type selector } else if ( match[ 2 ] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Class selector } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // Take advantage of querySelectorAll if ( support.qsa && !nonnativeSelectorCache[ selector + " " ] && ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && // Support: IE 8 only // Exclude object elements ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { newSelector = selector; newContext = context; // qSA considers elements outside a scoping root when evaluating child or // descendant combinators, which is not what we want. // In such cases, we work around the behavior by prefixing every selector in the // list with an ID selector referencing the scope context. // The technique has to be used as well when a leading combinator is used // as such selectors are not recognized by querySelectorAll. // Thanks to Andrew Dupont for this technique. if ( nodeType === 1 && ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { // Expand context for sibling selectors newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; // We can use :scope instead of the ID hack if the browser // supports it & if we're not changing the context. if ( newContext !== context || !support.scope ) { // Capture the context ID, setting it first if necessary if ( ( nid = context.getAttribute( "id" ) ) ) { nid = nid.replace( rcssescape, fcssescape ); } else { context.setAttribute( "id", ( nid = expando ) ); } } // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; while ( i-- ) { groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + toSelector( groups[ i ] ); } newSelector = groups.join( "," ); } try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch ( qsaError ) { nonnativeSelectorCache( selector, true ); } finally { if ( nid === expando ) { context.removeAttribute( "id" ); } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return ( cache[ key + " " ] = value ); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { var el = document.createElement( "fieldset" ); try { return !!fn( el ); } catch ( e ) { return false; } finally { // Remove from its parent by default if ( el.parentNode ) { el.parentNode.removeChild( el ); } // release memory in IE el = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { var arr = attrs.split( "|" ), i = arr.length; while ( i-- ) { Expr.attrHandle[ arr[ i ] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( ( cur = cur.nextSibling ) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return ( name === "input" || name === "button" ) && elem.type === type; }; } /** * Returns a function to use in pseudos for :enabled/:disabled * @param {Boolean} disabled true for :disabled; false for :enabled */ function createDisabledPseudo( disabled ) { // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable return function( elem ) { // Only certain elements can match :enabled or :disabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled if ( "form" in elem ) { // Check for inherited disabledness on relevant non-disabled elements: // * listed form-associated elements in a disabled fieldset // https://html.spec.whatwg.org/multipage/forms.html#category-listed // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled // * option elements in a disabled optgroup // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled // All such elements have a "form" property. if ( elem.parentNode && elem.disabled === false ) { // Option elements defer to a parent optgroup if present if ( "label" in elem ) { if ( "label" in elem.parentNode ) { return elem.parentNode.disabled === disabled; } else { return elem.disabled === disabled; } } // Support: IE 6 - 11 // Use the isDisabled shortcut property to check for disabled fieldset ancestors return elem.isDisabled === disabled || // Where there is no isDisabled, check manually /* jshint -W018 */ elem.isDisabled !== !disabled && inDisabledFieldset( elem ) === disabled; } return elem.disabled === disabled; // Try to winnow out elements that can't be disabled before trusting the disabled property. // Some victims get caught in our net (label, legend, menu, track), but it shouldn't // even exist on them, let alone have a boolean value. } else if ( "label" in elem ) { return elem.disabled === disabled; } // Remaining elements are neither :enabled nor :disabled return false; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction( function( argument ) { argument = +argument; return markFunction( function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ ( j = matchIndexes[ i ] ) ] ) { seed[ j ] = !( matches[ j ] = seed[ j ] ); } } } ); } ); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { var namespace = elem && elem.namespaceURI, docElem = elem && ( elem.ownerDocument || elem ).documentElement; // Support: IE <=8 // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes // https://bugs.jquery.com/ticket/4833 return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Update global variables document = doc; docElem = document.documentElement; documentIsHTML = !isXML( document ); // Support: IE 9 - 11+, Edge 12 - 18+ // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( preferredDoc != document && ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { // Support: IE 11, Edge if ( subWindow.addEventListener ) { subWindow.addEventListener( "unload", unloadHandler, false ); // Support: IE 9 - 10 only } else if ( subWindow.attachEvent ) { subWindow.attachEvent( "onunload", unloadHandler ); } } // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, // Safari 4 - 5 only, Opera <=11.6 - 12.x only // IE/Edge & older browsers don't support the :scope pseudo-class. // Support: Safari 6.0 only // Safari 6.0 supports :scope but it's an alias of :root there. support.scope = assert( function( el ) { docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); return typeof el.querySelectorAll !== "undefined" && !el.querySelectorAll( ":scope fieldset div" ).length; } ); /* Attributes ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // (excepting IE8 booleans) support.attributes = assert( function( el ) { el.className = "i"; return !el.getAttribute( "className" ); } ); /* getElement(s)By* ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements support.getElementsByTagName = assert( function( el ) { el.appendChild( document.createComment( "" ) ); return !el.getElementsByTagName( "*" ).length; } ); // Support: IE<9 support.getElementsByClassName = rnative.test( document.getElementsByClassName ); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programmatically-set names, // so use a roundabout getElementsByName test support.getById = assert( function( el ) { docElem.appendChild( el ).id = expando; return !document.getElementsByName || !document.getElementsByName( expando ).length; } ); // ID filter and find if ( support.getById ) { Expr.filter[ "ID" ] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute( "id" ) === attrId; }; }; Expr.find[ "ID" ] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var elem = context.getElementById( id ); return elem ? [ elem ] : []; } }; } else { Expr.filter[ "ID" ] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode( "id" ); return node && node.value === attrId; }; }; // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut Expr.find[ "ID" ] = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var node, i, elems, elem = context.getElementById( id ); if ( elem ) { // Verify the id attribute node = elem.getAttributeNode( "id" ); if ( node && node.value === id ) { return [ elem ]; } // Fall back on getElementsByName elems = context.getElementsByName( id ); i = 0; while ( ( elem = elems[ i++ ] ) ) { node = elem.getAttributeNode( "id" ); if ( node && node.value === id ) { return [ elem ]; } } } return []; } }; } // Tag Expr.find[ "TAG" ] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== "undefined" ) { return context.getElementsByTagName( tag ); // DocumentFragment nodes don't have gEBTN } else if ( support.qsa ) { return context.querySelectorAll( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( ( elem = results[ i++ ] ) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Class Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert( function( el ) { var input; // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // https://bugs.jquery.com/ticket/12359 docElem.appendChild( el ).innerHTML = "" + ""; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } // Support: IE8 // Boolean attributes and "value" are not treated correctly if ( !el.querySelectorAll( "[selected]" ).length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { rbuggyQSA.push( "~=" ); } // Support: IE 11+, Edge 15 - 18+ // IE 11/Edge don't find elements on a `[name='']` query in some cases. // Adding a temporary attribute to the document before the selection works // around the issue. // Interestingly, IE 10 & older don't seem to have the issue. input = document.createElement( "input" ); input.setAttribute( "name", "" ); el.appendChild( input ); if ( !el.querySelectorAll( "[name='']" ).length ) { rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + whitespace + "*(?:''|\"\")" ); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !el.querySelectorAll( ":checked" ).length ) { rbuggyQSA.push( ":checked" ); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 // In-page `selector#id sibling-combinator selector` fails if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { rbuggyQSA.push( ".#.+[+~]" ); } // Support: Firefox <=3.6 - 5 only // Old Firefox doesn't throw on a badly-escaped identifier. el.querySelectorAll( "\\\f" ); rbuggyQSA.push( "[\\r\\n\\f]" ); } ); assert( function( el ) { el.innerHTML = "" + ""; // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = document.createElement( "input" ); input.setAttribute( "type", "hidden" ); el.appendChild( input ).setAttribute( "name", "D" ); // Support: IE8 // Enforce case-sensitivity of name attribute if ( el.querySelectorAll( "[name=d]" ).length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Support: IE9-11+ // IE's :disabled selector does not pick up the children of disabled fieldsets docElem.appendChild( el ).disabled = true; if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Support: Opera 10 - 11 only // Opera 10-11 does not throw on post-comma invalid pseudos el.querySelectorAll( "*,:x" ); rbuggyQSA.push( ",.*:" ); } ); } if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector ) ) ) ) { assert( function( el ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( el, "*" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( el, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); } ); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully self-exclusive // As in, an element does not contain itself contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 ) ); } : function( a, b ) { if ( b ) { while ( ( b = b.parentNode ) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal if ( a === b ) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } // Calculate position if both inputs belong to the same document // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : // Otherwise we know they are disconnected 1; // Disconnected nodes if ( compare & 1 || ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { // Choose the first element that is related to our preferred document // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( a == document || a.ownerDocument == preferredDoc && contains( preferredDoc, a ) ) { return -1; } // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( b == document || b.ownerDocument == preferredDoc && contains( preferredDoc, b ) ) { return 1; } // Maintain original order return sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } : function( a, b ) { // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Parentless nodes are either documents or disconnected if ( !aup || !bup ) { // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. /* eslint-disable eqeqeq */ return a == document ? -1 : b == document ? 1 : /* eslint-enable eqeqeq */ aup ? -1 : bup ? 1 : sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( ( cur = cur.parentNode ) ) { ap.unshift( cur ); } cur = b; while ( ( cur = cur.parentNode ) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[ i ] === bp[ i ] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[ i ], bp[ i ] ) : // Otherwise nodes in our document sort first // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. /* eslint-disable eqeqeq */ ap[ i ] == preferredDoc ? -1 : bp[ i ] == preferredDoc ? 1 : /* eslint-enable eqeqeq */ 0; }; return document; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { setDocument( elem ); if ( support.matchesSelector && documentIsHTML && !nonnativeSelectorCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch ( e ) { nonnativeSelectorCache( expr, true ); } } return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( ( context.ownerDocument || context ) != document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( ( elem.ownerDocument || elem ) != document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], // Don't get fooled by Object.prototype properties (jQuery #13807) val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : ( val = elem.getAttributeNode( name ) ) && val.specified ? val.value : null; }; Sizzle.escape = function( sel ) { return ( sel + "" ).replace( rcssescape, fcssescape ); }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( ( elem = results[ i++ ] ) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array while ( ( node = elem[ i++ ] ) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[ 1 ] = match[ 1 ].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ).replace( runescape, funescape ); if ( match[ 2 ] === "~=" ) { match[ 3 ] = " " + match[ 3 ] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[ 1 ] = match[ 1 ].toLowerCase(); if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[ 3 ] ) { Sizzle.error( match[ 0 ] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[ 4 ] = +( match[ 4 ] ? match[ 5 ] + ( match[ 6 ] || 1 ) : 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); // other types prohibit arguments } else if ( match[ 3 ] ) { Sizzle.error( match[ 0 ] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[ 6 ] && match[ 2 ]; if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { return null; } // Accept quoted arguments as-is if ( match[ 3 ] ) { match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) ( excess = tokenize( unquoted, true ) ) && // advance to the next closing parenthesis ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { // excess is a negative index match[ 0 ] = match[ 0 ].slice( 0, excess ); match[ 2 ] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || ( pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute( "class" ) || "" ); } ); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; /* eslint-disable max-len */ return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; /* eslint-enable max-len */ }; }, "CHILD": function( type, what, _argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, _context, xml ) { var cache, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType, diff = false; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( ( node = node[ dir ] ) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index // ...in a gzip-friendly way node = parent; outerCache = node[ expando ] || ( node[ expando ] = {} ); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || ( outerCache[ node.uniqueID ] = {} ); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( ( node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start ( diff = nodeIndex = 0 ) || start.pop() ) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } } else { // Use previously-cached element index if available if ( useCache ) { // ...in a gzip-friendly way node = elem; outerCache = node[ expando ] || ( node[ expando ] = {} ); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || ( outerCache[ node.uniqueID ] = {} ); cache = uniqueCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex; } // xml :nth-child(...) // or :nth-last-child(...) or :nth(-last)?-of-type(...) if ( diff === false ) { // Use the same loop as above to seek `elem` from the start while ( ( node = ++nodeIndex && node && node[ dir ] || ( diff = nodeIndex = 0 ) || start.pop() ) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { outerCache = node[ expando ] || ( node[ expando ] = {} ); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ node.uniqueID ] || ( outerCache[ node.uniqueID ] = {} ); uniqueCache[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction( function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf( seed, matched[ i ] ); seed[ idx ] = !( matches[ idx ] = matched[ i ] ); } } ) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction( function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction( function( seed, matches, _context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( ( elem = unmatched[ i ] ) ) { seed[ i ] = !( matches[ i ] = elem ); } } } ) : function( elem, _context, xml ) { input[ 0 ] = elem; matcher( input, null, xml, results ); // Don't keep the element (issue #299) input[ 0 ] = null; return !results.pop(); }; } ), "has": markFunction( function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; } ), "contains": markFunction( function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; }; } ), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test( lang || "" ) ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( ( elemLang = documentIsHTML ? elem.lang : elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); return false; }; } ), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && ( !document.hasFocus || document.hasFocus() ) && !!( elem.type || elem.href || ~elem.tabIndex ); }, // Boolean properties "enabled": createDisabledPseudo( false ), "disabled": createDisabledPseudo( true ), "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return ( nodeName === "input" && !!elem.checked ) || ( nodeName === "option" && !!elem.selected ); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { // eslint-disable-next-line no-unused-expressions elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeType < 6 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos[ "empty" ]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ( ( attr = elem.getAttribute( "type" ) ) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection "first": createPositionalPseudo( function() { return [ 0 ]; } ), "last": createPositionalPseudo( function( _matchIndexes, length ) { return [ length - 1 ]; } ), "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; } ), "even": createPositionalPseudo( function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; } ), "odd": createPositionalPseudo( function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; } ), "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument > length ? length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; } ), "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; } ) } }; Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } // Easy API for creating new setFilters function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || ( match = rcomma.exec( soFar ) ) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[ 0 ].length ) || soFar; } groups.push( ( tokens = [] ) ); } matched = false; // Combinators if ( ( match = rcombinators.exec( soFar ) ) ) { matched = match.shift(); tokens.push( { value: matched, // Cast descendant combinators to space type: match[ 0 ].replace( rtrim, " " ) } ); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || ( match = preFilters[ type ]( match ) ) ) ) { matched = match.shift(); tokens.push( { value: matched, type: type, matches: match } ); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); }; function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[ i ].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, skip = combinator.next, key = skip || dir, checkNonElements = base && key === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( ( elem = elem[ dir ] ) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } return false; } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var oldCache, uniqueCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if ( xml ) { while ( ( elem = elem[ dir ] ) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( ( elem = elem[ dir ] ) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || ( elem[ expando ] = {} ); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[ elem.uniqueID ] || ( outerCache[ elem.uniqueID ] = {} ); if ( skip && skip === elem.nodeName.toLowerCase() ) { elem = elem[ dir ] || elem; } else if ( ( oldCache = uniqueCache[ key ] ) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements return ( newCache[ 2 ] = oldCache[ 2 ] ); } else { // Reuse newcache so results back-propagate to previous elements uniqueCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { return true; } } } } } return false; }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[ i ]( elem, context, xml ) ) { return false; } } return true; } : matchers[ 0 ]; } function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[ i ], results ); } return results; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( ( elem = unmatched[ i ] ) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction( function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( ( elem = temp[ i ] ) ) { matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( ( elem = matcherOut[ i ] ) ) { // Restore matcherIn since elem is not yet a final match temp.push( ( matcherIn[ i ] = elem ) ); } } postFinder( null, ( matcherOut = [] ), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( ( elem = matcherOut[ i ] ) && ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { seed[ temp ] = !( results[ temp ] = elem ); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } } ); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[ 0 ].type ], implicitRelative = leadingRelative || Expr.relative[ " " ], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( ( checkContext = context ).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); // Avoid hanging onto element (issue #299) checkContext = null; return ret; } ]; for ( ; i < len; i++ ) { if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; } else { matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[ j ].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` tokens .slice( 0, i - 1 ) .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), len = elems.length; if ( outermost ) { // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq outermostContext = context == document || context || outermost; } // Add elements passing elementMatchers directly to results // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { if ( byElement && elem ) { j = 0; // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq if ( !context && elem.ownerDocument != document ) { setDocument( elem ); xml = !documentIsHTML; } while ( ( matcher = elementMatchers[ j++ ] ) ) { if ( matcher( elem, context || document, xml ) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( ( elem = !matcher && elem ) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // `i` is now the count of elements visited above, and adding it to `matchedCount` // makes the latter nonnegative. matchedCount += i; // Apply set filters to unmatched elements // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` // equals `i`), unless we didn't visit _any_ elements in the above loop because we have // no element matchers and no seed. // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that // case, which will result in a "00" `matchedCount` that differs from `i` but is also // numerically zero. if ( bySet && i !== matchedCount ) { j = 0; while ( ( matcher = setMatchers[ j++ ] ) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !( unmatched[ i ] || setMatched[ i ] ) ) { setMatched[ i ] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !match ) { match = tokenize( selector ); } i = match.length; while ( i-- ) { cached = matcherFromTokens( match[ i ] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); // Save selector and tokenization cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( ( selector = compiled.selector || selector ) ); results = results || []; // Try to minimize operations if there is only one selector in the list and no seed // (the latter of which guarantees us context) if ( match.length === 1 ) { // Reduce context if the leading compound selector is an ID tokens = match[ 0 ] = match[ 0 ].slice( 0 ); if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { context = ( Expr.find[ "ID" ]( token.matches[ 0 ] .replace( runescape, funescape ), context ) || [] )[ 0 ]; if ( !context ) { return results; // Precompiled matchers will still verify ancestry, so step up a level } else if ( compiled ) { context = context.parentNode; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[ i ]; // Abort if we hit a combinator if ( Expr.relative[ ( type = token.type ) ] ) { break; } if ( ( find = Expr.find[ type ] ) ) { // Search, expanding context for leading sibling combinators if ( ( seed = find( token.matches[ 0 ].replace( runescape, funescape ), rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || context ) ) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; // One-time assignments // Sort stability support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; // Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* support.sortDetached = assert( function( el ) { // Should return 1, but returns 4 (following) return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; } ); // Support: IE<8 // Prevent attribute/property "interpolation" // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !assert( function( el ) { el.innerHTML = ""; return el.firstChild.getAttribute( "href" ) === "#"; } ) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } } ); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") if ( !support.attributes || !assert( function( el ) { el.innerHTML = ""; el.firstChild.setAttribute( "value", "" ); return el.firstChild.getAttribute( "value" ) === ""; } ) ) { addHandle( "value", function( elem, _name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } } ); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies if ( !assert( function( el ) { return el.getAttribute( "disabled" ) == null; } ) ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { return elem[ name ] === true ? name.toLowerCase() : ( val = elem.getAttributeNode( name ) ) && val.specified ? val.value : null; } } ); } return Sizzle; } )( window ); jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; // Deprecated jQuery.expr[ ":" ] = jQuery.expr.pseudos; jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; jQuery.escapeSelector = Sizzle.escape; var dir = function( elem, dir, until ) { var matched = [], truncate = until !== undefined; while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { if ( elem.nodeType === 1 ) { if ( truncate && jQuery( elem ).is( until ) ) { break; } matched.push( elem ); } } return matched; }; var siblings = function( n, elem ) { var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { matched.push( n ); } } return matched; }; var rneedsContext = jQuery.expr.match.needsContext; function nodeName( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); } var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); // Implement the identical functionality for filter and not function winnow( elements, qualifier, not ) { if ( isFunction( qualifier ) ) { return jQuery.grep( elements, function( elem, i ) { return !!qualifier.call( elem, i, elem ) !== not; } ); } // Single element if ( qualifier.nodeType ) { return jQuery.grep( elements, function( elem ) { return ( elem === qualifier ) !== not; } ); } // Arraylike of elements (jQuery, arguments, Array) if ( typeof qualifier !== "string" ) { return jQuery.grep( elements, function( elem ) { return ( indexOf.call( qualifier, elem ) > -1 ) !== not; } ); } // Filtered directly for both simple and complex selectors return jQuery.filter( qualifier, elements, not ); } jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } if ( elems.length === 1 && elem.nodeType === 1 ) { return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; } ) ); }; jQuery.fn.extend( { find: function( selector ) { var i, ret, len = this.length, self = this; if ( typeof selector !== "string" ) { return this.pushStack( jQuery( selector ).filter( function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } } ) ); } ret = this.pushStack( [] ); for ( i = 0; i < len; i++ ) { jQuery.find( selector, self[ i ], ret ); } return len > 1 ? jQuery.uniqueSort( ret ) : ret; }, filter: function( selector ) { return this.pushStack( winnow( this, selector || [], false ) ); }, not: function( selector ) { return this.pushStack( winnow( this, selector || [], true ) ); }, is: function( selector ) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". typeof selector === "string" && rneedsContext.test( selector ) ? jQuery( selector ) : selector || [], false ).length; } } ); // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[ 2 ] ); if ( elem ) { // Inject the element directly into the jQuery object this[ 0 ] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( isFunction( selector ) ) { return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this ); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend( { has: function( target ) { var targets = jQuery( target, this ), l = targets.length; return this.filter( function() { var i = 0; for ( ; i < l; i++ ) { if ( jQuery.contains( this, targets[ i ] ) ) { return true; } } } ); }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, matched = [], targets = typeof selectors !== "string" && jQuery( selectors ); // Positional selectors never match, since there's no _selection_ context if ( !rneedsContext.test( selectors ) ) { for ( ; i < l; i++ ) { for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { // Always skip document fragments if ( cur.nodeType < 11 && ( targets ? targets.index( cur ) > -1 : // Don't pass non-elements to Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector( cur, selectors ) ) ) { matched.push( cur ); break; } } } } return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); }, // Determine the position of an element within the set index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; } // Index in selector if ( typeof elem === "string" ) { return indexOf.call( jQuery( elem ), this[ 0 ] ); } // Locate the position of the desired element return indexOf.call( this, // If it receives a jQuery object, the first element is used elem.jquery ? elem[ 0 ] : elem ); }, add: function( selector, context ) { return this.pushStack( jQuery.uniqueSort( jQuery.merge( this.get(), jQuery( selector, context ) ) ) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter( selector ) ); } } ); function sibling( cur, dir ) { while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return dir( elem, "parentNode" ); }, parentsUntil: function( elem, _i, until ) { return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return dir( elem, "previousSibling" ); }, nextUntil: function( elem, _i, until ) { return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, _i, until ) { return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return siblings( elem.firstChild ); }, contents: function( elem ) { if ( elem.contentDocument != null && // Support: IE 11+ // elements with no `data` attribute has an object // `contentDocument` with a `null` prototype. getProto( elem.contentDocument ) ) { return elem.contentDocument; } // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only // Treat the template element as a regular one in browsers that // don't support it. if ( nodeName( elem, "template" ) ) { elem = elem.content || elem; } return jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { matched.reverse(); } } return this.pushStack( matched ); }; } ); var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); // Convert String-formatted options into Object-formatted ones function createOptions( options ) { var object = {}; jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { object[ flag ] = true; } ); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? createOptions( options ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value for non-forgettable lists memory, // Flag to know if list was already fired fired, // Flag to prevent firing locked, // Actual callback list list = [], // Queue of execution data for repeatable lists queue = [], // Index of currently firing callback (modified by add/remove as needed) firingIndex = -1, // Fire callbacks fire = function() { // Enforce single-firing locked = locked || options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes fired = firing = true; for ( ; queue.length; firingIndex = -1 ) { memory = queue.shift(); while ( ++firingIndex < list.length ) { // Run callback and check for early termination if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && options.stopOnFalse ) { // Jump to end and forget the data so .add doesn't re-fire firingIndex = list.length; memory = false; } } } // Forget the data if we're done with it if ( !options.memory ) { memory = false; } firing = false; // Clean up if we're done firing for good if ( locked ) { // Keep an empty list if we have data for future add calls if ( memory ) { list = []; // Otherwise, this object is spent } else { list = ""; } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // If we have memory from a past run, we should fire after adding if ( memory && !firing ) { firingIndex = list.length - 1; queue.push( memory ); } ( function add( args ) { jQuery.each( args, function( _, arg ) { if ( isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && toType( arg ) !== "string" ) { // Inspect recursively add( arg ); } } ); } )( arguments ); if ( memory && !firing ) { fire(); } } return this; }, // Remove a callback from the list remove: function() { jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( index <= firingIndex ) { firingIndex--; } } } ); return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : list.length > 0; }, // Remove all callbacks from the list empty: function() { if ( list ) { list = []; } return this; }, // Disable .fire and .add // Abort any current/pending executions // Clear all callbacks and values disable: function() { locked = queue = []; list = memory = ""; return this; }, disabled: function() { return !list; }, // Disable .fire // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions lock: function() { locked = queue = []; if ( !memory && !firing ) { list = memory = ""; } return this; }, locked: function() { return !!locked; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { if ( !locked ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; queue.push( args ); if ( !firing ) { fire(); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; function Identity( v ) { return v; } function Thrower( ex ) { throw ex; } function adoptValue( value, resolve, reject, noValue ) { var method; try { // Check for promise aspect first to privilege synchronous behavior if ( value && isFunction( ( method = value.promise ) ) ) { method.call( value ).done( resolve ).fail( reject ); // Other thenables } else if ( value && isFunction( ( method = value.then ) ) ) { method.call( value, resolve, reject ); // Other non-thenables } else { // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: // * false: [ value ].slice( 0 ) => resolve( value ) // * true: [ value ].slice( 1 ) => resolve() resolve.apply( undefined, [ value ].slice( noValue ) ); } // For Promises/A+, convert exceptions into rejections // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in // Deferred#then to conditionally suppress rejection. } catch ( value ) { // Support: Android 4.0 only // Strict mode functions invoked without .call/.apply get global-object context reject.apply( undefined, [ value ] ); } } jQuery.extend( { Deferred: function( func ) { var tuples = [ // action, add listener, callbacks, // ... .then handlers, argument index, [final state] [ "notify", "progress", jQuery.Callbacks( "memory" ), jQuery.Callbacks( "memory" ), 2 ], [ "resolve", "done", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 0, "resolved" ], [ "reject", "fail", jQuery.Callbacks( "once memory" ), jQuery.Callbacks( "once memory" ), 1, "rejected" ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, "catch": function( fn ) { return promise.then( null, fn ); }, // Keep pipe for back-compat pipe: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred( function( newDefer ) { jQuery.each( tuples, function( _i, tuple ) { // Map tuples (progress, done, fail) to arguments (done, fail, progress) var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; // deferred.progress(function() { bind to newDefer or newDefer.notify }) // deferred.done(function() { bind to newDefer or newDefer.resolve }) // deferred.fail(function() { bind to newDefer or newDefer.reject }) deferred[ tuple[ 1 ] ]( function() { var returned = fn && fn.apply( this, arguments ); if ( returned && isFunction( returned.promise ) ) { returned.promise() .progress( newDefer.notify ) .done( newDefer.resolve ) .fail( newDefer.reject ); } else { newDefer[ tuple[ 0 ] + "With" ]( this, fn ? [ returned ] : arguments ); } } ); } ); fns = null; } ).promise(); }, then: function( onFulfilled, onRejected, onProgress ) { var maxDepth = 0; function resolve( depth, deferred, handler, special ) { return function() { var that = this, args = arguments, mightThrow = function() { var returned, then; // Support: Promises/A+ section 2.3.3.3.3 // https://promisesaplus.com/#point-59 // Ignore double-resolution attempts if ( depth < maxDepth ) { return; } returned = handler.apply( that, args ); // Support: Promises/A+ section 2.3.1 // https://promisesaplus.com/#point-48 if ( returned === deferred.promise() ) { throw new TypeError( "Thenable self-resolution" ); } // Support: Promises/A+ sections 2.3.3.1, 3.5 // https://promisesaplus.com/#point-54 // https://promisesaplus.com/#point-75 // Retrieve `then` only once then = returned && // Support: Promises/A+ section 2.3.4 // https://promisesaplus.com/#point-64 // Only check objects and functions for thenability ( typeof returned === "object" || typeof returned === "function" ) && returned.then; // Handle a returned thenable if ( isFunction( then ) ) { // Special processors (notify) just wait for resolution if ( special ) { then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ) ); // Normal processors (resolve) also hook into progress } else { // ...and disregard older resolution values maxDepth++; then.call( returned, resolve( maxDepth, deferred, Identity, special ), resolve( maxDepth, deferred, Thrower, special ), resolve( maxDepth, deferred, Identity, deferred.notifyWith ) ); } // Handle all other returned values } else { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Identity ) { that = undefined; args = [ returned ]; } // Process the value(s) // Default process is resolve ( special || deferred.resolveWith )( that, args ); } }, // Only normal processors (resolve) catch and reject exceptions process = special ? mightThrow : function() { try { mightThrow(); } catch ( e ) { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, process.stackTrace ); } // Support: Promises/A+ section 2.3.3.3.4.1 // https://promisesaplus.com/#point-61 // Ignore post-resolution exceptions if ( depth + 1 >= maxDepth ) { // Only substitute handlers pass on context // and multiple values (non-spec behavior) if ( handler !== Thrower ) { that = undefined; args = [ e ]; } deferred.rejectWith( that, args ); } } }; // Support: Promises/A+ section 2.3.3.3.1 // https://promisesaplus.com/#point-57 // Re-resolve promises immediately to dodge false rejection from // subsequent errors if ( depth ) { process(); } else { // Call an optional hook to record the stack, in case of exception // since it's otherwise lost when execution goes async if ( jQuery.Deferred.getStackHook ) { process.stackTrace = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } }; } return jQuery.Deferred( function( newDefer ) { // progress_handlers.add( ... ) tuples[ 0 ][ 3 ].add( resolve( 0, newDefer, isFunction( onProgress ) ? onProgress : Identity, newDefer.notifyWith ) ); // fulfilled_handlers.add( ... ) tuples[ 1 ][ 3 ].add( resolve( 0, newDefer, isFunction( onFulfilled ) ? onFulfilled : Identity ) ); // rejected_handlers.add( ... ) tuples[ 2 ][ 3 ].add( resolve( 0, newDefer, isFunction( onRejected ) ? onRejected : Thrower ) ); } ).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 5 ]; // promise.progress = list.add // promise.done = list.add // promise.fail = list.add promise[ tuple[ 1 ] ] = list.add; // Handle state if ( stateString ) { list.add( function() { // state = "resolved" (i.e., fulfilled) // state = "rejected" state = stateString; }, // rejected_callbacks.disable // fulfilled_callbacks.disable tuples[ 3 - i ][ 2 ].disable, // rejected_handlers.disable // fulfilled_handlers.disable tuples[ 3 - i ][ 3 ].disable, // progress_callbacks.lock tuples[ 0 ][ 2 ].lock, // progress_handlers.lock tuples[ 0 ][ 3 ].lock ); } // progress_handlers.fire // fulfilled_handlers.fire // rejected_handlers.fire list.add( tuple[ 3 ].fire ); // deferred.notify = function() { deferred.notifyWith(...) } // deferred.resolve = function() { deferred.resolveWith(...) } // deferred.reject = function() { deferred.rejectWith(...) } deferred[ tuple[ 0 ] ] = function() { deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); return this; }; // deferred.notifyWith = list.fireWith // deferred.resolveWith = list.fireWith // deferred.rejectWith = list.fireWith deferred[ tuple[ 0 ] + "With" ] = list.fireWith; } ); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( singleValue ) { var // count of uncompleted subordinates remaining = arguments.length, // count of unprocessed arguments i = remaining, // subordinate fulfillment data resolveContexts = Array( i ), resolveValues = slice.call( arguments ), // the primary Deferred primary = jQuery.Deferred(), // subordinate callback factory updateFunc = function( i ) { return function( value ) { resolveContexts[ i ] = this; resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; if ( !( --remaining ) ) { primary.resolveWith( resolveContexts, resolveValues ); } }; }; // Single- and empty arguments are adopted like Promise.resolve if ( remaining <= 1 ) { adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, !remaining ); // Use .then() to unwrap secondary thenables (cf. gh-3000) if ( primary.state() === "pending" || isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { return primary.then(); } } // Multiple arguments are aggregated like Promise.all array elements while ( i-- ) { adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); } return primary.promise(); } } ); // These usually indicate a programmer mistake during development, // warn about them ASAP rather than swallowing them by default. var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; jQuery.Deferred.exceptionHook = function( error, stack ) { // Support: IE 8 - 9 only // Console exists when dev tools are open, which can happen at any time if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); } }; jQuery.readyException = function( error ) { window.setTimeout( function() { throw error; } ); }; // The deferred used on DOM ready var readyList = jQuery.Deferred(); jQuery.fn.ready = function( fn ) { readyList .then( fn ) // Wrap jQuery.readyException in a function so that the lookup // happens at the time of error handling instead of callback // registration. .catch( function( error ) { jQuery.readyException( error ); } ); return this; }; jQuery.extend( { // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); } } ); jQuery.ready.then = readyList.then; // The ready event handler and self cleanup method function completed() { document.removeEventListener( "DOMContentLoaded", completed ); window.removeEventListener( "load", completed ); jQuery.ready(); } // Catch cases where $(document).ready() is called // after the browser event has already occurred. // Support: IE <=9 - 10 only // Older IE sometimes signals "interactive" too soon if ( document.readyState === "complete" || ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { // Handle it asynchronously to allow scripts the opportunity to delay ready window.setTimeout( jQuery.ready ); } else { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed ); } // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, len = elems.length, bulk = key == null; // Sets many values if ( toType( key ) === "object" ) { chainable = true; for ( i in key ) { access( elems, fn, i, key[ i ], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, _key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < len; i++ ) { fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); } } } if ( chainable ) { return elems; } // Gets if ( bulk ) { return fn.call( elems ); } return len ? fn( elems[ 0 ], key ) : emptyGet; }; // Matches dashed string for camelizing var rmsPrefix = /^-ms-/, rdashAlpha = /-([a-z])/g; // Used by camelCase as callback to replace() function fcamelCase( _all, letter ) { return letter.toUpperCase(); } // Convert dashed to camelCase; used by the css and data modules // Support: IE <=9 - 11, Edge 12 - 15 // Microsoft forgot to hump their vendor prefix (#9572) function camelCase( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); } var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }; function Data() { this.expando = jQuery.expando + Data.uid++; } Data.uid = 1; Data.prototype = { cache: function( owner ) { // Check if the owner object already has a cache var value = owner[ this.expando ]; // If not, create one if ( !value ) { value = {}; // We can accept data for non-element nodes in modern browsers, // but we should not, see #8335. // Always return an empty object. if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment if ( owner.nodeType ) { owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property // configurable must be true to allow the property to be // deleted when data is removed } else { Object.defineProperty( owner, this.expando, { value: value, configurable: true } ); } } } return value; }, set: function( owner, data, value ) { var prop, cache = this.cache( owner ); // Handle: [ owner, key, value ] args // Always use camelCase key (gh-2257) if ( typeof data === "string" ) { cache[ camelCase( data ) ] = value; // Handle: [ owner, { properties } ] args } else { // Copy the properties one-by-one to the cache object for ( prop in data ) { cache[ camelCase( prop ) ] = data[ prop ]; } } return cache; }, get: function( owner, key ) { return key === undefined ? this.cache( owner ) : // Always use camelCase key (gh-2257) owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; }, access: function( owner, key, value ) { // In cases where either: // // 1. No key was specified // 2. A string key was specified, but no value provided // // Take the "read" path and allow the get method to determine // which value to return, respectively either: // // 1. The entire cache object // 2. The data stored at the key // if ( key === undefined || ( ( key && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key ); } // When the key is not a string, or both a key and value // are specified, set or extend (existing objects) with either: // // 1. An object of properties // 2. A key and value // this.set( owner, key, value ); // Since the "set" path can have two possible entry points // return the expected data based on which path was taken[*] return value !== undefined ? value : key; }, remove: function( owner, key ) { var i, cache = owner[ this.expando ]; if ( cache === undefined ) { return; } if ( key !== undefined ) { // Support array or space separated string of keys if ( Array.isArray( key ) ) { // If key is an array of keys... // We always set camelCase keys, so remove that. key = key.map( camelCase ); } else { key = camelCase( key ); // If a key with the spaces exists, use it. // Otherwise, create an array by matching non-whitespace key = key in cache ? [ key ] : ( key.match( rnothtmlwhite ) || [] ); } i = key.length; while ( i-- ) { delete cache[ key[ i ] ]; } } // Remove the expando if there's no more data if ( key === undefined || jQuery.isEmptyObject( cache ) ) { // Support: Chrome <=35 - 45 // Webkit & Blink performance suffers when deleting properties // from DOM nodes, so set to undefined instead // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) if ( owner.nodeType ) { owner[ this.expando ] = undefined; } else { delete owner[ this.expando ]; } } }, hasData: function( owner ) { var cache = owner[ this.expando ]; return cache !== undefined && !jQuery.isEmptyObject( cache ); } }; var dataPriv = new Data(); var dataUser = new Data(); // Implementation Summary // // 1. Enforce API surface and semantic compatibility with 1.9.x branch // 2. Improve the module's maintainability by reducing the storage // paths to a single mechanism. // 3. Use the same single mechanism to support "private" and "user" data. // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) // 5. Avoid exposing implementation details on user objects (eg. expando properties) // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, rmultiDash = /[A-Z]/g; function getData( data ) { if ( data === "true" ) { return true; } if ( data === "false" ) { return false; } if ( data === "null" ) { return null; } // Only convert to a number if it doesn't change the string if ( data === +data + "" ) { return +data; } if ( rbrace.test( data ) ) { return JSON.parse( data ); } return data; } function dataAttr( elem, key, data ) { var name; // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = getData( data ); } catch ( e ) {} // Make sure we set the data so it isn't changed later dataUser.set( elem, key, data ); } else { data = undefined; } } return data; } jQuery.extend( { hasData: function( elem ) { return dataUser.hasData( elem ) || dataPriv.hasData( elem ); }, data: function( elem, name, data ) { return dataUser.access( elem, name, data ); }, removeData: function( elem, name ) { dataUser.remove( elem, name ); }, // TODO: Now that all calls to _data and _removeData have been replaced // with direct calls to dataPriv methods, these can be deprecated. _data: function( elem, name, data ) { return dataPriv.access( elem, name, data ); }, _removeData: function( elem, name ) { dataPriv.remove( elem, name ); } } ); jQuery.fn.extend( { data: function( key, value ) { var i, name, data, elem = this[ 0 ], attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { if ( this.length ) { data = dataUser.get( elem ); if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE 11 only // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = camelCase( name.slice( 5 ) ); dataAttr( elem, name, data[ name ] ); } } } dataPriv.set( elem, "hasDataAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each( function() { dataUser.set( this, key ); } ); } return access( this, function( value ) { var data; // The calling jQuery object (element matches) is not empty // (and therefore has an element appears at this[ 0 ]) and the // `value` parameter was not undefined. An empty jQuery object // will result in `undefined` for elem = this[ 0 ] which will // throw an exception if an attempt to read a data cache is made. if ( elem && value === undefined ) { // Attempt to get data from the cache // The key will always be camelCased in Data data = dataUser.get( elem, key ); if ( data !== undefined ) { return data; } // Attempt to "discover" the data in // HTML5 custom data-* attrs data = dataAttr( elem, key ); if ( data !== undefined ) { return data; } // We tried really hard, but the data doesn't exist. return; } // Set the data... this.each( function() { // We always store the camelCased key dataUser.set( this, key, value ); } ); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each( function() { dataUser.remove( this, key ); } ); } } ); jQuery.extend( { queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = dataPriv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || Array.isArray( data ) ) { queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // Clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // Not public - generate a queueHooks object, or return the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { empty: jQuery.Callbacks( "once memory" ).add( function() { dataPriv.remove( elem, [ type + "queue", key ] ); } ) } ); } } ); jQuery.fn.extend( { queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[ 0 ], type ); } return data === undefined ? this : this.each( function() { var queue = jQuery.queue( this, type, data ); // Ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { jQuery.dequeue( this, type ); } } ); }, dequeue: function( type ) { return this.each( function() { jQuery.dequeue( this, type ); } ); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while ( i-- ) { tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } } ); var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; var documentElement = document.documentElement; var isAttached = function( elem ) { return jQuery.contains( elem.ownerDocument, elem ); }, composed = { composed: true }; // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only // Check attachment across shadow DOM boundaries when possible (gh-3504) // Support: iOS 10.0-10.2 only // Early iOS 10 versions support `attachShadow` but not `getRootNode`, // leading to errors. We need to check for `getRootNode`. if ( documentElement.getRootNode ) { isAttached = function( elem ) { return jQuery.contains( elem.ownerDocument, elem ) || elem.getRootNode( composed ) === elem.ownerDocument; }; } var isHiddenWithinTree = function( elem, el ) { // isHiddenWithinTree might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; // Inline style trumps all return elem.style.display === "none" || elem.style.display === "" && // Otherwise, check computed style // Support: Firefox <=43 - 45 // Disconnected elements can have computed display: none, so first confirm that elem is // in the document. isAttached( elem ) && jQuery.css( elem, "display" ) === "none"; }; function adjustCSS( elem, prop, valueParts, tween ) { var adjusted, scale, maxIterations = 20, currentValue = tween ? function() { return tween.cur(); } : function() { return jQuery.css( elem, prop, "" ); }, initial = currentValue(), unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), // Starting value computation is required for potential unit mismatches initialInUnit = elem.nodeType && ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && rcssNum.exec( jQuery.css( elem, prop ) ); if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { // Support: Firefox <=54 // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) initial = initial / 2; // Trust units reported by jQuery.css unit = unit || initialInUnit[ 3 ]; // Iteratively approximate from a nonzero starting point initialInUnit = +initial || 1; while ( maxIterations-- ) { // Evaluate and update our best guess (doubling guesses that zero out). // Finish if the scale equals or crosses 1 (making the old*new product non-positive). jQuery.style( elem, prop, initialInUnit + unit ); if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { maxIterations = 0; } initialInUnit = initialInUnit / scale; } initialInUnit = initialInUnit * 2; jQuery.style( elem, prop, initialInUnit + unit ); // Make sure we update the tween properties later on valueParts = valueParts || []; } if ( valueParts ) { initialInUnit = +initialInUnit || +initial || 0; // Apply relative offset (+=/-=) if specified adjusted = valueParts[ 1 ] ? initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : +valueParts[ 2 ]; if ( tween ) { tween.unit = unit; tween.start = initialInUnit; tween.end = adjusted; } } return adjusted; } var defaultDisplayMap = {}; function getDefaultDisplay( elem ) { var temp, doc = elem.ownerDocument, nodeName = elem.nodeName, display = defaultDisplayMap[ nodeName ]; if ( display ) { return display; } temp = doc.body.appendChild( doc.createElement( nodeName ) ); display = jQuery.css( temp, "display" ); temp.parentNode.removeChild( temp ); if ( display === "none" ) { display = "block"; } defaultDisplayMap[ nodeName ] = display; return display; } function showHide( elements, show ) { var display, elem, values = [], index = 0, length = elements.length; // Determine new display value for elements that need to change for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } display = elem.style.display; if ( show ) { // Since we force visibility upon cascade-hidden elements, an immediate (and slow) // check is required in this first loop unless we have a nonempty display value (either // inline or about-to-be-restored) if ( display === "none" ) { values[ index ] = dataPriv.get( elem, "display" ) || null; if ( !values[ index ] ) { elem.style.display = ""; } } if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { values[ index ] = getDefaultDisplay( elem ); } } else { if ( display !== "none" ) { values[ index ] = "none"; // Remember what we're overwriting dataPriv.set( elem, "display", display ); } } } // Set the display of the elements in a second loop to avoid constant reflow for ( index = 0; index < length; index++ ) { if ( values[ index ] != null ) { elements[ index ].style.display = values[ index ]; } } return elements; } jQuery.fn.extend( { show: function() { return showHide( this, true ); }, hide: function() { return showHide( this ); }, toggle: function( state ) { if ( typeof state === "boolean" ) { return state ? this.show() : this.hide(); } return this.each( function() { if ( isHiddenWithinTree( this ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } } ); } } ); var rcheckableType = ( /^(?:checkbox|radio)$/i ); var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); ( function() { var fragment = document.createDocumentFragment(), div = fragment.appendChild( document.createElement( "div" ) ), input = document.createElement( "input" ); // Support: Android 4.0 - 4.3 only // Check state lost if the name is set (#11217) // Support: Windows Web Apps (WWA) // `name` and `type` must use .setAttribute for WWA (#14901) input.setAttribute( "type", "radio" ); input.setAttribute( "checked", "checked" ); input.setAttribute( "name", "t" ); div.appendChild( input ); // Support: Android <=4.1 only // Older WebKit doesn't clone checked state correctly in fragments support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE <=11 only // Make sure textarea (and checkbox) defaultValue is properly cloned div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; // Support: IE <=9 only // IE <=9 replaces "; support.option = !!div.lastChild; } )(); // We have to close these tags to support XHTML (#13200) var wrapMap = { // XHTML parsers do not magically insert elements in the // same way that tag soup parsers do. So we cannot shorten // this by omitting or other required elements. thead: [ 1, "", "
    " ], col: [ 2, "", "
    " ], tr: [ 2, "", "
    " ], td: [ 3, "", "
    " ], _default: [ 0, "", "" ] }; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; // Support: IE <=9 only if ( !support.option ) { wrapMap.optgroup = wrapMap.option = [ 1, "" ]; } function getAll( context, tag ) { // Support: IE <=9 - 11 only // Use typeof to avoid zero-argument method invocation on host objects (#15151) var ret; if ( typeof context.getElementsByTagName !== "undefined" ) { ret = context.getElementsByTagName( tag || "*" ); } else if ( typeof context.querySelectorAll !== "undefined" ) { ret = context.querySelectorAll( tag || "*" ); } else { ret = []; } if ( tag === undefined || tag && nodeName( context, tag ) ) { return jQuery.merge( [ context ], ret ); } return ret; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var i = 0, l = elems.length; for ( ; i < l; i++ ) { dataPriv.set( elems[ i ], "globalEval", !refElements || dataPriv.get( refElements[ i ], "globalEval" ) ); } } var rhtml = /<|&#?\w+;/; function buildFragment( elems, context, scripts, selection, ignored ) { var elem, tmp, tag, wrap, attached, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, l = elems.length; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( toType( elem ) === "object" ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; // Descend through wrappers to the right content j = wrap[ 0 ]; while ( j-- ) { tmp = tmp.lastChild; } // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container tmp = fragment.firstChild; // Ensure the created nodes are orphaned (#12392) tmp.textContent = ""; } } } // Remove wrapper from fragment fragment.textContent = ""; i = 0; while ( ( elem = nodes[ i++ ] ) ) { // Skip elements already in the context collection (trac-4087) if ( selection && jQuery.inArray( elem, selection ) > -1 ) { if ( ignored ) { ignored.push( elem ); } continue; } attached = isAttached( elem ); // Append to fragment tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history if ( attached ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( ( elem = tmp[ j++ ] ) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } return fragment; } var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; function returnTrue() { return true; } function returnFalse() { return false; } // Support: IE <=9 - 11+ // focus() and blur() are asynchronous, except when they are no-op. // So expect focus to be synchronous when the element is already active, // and blur to be synchronous when the element is not already active. // (focus and blur are always synchronous in other supported browsers, // this just defines when we can count on it). function expectSync( elem, type ) { return ( elem === safeActiveElement() ) === ( type === "focus" ); } // Support: IE <=9 only // Accessing document.activeElement can throw unexpectedly // https://bugs.jquery.com/ticket/13393 function safeActiveElement() { try { return document.activeElement; } catch ( err ) { } } function on( elem, types, selector, data, fn, one ) { var origFn, type; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { on( elem, type, selector, data, types[ type ], one ); } return elem; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return elem; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return elem.each( function() { jQuery.event.add( this, types, fn, data, selector ); } ); } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.get( elem ); // Only attach events to objects that accept data if ( !acceptData( elem ) ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Ensure that invalid selectors throw exceptions at attach time // Evaluate against documentElement in case elem is a non-element node (e.g., document) if ( selector ) { jQuery.find.matchesSelector( documentElement, selector ); } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !( events = elemData.events ) ) { events = elemData.events = Object.create( null ); } if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; } // Handle multiple events separated by a space types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers if ( !type ) { continue; } // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend( { type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join( "." ) }, handleObjIn ); // Init the event handler queue if we're the first if ( !( handlers = events[ type ] ) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); if ( !elemData || !( events = elemData.events ) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[ t ] ) || []; type = origType = tmp[ 1 ]; namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[ 2 ] && new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove data and the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { dataPriv.remove( elem, "handle events" ); } }, dispatch: function( nativeEvent ) { var i, j, ret, matched, handleObj, handlerQueue, args = new Array( arguments.length ), // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( nativeEvent ), handlers = ( dataPriv.get( this, "events" ) || Object.create( null ) )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[ 0 ] = event; for ( i = 1; i < arguments.length; i++ ) { args[ i ] = arguments[ i ]; } event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { // If the event is namespaced, then each handler is only invoked if it is // specially universal or its namespaces are a superset of the event's. if ( !event.rnamespace || handleObj.namespace === false || event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { if ( ( event.result = ret ) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers if ( delegateCount && // Support: IE <=9 // Black-hole SVG instance trees (trac-13180) cur.nodeType && // Support: Firefox <=42 // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click // Support: IE 11 only // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) !( event.type === "click" && event.button >= 1 ) ) { for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { matchedHandlers = []; matchedSelectors = {}; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matchedSelectors[ sel ] === undefined ) { matchedSelectors[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) > -1 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matchedSelectors[ sel ] ) { matchedHandlers.push( handleObj ); } } if ( matchedHandlers.length ) { handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); } } } } // Add the remaining (directly-bound) handlers cur = this; if ( delegateCount < handlers.length ) { handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); } return handlerQueue; }, addProp: function( name, hook ) { Object.defineProperty( jQuery.Event.prototype, name, { enumerable: true, configurable: true, get: isFunction( hook ) ? function() { if ( this.originalEvent ) { return hook( this.originalEvent ); } } : function() { if ( this.originalEvent ) { return this.originalEvent[ name ]; } }, set: function( value ) { Object.defineProperty( this, name, { enumerable: true, configurable: true, writable: true, value: value } ); } } ); }, fix: function( originalEvent ) { return originalEvent[ jQuery.expando ] ? originalEvent : new jQuery.Event( originalEvent ); }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, click: { // Utilize native event to ensure correct state for checkable inputs setup: function( data ) { // For mutual compressibility with _default, replace `this` access with a local var. // `|| data` is dead code meant only to preserve the variable through minification. var el = this || data; // Claim the first handler if ( rcheckableType.test( el.type ) && el.click && nodeName( el, "input" ) ) { // dataPriv.set( el, "click", ... ) leverageNative( el, "click", returnTrue ); } // Return false to allow normal processing in the caller return false; }, trigger: function( data ) { // For mutual compressibility with _default, replace `this` access with a local var. // `|| data` is dead code meant only to preserve the variable through minification. var el = this || data; // Force setup before triggering a click if ( rcheckableType.test( el.type ) && el.click && nodeName( el, "input" ) ) { leverageNative( el, "click" ); } // Return non-false to allow normal event-path propagation return true; }, // For cross-browser consistency, suppress native .click() on links // Also prevent it if we're currently inside a leveraged native-event stack _default: function( event ) { var target = event.target; return rcheckableType.test( target.type ) && target.click && nodeName( target, "input" ) && dataPriv.get( target, "click" ) || nodeName( target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } } } }; // Ensure the presence of an event listener that handles manually-triggered // synthetic events by interrupting progress until reinvoked in response to // *native* events that it fires directly, ensuring that state changes have // already occurred before other listeners are invoked. function leverageNative( el, type, expectSync ) { // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add if ( !expectSync ) { if ( dataPriv.get( el, type ) === undefined ) { jQuery.event.add( el, type, returnTrue ); } return; } // Register the controller as a special universal handler for all event namespaces dataPriv.set( el, type, false ); jQuery.event.add( el, type, { namespace: false, handler: function( event ) { var notAsync, result, saved = dataPriv.get( this, type ); if ( ( event.isTrigger & 1 ) && this[ type ] ) { // Interrupt processing of the outer synthetic .trigger()ed event // Saved data should be false in such cases, but might be a leftover capture object // from an async native handler (gh-4350) if ( !saved.length ) { // Store arguments for use when handling the inner native event // There will always be at least one argument (an event object), so this array // will not be confused with a leftover capture object. saved = slice.call( arguments ); dataPriv.set( this, type, saved ); // Trigger the native event and capture its result // Support: IE <=9 - 11+ // focus() and blur() are asynchronous notAsync = expectSync( this, type ); this[ type ](); result = dataPriv.get( this, type ); if ( saved !== result || notAsync ) { dataPriv.set( this, type, false ); } else { result = {}; } if ( saved !== result ) { // Cancel the outer synthetic event event.stopImmediatePropagation(); event.preventDefault(); // Support: Chrome 86+ // In Chrome, if an element having a focusout handler is blurred by // clicking outside of it, it invokes the handler synchronously. If // that handler calls `.remove()` on the element, the data is cleared, // leaving `result` undefined. We need to guard against this. return result && result.value; } // If this is an inner synthetic event for an event with a bubbling surrogate // (focus or blur), assume that the surrogate already propagated from triggering the // native event and prevent that from happening again here. // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the // bubbling surrogate propagates *after* the non-bubbling base), but that seems // less bad than duplication. } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { event.stopPropagation(); } // If this is a native event triggered above, everything is now in order // Fire an inner synthetic event with the original arguments } else if ( saved.length ) { // ...and capture the result dataPriv.set( this, type, { value: jQuery.event.trigger( // Support: IE <=9 - 11+ // Extend with the prototype to reset the above stopImmediatePropagation() jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), saved.slice( 1 ), this ) } ); // Abort handling of the native event event.stopImmediatePropagation(); } } } ); } jQuery.removeEvent = function( elem, type, handle ) { // This "if" is needed for plain objects if ( elem.removeEventListener ) { elem.removeEventListener( type, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android <=2.3 only src.returnValue === false ? returnTrue : returnFalse; // Create target properties // Support: Safari <=6 - 7 only // Target should not be a text node (#504, #13143) this.target = ( src.target && src.target.nodeType === 3 ) ? src.target.parentNode : src.target; this.currentTarget = src.currentTarget; this.relatedTarget = src.relatedTarget; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || Date.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { constructor: jQuery.Event, isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, isSimulated: false, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( e && !this.isSimulated ) { e.preventDefault(); } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopPropagation(); } }, stopImmediatePropagation: function() { var e = this.originalEvent; this.isImmediatePropagationStopped = returnTrue; if ( e && !this.isSimulated ) { e.stopImmediatePropagation(); } this.stopPropagation(); } }; // Includes all common event props including KeyEvent and MouseEvent specific props jQuery.each( { altKey: true, bubbles: true, cancelable: true, changedTouches: true, ctrlKey: true, detail: true, eventPhase: true, metaKey: true, pageX: true, pageY: true, shiftKey: true, view: true, "char": true, code: true, charCode: true, key: true, keyCode: true, button: true, buttons: true, clientX: true, clientY: true, offsetX: true, offsetY: true, pointerId: true, pointerType: true, screenX: true, screenY: true, targetTouches: true, toElement: true, touches: true, which: true }, jQuery.event.addProp ); jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { jQuery.event.special[ type ] = { // Utilize native event if possible so blur/focus sequence is correct setup: function() { // Claim the first handler // dataPriv.set( this, "focus", ... ) // dataPriv.set( this, "blur", ... ) leverageNative( this, type, expectSync ); // Return false to allow normal processing in the caller return false; }, trigger: function() { // Force setup before trigger leverageNative( this, type ); // Return non-false to allow normal event-path propagation return true; }, // Suppress native focus or blur as it's already being fired // in leverageNative. _default: function() { return true; }, delegateType: delegateType }; } ); // Create mouseenter/leave events using mouseover/out and event-time checks // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout // // Support: Safari 7 only // Safari sends mouseenter too often; see: // https://bugs.chromium.org/p/chromium/issues/detail?id=470258 // for the description of the bug (it existed in older Chrome versions as well). jQuery.each( { mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mouseenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; } ); jQuery.fn.extend( { on: function( types, selector, data, fn ) { return on( this, types, selector, data, fn ); }, one: function( types, selector, data, fn ) { return on( this, types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each( function() { jQuery.event.remove( this, types, fn, selector ); } ); } } ); var // Support: IE <=10 - 11, Edge 12 - 13 only // In IE/Edge using regex groups here causes severe slowdowns. // See https://connect.microsoft.com/IE/feedback/details/1736512/ rnoInnerhtml = /\s*$/g; // Prefer a tbody over its parent table for containing new rows function manipulationTarget( elem, content ) { if ( nodeName( elem, "table" ) && nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { return jQuery( elem ).children( "tbody" )[ 0 ] || elem; } return elem; } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; return elem; } function restoreScript( elem ) { if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { elem.type = elem.type.slice( 5 ); } else { elem.removeAttribute( "type" ); } return elem; } function cloneCopyEvent( src, dest ) { var i, l, type, pdataOld, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) { return; } // 1. Copy private data: events, handlers, etc. if ( dataPriv.hasData( src ) ) { pdataOld = dataPriv.get( src ); events = pdataOld.events; if ( events ) { dataPriv.remove( dest, "handle events" ); for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } } // 2. Copy user data if ( dataUser.hasData( src ) ) { udataOld = dataUser.access( src ); udataCur = jQuery.extend( {}, udataOld ); dataUser.set( dest, udataCur ); } } // Fix IE bugs, see support tests function fixInput( src, dest ) { var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button. if ( nodeName === "input" && rcheckableType.test( src.type ) ) { dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays args = flat( args ); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[ 0 ], valueIsFunction = isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( valueIsFunction || ( l > 1 && typeof value === "string" && !support.checkClone && rchecked.test( value ) ) ) { return collection.each( function( index ) { var self = collection.eq( index ); if ( valueIsFunction ) { args[ 0 ] = value.call( this, index, self.html() ); } domManip( self, args, callback, ignored ); } ); } if ( l ) { fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } // Require either new content or an interest in ignored elements to invoke the callback if ( first || ignored ) { scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item // instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( collection[ i ], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !dataPriv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl && !node.noModule ) { jQuery._evalUrl( node.src, { nonce: node.nonce || node.getAttribute( "nonce" ) }, doc ); } } else { DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); } } } } } } return collection; } function remove( elem, selector, keepData ) { var node, nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; for ( ; ( node = nodes[ i ] ) != null; i++ ) { if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } if ( node.parentNode ) { if ( keepData && isAttached( node ) ) { setGlobalEval( getAll( node, "script" ) ); } node.parentNode.removeChild( node ); } } return elem; } jQuery.extend( { htmlPrefilter: function( html ) { return html; }, clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, clone = elem.cloneNode( true ), inPage = isAttached( elem ); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) { fixInput( srcElements[ i ], destElements[ i ] ); } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) { cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } // Return the cloned set return clone; }, cleanData: function( elems ) { var data, elem, type, special = jQuery.event.special, i = 0; for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { if ( acceptData( elem ) ) { if ( ( data = elem[ dataPriv.expando ] ) ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataPriv.expando ] = undefined; } if ( elem[ dataUser.expando ] ) { // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataUser.expando ] = undefined; } } } } } ); jQuery.fn.extend( { detach: function( selector ) { return remove( this, selector, true ); }, remove: function( selector ) { return remove( this, selector ); }, text: function( value ) { return access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().each( function() { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.textContent = value; } } ); }, null, value, arguments.length ); }, append: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.appendChild( elem ); } } ); }, prepend: function() { return domManip( this, arguments, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { var target = manipulationTarget( this, elem ); target.insertBefore( elem, target.firstChild ); } } ); }, before: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } } ); }, after: function() { return domManip( this, arguments, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } } ); }, empty: function() { var elem, i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { if ( elem.nodeType === 1 ) { // Prevent memory leaks jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes elem.textContent = ""; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); } ); }, html: function( value ) { return access( this, function( value ) { var elem = this[ 0 ] || {}, i = 0, l = this.length; if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = jQuery.htmlPrefilter( value ); try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function() { var ignored = []; // Make the changes, replacing each non-ignored context element with the new content return domManip( this, arguments, function( elem ) { var parent = this.parentNode; if ( jQuery.inArray( this, ignored ) < 0 ) { jQuery.cleanData( getAll( this ) ); if ( parent ) { parent.replaceChild( elem, this ); } } // Force callback invocation }, ignored ); } } ); jQuery.each( { appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, ret = [], insert = jQuery( selector ), last = insert.length - 1, i = 0; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems ); // Support: Android <=4.0 only, PhantomJS 1 only // .get() because push.apply(_, arraylike) throws on ancient WebKit push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; } ); var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); var getStyles = function( elem ) { // Support: IE <=11 only, Firefox <=30 (#15098, #14150) // IE throws on elements created in popups // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" var view = elem.ownerDocument.defaultView; if ( !view || !view.opener ) { view = window; } return view.getComputedStyle( elem ); }; var swap = function( elem, options, callback ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.call( elem ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; }; var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); ( function() { // Executing both pixelPosition & boxSizingReliable tests require only one layout // so they're executed at the same time to save the second computation. function computeStyleTests() { // This is a singleton, we need to execute it only once if ( !div ) { return; } container.style.cssText = "position:absolute;left:-11111px;width:60px;" + "margin-top:1px;padding:0;border:0"; div.style.cssText = "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + "margin:auto;border:1px;padding:1px;" + "width:60%;top:1%"; documentElement.appendChild( container ).appendChild( div ); var divStyle = window.getComputedStyle( div ); pixelPositionVal = divStyle.top !== "1%"; // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 // Some styles come back with percentage values, even though they shouldn't div.style.right = "60%"; pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; // Support: IE 9 - 11 only // Detect misreporting of content dimensions for box-sizing:border-box elements boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; // Support: IE 9 only // Detect overflow:scroll screwiness (gh-3699) // Support: Chrome <=64 // Don't get tricked when zoom affects offsetWidth (gh-4029) div.style.position = "absolute"; scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; documentElement.removeChild( container ); // Nullify the div so it wouldn't be stored in the memory and // it will also be a sign that checks already performed div = null; } function roundPixelMeasures( measure ) { return Math.round( parseFloat( measure ) ); } var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, reliableTrDimensionsVal, reliableMarginLeftVal, container = document.createElement( "div" ), div = document.createElement( "div" ); // Finish early in limited (non-browser) environments if ( !div.style ) { return; } // Support: IE <=9 - 11 only // Style of cloned element affects source element cloned (#8908) div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; jQuery.extend( support, { boxSizingReliable: function() { computeStyleTests(); return boxSizingReliableVal; }, pixelBoxStyles: function() { computeStyleTests(); return pixelBoxStylesVal; }, pixelPosition: function() { computeStyleTests(); return pixelPositionVal; }, reliableMarginLeft: function() { computeStyleTests(); return reliableMarginLeftVal; }, scrollboxSize: function() { computeStyleTests(); return scrollboxSizeVal; }, // Support: IE 9 - 11+, Edge 15 - 18+ // IE/Edge misreport `getComputedStyle` of table rows with width/height // set in CSS while `offset*` properties report correct values. // Behavior in IE 9 is more subtle than in newer versions & it passes // some versions of this test; make sure not to make it pass there! // // Support: Firefox 70+ // Only Firefox includes border widths // in computed dimensions. (gh-4529) reliableTrDimensions: function() { var table, tr, trChild, trStyle; if ( reliableTrDimensionsVal == null ) { table = document.createElement( "table" ); tr = document.createElement( "tr" ); trChild = document.createElement( "div" ); table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; tr.style.cssText = "border:1px solid"; // Support: Chrome 86+ // Height set through cssText does not get applied. // Computed height then comes back as 0. tr.style.height = "1px"; trChild.style.height = "9px"; // Support: Android 8 Chrome 86+ // In our bodyBackground.html iframe, // display for all div elements is set to "inline", // which causes a problem only in Android 8 Chrome 86. // Ensuring the div is display: block // gets around this issue. trChild.style.display = "block"; documentElement .appendChild( table ) .appendChild( tr ) .appendChild( trChild ); trStyle = window.getComputedStyle( tr ); reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + parseInt( trStyle.borderTopWidth, 10 ) + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; documentElement.removeChild( table ); } return reliableTrDimensionsVal; } } ); } )(); function curCSS( elem, name, computed ) { var width, minWidth, maxWidth, ret, // Support: Firefox 51+ // Retrieving style before computed somehow // fixes an issue with getting wrong values // on detached elements style = elem.style; computed = computed || getStyles( elem ); // getPropertyValue is needed for: // .css('filter') (IE 9 only, #12537) // .css('--customProperty) (#3144) if ( computed ) { ret = computed.getPropertyValue( name ) || computed[ name ]; if ( ret === "" && !isAttached( elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Android Browser returns percentage for some values, // but width seems to be reliably pixels. // This is against the CSSOM draft spec: // https://drafts.csswg.org/cssom/#resolved-values if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } return ret !== undefined ? // Support: IE <=9 - 11 only // IE returns zIndex value as an integer. ret + "" : ret; } function addGetHookIf( conditionFn, hookFn ) { // Define the hook, we'll check on the first run if it's really needed. return { get: function() { if ( conditionFn() ) { // Hook not needed (or it's not possible to use it due // to missing dependency), remove it. delete this.get; return; } // Hook needed; redefine it so that the support test is not executed again. return ( this.get = hookFn ).apply( this, arguments ); } }; } var cssPrefixes = [ "Webkit", "Moz", "ms" ], emptyStyle = document.createElement( "div" ).style, vendorProps = {}; // Return a vendor-prefixed property or undefined function vendorPropName( name ) { // Check for vendor prefixed names var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; if ( name in emptyStyle ) { return name; } } } // Return a potentially-mapped jQuery.cssProps or vendor prefixed property function finalPropName( name ) { var final = jQuery.cssProps[ name ] || vendorProps[ name ]; if ( final ) { return final; } if ( name in emptyStyle ) { return name; } return vendorProps[ name ] = vendorPropName( name ) || name; } var // Swappable if display is none or starts with table // except "table", "table-cell", or "table-caption" // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, rcustomProp = /^--/, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: "0", fontWeight: "400" }; function setPositiveNumber( _elem, value, subtract ) { // Any relative (+/-) values have already been // normalized at this point var matches = rcssNum.exec( value ); return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : value; } function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { var i = dimension === "width" ? 1 : 0, extra = 0, delta = 0; // Adjustment may not be necessary if ( box === ( isBorderBox ? "border" : "content" ) ) { return 0; } for ( ; i < 4; i += 2 ) { // Both box models exclude margin if ( box === "margin" ) { delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); } // If we get here with a content-box, we're seeking "padding" or "border" or "margin" if ( !isBorderBox ) { // Add padding delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // For "border" or "margin", add border if ( box !== "padding" ) { delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); // But still keep track of it otherwise } else { extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } // If we get here with a border-box (content + padding + border), we're seeking "content" or // "padding" or "margin" } else { // For "content", subtract padding if ( box === "content" ) { delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // For "content" or "padding", subtract border if ( box !== "margin" ) { delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } // Account for positive content-box scroll gutter when requested by providing computedVal if ( !isBorderBox && computedVal >= 0 ) { // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border // Assuming integer scroll gutter, subtract the rest and round down delta += Math.max( 0, Math.ceil( elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - computedVal - delta - extra - 0.5 // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter // Use an explicit zero to avoid NaN (gh-3964) ) ) || 0; } return delta; } function getWidthOrHeight( elem, dimension, extra ) { // Start with computed style var styles = getStyles( elem ), // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). // Fake content-box until we know it's needed to know the true value. boxSizingNeeded = !support.boxSizingReliable() || extra, isBorderBox = boxSizingNeeded && jQuery.css( elem, "boxSizing", false, styles ) === "border-box", valueIsBorderBox = isBorderBox, val = curCSS( elem, dimension, styles ), offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); // Support: Firefox <=54 // Return a confounding non-pixel value or feign ignorance, as appropriate. if ( rnumnonpx.test( val ) ) { if ( !extra ) { return val; } val = "auto"; } // Support: IE 9 - 11 only // Use offsetWidth/offsetHeight for when box sizing is unreliable. // In those cases, the computed value can be trusted to be border-box. if ( ( !support.boxSizingReliable() && isBorderBox || // Support: IE 10 - 11+, Edge 15 - 18+ // IE/Edge misreport `getComputedStyle` of table rows with width/height // set in CSS while `offset*` properties report correct values. // Interestingly, in some cases IE 9 doesn't suffer from this issue. !support.reliableTrDimensions() && nodeName( elem, "tr" ) || // Fall back to offsetWidth/offsetHeight when value is "auto" // This happens for inline elements with no explicit setting (gh-3571) val === "auto" || // Support: Android <=4.1 - 4.3 only // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && // Make sure the element is visible & connected elem.getClientRects().length ) { isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // Where available, offsetWidth/offsetHeight approximate border box dimensions. // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the // retrieved value as a content box dimension. valueIsBorderBox = offsetProp in elem; if ( valueIsBorderBox ) { val = elem[ offsetProp ]; } } // Normalize "" and auto val = parseFloat( val ) || 0; // Adjust for the element's box model return ( val + boxModelAdjustment( elem, dimension, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles, // Provide the current computed size to request scroll gutter calculation (gh-3589) val ) ) + "px"; } jQuery.extend( { // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } } } }, // Don't automatically add "px" to these possibly-unitless properties cssNumber: { "animationIterationCount": true, "columnCount": true, "fillOpacity": true, "flexGrow": true, "flexShrink": true, "fontWeight": true, "gridArea": true, "gridColumn": true, "gridColumnEnd": true, "gridColumnStart": true, "gridRow": true, "gridRowEnd": true, "gridRowStart": true, "lineHeight": true, "opacity": true, "order": true, "orphans": true, "widows": true, "zIndex": true, "zoom": true }, // Add in properties whose names you wish to fix before // setting or getting the value cssProps: {}, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; } // Make sure that we're working with the right name var ret, type, hooks, origName = camelCase( name ), isCustomProp = rcustomProp.test( name ), style = elem.style; // Make sure that we're working with the right name. We don't // want to query the value if it is a CSS custom property // since they are user-defined. if ( !isCustomProp ) { name = finalPropName( origName ); } // Gets hook for the prefixed version, then unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; // Convert "+=" or "-=" to relative numbers (#7345) if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { value = adjustCSS( elem, name, ret ); // Fixes bug #9237 type = "number"; } // Make sure that null and NaN values aren't set (#7116) if ( value == null || value !== value ) { return; } // If a number was passed in, add the unit (except for certain CSS properties) // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append // "px" to a few hardcoded values. if ( type === "number" && !isCustomProp ) { value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } // background-* props affect original clone's values if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value if ( !hooks || !( "set" in hooks ) || ( value = hooks.set( elem, value, extra ) ) !== undefined ) { if ( isCustomProp ) { style.setProperty( name, value ); } else { style[ name ] = value; } } } else { // If a hook was provided get the non-computed value from there if ( hooks && "get" in hooks && ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { return ret; } // Otherwise just get the value from the style object return style[ name ]; } }, css: function( elem, name, extra, styles ) { var val, num, hooks, origName = camelCase( name ), isCustomProp = rcustomProp.test( name ); // Make sure that we're working with the right name. We don't // want to modify the value if it is a CSS custom property // since they are user-defined. if ( !isCustomProp ) { name = finalPropName( origName ); } // Try prefixed name followed by the unprefixed name hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there if ( hooks && "get" in hooks ) { val = hooks.get( elem, true, extra ); } // Otherwise, if a way to get the computed value exists, use that if ( val === undefined ) { val = curCSS( elem, name, styles ); } // Convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } // Make numeric if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || isFinite( num ) ? num || 0 : val; } return val; } } ); jQuery.each( [ "height", "width" ], function( _i, dimension ) { jQuery.cssHooks[ dimension ] = { get: function( elem, computed, extra ) { if ( computed ) { // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit return rdisplayswap.test( jQuery.css( elem, "display" ) ) && // Support: Safari 8+ // Table columns in Safari have non-zero offsetWidth & zero // getBoundingClientRect().width unless display is changed. // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, dimension, extra ); } ) : getWidthOrHeight( elem, dimension, extra ); } }, set: function( elem, value, extra ) { var matches, styles = getStyles( elem ), // Only read styles.position if the test has a chance to fail // to avoid forcing a reflow. scrollboxSizeBuggy = !support.scrollboxSize() && styles.position === "absolute", // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) boxSizingNeeded = scrollboxSizeBuggy || extra, isBorderBox = boxSizingNeeded && jQuery.css( elem, "boxSizing", false, styles ) === "border-box", subtract = extra ? boxModelAdjustment( elem, dimension, extra, isBorderBox, styles ) : 0; // Account for unreliable border-box dimensions by comparing offset* to computed and // faking a content-box to get border and padding (gh-3699) if ( isBorderBox && scrollboxSizeBuggy ) { subtract -= Math.ceil( elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - parseFloat( styles[ dimension ] ) - boxModelAdjustment( elem, dimension, "border", false, styles ) - 0.5 ); } // Convert to pixels if value adjustment is needed if ( subtract && ( matches = rcssNum.exec( value ) ) && ( matches[ 3 ] || "px" ) !== "px" ) { elem.style[ dimension ] = value; value = jQuery.css( elem, dimension ); } return setPositiveNumber( elem, value, subtract ); } }; } ); jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, function( elem, computed ) { if ( computed ) { return ( parseFloat( curCSS( elem, "marginLeft" ) ) || elem.getBoundingClientRect().left - swap( elem, { marginLeft: 0 }, function() { return elem.getBoundingClientRect().left; } ) ) + "px"; } } ); // These hooks are used by animate to expand properties jQuery.each( { margin: "", padding: "", border: "Width" }, function( prefix, suffix ) { jQuery.cssHooks[ prefix + suffix ] = { expand: function( value ) { var i = 0, expanded = {}, // Assumes a single number if not a string parts = typeof value === "string" ? value.split( " " ) : [ value ]; for ( ; i < 4; i++ ) { expanded[ prefix + cssExpand[ i ] + suffix ] = parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; } return expanded; } }; if ( prefix !== "margin" ) { jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; } } ); jQuery.fn.extend( { css: function( name, value ) { return access( this, function( elem, name, value ) { var styles, len, map = {}, i = 0; if ( Array.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); } } ); function Tween( elem, options, prop, end, easing ) { return new Tween.prototype.init( elem, options, prop, end, easing ); } jQuery.Tween = Tween; Tween.prototype = { constructor: Tween, init: function( elem, options, prop, end, easing, unit ) { this.elem = elem; this.prop = prop; this.easing = easing || jQuery.easing._default; this.options = options; this.start = this.now = this.cur(); this.end = end; this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); }, cur: function() { var hooks = Tween.propHooks[ this.prop ]; return hooks && hooks.get ? hooks.get( this ) : Tween.propHooks._default.get( this ); }, run: function( percent ) { var eased, hooks = Tween.propHooks[ this.prop ]; if ( this.options.duration ) { this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration ); } else { this.pos = eased = percent; } this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) { this.options.step.call( this.elem, this.now, this ); } if ( hooks && hooks.set ) { hooks.set( this ); } else { Tween.propHooks._default.set( this ); } return this; } }; Tween.prototype.init.prototype = Tween.prototype; Tween.propHooks = { _default: { get: function( tween ) { var result; // Use a property on the element directly when it is not a DOM element, // or when there is no matching style property that exists. if ( tween.elem.nodeType !== 1 || tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { return tween.elem[ tween.prop ]; } // Passing an empty string as a 3rd parameter to .css will automatically // attempt a parseFloat and fallback to a string if the parse fails. // Simple values such as "10px" are parsed to Float; // complex values such as "rotate(1rad)" are returned as-is. result = jQuery.css( tween.elem, tween.prop, "" ); // Empty strings, null, undefined and "auto" are converted to 0. return !result || result === "auto" ? 0 : result; }, set: function( tween ) { // Use step hook for back compat. // Use cssHook if its there. // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); } else if ( tween.elem.nodeType === 1 && ( jQuery.cssHooks[ tween.prop ] || tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { tween.elem[ tween.prop ] = tween.now; } } } }; // Support: IE <=9 only // Panic based approach to setting things on disconnected nodes Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { set: function( tween ) { if ( tween.elem.nodeType && tween.elem.parentNode ) { tween.elem[ tween.prop ] = tween.now; } } }; jQuery.easing = { linear: function( p ) { return p; }, swing: function( p ) { return 0.5 - Math.cos( p * Math.PI ) / 2; }, _default: "swing" }; jQuery.fx = Tween.prototype.init; // Back compat <1.8 extension point jQuery.fx.step = {}; var fxNow, inProgress, rfxtypes = /^(?:toggle|show|hide)$/, rrun = /queueHooks$/; function schedule() { if ( inProgress ) { if ( document.hidden === false && window.requestAnimationFrame ) { window.requestAnimationFrame( schedule ); } else { window.setTimeout( schedule, jQuery.fx.interval ); } jQuery.fx.tick(); } } // Animations created synchronously will run synchronously function createFxNow() { window.setTimeout( function() { fxNow = undefined; } ); return ( fxNow = Date.now() ); } // Generate parameters to create a standard animation function genFx( type, includeWidth ) { var which, i = 0, attrs = { height: type }; // If we include width, step value is 1 to do all cssExpand values, // otherwise step value is 2 to skip over Left and Right includeWidth = includeWidth ? 1 : 0; for ( ; i < 4; i += 2 - includeWidth ) { which = cssExpand[ i ]; attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; } if ( includeWidth ) { attrs.opacity = attrs.width = type; } return attrs; } function createTween( value, prop, animation ) { var tween, collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), index = 0, length = collection.length; for ( ; index < length; index++ ) { if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { // We're done with this property return tween; } } } function defaultPrefilter( elem, props, opts ) { var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, isBox = "width" in props || "height" in props, anim = this, orig = {}, style = elem.style, hidden = elem.nodeType && isHiddenWithinTree( elem ), dataShow = dataPriv.get( elem, "fxshow" ); // Queue-skipping animations hijack the fx hooks if ( !opts.queue ) { hooks = jQuery._queueHooks( elem, "fx" ); if ( hooks.unqueued == null ) { hooks.unqueued = 0; oldfire = hooks.empty.fire; hooks.empty.fire = function() { if ( !hooks.unqueued ) { oldfire(); } }; } hooks.unqueued++; anim.always( function() { // Ensure the complete handler is called before this completes anim.always( function() { hooks.unqueued--; if ( !jQuery.queue( elem, "fx" ).length ) { hooks.empty.fire(); } } ); } ); } // Detect show/hide animations for ( prop in props ) { value = props[ prop ]; if ( rfxtypes.test( value ) ) { delete props[ prop ]; toggle = toggle || value === "toggle"; if ( value === ( hidden ? "hide" : "show" ) ) { // Pretend to be hidden if this is a "show" and // there is still data from a stopped show/hide if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { hidden = true; // Ignore all other no-op show/hide data } else { continue; } } orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); } } // Bail out if this is a no-op like .hide().hide() propTween = !jQuery.isEmptyObject( props ); if ( !propTween && jQuery.isEmptyObject( orig ) ) { return; } // Restrict "overflow" and "display" styles during box animations if ( isBox && elem.nodeType === 1 ) { // Support: IE <=9 - 11, Edge 12 - 15 // Record all 3 overflow attributes because IE does not infer the shorthand // from identically-valued overflowX and overflowY and Edge just mirrors // the overflowX value there. opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; // Identify a display type, preferring old show/hide data over the CSS cascade restoreDisplay = dataShow && dataShow.display; if ( restoreDisplay == null ) { restoreDisplay = dataPriv.get( elem, "display" ); } display = jQuery.css( elem, "display" ); if ( display === "none" ) { if ( restoreDisplay ) { display = restoreDisplay; } else { // Get nonempty value(s) by temporarily forcing visibility showHide( [ elem ], true ); restoreDisplay = elem.style.display || restoreDisplay; display = jQuery.css( elem, "display" ); showHide( [ elem ] ); } } // Animate inline elements as inline-block if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { if ( jQuery.css( elem, "float" ) === "none" ) { // Restore the original display value at the end of pure show/hide animations if ( !propTween ) { anim.done( function() { style.display = restoreDisplay; } ); if ( restoreDisplay == null ) { display = style.display; restoreDisplay = display === "none" ? "" : display; } } style.display = "inline-block"; } } } if ( opts.overflow ) { style.overflow = "hidden"; anim.always( function() { style.overflow = opts.overflow[ 0 ]; style.overflowX = opts.overflow[ 1 ]; style.overflowY = opts.overflow[ 2 ]; } ); } // Implement show/hide animations propTween = false; for ( prop in orig ) { // General show/hide setup for this element animation if ( !propTween ) { if ( dataShow ) { if ( "hidden" in dataShow ) { hidden = dataShow.hidden; } } else { dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); } // Store hidden/visible for toggle so `.stop().toggle()` "reverses" if ( toggle ) { dataShow.hidden = !hidden; } // Show elements before animating them if ( hidden ) { showHide( [ elem ], true ); } /* eslint-disable no-loop-func */ anim.done( function() { /* eslint-enable no-loop-func */ // The final step of a "hide" animation is actually hiding the element if ( !hidden ) { showHide( [ elem ] ); } dataPriv.remove( elem, "fxshow" ); for ( prop in orig ) { jQuery.style( elem, prop, orig[ prop ] ); } } ); } // Per-property setup propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); if ( !( prop in dataShow ) ) { dataShow[ prop ] = propTween.start; if ( hidden ) { propTween.end = propTween.start; propTween.start = 0; } } } } function propFilter( props, specialEasing ) { var index, name, easing, value, hooks; // camelCase, specialEasing and expand cssHook pass for ( index in props ) { name = camelCase( index ); easing = specialEasing[ name ]; value = props[ index ]; if ( Array.isArray( value ) ) { easing = value[ 1 ]; value = props[ index ] = value[ 0 ]; } if ( index !== name ) { props[ name ] = value; delete props[ index ]; } hooks = jQuery.cssHooks[ name ]; if ( hooks && "expand" in hooks ) { value = hooks.expand( value ); delete props[ name ]; // Not quite $.extend, this won't overwrite existing keys. // Reusing 'index' because we have the correct "name" for ( index in value ) { if ( !( index in props ) ) { props[ index ] = value[ index ]; specialEasing[ index ] = easing; } } } else { specialEasing[ name ] = easing; } } } function Animation( elem, properties, options ) { var result, stopped, index = 0, length = Animation.prefilters.length, deferred = jQuery.Deferred().always( function() { // Don't match elem in the :animated selector delete tick.elem; } ), tick = function() { if ( stopped ) { return false; } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // Support: Android 2.3 only // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) temp = remaining / animation.duration || 0, percent = 1 - temp, index = 0, length = animation.tweens.length; for ( ; index < length; index++ ) { animation.tweens[ index ].run( percent ); } deferred.notifyWith( elem, [ animation, percent, remaining ] ); // If there's more to do, yield if ( percent < 1 && length ) { return remaining; } // If this was an empty animation, synthesize a final progress notification if ( !length ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); } // Resolve the animation and report its conclusion deferred.resolveWith( elem, [ animation ] ); return false; }, animation = deferred.promise( { elem: elem, props: jQuery.extend( {}, properties ), opts: jQuery.extend( true, { specialEasing: {}, easing: jQuery.easing._default }, options ), originalProperties: properties, originalOptions: options, startTime: fxNow || createFxNow(), duration: options.duration, tweens: [], createTween: function( prop, end ) { var tween = jQuery.Tween( elem, animation.opts, prop, end, animation.opts.specialEasing[ prop ] || animation.opts.easing ); animation.tweens.push( tween ); return tween; }, stop: function( gotoEnd ) { var index = 0, // If we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; if ( stopped ) { return this; } stopped = true; for ( ; index < length; index++ ) { animation.tweens[ index ].run( 1 ); } // Resolve when we played the last frame; otherwise, reject if ( gotoEnd ) { deferred.notifyWith( elem, [ animation, 1, 0 ] ); deferred.resolveWith( elem, [ animation, gotoEnd ] ); } else { deferred.rejectWith( elem, [ animation, gotoEnd ] ); } return this; } } ), props = animation.props; propFilter( props, animation.opts.specialEasing ); for ( ; index < length; index++ ) { result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); if ( result ) { if ( isFunction( result.stop ) ) { jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = result.stop.bind( result ); } return result; } } jQuery.map( props, createTween, animation ); if ( isFunction( animation.opts.start ) ) { animation.opts.start.call( elem, animation ); } // Attach callbacks from options animation .progress( animation.opts.progress ) .done( animation.opts.done, animation.opts.complete ) .fail( animation.opts.fail ) .always( animation.opts.always ); jQuery.fx.timer( jQuery.extend( tick, { elem: elem, anim: animation, queue: animation.opts.queue } ) ); return animation; } jQuery.Animation = jQuery.extend( Animation, { tweeners: { "*": [ function( prop, value ) { var tween = this.createTween( prop, value ); adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); return tween; } ] }, tweener: function( props, callback ) { if ( isFunction( props ) ) { callback = props; props = [ "*" ]; } else { props = props.match( rnothtmlwhite ); } var prop, index = 0, length = props.length; for ( ; index < length; index++ ) { prop = props[ index ]; Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; Animation.tweeners[ prop ].unshift( callback ); } }, prefilters: [ defaultPrefilter ], prefilter: function( callback, prepend ) { if ( prepend ) { Animation.prefilters.unshift( callback ); } else { Animation.prefilters.push( callback ); } } } ); jQuery.speed = function( speed, easing, fn ) { var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { complete: fn || !fn && easing || isFunction( speed ) && speed, duration: speed, easing: fn && easing || easing && !isFunction( easing ) && easing }; // Go to the end state if fx are off if ( jQuery.fx.off ) { opt.duration = 0; } else { if ( typeof opt.duration !== "number" ) { if ( opt.duration in jQuery.fx.speeds ) { opt.duration = jQuery.fx.speeds[ opt.duration ]; } else { opt.duration = jQuery.fx.speeds._default; } } } // Normalize opt.queue - true/undefined/null -> "fx" if ( opt.queue == null || opt.queue === true ) { opt.queue = "fx"; } // Queueing opt.old = opt.complete; opt.complete = function() { if ( isFunction( opt.old ) ) { opt.old.call( this ); } if ( opt.queue ) { jQuery.dequeue( this, opt.queue ); } }; return opt; }; jQuery.fn.extend( { fadeTo: function( speed, to, easing, callback ) { // Show any hidden elements after setting opacity to 0 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() // Animate to the value specified .end().animate( { opacity: to }, speed, easing, callback ); }, animate: function( prop, speed, easing, callback ) { var empty = jQuery.isEmptyObject( prop ), optall = jQuery.speed( speed, easing, callback ), doAnimation = function() { // Operate on a copy of prop so per-property easing won't be lost var anim = Animation( this, jQuery.extend( {}, prop ), optall ); // Empty animations, or finishing resolves immediately if ( empty || dataPriv.get( this, "finish" ) ) { anim.stop( true ); } }; doAnimation.finish = doAnimation; return empty || optall.queue === false ? this.each( doAnimation ) : this.queue( optall.queue, doAnimation ); }, stop: function( type, clearQueue, gotoEnd ) { var stopQueue = function( hooks ) { var stop = hooks.stop; delete hooks.stop; stop( gotoEnd ); }; if ( typeof type !== "string" ) { gotoEnd = clearQueue; clearQueue = type; type = undefined; } if ( clearQueue ) { this.queue( type || "fx", [] ); } return this.each( function() { var dequeue = true, index = type != null && type + "queueHooks", timers = jQuery.timers, data = dataPriv.get( this ); if ( index ) { if ( data[ index ] && data[ index ].stop ) { stopQueue( data[ index ] ); } } else { for ( index in data ) { if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { stopQueue( data[ index ] ); } } } for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && ( type == null || timers[ index ].queue === type ) ) { timers[ index ].anim.stop( gotoEnd ); dequeue = false; timers.splice( index, 1 ); } } // Start the next in the queue if the last step wasn't forced. // Timers currently will call their complete callbacks, which // will dequeue but only if they were gotoEnd. if ( dequeue || !gotoEnd ) { jQuery.dequeue( this, type ); } } ); }, finish: function( type ) { if ( type !== false ) { type = type || "fx"; } return this.each( function() { var index, data = dataPriv.get( this ), queue = data[ type + "queue" ], hooks = data[ type + "queueHooks" ], timers = jQuery.timers, length = queue ? queue.length : 0; // Enable finishing flag on private data data.finish = true; // Empty the queue first jQuery.queue( this, type, [] ); if ( hooks && hooks.stop ) { hooks.stop.call( this, true ); } // Look for any active animations, and finish them for ( index = timers.length; index--; ) { if ( timers[ index ].elem === this && timers[ index ].queue === type ) { timers[ index ].anim.stop( true ); timers.splice( index, 1 ); } } // Look for any animations in the old queue and finish them for ( index = 0; index < length; index++ ) { if ( queue[ index ] && queue[ index ].finish ) { queue[ index ].finish.call( this ); } } // Turn off finishing flag delete data.finish; } ); } } ); jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { var cssFn = jQuery.fn[ name ]; jQuery.fn[ name ] = function( speed, easing, callback ) { return speed == null || typeof speed === "boolean" ? cssFn.apply( this, arguments ) : this.animate( genFx( name, true ), speed, easing, callback ); }; } ); // Generate shortcuts for custom animations jQuery.each( { slideDown: genFx( "show" ), slideUp: genFx( "hide" ), slideToggle: genFx( "toggle" ), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } }, function( name, props ) { jQuery.fn[ name ] = function( speed, easing, callback ) { return this.animate( props, speed, easing, callback ); }; } ); jQuery.timers = []; jQuery.fx.tick = function() { var timer, i = 0, timers = jQuery.timers; fxNow = Date.now(); for ( ; i < timers.length; i++ ) { timer = timers[ i ]; // Run the timer and safely remove it when done (allowing for external removal) if ( !timer() && timers[ i ] === timer ) { timers.splice( i--, 1 ); } } if ( !timers.length ) { jQuery.fx.stop(); } fxNow = undefined; }; jQuery.fx.timer = function( timer ) { jQuery.timers.push( timer ); jQuery.fx.start(); }; jQuery.fx.interval = 13; jQuery.fx.start = function() { if ( inProgress ) { return; } inProgress = true; schedule(); }; jQuery.fx.stop = function() { inProgress = null; }; jQuery.fx.speeds = { slow: 600, fast: 200, // Default speed _default: 400 }; // Based off of the plugin by Clint Helfers, with permission. // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ jQuery.fn.delay = function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = window.setTimeout( next, time ); hooks.stop = function() { window.clearTimeout( timeout ); }; } ); }; ( function() { var input = document.createElement( "input" ), select = document.createElement( "select" ), opt = select.appendChild( document.createElement( "option" ) ); input.type = "checkbox"; // Support: Android <=4.3 only // Default value for a checkbox should be "on" support.checkOn = input.value !== ""; // Support: IE <=11 only // Must access selectedIndex to make default options select support.optSelected = opt.selected; // Support: IE <=11 only // An input loses its value after becoming a radio input = document.createElement( "input" ); input.value = "t"; input.type = "radio"; support.radioValue = input.value === "t"; } )(); var boolHook, attrHandle = jQuery.expr.attrHandle; jQuery.fn.extend( { attr: function( name, value ) { return access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each( function() { jQuery.removeAttr( this, name ); } ); } } ); jQuery.extend( { attr: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set attributes on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === "undefined" ) { return jQuery.prop( elem, name, value ); } // Attribute hooks are determined by the lowercase version // Grab necessary hook if one is defined if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { hooks = jQuery.attrHooks[ name.toLowerCase() ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); return; } if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } elem.setAttribute( name, value + "" ); return value; } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; }, attrHooks: { type: { set: function( elem, value ) { if ( !support.radioValue && value === "radio" && nodeName( elem, "input" ) ) { var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, removeAttr: function( elem, value ) { var name, i = 0, // Attribute names can contain non-HTML whitespace characters // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 attrNames = value && value.match( rnothtmlwhite ); if ( attrNames && elem.nodeType === 1 ) { while ( ( name = attrNames[ i++ ] ) ) { elem.removeAttribute( name ); } } } } ); // Hooks for boolean attributes boolHook = { set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else { elem.setAttribute( name, name ); } return name; } }; jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { var getter = attrHandle[ name ] || jQuery.find.attr; attrHandle[ name ] = function( elem, name, isXML ) { var ret, handle, lowercaseName = name.toLowerCase(); if ( !isXML ) { // Avoid an infinite loop by temporarily removing this function from the getter handle = attrHandle[ lowercaseName ]; attrHandle[ lowercaseName ] = ret; ret = getter( elem, name, isXML ) != null ? lowercaseName : null; attrHandle[ lowercaseName ] = handle; } return ret; }; } ); var rfocusable = /^(?:input|select|textarea|button)$/i, rclickable = /^(?:a|area)$/i; jQuery.fn.extend( { prop: function( name, value ) { return access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { return this.each( function() { delete this[ jQuery.propFix[ name ] || name ]; } ); } } ); jQuery.extend( { prop: function( elem, name, value ) { var ret, hooks, nType = elem.nodeType; // Don't get/set properties on text, comment and attribute nodes if ( nType === 3 || nType === 8 || nType === 2 ) { return; } if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { return ret; } return ( elem[ name ] = value ); } if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { return ret; } return elem[ name ]; }, propHooks: { tabIndex: { get: function( elem ) { // Support: IE <=9 - 11 only // elem.tabIndex doesn't always return the // correct value when it hasn't been explicitly set // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // Use proper attribute retrieval(#12072) var tabindex = jQuery.find.attr( elem, "tabindex" ); if ( tabindex ) { return parseInt( tabindex, 10 ); } if ( rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ) { return 0; } return -1; } } }, propFix: { "for": "htmlFor", "class": "className" } } ); // Support: IE <=11 only // Accessing the selectedIndex property // forces the browser to respect setting selected // on the option // The getter ensures a default option is selected // when in an optgroup // eslint rule "no-unused-expressions" is disabled for this code // since it considers such accessions noop if ( !support.optSelected ) { jQuery.propHooks.selected = { get: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent && parent.parentNode ) { parent.parentNode.selectedIndex; } return null; }, set: function( elem ) { /* eslint no-unused-expressions: "off" */ var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } } }; } jQuery.each( [ "tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable" ], function() { jQuery.propFix[ this.toLowerCase() ] = this; } ); // Strip and collapse whitespace according to HTML spec // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace function stripAndCollapse( value ) { var tokens = value.match( rnothtmlwhite ) || []; return tokens.join( " " ); } function getClass( elem ) { return elem.getAttribute && elem.getAttribute( "class" ) || ""; } function classesToArray( value ) { if ( Array.isArray( value ) ) { return value; } if ( typeof value === "string" ) { return value.match( rnothtmlwhite ) || []; } return []; } jQuery.fn.extend( { addClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); } ); } classes = classesToArray( value ); if ( classes.length ) { while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, removeClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; if ( isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); } ); } if ( !arguments.length ) { return this.attr( "class", "" ); } classes = classesToArray( value ); if ( classes.length ) { while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) > -1 ) { cur = cur.replace( " " + clazz + " ", " " ); } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value, isValidValue = type === "string" || Array.isArray( value ); if ( typeof stateVal === "boolean" && isValidValue ) { return stateVal ? this.addClass( value ) : this.removeClass( value ); } if ( isFunction( value ) ) { return this.each( function( i ) { jQuery( this ).toggleClass( value.call( this, i, getClass( this ), stateVal ), stateVal ); } ); } return this.each( function() { var className, i, self, classNames; if ( isValidValue ) { // Toggle individual class names i = 0; self = jQuery( this ); classNames = classesToArray( value ); while ( ( className = classNames[ i++ ] ) ) { // Check each className given, space separated list if ( self.hasClass( className ) ) { self.removeClass( className ); } else { self.addClass( className ); } } // Toggle whole class name } else if ( value === undefined || type === "boolean" ) { className = getClass( this ); if ( className ) { // Store className if set dataPriv.set( this, "__className__", className ); } // If the element has a class name or if we're passed `false`, // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? "" : dataPriv.get( this, "__className__" ) || "" ); } } } ); }, hasClass: function( selector ) { var className, elem, i = 0; className = " " + selector + " "; while ( ( elem = this[ i++ ] ) ) { if ( elem.nodeType === 1 && ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { return true; } } return false; } } ); var rreturn = /\r/g; jQuery.fn.extend( { val: function( value ) { var hooks, ret, valueIsFunction, elem = this[ 0 ]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && ( ret = hooks.get( elem, "value" ) ) !== undefined ) { return ret; } ret = elem.value; // Handle most common string cases if ( typeof ret === "string" ) { return ret.replace( rreturn, "" ); } // Handle cases where value is null/undef or number return ret == null ? "" : ret; } return; } valueIsFunction = isFunction( value ); return this.each( function( i ) { var val; if ( this.nodeType !== 1 ) { return; } if ( valueIsFunction ) { val = value.call( this, i, jQuery( this ).val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( Array.isArray( val ) ) { val = jQuery.map( val, function( value ) { return value == null ? "" : value + ""; } ); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } } ); } } ); jQuery.extend( { valHooks: { option: { get: function( elem ) { var val = jQuery.find.attr( elem, "value" ); return val != null ? val : // Support: IE <=10 - 11 only // option.text throws exceptions (#14686, #14858) // Strip and collapse whitespace // https://html.spec.whatwg.org/#strip-and-collapse-whitespace stripAndCollapse( jQuery.text( elem ) ); } }, select: { get: function( elem ) { var value, option, i, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one", values = one ? null : [], max = one ? index + 1 : options.length; if ( index < 0 ) { i = max; } else { i = one ? index : 0; } // Loop through all the selected options for ( ; i < max; i++ ) { option = options[ i ]; // Support: IE <=9 only // IE8-9 doesn't update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don't return options that are disabled or in a disabled optgroup !option.disabled && ( !option.parentNode.disabled || !nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, set: function( elem, value ) { var optionSet, option, options = elem.options, values = jQuery.makeArray( value ), i = options.length; while ( i-- ) { option = options[ i ]; /* eslint-disable no-cond-assign */ if ( option.selected = jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 ) { optionSet = true; } /* eslint-enable no-cond-assign */ } // Force browsers to behave consistently when non-matching value is set if ( !optionSet ) { elem.selectedIndex = -1; } return values; } } } } ); // Radios and checkboxes getter/setter jQuery.each( [ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { set: function( elem, value ) { if ( Array.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); } } }; if ( !support.checkOn ) { jQuery.valHooks[ this ].get = function( elem ) { return elem.getAttribute( "value" ) === null ? "on" : elem.value; }; } } ); // Return jQuery for attributes-only inclusion support.focusin = "onfocusin" in window; var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, stopPropagationCallback = function( e ) { e.stopPropagation(); }; jQuery.extend( jQuery.event, { trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; cur = lastElement = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf( "." ) > -1 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf( ":" ) < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join( "." ); event.rnamespace = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { lastElement = cur; event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && dataPriv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && handle.apply && acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( ( !special._default || special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; if ( event.isPropagationStopped() ) { lastElement.addEventListener( type, stopPropagationCallback ); } elem[ type ](); if ( event.isPropagationStopped() ) { lastElement.removeEventListener( type, stopPropagationCallback ); } jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, // Piggyback on a donor event to simulate a different one // Used only for `focus(in | out)` events simulate: function( type, elem, event ) { var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true } ); jQuery.event.trigger( e, null, elem ); } } ); jQuery.fn.extend( { trigger: function( type, data ) { return this.each( function() { jQuery.event.trigger( type, data, this ); } ); }, triggerHandler: function( type, data ) { var elem = this[ 0 ]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } } ); // Support: Firefox <=44 // Firefox doesn't have focus(in | out) events // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 // // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 // focus(in | out) events fire after focus & blur events, // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 if ( !support.focusin ) { jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); }; jQuery.event.special[ fix ] = { setup: function() { // Handle: regular nodes (via `this.ownerDocument`), window // (via `this.document`) & document (via `this`). var doc = this.ownerDocument || this.document || this, attaches = dataPriv.access( doc, fix ); if ( !attaches ) { doc.addEventListener( orig, handler, true ); } dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { var doc = this.ownerDocument || this.document || this, attaches = dataPriv.access( doc, fix ) - 1; if ( !attaches ) { doc.removeEventListener( orig, handler, true ); dataPriv.remove( doc, fix ); } else { dataPriv.access( doc, fix, attaches ); } } }; } ); } var location = window.location; var nonce = { guid: Date.now() }; var rquery = ( /\?/ ); // Cross-browser xml parsing jQuery.parseXML = function( data ) { var xml, parserErrorElem; if ( !data || typeof data !== "string" ) { return null; } // Support: IE 9 - 11 only // IE throws on parseFromString with invalid input. try { xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); } catch ( e ) {} parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; if ( !xml || parserErrorElem ) { jQuery.error( "Invalid XML: " + ( parserErrorElem ? jQuery.map( parserErrorElem.childNodes, function( el ) { return el.textContent; } ).join( "\n" ) : data ) ); } return xml; }; var rbracket = /\[\]$/, rCRLF = /\r?\n/g, rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i; function buildParams( prefix, obj, traditional, add ) { var name; if ( Array.isArray( obj ) ) { // Serialize array item. jQuery.each( obj, function( i, v ) { if ( traditional || rbracket.test( prefix ) ) { // Treat each array item as a scalar. add( prefix, v ); } else { // Item is non-scalar (array or object), encode its numeric index. buildParams( prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", v, traditional, add ); } } ); } else if ( !traditional && toType( obj ) === "object" ) { // Serialize object item. for ( name in obj ) { buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); } } else { // Serialize scalar item. add( prefix, obj ); } } // Serialize an array of form elements or a set of // key/values into a query string jQuery.param = function( a, traditional ) { var prefix, s = [], add = function( key, valueOrFunction ) { // If value is a function, invoke it and use its return value var value = isFunction( valueOrFunction ) ? valueOrFunction() : valueOrFunction; s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value == null ? "" : value ); }; if ( a == null ) { return ""; } // If an array was passed in, assume that it is an array of form elements. if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { // Serialize the form elements jQuery.each( a, function() { add( this.name, this.value ); } ); } else { // If traditional, encode the "old" way (the way 1.3.2 or older // did it), otherwise encode params recursively. for ( prefix in a ) { buildParams( prefix, a[ prefix ], traditional, add ); } } // Return the resulting serialization return s.join( "&" ); }; jQuery.fn.extend( { serialize: function() { return jQuery.param( this.serializeArray() ); }, serializeArray: function() { return this.map( function() { // Can add propHook for "elements" to filter or add form elements var elements = jQuery.prop( this, "elements" ); return elements ? jQuery.makeArray( elements ) : this; } ).filter( function() { var type = this.type; // Use .is( ":disabled" ) so that fieldset[disabled] works return this.name && !jQuery( this ).is( ":disabled" ) && rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && ( this.checked || !rcheckableType.test( type ) ); } ).map( function( _i, elem ) { var val = jQuery( this ).val(); if ( val == null ) { return null; } if ( Array.isArray( val ) ) { return jQuery.map( val, function( val ) { return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ); } return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; } ).get(); } } ); var r20 = /%20/g, rhash = /#.*$/, rantiCache = /([?&])_=[^&]*/, rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, // #7653, #8125, #8152: local protocol detection rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, /* Prefilters * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) * 2) These are called: * - BEFORE asking for a transport * - AFTER param serialization (s.data is a string if s.processData is true) * 3) key is the dataType * 4) the catchall symbol "*" can be used * 5) execution will start with transport dataType and THEN continue down to "*" if needed */ prefilters = {}, /* Transports bindings * 1) key is the dataType * 2) the catchall symbol "*" can be used * 3) selection will start with transport dataType and THEN go to "*" if needed */ transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression allTypes = "*/".concat( "*" ), // Anchor tag for parsing the document origin originAnchor = document.createElement( "a" ); originAnchor.href = location.href; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; if ( isFunction( func ) ) { // For each dataType in the dataTypeExpression while ( ( dataType = dataTypes[ i++ ] ) ) { // Prepend if requested if ( dataType[ 0 ] === "+" ) { dataType = dataType.slice( 1 ) || "*"; ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); // Otherwise append } else { ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); } } } }; } // Base inspection function for prefilters and transports function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {}, seekingTransport = ( structure === transports ); function inspect( dataType ) { var selected; inspected[ dataType ] = true; jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } } ); return selected; } return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); } // A special extend for ajax options // that takes "flat" options (not to be deep extended) // Fixes #9887 function ajaxExtend( target, src ) { var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) { if ( src[ key ] !== undefined ) { ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; } } if ( deep ) { jQuery.extend( true, target, deep ); } return target; } /* Handles responses to an ajax request: * - finds the right dataType (mediates between content-type and expected dataType) * - returns the corresponding response */ function ajaxHandleResponses( s, jqXHR, responses ) { var ct, type, finalDataType, firstDataType, contents = s.contents, dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process while ( dataTypes[ 0 ] === "*" ) { dataTypes.shift(); if ( ct === undefined ) { ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); } } // Check if we're dealing with a known content-type if ( ct ) { for ( type in contents ) { if ( contents[ type ] && contents[ type ].test( ct ) ) { dataTypes.unshift( type ); break; } } } // Check to see if we have a response for the expected dataType if ( dataTypes[ 0 ] in responses ) { finalDataType = dataTypes[ 0 ]; } else { // Try convertible dataTypes for ( type in responses ) { if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { finalDataType = type; break; } if ( !firstDataType ) { firstDataType = type; } } // Or just use first one finalDataType = finalDataType || firstDataType; } // If we found a dataType // We add the dataType to the list if needed // and return the corresponding response if ( finalDataType ) { if ( finalDataType !== dataTypes[ 0 ] ) { dataTypes.unshift( finalDataType ); } return responses[ finalDataType ]; } } /* Chain conversions given the request and the original response * Also sets the responseXXX fields on the jqXHR instance */ function ajaxConvert( s, response, jqXHR, isSuccess ) { var conv2, current, conv, tmp, prev, converters = {}, // Work with a copy of dataTypes in case we need to modify it for conversion dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys if ( dataTypes[ 1 ] ) { for ( conv in s.converters ) { converters[ conv.toLowerCase() ] = s.converters[ conv ]; } } current = dataTypes.shift(); // Convert to each sequential dataType while ( current ) { if ( s.responseFields[ current ] ) { jqXHR[ s.responseFields[ current ] ] = response; } // Apply the dataFilter if provided if ( !prev && isSuccess && s.dataFilter ) { response = s.dataFilter( response, s.dataType ); } prev = current; current = dataTypes.shift(); if ( current ) { // There's only work to do if current dataType is non-auto if ( current === "*" ) { current = prev; // Convert response if prev dataType is non-auto and differs from current } else if ( prev !== "*" && prev !== current ) { // Seek a direct converter conv = converters[ prev + " " + current ] || converters[ "* " + current ]; // If none found, seek a pair if ( !conv ) { for ( conv2 in converters ) { // If conv2 outputs current tmp = conv2.split( " " ); if ( tmp[ 1 ] === current ) { // If prev can be converted to accepted input conv = converters[ prev + " " + tmp[ 0 ] ] || converters[ "* " + tmp[ 0 ] ]; if ( conv ) { // Condense equivalence converters if ( conv === true ) { conv = converters[ conv2 ]; // Otherwise, insert the intermediate dataType } else if ( converters[ conv2 ] !== true ) { current = tmp[ 0 ]; dataTypes.unshift( tmp[ 1 ] ); } break; } } } } // Apply converter (if not an equivalence) if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them if ( conv && s.throws ) { response = conv( response ); } else { try { response = conv( response ); } catch ( e ) { return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; } } } } } } return { state: "success", data: response }; } jQuery.extend( { // Counter for holding the number of active queries active: 0, // Last-Modified header cache for next request lastModified: {}, etag: {}, ajaxSettings: { url: location.href, type: "GET", isLocal: rlocalProtocol.test( location.protocol ), global: true, processData: true, async: true, contentType: "application/x-www-form-urlencoded; charset=UTF-8", /* timeout: 0, data: null, dataType: null, username: null, password: null, cache: null, throws: false, traditional: false, headers: {}, */ accepts: { "*": allTypes, text: "text/plain", html: "text/html", xml: "application/xml, text/xml", json: "application/json, text/javascript" }, contents: { xml: /\bxml\b/, html: /\bhtml/, json: /\bjson\b/ }, responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" }, // Data converters // Keys separate source (or catchall "*") and destination types with a single space converters: { // Convert anything to text "* text": String, // Text to html (true = no transformation) "text html": true, // Evaluate text as a json expression "text json": JSON.parse, // Parse text as xml "text xml": jQuery.parseXML }, // For options that shouldn't be deep extended: // you can add your own custom options here if // and when you create one that shouldn't be // deep extended (see ajaxExtend) flatOptions: { url: true, context: true } }, // Creates a full fledged settings object into target // with both ajaxSettings and settings fields. // If target is omitted, writes into ajaxSettings. ajaxSetup: function( target, settings ) { return settings ? // Building a settings object ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : // Extending ajaxSettings ajaxExtend( jQuery.ajaxSettings, target ); }, ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), ajaxTransport: addToPrefiltersOrTransports( transports ), // Main method ajax: function( url, options ) { // If url is an object, simulate pre-1.5 signature if ( typeof url === "object" ) { options = url; url = undefined; } // Force options to be an object options = options || {}; var transport, // URL without anti-cache param cacheURL, // Response headers responseHeadersString, responseHeaders, // timeout handle timeoutTimer, // Url cleanup var urlAnchor, // Request state (becomes false upon send and true upon completion) completed, // To know if global events are to be dispatched fireGlobals, // Loop variable i, // uncached part of the url uncached, // Create the final options object s = jQuery.ajaxSetup( {}, options ), // Callbacks context callbackContext = s.context || s, // Context for global events is callbackContext if it is a DOM node or jQuery collection globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? jQuery( callbackContext ) : jQuery.event, // Deferreds deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks( "once memory" ), // Status-dependent callbacks statusCode = s.statusCode || {}, // Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, // Default abort message strAbort = "canceled", // Fake xhr jqXHR = { readyState: 0, // Builds headers hashtable if needed getResponseHeader: function( key ) { var match; if ( completed ) { if ( !responseHeaders ) { responseHeaders = {}; while ( ( match = rheaders.exec( responseHeadersString ) ) ) { responseHeaders[ match[ 1 ].toLowerCase() + " " ] = ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) .concat( match[ 2 ] ); } } match = responseHeaders[ key.toLowerCase() + " " ]; } return match == null ? null : match.join( ", " ); }, // Raw string getAllResponseHeaders: function() { return completed ? responseHeadersString : null; }, // Caches the header setRequestHeader: function( name, value ) { if ( completed == null ) { name = requestHeadersNames[ name.toLowerCase() ] = requestHeadersNames[ name.toLowerCase() ] || name; requestHeaders[ name ] = value; } return this; }, // Overrides response content-type header overrideMimeType: function( type ) { if ( completed == null ) { s.mimeType = type; } return this; }, // Status-dependent callbacks statusCode: function( map ) { var code; if ( map ) { if ( completed ) { // Execute the appropriate callbacks jqXHR.always( map[ jqXHR.status ] ); } else { // Lazy-add the new callbacks in a way that preserves old ones for ( code in map ) { statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } } } return this; }, // Cancel the request abort: function( statusText ) { var finalText = statusText || strAbort; if ( transport ) { transport.abort( finalText ); } done( 0, finalText ); return this; } }; // Attach deferreds deferred.promise( jqXHR ); // Add protocol if not provided (prefilters might expect it) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available s.url = ( ( url || s.url || location.href ) + "" ) .replace( rprotocol, location.protocol + "//" ); // Alias method option to type as per ticket #12004 s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; // A cross-domain request is in order when the origin doesn't match the current origin. if ( s.crossDomain == null ) { urlAnchor = document.createElement( "a" ); // Support: IE <=8 - 11, Edge 12 - 15 // IE throws exception on accessing the href property if url is malformed, // e.g. http://example.com:80x/ try { urlAnchor.href = s.url; // Support: IE <=8 - 11 only // Anchor's host property isn't correctly set when s.url is relative urlAnchor.href = urlAnchor.href; s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host; } catch ( e ) { // If there is an error parsing the URL, assume it is crossDomain, // it can be rejected by the transport if it is invalid s.crossDomain = true; } } // Convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); } // Apply prefilters inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // If request was aborted inside a prefilter, stop there if ( completed ) { return jqXHR; } // We can fire global events as of now if asked to // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { jQuery.event.trigger( "ajaxStart" ); } // Uppercase the type s.type = s.type.toUpperCase(); // Determine if request has content s.hasContent = !rnoContent.test( s.type ); // Save the URL in case we're toying with the If-Modified-Since // and/or If-None-Match header later on // Remove hash to simplify url manipulation cacheURL = s.url.replace( rhash, "" ); // More options handling for requests with no content if ( !s.hasContent ) { // Remember the hash so we can put it back uncached = s.url.slice( cacheURL.length ); // If data is available and should be processed, append data to url if ( s.data && ( s.processData || typeof s.data === "string" ) ) { cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; // #9682: remove data so that it's not used in an eventual retry delete s.data; } // Add or update anti-cache param if needed if ( s.cache === false ) { cacheURL = cacheURL.replace( rantiCache, "$1" ); uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + uncached; } // Put hash and anti-cache on the URL that will be requested (gh-1732) s.url = cacheURL + uncached; // Change '%20' to '+' if this is encoded form body content (gh-2658) } else if ( s.data && s.processData && ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { s.data = s.data.replace( r20, "+" ); } // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { if ( jQuery.lastModified[ cacheURL ] ) { jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); } if ( jQuery.etag[ cacheURL ] ) { jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); } } // Set the correct header, if data is being sent if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { jqXHR.setRequestHeader( "Content-Type", s.contentType ); } // Set the Accepts header for the server, depending on the dataType jqXHR.setRequestHeader( "Accept", s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : s.accepts[ "*" ] ); // Check for headers option for ( i in s.headers ) { jqXHR.setRequestHeader( i, s.headers[ i ] ); } // Allow custom headers/mimetypes and early abort if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { // Abort if not done already and return return jqXHR.abort(); } // Aborting is no longer a cancellation strAbort = "abort"; // Install callbacks on deferreds completeDeferred.add( s.complete ); jqXHR.done( s.success ); jqXHR.fail( s.error ); // Get transport transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); // If no transport, we auto-abort if ( !transport ) { done( -1, "No Transport" ); } else { jqXHR.readyState = 1; // Send global event if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); } // If request was aborted inside ajaxSend, stop there if ( completed ) { return jqXHR; } // Timeout if ( s.async && s.timeout > 0 ) { timeoutTimer = window.setTimeout( function() { jqXHR.abort( "timeout" ); }, s.timeout ); } try { completed = false; transport.send( requestHeaders, done ); } catch ( e ) { // Rethrow post-completion exceptions if ( completed ) { throw e; } // Propagate others as results done( -1, e ); } } // Callback for when everything is done function done( status, nativeStatusText, responses, headers ) { var isSuccess, success, error, response, modified, statusText = nativeStatusText; // Ignore repeat invocations if ( completed ) { return; } completed = true; // Clear timeout if it exists if ( timeoutTimer ) { window.clearTimeout( timeoutTimer ); } // Dereference transport for early garbage collection // (no matter how long the jqXHR object will be used) transport = undefined; // Cache response headers responseHeadersString = headers || ""; // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; // Determine if successful isSuccess = status >= 200 && status < 300 || status === 304; // Get response data if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } // Use a noop converter for missing script but not if jsonp if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 && jQuery.inArray( "json", s.dataTypes ) < 0 ) { s.converters[ "text script" ] = function() {}; } // Convert no matter what (that way responseXXX fields are always set) response = ajaxConvert( s, response, jqXHR, isSuccess ); // If successful, handle type chaining if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { modified = jqXHR.getResponseHeader( "Last-Modified" ); if ( modified ) { jQuery.lastModified[ cacheURL ] = modified; } modified = jqXHR.getResponseHeader( "etag" ); if ( modified ) { jQuery.etag[ cacheURL ] = modified; } } // if no content if ( status === 204 || s.type === "HEAD" ) { statusText = "nocontent"; // if not modified } else if ( status === 304 ) { statusText = "notmodified"; // If we have data, let's convert it } else { statusText = response.state; success = response.data; error = response.error; isSuccess = !error; } } else { // Extract error from statusText and normalize for non-aborts error = statusText; if ( status || !statusText ) { statusText = "error"; if ( status < 0 ) { status = 0; } } } // Set data for the fake xhr object jqXHR.status = status; jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error if ( isSuccess ) { deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); } else { deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); } // Status-dependent callbacks jqXHR.statusCode( statusCode ); statusCode = undefined; if ( fireGlobals ) { globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", [ jqXHR, s, isSuccess ? success : error ] ); } // Complete completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); // Handle the global AJAX counter if ( !( --jQuery.active ) ) { jQuery.event.trigger( "ajaxStop" ); } } } return jqXHR; }, getJSON: function( url, data, callback ) { return jQuery.get( url, data, callback, "json" ); }, getScript: function( url, callback ) { return jQuery.get( url, undefined, callback, "script" ); } } ); jQuery.each( [ "get", "post" ], function( _i, method ) { jQuery[ method ] = function( url, data, callback, type ) { // Shift arguments if data argument was omitted if ( isFunction( data ) ) { type = type || callback; callback = data; data = undefined; } // The url can be an options object (which then must have .url) return jQuery.ajax( jQuery.extend( { url: url, type: method, dataType: type, data: data, success: callback }, jQuery.isPlainObject( url ) && url ) ); }; } ); jQuery.ajaxPrefilter( function( s ) { var i; for ( i in s.headers ) { if ( i.toLowerCase() === "content-type" ) { s.contentType = s.headers[ i ] || ""; } } } ); jQuery._evalUrl = function( url, options, doc ) { return jQuery.ajax( { url: url, // Make this explicit, since user can override this through ajaxSetup (#11264) type: "GET", dataType: "script", cache: true, async: false, global: false, // Only evaluate the response if it is successful (gh-4126) // dataFilter is not invoked for failure responses, so using it instead // of the default converter is kludgy but it works. converters: { "text script": function() {} }, dataFilter: function( response ) { jQuery.globalEval( response, options, doc ); } } ); }; jQuery.fn.extend( { wrapAll: function( html ) { var wrap; if ( this[ 0 ] ) { if ( isFunction( html ) ) { html = html.call( this[ 0 ] ); } // The elements to wrap the target around wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); if ( this[ 0 ].parentNode ) { wrap.insertBefore( this[ 0 ] ); } wrap.map( function() { var elem = this; while ( elem.firstElementChild ) { elem = elem.firstElementChild; } return elem; } ).append( this ); } return this; }, wrapInner: function( html ) { if ( isFunction( html ) ) { return this.each( function( i ) { jQuery( this ).wrapInner( html.call( this, i ) ); } ); } return this.each( function() { var self = jQuery( this ), contents = self.contents(); if ( contents.length ) { contents.wrapAll( html ); } else { self.append( html ); } } ); }, wrap: function( html ) { var htmlIsFunction = isFunction( html ); return this.each( function( i ) { jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); } ); }, unwrap: function( selector ) { this.parent( selector ).not( "body" ).each( function() { jQuery( this ).replaceWith( this.childNodes ); } ); return this; } } ); jQuery.expr.pseudos.hidden = function( elem ) { return !jQuery.expr.pseudos.visible( elem ); }; jQuery.expr.pseudos.visible = function( elem ) { return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); }; jQuery.ajaxSettings.xhr = function() { try { return new window.XMLHttpRequest(); } catch ( e ) {} }; var xhrSuccessStatus = { // File protocol always yields status code 0, assume 200 0: 200, // Support: IE <=9 only // #1450: sometimes IE returns 1223 when it should be 204 1223: 204 }, xhrSupported = jQuery.ajaxSettings.xhr(); support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); support.ajax = xhrSupported = !!xhrSupported; jQuery.ajaxTransport( function( options ) { var callback, errorCallback; // Cross domain only allowed if supported through XMLHttpRequest if ( support.cors || xhrSupported && !options.crossDomain ) { return { send: function( headers, complete ) { var i, xhr = options.xhr(); xhr.open( options.type, options.url, options.async, options.username, options.password ); // Apply custom fields if provided if ( options.xhrFields ) { for ( i in options.xhrFields ) { xhr[ i ] = options.xhrFields[ i ]; } } // Override mime type if needed if ( options.mimeType && xhr.overrideMimeType ) { xhr.overrideMimeType( options.mimeType ); } // X-Requested-With header // For cross-domain requests, seeing as conditions for a preflight are // akin to a jigsaw puzzle, we simply never set it to be sure. // (it can always be set on a per-request basis or even using ajaxSetup) // For same-domain requests, won't change header if already provided. if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { headers[ "X-Requested-With" ] = "XMLHttpRequest"; } // Set headers for ( i in headers ) { xhr.setRequestHeader( i, headers[ i ] ); } // Callback callback = function( type ) { return function() { if ( callback ) { callback = errorCallback = xhr.onload = xhr.onerror = xhr.onabort = xhr.ontimeout = xhr.onreadystatechange = null; if ( type === "abort" ) { xhr.abort(); } else if ( type === "error" ) { // Support: IE <=9 only // On a manual native abort, IE9 throws // errors on any property access that is not readyState if ( typeof xhr.status !== "number" ) { complete( 0, "error" ); } else { complete( // File: protocol always yields status 0; see #8605, #14207 xhr.status, xhr.statusText ); } } else { complete( xhrSuccessStatus[ xhr.status ] || xhr.status, xhr.statusText, // Support: IE <=9 only // IE9 has no XHR2 but throws on binary (trac-11426) // For XHR2 non-text, let the caller handle it (gh-2498) ( xhr.responseType || "text" ) !== "text" || typeof xhr.responseText !== "string" ? { binary: xhr.response } : { text: xhr.responseText }, xhr.getAllResponseHeaders() ); } } }; }; // Listen to events xhr.onload = callback(); errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); // Support: IE 9 only // Use onreadystatechange to replace onabort // to handle uncaught aborts if ( xhr.onabort !== undefined ) { xhr.onabort = errorCallback; } else { xhr.onreadystatechange = function() { // Check readyState before timeout as it changes if ( xhr.readyState === 4 ) { // Allow onerror to be called first, // but that will not handle a native abort // Also, save errorCallback to a variable // as xhr.onerror cannot be accessed window.setTimeout( function() { if ( callback ) { errorCallback(); } } ); } }; } // Create the abort callback callback = callback( "abort" ); try { // Do send the request (this may raise an exception) xhr.send( options.hasContent && options.data || null ); } catch ( e ) { // #14683: Only rethrow if this hasn't been notified as an error yet if ( callback ) { throw e; } } }, abort: function() { if ( callback ) { callback(); } } }; } } ); // Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) jQuery.ajaxPrefilter( function( s ) { if ( s.crossDomain ) { s.contents.script = false; } } ); // Install script dataType jQuery.ajaxSetup( { accepts: { script: "text/javascript, application/javascript, " + "application/ecmascript, application/x-ecmascript" }, contents: { script: /\b(?:java|ecma)script\b/ }, converters: { "text script": function( text ) { jQuery.globalEval( text ); return text; } } } ); // Handle cache's special case and crossDomain jQuery.ajaxPrefilter( "script", function( s ) { if ( s.cache === undefined ) { s.cache = false; } if ( s.crossDomain ) { s.type = "GET"; } } ); // Bind script tag hack transport jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain or forced-by-attrs requests if ( s.crossDomain || s.scriptAttrs ) { var script, callback; return { send: function( _, complete ) { script = jQuery( " Skip to contents

    santoku is a versatile cutting tool for R. It provides chop(), a replacement for base::cut().

    Installation

    Install from r-universe:

    install.packages("santoku", repos = c("https://hughjonesd.r-universe.dev", 
                                          "https://cloud.r-project.org"))

    Or from CRAN:

    install.packages("santoku")

    Or get the development version from github:

    # install.packages("remotes")
    remotes::install_github("hughjonesd/santoku")

    Advantages

    Here are some advantages of santoku:

    • By default, chop() always covers the whole range of the data, so you won’t get unexpected NA values.

    • chop() can handle single values as well as intervals. For example, chop(x, breaks = c(1, 2, 2, 3)) will create a separate factor level for values exactly equal to 2.

    • chop() can handle many kinds of data, including numbers, dates and times, and units.

    • chop_* functions create intervals in many ways, using quantiles of the data, standard deviations, fixed-width intervals, equal-sized groups, or pretty intervals for use in graphs.

    • It’s easy to label intervals: use names for your breaks vector, or use a lbl_* function to create interval notation like [1, 2), dash notation like 1-2, or arbitrary styles using glue::glue().

    • tab_* functions quickly chop data, then tabulate it.

    These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance.

    Examples

    chop returns a factor:

    chop(1:5, c(2, 4))
    #> [1] [1, 2) [2, 4) [2, 4) [4, 5] [4, 5]
    #> Levels: [1, 2) [2, 4) [4, 5]

    Include a number twice to match it exactly:

    chop(1:5, c(2, 2, 4))
    #> [1] [1, 2) {2}    (2, 4) [4, 5] [4, 5]
    #> Levels: [1, 2) {2} (2, 4) [4, 5]

    Use names in breaks for labels:

    chop(1:5, c(Low = 1, Mid = 2, High = 4))
    #> [1] Low  Mid  Mid  High High
    #> Levels: Low Mid High

    Or use lbl_* functions:

    chop(1:5, c(2, 4), labels = lbl_dash())
    #> [1] 1—2 2—4 2—4 4—5 4—5
    #> Levels: 1—2 2—4 4—5

    Chop into fixed-width intervals:

    chop_width(runif(10), 0.1)
    #>  [1] [0.368, 0.468)   [0.268, 0.368)   [0.768, 0.868]   [0.568, 0.668)  
    #>  [5] [0.668, 0.768)   [0.768, 0.868]   [0.06801, 0.168) [0.668, 0.768)  
    #>  [9] [0.06801, 0.168) [0.468, 0.568)  
    #> 7 Levels: [0.06801, 0.168) [0.268, 0.368) [0.368, 0.468) ... [0.768, 0.868]

    Or into fixed-size groups:

    chop_n(1:10, 5)
    #>  [1] [1, 6)  [1, 6)  [1, 6)  [1, 6)  [1, 6)  [6, 10] [6, 10] [6, 10] [6, 10]
    #> [10] [6, 10]
    #> Levels: [1, 6) [6, 10]

    Chop dates by calendar month, then tabulate:

    library(lubridate)
    #> 
    #> Attaching package: 'lubridate'
    #> The following objects are masked from 'package:base':
    #> 
    #>     date, intersect, setdiff, union
    
    dates <- as.Date("2021-12-31") + 1:90
    
    tab_width(dates, months(1), labels = lbl_discrete(fmt = "%d %b"))
    #> 01 Jan—31 Jan 01 Feb—28 Feb 01 Mar—31 Mar 
    #>            31            28            31

    For more information, see the vignette.

    ================================================ FILE: docs/index.md ================================================ # santoku santoku is a versatile cutting tool for R. It provides [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), a replacement for [`base::cut()`](https://rdrr.io/r/base/cut.html). ## Installation Install from [r-universe](https://r-universe.dev): ``` r install.packages("santoku", repos = c("https://hughjonesd.r-universe.dev", "https://cloud.r-project.org")) ``` Or from CRAN: ``` r install.packages("santoku") ``` Or get the development version from github: ``` r # install.packages("remotes") remotes::install_github("hughjonesd/santoku") ``` ## Advantages Here are some advantages of santoku: - By default, [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) always covers the whole range of the data, so you won’t get unexpected `NA` values. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) can handle single values as well as intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a separate factor level for values exactly equal to 2. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) can handle many kinds of data, including numbers, dates and times, and [units](https://r-quantities.github.io/units/). - `chop_*` functions create intervals in many ways, using quantiles of the data, standard deviations, fixed-width intervals, equal-sized groups, or pretty intervals for use in graphs. - It’s easy to label intervals: use names for your breaks vector, or use a `lbl_*` function to create interval notation like `[1, 2)`, dash notation like `1-2`, or arbitrary styles using [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html). - `tab_*` functions quickly chop data, then tabulate it. These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. ## Examples ``` r library(santoku) ``` `chop` returns a factor: ``` r chop(1:5, c(2, 4)) #> [1] [1, 2) [2, 4) [2, 4) [4, 5] [4, 5] #> Levels: [1, 2) [2, 4) [4, 5] ``` Include a number twice to match it exactly: ``` r chop(1:5, c(2, 2, 4)) #> [1] [1, 2) {2} (2, 4) [4, 5] [4, 5] #> Levels: [1, 2) {2} (2, 4) [4, 5] ``` Use names in breaks for labels: ``` r chop(1:5, c(Low = 1, Mid = 2, High = 4)) #> [1] Low Mid Mid High High #> Levels: Low Mid High ``` Or use `lbl_*` functions: ``` r chop(1:5, c(2, 4), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—5 4—5 #> Levels: 1—2 2—4 4—5 ``` Chop into fixed-width intervals: ``` r chop_width(runif(10), 0.1) #> [1] [0.368, 0.468) [0.268, 0.368) [0.768, 0.868] [0.568, 0.668) #> [5] [0.668, 0.768) [0.768, 0.868] [0.06801, 0.168) [0.668, 0.768) #> [9] [0.06801, 0.168) [0.468, 0.568) #> 7 Levels: [0.06801, 0.168) [0.268, 0.368) [0.368, 0.468) ... [0.768, 0.868] ``` Or into fixed-size groups: ``` r chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] ``` Chop dates by calendar month, then tabulate: ``` r library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union dates <- as.Date("2021-12-31") + 1:90 tab_width(dates, months(1), labels = lbl_discrete(fmt = "%d %b")) #> 01 Jan—31 Jan 01 Feb—28 Feb 01 Mar—31 Mar #> 31 28 31 ``` For more information, see the [vignette](https://hughjonesd.github.io/santoku/articles/santoku.html). ================================================ FILE: docs/katex-auto.js ================================================ // https://github.com/jgm/pandoc/blob/29fa97ab96b8e2d62d48326e1b949a71dc41f47a/src/Text/Pandoc/Writers/HTML.hs#L332-L345 document.addEventListener("DOMContentLoaded", function () { var mathElements = document.getElementsByClassName("math"); var macros = []; for (var i = 0; i < mathElements.length; i++) { var texText = mathElements[i].firstChild; if (mathElements[i].tagName == "SPAN") { katex.render(texText.data, mathElements[i], { displayMode: mathElements[i].classList.contains("display"), throwOnError: false, macros: macros, fleqn: false }); } } }); ================================================ FILE: docs/lightswitch.js ================================================ /*! * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors * Licensed under the Creative Commons Attribution 3.0 Unported License. * Updates for {pkgdown} by the {bslib} authors, also licensed under CC-BY-3.0. */ const getStoredTheme = () => localStorage.getItem('theme') const setStoredTheme = theme => localStorage.setItem('theme', theme) const getPreferredTheme = () => { const storedTheme = getStoredTheme() if (storedTheme) { return storedTheme } return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' } const setTheme = theme => { if (theme === 'auto') { document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) } else { document.documentElement.setAttribute('data-bs-theme', theme) } } function bsSetupThemeToggle() { 'use strict' const showActiveTheme = (theme, focus = false) => { var activeLabel, activeIcon; document.querySelectorAll('[data-bs-theme-value]').forEach(element => { const buttonTheme = element.getAttribute('data-bs-theme-value') const isActive = buttonTheme == theme element.classList.toggle('active', isActive) element.setAttribute('aria-pressed', isActive) if (isActive) { activeLabel = element.textContent; activeIcon = element.querySelector('span').classList.value; } }) const themeSwitcher = document.querySelector('#dropdown-lightswitch') if (!themeSwitcher) { return } themeSwitcher.setAttribute('aria-label', activeLabel) themeSwitcher.querySelector('span').classList.value = activeIcon; if (focus) { themeSwitcher.focus() } } window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { const storedTheme = getStoredTheme() if (storedTheme !== 'light' && storedTheme !== 'dark') { setTheme(getPreferredTheme()) } }) window.addEventListener('DOMContentLoaded', () => { showActiveTheme(getPreferredTheme()) document .querySelectorAll('[data-bs-theme-value]') .forEach(toggle => { toggle.addEventListener('click', () => { const theme = toggle.getAttribute('data-bs-theme-value') setTheme(theme) setStoredTheme(theme) showActiveTheme(theme, true) }) }) }) } setTheme(getPreferredTheme()); bsSetupThemeToggle(); ================================================ FILE: docs/llms.txt ================================================ # santoku santoku is a versatile cutting tool for R. It provides [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), a replacement for [`base::cut()`](https://rdrr.io/r/base/cut.html). ## Installation Install from [r-universe](https://r-universe.dev): ``` r install.packages("santoku", repos = c("https://hughjonesd.r-universe.dev", "https://cloud.r-project.org")) ``` Or from CRAN: ``` r install.packages("santoku") ``` Or get the development version from github: ``` r # install.packages("remotes") remotes::install_github("hughjonesd/santoku") ``` ## Advantages Here are some advantages of santoku: - By default, [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) always covers the whole range of the data, so you won’t get unexpected `NA` values. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) can handle single values as well as intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a separate factor level for values exactly equal to 2. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) can handle many kinds of data, including numbers, dates and times, and [units](https://r-quantities.github.io/units/). - `chop_*` functions create intervals in many ways, using quantiles of the data, standard deviations, fixed-width intervals, equal-sized groups, or pretty intervals for use in graphs. - It’s easy to label intervals: use names for your breaks vector, or use a `lbl_*` function to create interval notation like `[1, 2)`, dash notation like `1-2`, or arbitrary styles using [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html). - `tab_*` functions quickly chop data, then tabulate it. These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. ## Examples ``` r library(santoku) ``` `chop` returns a factor: ``` r chop(1:5, c(2, 4)) #> [1] [1, 2) [2, 4) [2, 4) [4, 5] [4, 5] #> Levels: [1, 2) [2, 4) [4, 5] ``` Include a number twice to match it exactly: ``` r chop(1:5, c(2, 2, 4)) #> [1] [1, 2) {2} (2, 4) [4, 5] [4, 5] #> Levels: [1, 2) {2} (2, 4) [4, 5] ``` Use names in breaks for labels: ``` r chop(1:5, c(Low = 1, Mid = 2, High = 4)) #> [1] Low Mid Mid High High #> Levels: Low Mid High ``` Or use `lbl_*` functions: ``` r chop(1:5, c(2, 4), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—5 4—5 #> Levels: 1—2 2—4 4—5 ``` Chop into fixed-width intervals: ``` r chop_width(runif(10), 0.1) #> [1] [0.368, 0.468) [0.268, 0.368) [0.768, 0.868] [0.568, 0.668) #> [5] [0.668, 0.768) [0.768, 0.868] [0.06801, 0.168) [0.668, 0.768) #> [9] [0.06801, 0.168) [0.468, 0.568) #> 7 Levels: [0.06801, 0.168) [0.268, 0.368) [0.368, 0.468) ... [0.768, 0.868] ``` Or into fixed-size groups: ``` r chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] ``` Chop dates by calendar month, then tabulate: ``` r library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union dates <- as.Date("2021-12-31") + 1:90 tab_width(dates, months(1), labels = lbl_discrete(fmt = "%d %b")) #> 01 Jan—31 Jan 01 Feb—28 Feb 01 Mar—31 Mar #> 31 28 31 ``` For more information, see the [vignette](https://hughjonesd.github.io/santoku/articles/santoku.html). # Package index ## Package overview - [`santoku`](https://hughjonesd.github.io/santoku/reference/santoku-package.md) [`santoku-package`](https://hughjonesd.github.io/santoku/reference/santoku-package.md) : A versatile cutting tool for R: package overview and options ## Basic chop functions Cut a vector into intervals - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md) [`tab()`](https://hughjonesd.github.io/santoku/reference/chop.md) : Cut data into intervals - [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) : Chop data precisely (for programmers) ## Chopping by width Cut a vector into intervals defined by width - [`brk_width(`*``*`)`](https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.md) : Equal-width intervals for dates or datetimes - [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) [`brk_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) [`tab_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) : Chop into fixed-width intervals - [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) [`brk_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) [`tab_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) : Chop into proportions of the range of x - [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) [`brk_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) [`tab_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) : Chop into equal-width intervals ## Chopping by n Cut a vector into intervals defined by number of elements - [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) [`brk_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) [`tab_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) : Chop into fixed-sized groups - [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`chop_deciles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`tab_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`tab_deciles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) : Chop by quantiles - [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) [`brk_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) [`tab_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) : Chop equal-sized groups ## Chopping and separating Cut a vector into intervals, separating out common values - [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) [`brk_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) [`tab_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) : Chop common values into singleton intervals - [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) [`tab_dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) : Cut data into intervals, separating out common values ## Other chop functions Miscellaneous ways to cut a vector into intervals - [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) [`brk_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) [`tab_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) : Chop by standard deviations - [`chop_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) [`brk_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) [`tab_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) : Chop using pretty breakpoints - [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) [`brk_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) [`tab_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) : Chop using an existing function - [`brk_default()`](https://hughjonesd.github.io/santoku/reference/brk_default.md) : Create a standard set of breaks - [`brk_manual()`](https://hughjonesd.github.io/santoku/reference/brk_manual.md) : Create a `breaks` object manually ## Label functions Specify how to label the chopped intervals - [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) : Label chopped intervals like 1-4, 4-5, ... - [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) [`lbl_datetime()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) **\[experimental\]** : Label dates and datetimes - [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) : Label discrete data - [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) [`lbl_endpoint()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) : Label chopped intervals by their left or right endpoints - [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) : Label chopped intervals using the `glue` package - [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md) : Label chopped intervals using set notation - [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md) : Label chopped intervals by their midpoints - [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) : Label chopped intervals in sequence ## Miscellaneous Other helper functions - [`format(`*``*`)`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) [`print(`*``*`)`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) [`is.breaks()`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) : Class representing a set of intervals - [`exactly()`](https://hughjonesd.github.io/santoku/reference/exactly.md) : Define singleton intervals explicitly - [`non-standard-types`](https://hughjonesd.github.io/santoku/reference/non-standard-types.md) : Tips for chopping non-standard types - [`percent()`](https://hughjonesd.github.io/santoku/reference/percent.md) : Simple percentage formatter # Articles ### All vignettes - [Performance](https://hughjonesd.github.io/santoku/articles/website-articles/performance.md): - [Introduction to santoku](https://hughjonesd.github.io/santoku/articles/santoku.md): - [What's new in santoku 0.9.0](https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.md): ================================================ FILE: docs/news/index.html ================================================ Changelog • santoku Skip to contents

    santoku 1.2.0

    • New experimental lbl_date() and lbl_datetime() functions for pretty formatting of dates and date-times.
    • Bugfix: extended breaks were failing on haven::labelled objects.
    • The raw argument to lbl_* functions, deprecated since 0.9.0, now throws an error.

    santoku 1.1.0

    CRAN release: 2025-09-11

    • Core logic has been speeded up using raw pointers. This was vibe-coded by me and Claude Code. If it breaks, please file a bug report.
    • The experimental chop_spikes() and dissect() functions give common values of x their own singleton intervals.
    • On Unicode platforms, infinity will be represented as ∞ in breaks. Set options(santoku.infinity = "Inf") to use the old behaviour.
    • Singleton breaks are not labelled specially by default in chop_quantiles(..., raw = FALSE). This means that e.g. if the 10th and 20th percentiles are both the same number, the label will still be [10%, 20%].
    • When multiple quantiles are the same, santoku warns and returns the leftmost quantile interval. Before it would merge the intervals, creating labels that might be different to what the user asked for.
    • chop_quantiles() gains a recalc_probs argument. recalc_probs = TRUE recalculates probabilities using ecdf(x), which may give more accurate interval labels.
    • single = NULL has been documented explicitly in lbl_* functions.
    • Bugfix: brk_manual() no longer warns if close_end = TRUE (the default).

    santoku 1.0.0

    CRAN release: 2024-06-04

    • santoku is now considered stable.
    • chop_quantiles() and brk_quantiles() gain a new weights argument, letting you chop by weighted quantiles using Hmisc::wtd.quantile().
    • brk_quantiles() may now return singleton breaks, producing more accurate results when x has duplicate elements.
    • Some deprecated functions have been removed, and the raw argument to lbl_* functions now always gives a deprecation warning.

    santoku 0.10.0

    CRAN release: 2023-10-12

    • List arguments to fmt in lbl_* functions will be taken as arguments to base::format. This gives more flexibility in formatting, e.g., units breaks.
    • chop_n() gains a tail argument, to deal with a last interval containing less than n elements. Set tail = "merge" to merge it with the previous interval. This guarantees that all intervals contain at least n elements.
    • chop_equally() may return fewer than groups groups when there are duplicate elements. We now warn when this happens.
    • Bugfix: chop_n() could return intervals with fewer than n elements when there were duplicate elements. The new algorithm avoids this, but may be slower in this case.

    santoku 0.9.1

    CRAN release: 2023-03-08

    • endpoint_labels() methods gain an unused ... argument to satisfy R CMD CHECK.

    santoku 0.9.0

    CRAN release: 2022-11-01

    Breaking changes

    There are important changes to close_end.

    • close_end is now TRUE by default in chop() and fillet(). In previous versions:

      chop(1:2, 1:2)
      ## [1] [1, 2) {2}   
      ## Levels: [1, 2) {2}  

      Whereas now:

      chop(1:2, 1:2)
      ## [1] [1, 2] [1, 2]
      ## Levels: [1, 2]
    • close_end is now always applied after extend. For example, in previous versions:

      chop(1:4, 2:3, close_end = TRUE)
      ## [1] [1, 2) [2, 3] [2, 3] (3, 4]
      ## Levels: [1, 2) [2, 3] (3, 4]

      Whereas now:

      chop(1:4, 2:3, close_end = TRUE)
      ## [1] [1, 2) [2, 3) [3, 4] [3, 4]
      ## Levels: [1, 2) [2, 3) [3, 4]

    We changed this behaviour to be more in line with user expectations.

    • If breaks has names, they will be used as labels:

      chop(1:5, c(Low = 1, Mid = 2, High = 4))
      ## [1] Low  Mid  Mid  High High
      ## Levels: Low Mid High  

      Names can also be used for labels in probs in chop_quantiles() and proportions in chop_proportions().

    • There is a new raw parameter to chop(). This replaces the parameter raw in lbl_* functions, which is now soft-deprecated.

    • lbl_manual() is deprecated. Just use a vector argument to labels instead.

    • A labels argument to chop_quantiles() now needs to be explicitly named.

    I expect these to be the last important breaking changes before we release version 1.0 and mark the package as “stable”. If they cause problems for you, please file an issue.

    Other changes

    santoku 0.8.0

    CRAN release: 2022-06-08

    Breaking changes

    Other changes

    santoku 0.7.0

    CRAN release: 2022-03-18

    Breaking changes

    • In labelling functions, first and last arguments are now passed to glue::glue(). Variables l and r represent the left and right endpoints of the intervals.
    • chop_mean_sd() now takes a vector sds of standard deviations, rather than a single maximum number sd of standard deviations. Write e.g.  chop_mean_sd(sds = 1:3) rather than chop_mean_sd(sd = 3). The sd argument is deprecated.
    • The groups argument to chop_evenly(), deprecated in 0.4.0, has been removed.
    • brk_left() and brk_right(), deprecated in 0.4.0, have been removed.
    • knife(), deprecated in 0.4.0, has been removed.
    • lbl_format(), questioning since 0.4.0, has been removed.
    • Arguments of lbl_dash() and lbl_intervals() have been reordered for consistency with other labelling functions.

    Other changes

    • You can now chop many more types, including units from the units package, difftime objects, package_version objects, etc.
      • Character vectors will be chopped by lexicographic order, with an optional warning.
      • If you have problems chopping a vector type, file a bug report.
    • The glue package has become a hard dependency. It is used in many places to format labels.
    • There is a new lbl_glue() function using the glue package. Thanks to @dpprdan.
    • You can now set labels = NULL to return integer codes.
    • Arguments first, last and single can be used in lbl_intervals() and lbl_dash(), to override the first and last interval labels, or to label singleton intervals.
    • lbl_dash() and lbl_discrete() use unicode em-dash where possible.
    • brk_default() throws an error if breaks are not sorted.

    Bugfixes

    • Bugfix: tab() and friends no longer display an x as the variable name.
    • Bugfix: lbl_endpoint() was erroring for some types of breaks.

    santoku 0.6.0

    CRAN release: 2021-11-04

    • New arguments first and last in lbl_dash() and lbl_discrete() allow you to override the first and last interval labels.

    • Fixes for CRAN.

    santoku 0.5.0

    CRAN release: 2020-08-27

    • Negative numbers can be used in chop_width().
      • This sets left = FALSE by default.
      • Also works for negative time intervals.

    santoku 0.4.1

    CRAN release: 2020-06-16

    • Bugfix: chop(1:4, 1) was erroring.

    santoku 0.4.0

    CRAN release: 2020-06-09

    Interface changes

    The new version has some interface changes. These are based on user experience, and are designed to make using chop() more intuitive and predictable.

    • chop() has two new arguments, left and close_end.

      • Using left = FALSE is simpler and more intuitive than wrapping breaks in brk_right().
      • brk_left() and brk_right() have been kept for now, but cannot be used to wrap other break functions.
      • Using close_end is simpler than passing close_end into brk_left() or brk_right() (which no longer accept this argument directly).
      • left = TRUE by default, except for non-numeric objects in chop_quantiles() and chop_equally(), where left = FALSE works better.
    • close_end is now FALSE by default.

      • This prevents user surprises when e.g. chop(3, 1:3) puts 3 into a different category than chop(3, 1:4).
      • close_end is TRUE by default for chop_quantiles(), chop_n() and similar functions. This ensures that e.g.  chop_quantiles(x, c(0, 1/3, 2/3, 1)) does what you would expect.
    • The groups argument to chop_evenly() has been renamed from groups to intervals. This should make it easier to remember the difference between chop_evenly() and chop_equally(). (Chop evenly into n equal-width intervals, or chop equally into n equal-sized groups.)

    • knife() has been deprecated to keep the interface slim and focused. Use purrr::partial() instead.

    Other changes

    santoku 0.3.0

    CRAN release: 2020-01-24

    • First CRAN release.

    • Changed kut() to kiru(). kiru() is an alternative spelling for chop(), for use when the tidyr package is loaded.

    • lbl_sequence() has become lbl_manual().

    • lbl_letters() and friends have been replaced by lbl_seq():

      • to replace lbl_letters() use lbl_seq()
      • to replace lbl_LETTERS() use lbl_seq("A")
      • to replace lbl_roman() use lbl_seq("i")
      • to replace lbl_ROMAN() use lbl_seq("I")
      • to replace lbl_numerals() use lbl_seq("1")
      • for more complex formatting use e.g. lbl_seq("A:"), lbl_seq("(i)")

    santoku 0.2.0

    • Added a NEWS.md file to track changes to the package.

    • Default labels when extend = NULL have changed, from [-Inf, ... and ..., Inf] to [min(x), ... and ..., max(x)].

    ================================================ FILE: docs/news/index.md ================================================ # Changelog ## santoku 1.2.0 - New experimental [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) and [`lbl_datetime()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) functions for pretty formatting of dates and date-times. - Bugfix: extended breaks were failing on [`haven::labelled`](https://haven.tidyverse.org/reference/labelled.html) objects. - The `raw` argument to `lbl_*` functions, deprecated since 0.9.0, now throws an error. ## santoku 1.1.0 CRAN release: 2025-09-11 - Core logic has been speeded up using raw pointers. This was vibe-coded by me and Claude Code. If it breaks, please file a bug report. - The experimental [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) and [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) functions give common values of `x` their own singleton intervals. - On Unicode platforms, infinity will be represented as ∞ in breaks. Set `options(santoku.infinity = "Inf")` to use the old behaviour. - Singleton breaks are not labelled specially by default in `chop_quantiles(..., raw = FALSE)`. This means that e.g. if the 10th and 20th percentiles are both the same number, the label will still be `[10%, 20%]`. - When multiple quantiles are the same, santoku warns and returns the leftmost quantile interval. Before it would merge the intervals, creating labels that might be different to what the user asked for. - [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) gains a `recalc_probs` argument. `recalc_probs = TRUE` recalculates probabilities using `ecdf(x)`, which may give more accurate interval labels. - `single = NULL` has been documented explicitly in `lbl_*` functions. - Bugfix: [`brk_manual()`](https://hughjonesd.github.io/santoku/reference/brk_manual.md) no longer warns if `close_end = TRUE` (the default). ## santoku 1.0.0 CRAN release: 2024-06-04 - santoku is now considered stable. - [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) and [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) gain a new `weights` argument, letting you chop by weighted quantiles using [`Hmisc::wtd.quantile()`](https://rdrr.io/pkg/Hmisc/man/wtd.stats.html). - [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) may now return singleton breaks, producing more accurate results when `x` has duplicate elements. - Some deprecated functions have been removed, and the `raw` argument to `lbl_*` functions now always gives a deprecation warning. ## santoku 0.10.0 CRAN release: 2023-10-12 - List arguments to `fmt` in `lbl_*` functions will be taken as arguments to [`base::format`](https://rdrr.io/r/base/format.html). This gives more flexibility in formatting, e.g., `units` breaks. - [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) gains a `tail` argument, to deal with a last interval containing less than `n` elements. Set `tail = "merge"` to merge it with the previous interval. This guarantees that all intervals contain at least `n` elements. - [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) may return fewer than `groups` groups when there are duplicate elements. We now warn when this happens. - Bugfix: [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) could return intervals with fewer than `n` elements when there were duplicate elements. The new algorithm avoids this, but may be slower in this case. ## santoku 0.9.1 CRAN release: 2023-03-08 - `endpoint_labels()` methods gain an unused `...` argument to satisfy R CMD CHECK. ## santoku 0.9.0 CRAN release: 2022-11-01 ### Breaking changes There are important changes to `close_end`. - `close_end` is now `TRUE` by default in [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) and [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md). In previous versions: ``` r chop(1:2, 1:2) ## [1] [1, 2) {2} ## Levels: [1, 2) {2} ``` Whereas now: ``` r chop(1:2, 1:2) ## [1] [1, 2] [1, 2] ## Levels: [1, 2] ``` - `close_end` is now always applied after `extend`. For example, in previous versions: ``` r chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3] [2, 3] (3, 4] ## Levels: [1, 2) [2, 3] (3, 4] ``` Whereas now: ``` r chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3) [3, 4] [3, 4] ## Levels: [1, 2) [2, 3) [3, 4] ``` We changed this behaviour to be more in line with user expectations. - If `breaks` has names, they will be used as labels: ``` r chop(1:5, c(Low = 1, Mid = 2, High = 4)) ## [1] Low Mid Mid High High ## Levels: Low Mid High ``` Names can also be used for labels in `probs` in [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) and `proportions` in [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md). - There is a new `raw` parameter to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). This replaces the parameter `raw` in `lbl_*` functions, which is now soft-deprecated. - [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md) is deprecated. Just use a vector argument to `labels` instead. - A `labels` argument to [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) now needs to be explicitly named. I expect these to be the last important breaking changes before we release version 1.0 and mark the package as “stable”. If they cause problems for you, please file an issue. ### Other changes - New [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`brk_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) and [`tab_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) chop using an arbitrary function. - Added section on non-standard objects to vignette. ## santoku 0.8.0 CRAN release: 2022-06-08 ### Breaking changes - [`lbl_endpoint()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) has been renamed to [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md). The old version will trigger a deprecation warning. [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) gains `first`, `last` and `single` arguments like other labelling functions. ### Other changes - New [`chop_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md), [`brk_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) and [`tab_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) functions use [`base::pretty()`](https://rdrr.io/r/base/pretty.html) to calculate attractive breakpoints. Thanks [@davidhodge931](https://github.com/davidhodge931). - New [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`brk_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) and [`tab_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) functions chop `x` into proportions of its range. - [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) now uses `lbl_intervals(raw = TRUE)` by default, bringing it into line with [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) and [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md). - New [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md) function labels breaks by their midpoints. - [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) gains a `single` argument. - You can now chop `ts`, [`xts::xts`](https://rdrr.io/pkg/xts/man/xts.html) and [`zoo::zoo`](https://rdrr.io/pkg/zoo/man/zoo.html) objects. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) is more forgiving when mixing different types, e.g.: - `Date` objects with `POSIXct` breaks, and vice versa - [`bit64::integer64`](https://bit64.r-lib.org/reference/bit64-package.html) and `double`s - Bugfix: [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) sometimes had ugly label formatting. ## santoku 0.7.0 CRAN release: 2022-03-18 ### Breaking changes - In labelling functions, `first` and `last` arguments are now passed to [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html). Variables `l` and `r` represent the left and right endpoints of the intervals. - [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) now takes a vector `sds` of standard deviations, rather than a single maximum number `sd` of standard deviations. Write e.g.  `chop_mean_sd(sds = 1:3)` rather than `chop_mean_sd(sd = 3)`. The `sd` argument is deprecated. - The `groups` argument to [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), deprecated in 0.4.0, has been removed. - `brk_left()` and `brk_right()`, deprecated in 0.4.0, have been removed. - `knife()`, deprecated in 0.4.0, has been removed. - `lbl_format()`, questioning since 0.4.0, has been removed. - Arguments of [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) and [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md) have been reordered for consistency with other labelling functions. ### Other changes - You can now chop many more types, including `units` from the `units` package, `difftime` objects, `package_version` objects, etc. - Character vectors will be chopped by lexicographic order, with an optional warning. - If you have problems chopping a vector type, file a bug report. - The [glue](https://glue.tidyverse.org/) package has become a hard dependency. It is used in many places to format labels. - There is a new [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) function using the [glue](https://glue.tidyverse.org/) package. Thanks to [@dpprdan](https://github.com/dpprdan). - You can now set `labels = NULL` to return integer codes. - Arguments `first`, `last` and `single` can be used in [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md) and [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), to override the first and last interval labels, or to label singleton intervals. - [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) and [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) use unicode em-dash where possible. - [`brk_default()`](https://hughjonesd.github.io/santoku/reference/brk_default.md) throws an error if breaks are not sorted. ### Bugfixes - Bugfix: [`tab()`](https://hughjonesd.github.io/santoku/reference/chop.md) and friends no longer display an `x` as the variable name. - Bugfix: [`lbl_endpoint()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) was erroring for some types of breaks. ## santoku 0.6.0 CRAN release: 2021-11-04 - New arguments `first` and `last` in [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) and [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) allow you to override the first and last interval labels. - Fixes for CRAN. ## santoku 0.5.0 CRAN release: 2020-08-27 - Negative numbers can be used in [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md). - This sets `left = FALSE` by default. - Also works for negative time intervals. ## santoku 0.4.1 CRAN release: 2020-06-16 - Bugfix: `chop(1:4, 1)` was erroring. ## santoku 0.4.0 CRAN release: 2020-06-09 ### Interface changes The new version has some interface changes. These are based on user experience, and are designed to make using [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) more intuitive and predictable. - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) has two new arguments, `left` and `close_end`. - Using `left = FALSE` is simpler and more intuitive than wrapping breaks in `brk_right()`. - `brk_left()` and `brk_right()` have been kept for now, but cannot be used to wrap other break functions. - Using `close_end` is simpler than passing `close_end` into `brk_left()` or `brk_right()` (which no longer accept this argument directly). - `left = TRUE` by default, except for non-numeric objects in [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) and [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), where `left = FALSE` works better. - `close_end` is now `FALSE` by default. - This prevents user surprises when e.g. `chop(3, 1:3)` puts `3` into a different category than `chop(3, 1:4)`. - `close_end` is `TRUE` by default for [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) and similar functions. This ensures that e.g.  `chop_quantiles(x, c(0, 1/3, 2/3, 1))` does what you would expect. - The `groups` argument to [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) has been renamed from `groups` to `intervals`. This should make it easier to remember the difference between [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) and [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md). (Chop evenly into `n` equal-width *intervals*, or chop equally into `n` equal-sized *groups*.) - `knife()` has been deprecated to keep the interface slim and focused. Use [`purrr::partial()`](https://purrr.tidyverse.org/reference/partial.html) instead. ### Other changes - Date and datetime (`POSIXct`) objects can now be chopped. - [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) accepts `difftime`, [`lubridate::period`](https://lubridate.tidyverse.org/reference/period.html) or [`lubridate::duration`](https://lubridate.tidyverse.org/reference/duration.html) objects - all other `chop_` functions work as well. - Many labelling functions have a new `fmt` argument. This can be a string interpreted by [`sprintf()`](https://rdrr.io/r/base/sprintf.html) or [`format()`](https://rdrr.io/r/base/format.html), or a 1-argument formatting function for break endpoints, e.g. [`scales::label_percent()`](https://scales.r-lib.org/reference/label_percent.html). - Experimental: [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) for discrete data such as integers or (most) dates. - There is a new [`lbl_endpoint()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) function for labelling intervals solely by their left or right endpoint. - [`brk_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) now accepts non-integer positive numbers. - Add [`brk_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) for symmetry with [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md). - Minor tweaks to [`chop_deciles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md). - Bugfix: `lbl_format()` wasn’t accepting numeric formats, even when `raw = TRUE`. Thanks to Sharla Gelfand. ## santoku 0.3.0 CRAN release: 2020-01-24 - First CRAN release. - Changed `kut()` to [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md). [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md) is an alternative spelling for [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), for use when the tidyr package is loaded. - `lbl_sequence()` has become [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md). - `lbl_letters()` and friends have been replaced by [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md): - to replace `lbl_letters()` use [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) - to replace `lbl_LETTERS()` use `lbl_seq("A")` - to replace `lbl_roman()` use `lbl_seq("i")` - to replace `lbl_ROMAN()` use `lbl_seq("I")` - to replace `lbl_numerals()` use `lbl_seq("1")` - for more complex formatting use e.g. `lbl_seq("A:")`, `lbl_seq("(i)")` ## santoku 0.2.0 - Added a `NEWS.md` file to track changes to the package. - Default labels when `extend = NULL` have changed, from `[-Inf, ...` and `..., Inf]` to `[min(x), ...` and `..., max(x)]`. ================================================ FILE: docs/pkgdown.css ================================================ /* Sticky footer */ /** * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css * * .Site -> body > .container * .Site-content -> body > .container .row * .footer -> footer * * Key idea seems to be to ensure that .container and __all its parents__ * have height set to 100% * */ html, body { height: 100%; } body { position: relative; } body > .container { display: flex; height: 100%; flex-direction: column; } body > .container .row { flex: 1 0 auto; } footer { margin-top: 45px; padding: 35px 0 36px; border-top: 1px solid #e5e5e5; color: #666; display: flex; flex-shrink: 0; } footer p { margin-bottom: 0; } footer div { flex: 1; } footer .pkgdown { text-align: right; } footer p { margin-bottom: 0; } img.icon { float: right; } img { max-width: 100%; } /* Fix bug in bootstrap (only seen in firefox) */ summary { display: list-item; } /* Typographic tweaking ---------------------------------*/ .contents .page-header { margin-top: calc(-60px + 1em); } dd { margin-left: 3em; } /* Section anchors ---------------------------------*/ a.anchor { margin-left: -30px; display:inline-block; width: 30px; height: 30px; visibility: hidden; background-image: url(./link.svg); background-repeat: no-repeat; background-size: 20px 20px; background-position: center center; } .hasAnchor:hover a.anchor { visibility: visible; } @media (max-width: 767px) { .hasAnchor:hover a.anchor { visibility: hidden; } } /* Fixes for fixed navbar --------------------------*/ .contents h1, .contents h2, .contents h3, .contents h4 { padding-top: 60px; margin-top: -40px; } /* Navbar submenu --------------------------*/ .dropdown-submenu { position: relative; } .dropdown-submenu>.dropdown-menu { top: 0; left: 100%; margin-top: -6px; margin-left: -1px; border-radius: 0 6px 6px 6px; } .dropdown-submenu:hover>.dropdown-menu { display: block; } .dropdown-submenu>a:after { display: block; content: " "; float: right; width: 0; height: 0; border-color: transparent; border-style: solid; border-width: 5px 0 5px 5px; border-left-color: #cccccc; margin-top: 5px; margin-right: -10px; } .dropdown-submenu:hover>a:after { border-left-color: #ffffff; } .dropdown-submenu.pull-left { float: none; } .dropdown-submenu.pull-left>.dropdown-menu { left: -100%; margin-left: 10px; border-radius: 6px 0 6px 6px; } /* Sidebar --------------------------*/ #pkgdown-sidebar { margin-top: 30px; position: -webkit-sticky; position: sticky; top: 70px; } #pkgdown-sidebar h2 { font-size: 1.5em; margin-top: 1em; } #pkgdown-sidebar h2:first-child { margin-top: 0; } #pkgdown-sidebar .list-unstyled li { margin-bottom: 0.5em; } /* bootstrap-toc tweaks ------------------------------------------------------*/ /* All levels of nav */ nav[data-toggle='toc'] .nav > li > a { padding: 4px 20px 4px 6px; font-size: 1.5rem; font-weight: 400; color: inherit; } nav[data-toggle='toc'] .nav > li > a:hover, nav[data-toggle='toc'] .nav > li > a:focus { padding-left: 5px; color: inherit; border-left: 1px solid #878787; } nav[data-toggle='toc'] .nav > .active > a, nav[data-toggle='toc'] .nav > .active:hover > a, nav[data-toggle='toc'] .nav > .active:focus > a { padding-left: 5px; font-size: 1.5rem; font-weight: 400; color: inherit; border-left: 2px solid #878787; } /* Nav: second level (shown on .active) */ nav[data-toggle='toc'] .nav .nav { display: none; /* Hide by default, but at >768px, show it */ padding-bottom: 10px; } nav[data-toggle='toc'] .nav .nav > li > a { padding-left: 16px; font-size: 1.35rem; } nav[data-toggle='toc'] .nav .nav > li > a:hover, nav[data-toggle='toc'] .nav .nav > li > a:focus { padding-left: 15px; } nav[data-toggle='toc'] .nav .nav > .active > a, nav[data-toggle='toc'] .nav .nav > .active:hover > a, nav[data-toggle='toc'] .nav .nav > .active:focus > a { padding-left: 15px; font-weight: 500; font-size: 1.35rem; } /* orcid ------------------------------------------------------------------- */ .orcid { font-size: 16px; color: #A6CE39; /* margins are required by official ORCID trademark and display guidelines */ margin-left:4px; margin-right:4px; vertical-align: middle; } /* Reference index & topics ----------------------------------------------- */ .ref-index th {font-weight: normal;} .ref-index td {vertical-align: top; min-width: 100px} .ref-index .icon {width: 40px;} .ref-index .alias {width: 40%;} .ref-index-icons .alias {width: calc(40% - 40px);} .ref-index .title {width: 60%;} .ref-arguments th {text-align: right; padding-right: 10px;} .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} .ref-arguments .name {width: 20%;} .ref-arguments .desc {width: 80%;} /* Nice scrolling for wide elements --------------------------------------- */ table { display: block; overflow: auto; } /* Syntax highlighting ---------------------------------------------------- */ pre { word-wrap: normal; word-break: normal; border: 1px solid #eee; } pre, code { background-color: #f8f8f8; color: #333; } pre code { overflow: auto; word-wrap: normal; white-space: pre; } pre .img { margin: 5px 0; } pre .img img { background-color: #fff; display: block; height: auto; } code a, pre a { color: #375f84; } a.sourceLine:hover { text-decoration: none; } .fl {color: #1514b5;} .fu {color: #000000;} /* function */ .ch,.st {color: #036a07;} /* string */ .kw {color: #264D66;} /* keyword */ .co {color: #888888;} /* comment */ .message { color: black; font-weight: bolder;} .error { color: orange; font-weight: bolder;} .warning { color: #6A0366; font-weight: bolder;} /* Clipboard --------------------------*/ .hasCopyButton { position: relative; } .btn-copy-ex { position: absolute; right: 0; top: 0; visibility: hidden; } .hasCopyButton:hover button.btn-copy-ex { visibility: visible; } /* headroom.js ------------------------ */ .headroom { will-change: transform; transition: transform 200ms linear; } .headroom--pinned { transform: translateY(0%); } .headroom--unpinned { transform: translateY(-100%); } /* mark.js ----------------------------*/ mark { background-color: rgba(255, 255, 51, 0.5); border-bottom: 2px solid rgba(255, 153, 51, 0.3); padding: 1px; } /* vertical spacing after htmlwidgets */ .html-widget { margin-bottom: 10px; } /* fontawesome ------------------------ */ .fab { font-family: "Font Awesome 5 Brands" !important; } /* don't display links in code chunks when printing */ /* source: https://stackoverflow.com/a/10781533 */ @media print { code a:link:after, code a:visited:after { content: ""; } } ================================================ FILE: docs/pkgdown.js ================================================ /* http://gregfranko.com/blog/jquery-best-practices/ */ (function ($) { $(function () { $('nav.navbar').headroom(); Toc.init({ $nav: $("#toc"), $scope: $("main h2, main h3, main h4, main h5, main h6") }); if ($('#toc').length) { $('body').scrollspy({ target: '#toc', offset: $("nav.navbar").outerHeight() + 1 }); } // Activate popovers $('[data-bs-toggle="popover"]').popover({ container: 'body', html: true, trigger: 'focus', placement: "top", sanitize: false, }); $('[data-bs-toggle="tooltip"]').tooltip(); /* Clipboard --------------------------*/ function changeTooltipMessage(element, msg) { var tooltipOriginalTitle = element.getAttribute('data-bs-original-title'); element.setAttribute('data-bs-original-title', msg); $(element).tooltip('show'); element.setAttribute('data-bs-original-title', tooltipOriginalTitle); } if (ClipboardJS.isSupported()) { $(document).ready(function () { var copyButton = ""; $("div.sourceCode").addClass("hasCopyButton"); // Insert copy buttons: $(copyButton).prependTo(".hasCopyButton"); // Initialize tooltips: $('.btn-copy-ex').tooltip({ container: 'body' }); // Initialize clipboard: var clipboard = new ClipboardJS('[data-clipboard-copy]', { text: function (trigger) { return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); } }); clipboard.on('success', function (e) { changeTooltipMessage(e.trigger, 'Copied!'); e.clearSelection(); }); clipboard.on('error', function (e) { changeTooltipMessage(e.trigger, 'Press Ctrl+C or Command+C to copy'); }); }); } /* Search marking --------------------------*/ var url = new URL(window.location.href); var toMark = url.searchParams.get("q"); var mark = new Mark("main#main"); if (toMark) { mark.mark(toMark, { accuracy: { value: "complementary", limiters: [",", ".", ":", "/"], } }); } /* Search --------------------------*/ /* Adapted from https://github.com/rstudio/bookdown/blob/2d692ba4b61f1e466c92e78fd712b0ab08c11d31/inst/resources/bs4_book/bs4_book.js#L25 */ // Initialise search index on focus var fuse; $("#search-input").focus(async function (e) { if (fuse) { return; } $(e.target).addClass("loading"); var response = await fetch($("#search-input").data("search-index")); var data = await response.json(); var options = { keys: ["what", "text", "code"], ignoreLocation: true, threshold: 0.1, includeMatches: true, includeScore: true, }; fuse = new Fuse(data, options); $(e.target).removeClass("loading"); }); // Use algolia autocomplete var options = { autoselect: true, debug: true, hint: false, minLength: 2, }; var q; async function searchFuse(query, callback) { await fuse; var items; if (!fuse) { items = []; } else { q = query; var results = fuse.search(query, { limit: 20 }); items = results .filter((x) => x.score <= 0.75) .map((x) => x.item); if (items.length === 0) { items = [{ dir: "Sorry 😿", previous_headings: "", title: "No results found.", what: "No results found.", path: window.location.href }]; } } callback(items); } $("#search-input").autocomplete(options, [ { name: "content", source: searchFuse, templates: { suggestion: (s) => { if (s.title == s.what) { return `${s.dir} >
    ${s.title}
    `; } else if (s.previous_headings == "") { return `${s.dir} >
    ${s.title}
    > ${s.what}`; } else { return `${s.dir} >
    ${s.title}
    > ${s.previous_headings} > ${s.what}`; } }, }, }, ]).on('autocomplete:selected', function (event, s) { window.location.href = s.path + "?q=" + q + "#" + s.id; }); }); })(window.jQuery || window.$) document.addEventListener('keydown', function (event) { // Check if the pressed key is '/' if (event.key === '/') { event.preventDefault(); // Prevent any default action associated with the '/' key document.getElementById('search-input').focus(); // Set focus to the search input } }); ================================================ FILE: docs/pkgdown.yml ================================================ pandoc: 3.8.3 pkgdown: 2.2.0 pkgdown_sha: ~ articles: website-articles/performance: website-articles/performance.html santoku: santoku.html whats-new-in-0-9-0: whats-new-in-0-9-0.html last_built: 2026-04-28T14:11Z urls: reference: https://hughjonesd.github.io/santoku/reference article: https://hughjonesd.github.io/santoku/articles ================================================ FILE: docs/reference/breaks-class.html ================================================ Class representing a set of intervals — breaks-class • santoku Skip to contents

    Class representing a set of intervals

    Usage

    # S3 method for class 'breaks'
    format(x, ...)
    
    # S3 method for class 'breaks'
    print(x, ...)
    
    is.breaks(x, ...)

    Arguments

    x

    A breaks object

    ...

    Unused

    ================================================ FILE: docs/reference/breaks-class.md ================================================ # Class representing a set of intervals Class representing a set of intervals ## Usage ``` r # S3 method for class 'breaks' format(x, ...) # S3 method for class 'breaks' print(x, ...) is.breaks(x, ...) ``` ## Arguments - x: A breaks object - ...: Unused ================================================ FILE: docs/reference/brk-left-right.html ================================================ Left- or right-closed breaks — brk-left-right • santoku Skip to contents

    [Questioning]

    Usage

    brk_left(breaks)
    
    brk_right(breaks)

    Arguments

    breaks

    A numeric vector.

    Value

    A (function which returns an) object of class breaks.

    Details

    These functions are in the "questioning" stage because they clash with the left argument to chop() and friends.

    These functions override the left argument of chop().

    Examples

    chop(5:7, brk_left(5:7))
    #> Warning: `brk_left()` was deprecated in santoku 0.4.0.
    #> Please use the `left` argument to `chop()` instead.
    #> This warning is displayed once every 8 hours.
    #> Call `lifecycle::last_warnings()` to see where this warning was generated.
    #> [1] [5, 6) [6, 7) {7}   
    #> Levels: [5, 6) [6, 7) {7}
    
    chop(5:7, brk_right(5:7))
    #> Warning: `brk_right()` was deprecated in santoku 0.4.0.
    #> Please use the `left` argument to `chop()` instead.
    #> This warning is displayed once every 8 hours.
    #> Call `lifecycle::last_warnings()` to see where this warning was generated.
    #> Warning: `left` argument to `brk_right()` ignored
    #> [1] {5}    (5, 6] (6, 7]
    #> Levels: {5} (5, 6] (6, 7]
    
    chop(5:7, brk_left(5:7))
    #> [1] [5, 6) [6, 7) {7}   
    #> Levels: [5, 6) [6, 7) {7}
    
    
    ================================================ FILE: docs/reference/brk-left-right.md ================================================ # Left- or right-closed breaks **\[questioning\]** ## Usage ``` r brk_left(breaks) brk_right(breaks) ``` ## Arguments - breaks: A numeric vector. ## Value A (function which returns an) object of class `breaks`. ## Details These functions are in the "questioning" stage because they clash with the `left` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) and friends. These functions override the `left` argument of [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). ## Examples ``` r chop(5:7, brk_left(5:7)) #> Warning: `brk_left()` was deprecated in santoku 0.4.0. #> Please use the `left` argument to `chop()` instead. #> This warning is displayed once every 8 hours. #> Call `lifecycle::last_warnings()` to see where this warning was generated. #> [1] [5, 6) [6, 7) {7} #> Levels: [5, 6) [6, 7) {7} chop(5:7, brk_right(5:7)) #> Warning: `brk_right()` was deprecated in santoku 0.4.0. #> Please use the `left` argument to `chop()` instead. #> This warning is displayed once every 8 hours. #> Call `lifecycle::last_warnings()` to see where this warning was generated. #> Warning: `left` argument to `brk_right()` ignored #> [1] {5} (5, 6] (6, 7] #> Levels: {5} (5, 6] (6, 7] chop(5:7, brk_left(5:7)) #> [1] [5, 6) [6, 7) {7} #> Levels: [5, 6) [6, 7) {7} ``` ================================================ FILE: docs/reference/brk_default.html ================================================ Create a standard set of breaks — brk_default • santoku Skip to contents

    Create a standard set of breaks

    Usage

    brk_default(breaks)

    Arguments

    breaks

    A numeric vector.

    Value

    A function which returns an object of class breaks.

    Examples

    
    chop(1:10, c(2, 5, 8))
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  [5, 8)  [5, 8)  [5, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [1, 2) [2, 5) [5, 8) [8, 10]
    chop(1:10, brk_default(c(2, 5, 8)))
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  [5, 8)  [5, 8)  [5, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [1, 2) [2, 5) [5, 8) [8, 10]
    
    
    ================================================ FILE: docs/reference/brk_default.md ================================================ # Create a standard set of breaks Create a standard set of breaks ## Usage ``` r brk_default(breaks) ``` ## Arguments - breaks: A numeric vector. ## Value A function which returns an object of class `breaks`. ## Examples ``` r chop(1:10, c(2, 5, 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) [5, 8) [5, 8) [5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) [5, 8) [8, 10] chop(1:10, brk_default(c(2, 5, 8))) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) [5, 8) [5, 8) [5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) [5, 8) [8, 10] ``` ================================================ FILE: docs/reference/brk_equally.html ================================================ ================================================ FILE: docs/reference/brk_evenly.html ================================================ ================================================ FILE: docs/reference/brk_fn.html ================================================ ================================================ FILE: docs/reference/brk_manual.html ================================================ Create a breaks object manually — brk_manual • santoku Skip to contents

    Create a breaks object manually

    Usage

    brk_manual(breaks, left_vec)

    Arguments

    breaks

    A vector, which must be sorted.

    left_vec

    A logical vector, the same length as breaks. Specifies whether each break is left-closed or right-closed.

    Value

    A function which returns an object of class breaks.

    Details

    All breaks must be closed on exactly one side, like ..., x) [x, ... (left-closed) or ..., x) [x, ... (right-closed).

    For example, if breaks = 1:3 and left = c(TRUE, FALSE, TRUE), then the resulting intervals are

    
    T        F       T
    [ 1,  2 ] ( 2, 3 )
    

    Singleton breaks are created by repeating a number in breaks. Singletons must be closed on both sides, so if there is a repeated number at indices i, i+1, left[i] must be TRUE and left[i+1] must be FALSE.

    brk_manual() ignores left and close_end arguments passed in from chop(), since left_vec sets these manually. extend and drop arguments are respected as usual.

    Examples

    lbrks <- brk_manual(1:3, rep(TRUE, 3))
    chop(1:3, lbrks, extend = FALSE)
    #> [1] [1, 2) [2, 3) <NA>  
    #> Levels: [1, 2) [2, 3)
    
    rbrks <- brk_manual(1:3, rep(FALSE, 3))
    chop(1:3, rbrks, extend = FALSE)
    #> [1] <NA>   (1, 2] (2, 3]
    #> Levels: (1, 2] (2, 3]
    
    brks_singleton <- brk_manual(
          c(1,    2,    2,     3),
          c(TRUE, TRUE, FALSE, TRUE))
    
    chop(1:3, brks_singleton, extend = FALSE)
    #> [1] [1, 2) {2}    <NA>  
    #> Levels: [1, 2) {2}
    
    
    ================================================ FILE: docs/reference/brk_manual.md ================================================ # Create a `breaks` object manually Create a `breaks` object manually ## Usage ``` r brk_manual(breaks, left_vec) ``` ## Arguments - breaks: A vector, which must be sorted. - left_vec: A logical vector, the same length as `breaks`. Specifies whether each break is left-closed or right-closed. ## Value A function which returns an object of class `breaks`. ## Details All breaks must be closed on exactly one side, like `..., x) [x, ...` (left-closed) or `..., x) [x, ...` (right-closed). For example, if `breaks = 1:3` and `left = c(TRUE, FALSE, TRUE)`, then the resulting intervals are T F T [ 1, 2 ] ( 2, 3 ) Singleton breaks are created by repeating a number in `breaks`. Singletons must be closed on both sides, so if there is a repeated number at indices `i`, `i+1`, `left[i]` *must* be `TRUE` and `left[i+1]` must be `FALSE`. `brk_manual()` ignores `left` and `close_end` arguments passed in from [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), since `left_vec` sets these manually. `extend` and `drop` arguments are respected as usual. ## Examples ``` r lbrks <- brk_manual(1:3, rep(TRUE, 3)) chop(1:3, lbrks, extend = FALSE) #> [1] [1, 2) [2, 3) #> Levels: [1, 2) [2, 3) rbrks <- brk_manual(1:3, rep(FALSE, 3)) chop(1:3, rbrks, extend = FALSE) #> [1] (1, 2] (2, 3] #> Levels: (1, 2] (2, 3] brks_singleton <- brk_manual( c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE)) chop(1:3, brks_singleton, extend = FALSE) #> [1] [1, 2) {2} #> Levels: [1, 2) {2} ``` ================================================ FILE: docs/reference/brk_mean_sd.html ================================================ ================================================ FILE: docs/reference/brk_n.html ================================================ ================================================ FILE: docs/reference/brk_pretty.html ================================================ ================================================ FILE: docs/reference/brk_proportions.html ================================================ ================================================ FILE: docs/reference/brk_quantiles.html ================================================ ================================================ FILE: docs/reference/brk_spikes.html ================================================ ================================================ FILE: docs/reference/brk_width-for-datetime.html ================================================ Equal-width intervals for dates or datetimes — brk_width-for-datetime • santoku Skip to contents

    brk_width() can be used with time interval classes from base R or the lubridate package.

    Usage

    # S3 method for class 'Duration'
    brk_width(width, start)

    Arguments

    width

    A scalar difftime, Period or Duration object.

    start

    A scalar of class Date or POSIXct. Can be omitted.

    Details

    If width is a Period, lubridate::add_with_rollback() is used to calculate the widths. This can be useful for e.g. calendar months.

    Examples

    
    if (requireNamespace("lubridate")) {
      year2001 <- as.Date("2001-01-01") + 0:364
      tab_width(year2001, months(1),
            labels = lbl_discrete(" to ", fmt = "%e %b %y"))
    }
    #>  1 Jan 01 to 31 Jan 01  1 Feb 01 to 28 Feb 01  1 Mar 01 to 31 Mar 01 
    #>                     31                     28                     31 
    #>  1 Apr 01 to 30 Apr 01  1 May 01 to 31 May 01  1 Jun 01 to 30 Jun 01 
    #>                     30                     31                     30 
    #>  1 Jul 01 to 31 Jul 01  1 Aug 01 to 31 Aug 01  1 Sep 01 to 30 Sep 01 
    #>                     31                     31                     30 
    #>  1 Oct 01 to 31 Oct 01  1 Nov 01 to 30 Nov 01  1 Dec 01 to 31 Dec 01 
    #>                     31                     30                     31 
    
    
    ================================================ FILE: docs/reference/brk_width-for-datetime.md ================================================ # Equal-width intervals for dates or datetimes [`brk_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) can be used with time interval classes from base R or the `lubridate` package. ## Usage ``` r # S3 method for class 'Duration' brk_width(width, start) ``` ## Arguments - width: A scalar [difftime](https://rdrr.io/r/base/difftime.html), [Period](https://lubridate.tidyverse.org/reference/Period-class.html) or [Duration](https://lubridate.tidyverse.org/reference/Duration-class.html) object. - start: A scalar of class [Date](https://rdrr.io/r/base/Dates.html) or [POSIXct](https://rdrr.io/r/base/DateTimeClasses.html). Can be omitted. ## Details If `width` is a Period, [`lubridate::add_with_rollback()`](https://lubridate.tidyverse.org/reference/mplus.html) is used to calculate the widths. This can be useful for e.g. calendar months. ## Examples ``` r if (requireNamespace("lubridate")) { year2001 <- as.Date("2001-01-01") + 0:364 tab_width(year2001, months(1), labels = lbl_discrete(" to ", fmt = "%e %b %y")) } #> 1 Jan 01 to 31 Jan 01 1 Feb 01 to 28 Feb 01 1 Mar 01 to 31 Mar 01 #> 31 28 31 #> 1 Apr 01 to 30 Apr 01 1 May 01 to 31 May 01 1 Jun 01 to 30 Jun 01 #> 30 31 30 #> 1 Jul 01 to 31 Jul 01 1 Aug 01 to 31 Aug 01 1 Sep 01 to 30 Sep 01 #> 31 31 30 #> 1 Oct 01 to 31 Oct 01 1 Nov 01 to 30 Nov 01 1 Dec 01 to 31 Dec 01 #> 31 30 31 ``` ================================================ FILE: docs/reference/brk_width.Duration.html ================================================ ================================================ FILE: docs/reference/brk_width.default.html ================================================ ================================================ FILE: docs/reference/brk_width.html ================================================ ================================================ FILE: docs/reference/chop.html ================================================ Cut data into intervals — chop • santoku Skip to contents

    chop() cuts x into intervals. It returns a factor of the same length as x, representing which interval contains each element of x. kiru() is an alias for chop. tab() calls chop() and returns a contingency table from the result.

    Usage

    chop(
      x,
      breaks,
      labels = lbl_intervals(),
      extend = NULL,
      left = TRUE,
      close_end = TRUE,
      raw = NULL,
      drop = TRUE
    )
    
    kiru(
      x,
      breaks,
      labels = lbl_intervals(),
      extend = NULL,
      left = TRUE,
      close_end = TRUE,
      raw = NULL,
      drop = TRUE
    )
    
    tab(
      x,
      breaks,
      labels = lbl_intervals(),
      extend = NULL,
      left = TRUE,
      close_end = TRUE,
      raw = NULL,
      drop = TRUE
    )

    Arguments

    x

    A vector.

    breaks

    A numeric vector of cut-points, or a function to create cut-points from x.

    labels

    A character vector of labels or a function to create labels.

    extend

    Logical. If TRUE, always extend breaks to +/-Inf. If NULL, extend breaks to min(x) and/or max(x) only if necessary. If FALSE, never extend.

    left

    Logical. Left-closed or right-closed breaks?

    close_end

    Logical. Close last break at right? (If left is FALSE, close first break at left?)

    raw

    Logical. Use raw values in labels?

    drop

    Logical. Drop unused levels from the result?

    Value

    chop() returns a factor of the same length as x, representing the intervals containing the value of x.

    tab() returns a contingency table.

    Details

    x may be a numeric vector, or more generally, any vector which can be compared with < and == (see Ops). In particular Date and date-time objects are supported. Character vectors are supported with a warning.

    Breaks

    breaks may be a vector or a function.

    If it is a vector, breaks gives the interval endpoints. Repeating a value creates a "singleton" interval, which contains only that value. For example breaks = c(1, 3, 3, 5) creates 3 intervals: [1, 3), {3} and (3, 5].

    If breaks is a function, it is called with the x, extend, left and close_end arguments, and should return an object of class breaks. Use brk_* functions to create a variety of data-dependent breaks.

    Names of breaks may be used for labels. See "Labels" below.

    Options for breaks

    By default, left-closed intervals are created. If left is FALSE, right-closed intervals are created.

    If close_end is TRUE the final break (or first break if left is FALSE) will be closed at both ends. This guarantees that all values x with min(breaks) <= x <= max(breaks) are included in the intervals.

    Before version 0.9.0, close_end was FALSE by default, and also behaved differently with respect to extended breaks: see "Extending intervals" below.

    Using mathematical set notation:

    • If left is TRUE and close_end is TRUE, breaks will look like [b1, b2), [b2, b3) ... [b_(n-1), b_n].

    • If left is FALSE and close_end is TRUE, breaks will look like [b1, b2], (b2, b3] ... (b_(n-1), b_n].

    • If left is TRUE and close_end is FALSE, all breaks will look like ... [b1, b2) ....

    • If left is FALSE and close_end is FALSE, all breaks will look like ... (b1, b2] ....

    Extending intervals

    If extend is TRUE, intervals will be extended to [-Inf, min(breaks)) and (max(breaks), Inf].

    If extend is NULL (the default), intervals will be extended to [min(x), min(breaks)) and (max(breaks), max(x)], only if necessary, i.e. only if elements of x would be outside the unextended breaks.

    If extend is FALSE, intervals are never extended.

    Note that even when extend = TRUE, extended intervals will be dropped from the factor levels if they contain no elements and drop = TRUE.

    close_end is only relevant if intervals are not extended; extended intervals are always closed on the outside. This is a change from previous behaviour. Up to version 0.8.0, close_end was applied to the last user-specified interval, before any extended intervals were created.

    Since 1.1.0, infinity is represented as \(\infty\) in breaks on unicode platforms. Set options(santoku.infinity = "Inf") to get the old behaviour.

    Labels

    labels may be a character vector. It should have the same length as the (possibly extended) number of intervals. Alternatively, labels may be a lbl_* function such as lbl_dash().

    If breaks is a named vector, then names of breaks will be used as labels for the interval starting at the corresponding element. This overrides the labels argument (but unnamed breaks will still use labels). This feature is [Experimental].

    If labels is NULL, then integer codes will be returned instead of a factor.

    If raw is TRUE, labels will show the actual interval endpoints, usually numbers. If raw is FALSE then labels may show other objects, such as quantiles for chop_quantiles() and friends, proportions of the range for chop_proportions(), or standard deviations for chop_mean_sd().

    If raw is NULL then lbl_* functions will use their default (usually FALSE). Otherwise, the raw argument to chop() overrides raw arguments passed into lbl_* functions directly.

    Miscellaneous

    NA values in x, and values which are outside the extended endpoints, return NA.

    kiru() is a synonym for chop(). If you load {tidyr}, you can use it to avoid confusion with tidyr::chop().

    Note that chop(), like all of R, uses binary arithmetic. Thus, numbers may not be exactly equal to what you think they should be. There is an example below.

    Examples

    
    chop(1:7, c(2, 4, 6))
    #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7]
    #> Levels: [1, 2) [2, 4) [4, 6) [6, 7]
    
    chop(1:7, c(2, 4, 6), extend = FALSE)
    #> [1] <NA>   [2, 4) [2, 4) [4, 6] [4, 6] [4, 6] <NA>  
    #> Levels: [2, 4) [4, 6]
    
    # Repeat a number for a singleton break:
    chop(1:7, c(2, 4, 4, 6))
    #> [1] [1, 2) [2, 4) [2, 4) {4}    (4, 6) [6, 7] [6, 7]
    #> Levels: [1, 2) [2, 4) {4} (4, 6) [6, 7]
    
    chop(1:7, c(2, 4, 6), left = FALSE)
    #> [1] [1, 2] [1, 2] (2, 4] (2, 4] (4, 6] (4, 6] (6, 7]
    #> Levels: [1, 2] (2, 4] (4, 6] (6, 7]
    
    chop(1:7, c(2, 4, 6), close_end = FALSE)
    #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7]
    #> Levels: [1, 2) [2, 4) [4, 6) [6, 7]
    
    chop(1:7, brk_quantiles(c(0.25, 0.75)))
    #> [1] [0%, 25%)   [0%, 25%)   [25%, 75%)  [25%, 75%)  [25%, 75%)  [75%, 100%]
    #> [7] [75%, 100%]
    #> Levels: [0%, 25%) [25%, 75%) [75%, 100%]
    
    # A single break is fine if `extend` is not `FALSE`:
    chop(1:7, 4)
    #> [1] [1, 4) [1, 4) [1, 4) [4, 7] [4, 7] [4, 7] [4, 7]
    #> Levels: [1, 4) [4, 7]
    
    # Floating point inaccuracy:
    chop(0.3/3, c(0, 0.1, 0.1, 1), labels = c("< 0.1", "0.1", "> 0.1"))
    #> [1] < 0.1
    #> Levels: < 0.1
    
    # -- Labels --
    
    chop(1:7, c(Lowest = 1, Low = 2, Mid = 4, High = 6))
    #> [1] Lowest Low    Low    Mid    Mid    High   High  
    #> Levels: Lowest Low Mid High
    
    chop(1:7, c(2, 4, 6), labels = c("Lowest", "Low", "Mid", "High"))
    #> [1] Lowest Low    Low    Mid    Mid    High   High  
    #> Levels: Lowest Low Mid High
    
    chop(1:7, c(2, 4, 6), labels = lbl_dash())
    #> [1] 1—2 2—4 2—4 4—6 4—6 6—7 6—7
    #> Levels: 1—2 2—4 4—6 6—7
    
    # Mixing names and other labels:
    chop(1:7, c("<2" = 1, 2, 4, ">=6" = 6), labels = lbl_dash())
    #> [1] <2  2—4 2—4 4—6 4—6 >=6 >=6
    #> Levels: <2 2—4 4—6 >=6
    
    # -- Non-standard types --
    
    chop(as.Date("2001-01-01") + 1:7, as.Date("2001-01-04"))
    #> [1] [2001-01-02, 2001-01-04) [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08]
    #> [4] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08]
    #> [7] [2001-01-04, 2001-01-08]
    #> Levels: [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08]
    
    suppressWarnings(chop(LETTERS[1:7], "D"))
    #> [1] [A, D) [A, D) [A, D) [D, G] [D, G] [D, G] [D, G]
    #> Levels: [A, D) [D, G]
    
    
    tab(1:10, c(2, 5, 8))
    #>  [1, 2)  [2, 5)  [5, 8) [8, 10] 
    #>       1       3       3       3 
    
    
    ================================================ FILE: docs/reference/chop.md ================================================ # Cut data into intervals `chop()` cuts `x` into intervals. It returns a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`, representing which interval contains each element of `x`. `kiru()` is an alias for `chop`. `tab()` calls `chop()` and returns a contingency [`table`](https://rdrr.io/r/base/table.html) from the result. ## Usage ``` r chop( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) kiru( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) tab( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) ``` ## Arguments - x: A vector. - breaks: A numeric vector of cut-points, or a function to create cut-points from `x`. - labels: A character vector of labels or a function to create labels. - extend: Logical. If `TRUE`, always extend breaks to `+/-Inf`. If `NULL`, extend breaks to `min(x)` and/or `max(x)` only if necessary. If `FALSE`, never extend. - left: Logical. Left-closed or right-closed breaks? - close_end: Logical. Close last break at right? (If `left` is `FALSE`, close first break at left?) - raw: Logical. Use raw values in labels? - drop: Logical. Drop unused levels from the result? ## Value `chop()` returns a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`, representing the intervals containing the value of `x`. `tab()` returns a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details `x` may be a numeric vector, or more generally, any vector which can be compared with `<` and `==` (see [Ops](https://rdrr.io/r/base/groupGeneric.html)). In particular [Date](https://rdrr.io/r/base/Dates.html) and [date-time](https://rdrr.io/r/base/DateTimeClasses.html) objects are supported. Character vectors are supported with a warning. ### Breaks `breaks` may be a vector or a function. If it is a vector, `breaks` gives the interval endpoints. Repeating a value creates a "singleton" interval, which contains only that value. For example `breaks = c(1, 3, 3, 5)` creates 3 intervals: `[1, 3)`, `{3}` and `(3, 5]`. If `breaks` is a function, it is called with the `x`, `extend`, `left` and `close_end` arguments, and should return an object of class `breaks`. Use `brk_*` functions to create a variety of data-dependent breaks. Names of `breaks` may be used for labels. See "Labels" below. ### Options for breaks By default, left-closed intervals are created. If `left` is `FALSE`, right-closed intervals are created. If `close_end` is `TRUE` the final break (or first break if `left` is `FALSE`) will be closed at both ends. This guarantees that all values `x` with `min(breaks) <= x <= max(breaks)` are included in the intervals. Before version 0.9.0, `close_end` was `FALSE` by default, and also behaved differently with respect to extended breaks: see "Extending intervals" below. Using [mathematical set notation](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md): - If `left` is `TRUE` and `close_end` is `TRUE`, breaks will look like `[b1, b2), [b2, b3) ... [b_(n-1), b_n]`. - If `left` is `FALSE` and `close_end` is `TRUE`, breaks will look like `[b1, b2], (b2, b3] ... (b_(n-1), b_n]`. - If `left` is `TRUE` and `close_end` is `FALSE`, all breaks will look like `... [b1, b2) ...`. - If `left` is `FALSE` and `close_end` is `FALSE`, all breaks will look like `... (b1, b2] ...`. ### Extending intervals If `extend` is `TRUE`, intervals will be extended to `[-Inf, min(breaks))` and `(max(breaks), Inf]`. If `extend` is `NULL` (the default), intervals will be extended to `[min(x), min(breaks))` and `(max(breaks), max(x)]`, only if necessary, i.e. only if elements of `x` would be outside the unextended breaks. If `extend` is `FALSE`, intervals are never extended. Note that even when `extend = TRUE`, extended intervals will be dropped from the factor levels if they contain no elements and `drop = TRUE`. `close_end` is only relevant if intervals are not extended; extended intervals are always closed on the outside. This is a change from previous behaviour. Up to version 0.8.0, `close_end` was applied to the last user-specified interval, before any extended intervals were created. Since 1.1.0, infinity is represented as \\\infty\\ in breaks on unicode platforms. Set `options(santoku.infinity = "Inf")` to get the old behaviour. ### Labels `labels` may be a character vector. It should have the same length as the (possibly extended) number of intervals. Alternatively, `labels` may be a `lbl_*` function such as [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md). If `breaks` is a named vector, then names of `breaks` will be used as labels for the interval starting at the corresponding element. This overrides the `labels` argument (but unnamed breaks will still use `labels`). This feature is **\[experimental\]**. If `labels` is `NULL`, then integer codes will be returned instead of a factor. If `raw` is `TRUE`, labels will show the actual interval endpoints, usually numbers. If `raw` is `FALSE` then labels may show other objects, such as quantiles for [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) and friends, proportions of the range for [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), or standard deviations for [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md). If `raw` is `NULL` then `lbl_*` functions will use their default (usually `FALSE`). Otherwise, the `raw` argument to `chop()` overrides `raw` arguments passed into `lbl_*` functions directly. ### Miscellaneous `NA` values in `x`, and values which are outside the extended endpoints, return `NA`. `kiru()` is a synonym for `chop()`. If you load `{tidyr}`, you can use it to avoid confusion with [`tidyr::chop()`](https://tidyr.tidyverse.org/reference/chop.html). Note that `chop()`, like all of R, uses binary arithmetic. Thus, numbers may not be exactly equal to what you think they should be. There is an example below. ## See also [`base::cut()`](https://rdrr.io/r/base/cut.html), [`non-standard-types`](https://hughjonesd.github.io/santoku/reference/non-standard-types.md) for chopping objects that aren't numbers. Other chopping functions: [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop(1:7, c(2, 4, 6)) #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) [4, 6) [6, 7] chop(1:7, c(2, 4, 6), extend = FALSE) #> [1] [2, 4) [2, 4) [4, 6] [4, 6] [4, 6] #> Levels: [2, 4) [4, 6] # Repeat a number for a singleton break: chop(1:7, c(2, 4, 4, 6)) #> [1] [1, 2) [2, 4) [2, 4) {4} (4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) {4} (4, 6) [6, 7] chop(1:7, c(2, 4, 6), left = FALSE) #> [1] [1, 2] [1, 2] (2, 4] (2, 4] (4, 6] (4, 6] (6, 7] #> Levels: [1, 2] (2, 4] (4, 6] (6, 7] chop(1:7, c(2, 4, 6), close_end = FALSE) #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) [4, 6) [6, 7] chop(1:7, brk_quantiles(c(0.25, 0.75))) #> [1] [0%, 25%) [0%, 25%) [25%, 75%) [25%, 75%) [25%, 75%) [75%, 100%] #> [7] [75%, 100%] #> Levels: [0%, 25%) [25%, 75%) [75%, 100%] # A single break is fine if `extend` is not `FALSE`: chop(1:7, 4) #> [1] [1, 4) [1, 4) [1, 4) [4, 7] [4, 7] [4, 7] [4, 7] #> Levels: [1, 4) [4, 7] # Floating point inaccuracy: chop(0.3/3, c(0, 0.1, 0.1, 1), labels = c("< 0.1", "0.1", "> 0.1")) #> [1] < 0.1 #> Levels: < 0.1 # -- Labels -- chop(1:7, c(Lowest = 1, Low = 2, Mid = 4, High = 6)) #> [1] Lowest Low Low Mid Mid High High #> Levels: Lowest Low Mid High chop(1:7, c(2, 4, 6), labels = c("Lowest", "Low", "Mid", "High")) #> [1] Lowest Low Low Mid Mid High High #> Levels: Lowest Low Mid High chop(1:7, c(2, 4, 6), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—6 4—6 6—7 6—7 #> Levels: 1—2 2—4 4—6 6—7 # Mixing names and other labels: chop(1:7, c("<2" = 1, 2, 4, ">=6" = 6), labels = lbl_dash()) #> [1] <2 2—4 2—4 4—6 4—6 >=6 >=6 #> Levels: <2 2—4 4—6 >=6 # -- Non-standard types -- chop(as.Date("2001-01-01") + 1:7, as.Date("2001-01-04")) #> [1] [2001-01-02, 2001-01-04) [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08] #> [4] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08] #> [7] [2001-01-04, 2001-01-08] #> Levels: [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08] suppressWarnings(chop(LETTERS[1:7], "D")) #> [1] [A, D) [A, D) [A, D) [D, G] [D, G] [D, G] [D, G] #> Levels: [A, D) [D, G] tab(1:10, c(2, 5, 8)) #> [1, 2) [2, 5) [5, 8) [8, 10] #> 1 3 3 3 ``` ================================================ FILE: docs/reference/chop_deciles.html ================================================ ================================================ FILE: docs/reference/chop_equally.html ================================================ Chop equal-sized groups — chop_equally • santoku Skip to contents

    chop_equally() chops x into groups with an equal number of elements.

    Usage

    chop_equally(
      x,
      groups,
      ...,
      labels = lbl_intervals(),
      left = is.numeric(x),
      raw = TRUE
    )
    
    brk_equally(groups)
    
    tab_equally(x, groups, ..., left = is.numeric(x), raw = TRUE)

    Arguments

    x

    A vector.

    groups

    Number of groups.

    ...

    Passed to chop().

    labels

    A character vector of labels or a function to create labels.

    left

    Logical. Left-closed or right-closed breaks?

    raw

    Logical. Use raw values in labels?

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    chop_equally() uses brk_quantiles() under the hood. If x has duplicate elements, you may get fewer groups than requested. If so, a warning will be emitted. See the examples.

    Examples

    chop_equally(1:10, 5)
    #>  [1] [1, 2.8)   [1, 2.8)   [2.8, 4.6) [2.8, 4.6) [4.6, 6.4) [4.6, 6.4)
    #>  [7] [6.4, 8.2) [6.4, 8.2) [8.2, 10]  [8.2, 10] 
    #> Levels: [1, 2.8) [2.8, 4.6) [4.6, 6.4) [6.4, 8.2) [8.2, 10]
    
    # You can't always guarantee equal-sized groups:
    dupes <- c(1, 1, 1, 2, 3, 4, 4, 4)
    quantile(dupes, 0:4/4)
    #>   0%  25%  50%  75% 100% 
    #>  1.0  1.0  2.5  4.0  4.0 
    chop_equally(dupes, 4)
    #> Warning: `x` has duplicate quantiles: break labels may be misleading
    #> [1] {1}      {1}      {1}      (1, 2.5) [2.5, 4) {4}      {4}      {4}     
    #> Levels: {1} (1, 2.5) [2.5, 4) {4}
    # Or as many groups as you ask for:
    chop_equally(c(1, 1, 2, 2), 3)
    #> Warning: `x` has duplicate quantiles: break labels may be misleading
    #> [1] {1} {1} {2} {2}
    #> Levels: {1} {2}
    
    ================================================ FILE: docs/reference/chop_equally.md ================================================ # Chop equal-sized groups `chop_equally()` chops `x` into groups with an equal number of elements. ## Usage ``` r chop_equally( x, groups, ..., labels = lbl_intervals(), left = is.numeric(x), raw = TRUE ) brk_equally(groups) tab_equally(x, groups, ..., left = is.numeric(x), raw = TRUE) ``` ## Arguments - x: A vector. - groups: Number of groups. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - labels: A character vector of labels or a function to create labels. - left: Logical. Left-closed or right-closed breaks? - raw: Logical. Use raw values in labels? ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details `chop_equally()` uses [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) under the hood. If `x` has duplicate elements, you may get fewer `groups` than requested. If so, a warning will be emitted. See the examples. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_equally(1:10, 5) #> [1] [1, 2.8) [1, 2.8) [2.8, 4.6) [2.8, 4.6) [4.6, 6.4) [4.6, 6.4) #> [7] [6.4, 8.2) [6.4, 8.2) [8.2, 10] [8.2, 10] #> Levels: [1, 2.8) [2.8, 4.6) [4.6, 6.4) [6.4, 8.2) [8.2, 10] # You can't always guarantee equal-sized groups: dupes <- c(1, 1, 1, 2, 3, 4, 4, 4) quantile(dupes, 0:4/4) #> 0% 25% 50% 75% 100% #> 1.0 1.0 2.5 4.0 4.0 chop_equally(dupes, 4) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [1] {1} {1} {1} (1, 2.5) [2.5, 4) {4} {4} {4} #> Levels: {1} (1, 2.5) [2.5, 4) {4} # Or as many groups as you ask for: chop_equally(c(1, 1, 2, 2), 3) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [1] {1} {1} {2} {2} #> Levels: {1} {2} ``` ================================================ FILE: docs/reference/chop_evenly.html ================================================ Chop into equal-width intervals — chop_evenly • santoku Skip to contents

    chop_evenly() chops x into intervals intervals of equal width.

    Usage

    chop_evenly(x, intervals, ...)
    
    brk_evenly(intervals)
    
    tab_evenly(x, intervals, ...)

    Arguments

    x

    A vector.

    intervals

    Integer: number of intervals to create.

    ...

    Passed to chop().

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Examples

    chop_evenly(0:10, 5)
    #>  [1] [0, 2)  [0, 2)  [2, 4)  [2, 4)  [4, 6)  [4, 6)  [6, 8)  [6, 8)  [8, 10]
    #> [10] [8, 10] [8, 10]
    #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10]
    
    
    ================================================ FILE: docs/reference/chop_evenly.md ================================================ # Chop into equal-width intervals `chop_evenly()` chops `x` into `intervals` intervals of equal width. ## Usage ``` r chop_evenly(x, intervals, ...) brk_evenly(intervals) tab_evenly(x, intervals, ...) ``` ## Arguments - x: A vector. - intervals: Integer: number of intervals to create. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_evenly(0:10, 5) #> [1] [0, 2) [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] #> [10] [8, 10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] ``` ================================================ FILE: docs/reference/chop_fn.html ================================================ Chop using an existing function — chop_fn • santoku Skip to contents

    chop_fn() is a convenience wrapper: chop_fn(x, foo, ...) is the same as chop(x, foo(x, ...)).

    Usage

    chop_fn(
      x,
      fn,
      ...,
      extend = NULL,
      left = TRUE,
      close_end = TRUE,
      raw = NULL,
      drop = TRUE
    )
    
    brk_fn(fn, ...)
    
    tab_fn(
      x,
      fn,
      ...,
      extend = NULL,
      left = TRUE,
      close_end = TRUE,
      raw = NULL,
      drop = TRUE
    )

    Arguments

    x

    A vector.

    fn

    A function which returns a numeric vector of breaks.

    ...

    Further arguments to fn

    extend

    Logical. If TRUE, always extend breaks to +/-Inf. If NULL, extend breaks to min(x) and/or max(x) only if necessary. If FALSE, never extend.

    left

    Logical. Left-closed or right-closed breaks?

    close_end

    Logical. Close last break at right? (If left is FALSE, close first break at left?)

    raw

    Logical. Use raw values in labels?

    drop

    Logical. Drop unused levels from the result?

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Examples

    
    if (requireNamespace("scales")) {
      chop_fn(rlnorm(10), scales::breaks_log(5))
      # same as
      # x <- rlnorm(10)
      # chop(x, scales::breaks_log(5)(x))
    }
    #> Loading required namespace: scales
    #>  [1] [0.1, 0.3)  [1, 3)      [0.03, 0.1) [0.3, 1)    [1, 3)      [3, 10]    
    #>  [7] [0.1, 0.3)  [0.3, 1)    [0.3, 1)    [0.3, 1)   
    #> Levels: [0.03, 0.1) [0.1, 0.3) [0.3, 1) [1, 3) [3, 10]
    
    
    ================================================ FILE: docs/reference/chop_fn.md ================================================ # Chop using an existing function `chop_fn()` is a convenience wrapper: `chop_fn(x, foo, ...)` is the same as `chop(x, foo(x, ...))`. ## Usage ``` r chop_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) brk_fn(fn, ...) tab_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) ``` ## Arguments - x: A vector. - fn: A function which returns a numeric vector of breaks. - ...: Further arguments to `fn` - extend: Logical. If `TRUE`, always extend breaks to `+/-Inf`. If `NULL`, extend breaks to `min(x)` and/or `max(x)` only if necessary. If `FALSE`, never extend. - left: Logical. Left-closed or right-closed breaks? - close_end: Logical. Close last break at right? (If `left` is `FALSE`, close first break at left?) - raw: Logical. Use raw values in labels? - drop: Logical. Drop unused levels from the result? ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r if (requireNamespace("scales")) { chop_fn(rlnorm(10), scales::breaks_log(5)) # same as # x <- rlnorm(10) # chop(x, scales::breaks_log(5)(x)) } #> Loading required namespace: scales #> [1] [0.1, 0.3) [1, 3) [0.03, 0.1) [0.3, 1) [1, 3) [3, 10] #> [7] [0.1, 0.3) [0.3, 1) [0.3, 1) [0.3, 1) #> Levels: [0.03, 0.1) [0.1, 0.3) [0.3, 1) [1, 3) [3, 10] ``` ================================================ FILE: docs/reference/chop_mean_sd.html ================================================ Chop by standard deviations — chop_mean_sd • santoku Skip to contents

    Intervals are measured in standard deviations on either side of the mean.

    Usage

    chop_mean_sd(x, sds = 1:3, ..., raw = FALSE, sd = deprecated())
    
    brk_mean_sd(sds = 1:3, sd = deprecated())
    
    tab_mean_sd(x, sds = 1:3, ..., raw = FALSE)

    Arguments

    x

    A vector.

    sds

    Positive numeric vector of standard deviations.

    ...

    Passed to chop().

    raw

    Logical. Use raw values in labels?

    sd

    [Deprecated]

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    In version 0.7.0, these functions changed to specifying sds as a vector. To chop 1, 2 and 3 standard deviations around the mean, write chop_mean_sd(x, sds = 1:3) instead of chop_mean_sd(x, sd = 3).

    Examples

    chop_mean_sd(1:10)
    #>  [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd)  [-1 sd, 0 sd)  [-1 sd, 0 sd) 
    #>  [6] [0 sd, 1 sd)   [0 sd, 1 sd)   [0 sd, 1 sd)   [1 sd, 2 sd)   [1 sd, 2 sd)  
    #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd)
    
    chop(1:10, brk_mean_sd())
    #>  [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd)  [-1 sd, 0 sd)  [-1 sd, 0 sd) 
    #>  [6] [0 sd, 1 sd)   [0 sd, 1 sd)   [0 sd, 1 sd)   [1 sd, 2 sd)   [1 sd, 2 sd)  
    #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd)
    
    tab_mean_sd(1:10)
    #> [-2 sd, -1 sd)  [-1 sd, 0 sd)   [0 sd, 1 sd)   [1 sd, 2 sd) 
    #>              2              3              3              2 
    
    
    ================================================ FILE: docs/reference/chop_mean_sd.md ================================================ # Chop by standard deviations Intervals are measured in standard deviations on either side of the mean. ## Usage ``` r chop_mean_sd(x, sds = 1:3, ..., raw = FALSE, sd = deprecated()) brk_mean_sd(sds = 1:3, sd = deprecated()) tab_mean_sd(x, sds = 1:3, ..., raw = FALSE) ``` ## Arguments - x: A vector. - sds: Positive numeric vector of standard deviations. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - raw: Logical. Use raw values in labels? - sd: **\[deprecated\]** ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details In version 0.7.0, these functions changed to specifying `sds` as a vector. To chop 1, 2 and 3 standard deviations around the mean, write `chop_mean_sd(x, sds = 1:3)` instead of `chop_mean_sd(x, sd = 3)`. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_mean_sd(1:10) #> [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) #> [6] [0 sd, 1 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [1 sd, 2 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) chop(1:10, brk_mean_sd()) #> [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) #> [6] [0 sd, 1 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [1 sd, 2 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) tab_mean_sd(1:10) #> [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) #> 2 3 3 2 ``` ================================================ FILE: docs/reference/chop_n.html ================================================ Chop into fixed-sized groups — chop_n • santoku Skip to contents

    chop_n() creates intervals containing a fixed number of elements.

    Usage

    chop_n(x, n, ..., tail = "split")
    
    brk_n(n, tail = "split")
    
    tab_n(x, n, ..., tail = "split")

    Arguments

    x

    A vector.

    n

    Integer. Number of elements in each interval.

    ...

    Passed to chop().

    tail

    String. What to do if the final interval has fewer than n elements? "split" to keep it separate. "merge" to merge it with the neighbouring interval.

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    The algorithm guarantees that intervals contain no more than n elements, so long as there are no duplicates in x and tail = "split". It also guarantees that intervals contain no fewer than n elements, except possibly the last interval (or first interval if left is FALSE).

    To ensure that all intervals contain at least n elements (so long as there are at least n elements in x!) set tail = "merge".

    If tail = "split" and there are intervals containing duplicates with more than n elements, a warning is given.

    Examples

    chop_n(1:10, 5)
    #>  [1] [1, 6)  [1, 6)  [1, 6)  [1, 6)  [1, 6)  [6, 10] [6, 10] [6, 10] [6, 10]
    #> [10] [6, 10]
    #> Levels: [1, 6) [6, 10]
    
    chop_n(1:5, 2)
    #> [1] [1, 3) [1, 3) [3, 5) [3, 5) {5}   
    #> Levels: [1, 3) [3, 5) {5}
    chop_n(1:5, 2, tail = "merge")
    #> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5]
    #> Levels: [1, 3) [3, 5]
    
    # too many duplicates
    x <- rep(1:2, each = 3)
    chop_n(x, 2)
    #> Warning: Some intervals contain more than 2 elements
    #> [1] [1, 2) [1, 2) [1, 2) {2}    {2}    {2}   
    #> Levels: [1, 2) {2}
    
    tab_n(1:10, 5)
    #>  [1, 6) [6, 10] 
    #>       5       5 
    
    # fewer elements in one group
    tab_n(1:10, 4)
    #>  [1, 5)  [5, 9) [9, 10] 
    #>       4       4       2 
    
    
    ================================================ FILE: docs/reference/chop_n.md ================================================ # Chop into fixed-sized groups `chop_n()` creates intervals containing a fixed number of elements. ## Usage ``` r chop_n(x, n, ..., tail = "split") brk_n(n, tail = "split") tab_n(x, n, ..., tail = "split") ``` ## Arguments - x: A vector. - n: Integer. Number of elements in each interval. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - tail: String. What to do if the final interval has fewer than `n` elements? `"split"` to keep it separate. `"merge"` to merge it with the neighbouring interval. ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details The algorithm guarantees that intervals contain no more than `n` elements, so long as there are no duplicates in `x` and `tail = "split"`. It also guarantees that intervals contain no fewer than `n` elements, except possibly the last interval (or first interval if `left` is `FALSE`). To ensure that all intervals contain at least `n` elements (so long as there are at least `n` elements in `x`!) set `tail = "merge"`. If `tail = "split"` and there are intervals containing duplicates with more than `n` elements, a warning is given. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] chop_n(1:5, 2) #> [1] [1, 3) [1, 3) [3, 5) [3, 5) {5} #> Levels: [1, 3) [3, 5) {5} chop_n(1:5, 2, tail = "merge") #> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5] #> Levels: [1, 3) [3, 5] # too many duplicates x <- rep(1:2, each = 3) chop_n(x, 2) #> Warning: Some intervals contain more than 2 elements #> [1] [1, 2) [1, 2) [1, 2) {2} {2} {2} #> Levels: [1, 2) {2} tab_n(1:10, 5) #> [1, 6) [6, 10] #> 5 5 # fewer elements in one group tab_n(1:10, 4) #> [1, 5) [5, 9) [9, 10] #> 4 4 2 ``` ================================================ FILE: docs/reference/chop_pretty.html ================================================ Chop using pretty breakpoints — chop_pretty • santoku Skip to contents

    chop_pretty() uses base::pretty() to calculate breakpoints which are 1, 2 or 5 times a power of 10. These look nice in graphs.

    Usage

    chop_pretty(x, n = 5, ...)
    
    brk_pretty(n = 5, ...)
    
    tab_pretty(x, n = 5, ...)

    Arguments

    x

    A vector.

    n

    Positive integer passed to base::pretty(). How many intervals to chop into?

    ...

    Passed to chop() by chop_pretty() and tab_pretty(); passed to base::pretty() by brk_pretty().

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    base::pretty() tries to return n+1 breakpoints, i.e. n intervals, but note that this is not guaranteed. There are methods for Date and POSIXct objects.

    For fine-grained control over base::pretty() parameters, use chop(x, brk_pretty(...)).

    Examples

    chop_pretty(1:10)
    #>  [1] [0, 2)  [2, 4)  [2, 4)  [4, 6)  [4, 6)  [6, 8)  [6, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10]
    
    chop(1:10, brk_pretty(n = 5, high.u.bias = 0))
    #>  [1] [1, 2)  [2, 3)  [3, 4)  [4, 5)  [5, 6)  [6, 7)  [7, 8)  [8, 9)  [9, 10]
    #> [10] [9, 10]
    #> Levels: [1, 2) [2, 3) [3, 4) [4, 5) [5, 6) [6, 7) [7, 8) [8, 9) [9, 10]
    
    tab_pretty(1:10)
    #>  [0, 2)  [2, 4)  [4, 6)  [6, 8) [8, 10] 
    #>       1       2       2       2       3 
    
    
    ================================================ FILE: docs/reference/chop_pretty.md ================================================ # Chop using pretty breakpoints `chop_pretty()` uses [`base::pretty()`](https://rdrr.io/r/base/pretty.html) to calculate breakpoints which are 1, 2 or 5 times a power of 10. These look nice in graphs. ## Usage ``` r chop_pretty(x, n = 5, ...) brk_pretty(n = 5, ...) tab_pretty(x, n = 5, ...) ``` ## Arguments - x: A vector. - n: Positive integer passed to [`base::pretty()`](https://rdrr.io/r/base/pretty.html). How many intervals to chop into? - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) by `chop_pretty()` and `tab_pretty()`; passed to [`base::pretty()`](https://rdrr.io/r/base/pretty.html) by `brk_pretty()`. ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details [`base::pretty()`](https://rdrr.io/r/base/pretty.html) tries to return `n+1` breakpoints, i.e. `n` intervals, but note that this is not guaranteed. There are methods for Date and POSIXct objects. For fine-grained control over [`base::pretty()`](https://rdrr.io/r/base/pretty.html) parameters, use `chop(x, brk_pretty(...))`. ## Examples ``` r chop_pretty(1:10) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] chop(1:10, brk_pretty(n = 5, high.u.bias = 0)) #> [1] [1, 2) [2, 3) [3, 4) [4, 5) [5, 6) [6, 7) [7, 8) [8, 9) [9, 10] #> [10] [9, 10] #> Levels: [1, 2) [2, 3) [3, 4) [4, 5) [5, 6) [6, 7) [7, 8) [8, 9) [9, 10] tab_pretty(1:10) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] #> 1 2 2 2 3 ``` ================================================ FILE: docs/reference/chop_proportions.html ================================================ Chop into proportions of the range of x — chop_proportions • santoku Skip to contents

    chop_proportions() chops x into proportions of its range, excluding infinite values.

    Usage

    chop_proportions(x, proportions, ..., raw = TRUE)
    
    brk_proportions(proportions)
    
    tab_proportions(x, proportions, ..., raw = TRUE)

    Arguments

    x

    A vector.

    proportions

    Numeric vector between 0 and 1: proportions of x's range. If proportions has names, these will be used for labels.

    ...

    Passed to chop().

    raw

    Logical. Use raw values in labels?

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    By default, labels show the raw numeric endpoints. To label intervals by the proportions, use raw = FALSE.

    Examples

    chop_proportions(0:10, c(0.2, 0.8))
    #>  [1] [0, 2)  [0, 2)  [2, 8)  [2, 8)  [2, 8)  [2, 8)  [2, 8)  [2, 8)  [8, 10]
    #> [10] [8, 10] [8, 10]
    #> Levels: [0, 2) [2, 8) [8, 10]
    chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8))
    #>  [1] Low  Low  Mid  Mid  Mid  Mid  Mid  Mid  High High High
    #> Levels: Low Mid High
    
    
    ================================================ FILE: docs/reference/chop_proportions.md ================================================ # Chop into proportions of the range of x `chop_proportions()` chops `x` into `proportions` of its range, excluding infinite values. ## Usage ``` r chop_proportions(x, proportions, ..., raw = TRUE) brk_proportions(proportions) tab_proportions(x, proportions, ..., raw = TRUE) ``` ## Arguments - x: A vector. - proportions: Numeric vector between 0 and 1: proportions of x's range. If `proportions` has names, these will be used for labels. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - raw: Logical. Use raw values in labels? ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details By default, labels show the raw numeric endpoints. To label intervals by the proportions, use `raw = FALSE`. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_proportions(0:10, c(0.2, 0.8)) #> [1] [0, 2) [0, 2) [2, 8) [2, 8) [2, 8) [2, 8) [2, 8) [2, 8) [8, 10] #> [10] [8, 10] [8, 10] #> Levels: [0, 2) [2, 8) [8, 10] chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8)) #> [1] Low Low Mid Mid Mid Mid Mid Mid High High High #> Levels: Low Mid High ``` ================================================ FILE: docs/reference/chop_quantiles.html ================================================ Chop by quantiles — chop_quantiles • santoku Skip to contents

    chop_quantiles() chops data by quantiles. chop_deciles() is a convenience function which chops into deciles.

    Usage

    chop_quantiles(
      x,
      probs,
      ...,
      labels = if (raw) lbl_intervals() else lbl_intervals(single = NULL),
      left = is.numeric(x),
      raw = FALSE,
      weights = NULL,
      recalc_probs = FALSE
    )
    
    chop_deciles(x, ...)
    
    brk_quantiles(probs, ..., weights = NULL, recalc_probs = FALSE)
    
    tab_quantiles(x, probs, ..., left = is.numeric(x), raw = FALSE)
    
    tab_deciles(x, ...)

    Arguments

    x

    A vector.

    probs

    A vector of probabilities for the quantiles. If probs has names, these will be used for labels.

    ...

    For chop_quantiles, passed to chop(). For brk_quantiles(), passed to stats::quantile() or Hmisc::wtd.quantile().

    labels

    A character vector of labels or a function to create labels.

    left

    Logical. Left-closed or right-closed breaks?

    raw

    Logical. Use raw values in labels?

    weights

    NULL or numeric vector of same length as x. If not NULL, Hmisc::wtd.quantile() is used to calculate weighted quantiles.

    recalc_probs

    Logical. Recalculate probabilities of quantiles using ecdf(x)? See below.

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    For non-numeric x, left is set to FALSE by default. This works better for calculating "type 1" quantiles, since they round down. See stats::quantile().

    By default, chop_quantiles() shows the requested probabilities in the labels. To show the numeric quantiles themselves, set raw = TRUE.

    When x contains duplicates, consecutive quantiles may be the same number. If so, interval labels may be misleading, and if recalc_probs = FALSE a warning is emitted. Set recalc_probs = TRUE to recalculate the probabilities of the quantiles using the empirical cumulative distribution function of x. Doing so may give you different labels from what you expect, and will remove any names from probs, but it never changes the actual quantiles used for breaks. At present, recalc_probs = TRUE is incompatible with non-null weights. See the example below.

    Examples

    chop_quantiles(1:10, 1:3/4)
    #>  [1] [0%, 25%)   [0%, 25%)   [0%, 25%)   [25%, 50%)  [25%, 50%)  [50%, 75%) 
    #>  [7] [50%, 75%)  [75%, 100%] [75%, 100%] [75%, 100%]
    #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%]
    
    chop_quantiles(1:10, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75))
    #>  [1] Q1 Q1 Q1 Q2 Q2 Q3 Q3 Q4 Q4 Q4
    #> Levels: Q1 Q2 Q3 Q4
    
    chop(1:10, brk_quantiles(1:3/4))
    #>  [1] [0%, 25%)   [0%, 25%)   [0%, 25%)   [25%, 50%)  [25%, 50%)  [50%, 75%) 
    #>  [7] [50%, 75%)  [75%, 100%] [75%, 100%] [75%, 100%]
    #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%]
    
    chop_deciles(1:10)
    #>  [1] [0%, 10%)   [10%, 20%)  [20%, 30%)  [30%, 40%)  [40%, 50%)  [50%, 60%) 
    #>  [7] [60%, 70%)  [70%, 80%)  [80%, 90%)  [90%, 100%]
    #> 10 Levels: [0%, 10%) [10%, 20%) [20%, 30%) [30%, 40%) [40%, 50%) ... [90%, 100%]
    
    # to label by the quantiles themselves:
    chop_quantiles(1:10, 1:3/4, raw = TRUE)
    #>  [1] [1, 3.25)   [1, 3.25)   [1, 3.25)   [3.25, 5.5) [3.25, 5.5) [5.5, 7.75)
    #>  [7] [5.5, 7.75) [7.75, 10]  [7.75, 10]  [7.75, 10] 
    #> Levels: [1, 3.25) [3.25, 5.5) [5.5, 7.75) [7.75, 10]
    
    # duplicate quantiles:
    x <- c(1, 1, 1, 2, 3)
    quantile(x, 1:5/5)
    #>  20%  40%  60%  80% 100% 
    #>  1.0  1.0  1.4  2.2  3.0 
    tab_quantiles(x, 1:5/5)
    #> Warning: `x` has duplicate quantiles: break labels may be misleading
    #>  [20%, 40%]  [60%, 80%) [80%, 100%] 
    #>           3           1           1 
    tab_quantiles(x, 1:5/5, recalc_probs = TRUE)
    #>   [0%, 60%]  [60%, 80%) [80%, 100%] 
    #>           3           1           1 
    set.seed(42)
    tab_quantiles(rnorm(100), probs = 1:3/4, raw = TRUE)
    #> [-2.993, -0.6167) [-0.6167, 0.0898)  [0.0898, 0.6616)   [0.6616, 2.287] 
    #>                25                25                25                25 
    
    
    ================================================ FILE: docs/reference/chop_quantiles.md ================================================ # Chop by quantiles `chop_quantiles()` chops data by quantiles. `chop_deciles()` is a convenience function which chops into deciles. ## Usage ``` r chop_quantiles( x, probs, ..., labels = if (raw) lbl_intervals() else lbl_intervals(single = NULL), left = is.numeric(x), raw = FALSE, weights = NULL, recalc_probs = FALSE ) chop_deciles(x, ...) brk_quantiles(probs, ..., weights = NULL, recalc_probs = FALSE) tab_quantiles(x, probs, ..., left = is.numeric(x), raw = FALSE) tab_deciles(x, ...) ``` ## Arguments - x: A vector. - probs: A vector of probabilities for the quantiles. If `probs` has names, these will be used for labels. - ...: For `chop_quantiles`, passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). For `brk_quantiles()`, passed to [`stats::quantile()`](https://rdrr.io/r/stats/quantile.html) or [`Hmisc::wtd.quantile()`](https://rdrr.io/pkg/Hmisc/man/wtd.stats.html). - labels: A character vector of labels or a function to create labels. - left: Logical. Left-closed or right-closed breaks? - raw: Logical. Use raw values in labels? - weights: `NULL` or numeric vector of same length as `x`. If not `NULL`, [`Hmisc::wtd.quantile()`](https://rdrr.io/pkg/Hmisc/man/wtd.stats.html) is used to calculate weighted quantiles. - recalc_probs: Logical. Recalculate probabilities of quantiles using [`ecdf(x)`](https://rdrr.io/r/stats/ecdf.html)? See below. ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details For non-numeric `x`, `left` is set to `FALSE` by default. This works better for calculating "type 1" quantiles, since they round down. See [`stats::quantile()`](https://rdrr.io/r/stats/quantile.html). By default, `chop_quantiles()` shows the requested probabilities in the labels. To show the numeric quantiles themselves, set `raw = TRUE`. When `x` contains duplicates, consecutive quantiles may be the same number. If so, interval labels may be misleading, and if `recalc_probs = FALSE` a warning is emitted. Set `recalc_probs = TRUE` to recalculate the probabilities of the quantiles using the [empirical cumulative distribution function](https://rdrr.io/r/stats/ecdf.html) of `x`. Doing so may give you different labels from what you expect, and will remove any names from `probs`, but it never changes the actual quantiles used for breaks. At present, `recalc_probs = TRUE` is incompatible with non-null `weights`. See the example below. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_quantiles(1:10, 1:3/4) #> [1] [0%, 25%) [0%, 25%) [0%, 25%) [25%, 50%) [25%, 50%) [50%, 75%) #> [7] [50%, 75%) [75%, 100%] [75%, 100%] [75%, 100%] #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%] chop_quantiles(1:10, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75)) #> [1] Q1 Q1 Q1 Q2 Q2 Q3 Q3 Q4 Q4 Q4 #> Levels: Q1 Q2 Q3 Q4 chop(1:10, brk_quantiles(1:3/4)) #> [1] [0%, 25%) [0%, 25%) [0%, 25%) [25%, 50%) [25%, 50%) [50%, 75%) #> [7] [50%, 75%) [75%, 100%] [75%, 100%] [75%, 100%] #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%] chop_deciles(1:10) #> [1] [0%, 10%) [10%, 20%) [20%, 30%) [30%, 40%) [40%, 50%) [50%, 60%) #> [7] [60%, 70%) [70%, 80%) [80%, 90%) [90%, 100%] #> 10 Levels: [0%, 10%) [10%, 20%) [20%, 30%) [30%, 40%) [40%, 50%) ... [90%, 100%] # to label by the quantiles themselves: chop_quantiles(1:10, 1:3/4, raw = TRUE) #> [1] [1, 3.25) [1, 3.25) [1, 3.25) [3.25, 5.5) [3.25, 5.5) [5.5, 7.75) #> [7] [5.5, 7.75) [7.75, 10] [7.75, 10] [7.75, 10] #> Levels: [1, 3.25) [3.25, 5.5) [5.5, 7.75) [7.75, 10] # duplicate quantiles: x <- c(1, 1, 1, 2, 3) quantile(x, 1:5/5) #> 20% 40% 60% 80% 100% #> 1.0 1.0 1.4 2.2 3.0 tab_quantiles(x, 1:5/5) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [20%, 40%] [60%, 80%) [80%, 100%] #> 3 1 1 tab_quantiles(x, 1:5/5, recalc_probs = TRUE) #> [0%, 60%] [60%, 80%) [80%, 100%] #> 3 1 1 set.seed(42) tab_quantiles(rnorm(100), probs = 1:3/4, raw = TRUE) #> [-2.993, -0.6167) [-0.6167, 0.0898) [0.0898, 0.6616) [0.6616, 2.287] #> 25 25 25 25 ``` ================================================ FILE: docs/reference/chop_spikes.html ================================================ Chop common values into singleton intervals — chop_spikes • santoku Skip to contents

    chop_spikes() lets you chop common values of x into their own singleton intervals. This can help make unusual values visible.

    Usage

    chop_spikes(x, breaks, ..., n = NULL, prop = NULL)
    
    brk_spikes(breaks, n = NULL, prop = NULL)
    
    tab_spikes(x, breaks, ..., n = NULL, prop = NULL)

    Arguments

    x

    A vector.

    breaks

    A numeric vector of cut-points or a call to a brk_* function. The resulting breaks object will be modified to add singleton breaks.

    ...

    Passed to chop().

    n, prop

    Scalar. Provide either n, a number of values, or prop, a proportion of length(x). Values of x which occur at least this often will get their own singleton break.

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    This function is [Experimental].

    Examples

    x <- c(1:4, rep(5, 5), 6:10)
    chop_spikes(x, c(2, 7), n = 5)
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  {5}     {5}     {5}     {5}     {5}    
    #> [10] (5, 7)  [7, 10] [7, 10] [7, 10] [7, 10]
    #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10]
    chop_spikes(x, c(2, 7), prop = 0.25)
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  {5}     {5}     {5}     {5}     {5}    
    #> [10] (5, 7)  [7, 10] [7, 10] [7, 10] [7, 10]
    #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10]
    chop_spikes(x, brk_width(5), n = 5)
    #>  [1] [1, 5)  [1, 5)  [1, 5)  [1, 5)  {5}     {5}     {5}     {5}     {5}    
    #> [10] [6, 11] [6, 11] [6, 11] [6, 11] [6, 11]
    #> Levels: [1, 5) {5} [6, 11]
    
    set.seed(42)
    x <- runif(40, 0, 10)
    x <- sample(x, 200, replace = TRUE)
    tab_spikes(x, brk_width(2, 0), prop = 0.05)
    #>      [0, 2)      [2, 4)      [4, 6)      [6, 8)  [8, 9.057)     {9.057} 
    #>          30          24          36          40          22          11 
    #> (9.057, 10] 
    #>          37 
    
    ================================================ FILE: docs/reference/chop_spikes.md ================================================ # Chop common values into singleton intervals `chop_spikes()` lets you chop common values of `x` into their own singleton intervals. This can help make unusual values visible. ## Usage ``` r chop_spikes(x, breaks, ..., n = NULL, prop = NULL) brk_spikes(breaks, n = NULL, prop = NULL) tab_spikes(x, breaks, ..., n = NULL, prop = NULL) ``` ## Arguments - x: A vector. - breaks: A numeric vector of cut-points or a call to a `brk_*` function. The resulting [`breaks`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) object will be modified to add singleton breaks. - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - n, prop: Scalar. Provide either `n`, a number of values, or `prop`, a proportion of `length(x)`. Values of `x` which occur at least this often will get their own singleton break. ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details This function is **\[experimental\]**. ## See also [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) for a different approach. Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r x <- c(1:4, rep(5, 5), 6:10) chop_spikes(x, c(2, 7), n = 5) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} {5} {5} {5} {5} #> [10] (5, 7) [7, 10] [7, 10] [7, 10] [7, 10] #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10] chop_spikes(x, c(2, 7), prop = 0.25) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} {5} {5} {5} {5} #> [10] (5, 7) [7, 10] [7, 10] [7, 10] [7, 10] #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10] chop_spikes(x, brk_width(5), n = 5) #> [1] [1, 5) [1, 5) [1, 5) [1, 5) {5} {5} {5} {5} {5} #> [10] [6, 11] [6, 11] [6, 11] [6, 11] [6, 11] #> Levels: [1, 5) {5} [6, 11] set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) tab_spikes(x, brk_width(2, 0), prop = 0.05) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 9.057) {9.057} #> 30 24 36 40 22 11 #> (9.057, 10] #> 37 ``` ================================================ FILE: docs/reference/chop_width.html ================================================ Chop into fixed-width intervals — chop_width • santoku Skip to contents

    chop_width() chops x into intervals of fixed width.

    Usage

    chop_width(x, width, start, ..., left = sign(width) > 0)
    
    brk_width(width, start)
    
    # Default S3 method
    brk_width(width, start)
    
    tab_width(x, width, start, ..., left = sign(width) > 0)

    Arguments

    x

    A vector.

    width

    Width of intervals.

    start

    Starting point for intervals. By default the smallest finite x (largest if width is negative).

    ...

    Passed to chop().

    left

    Logical. Left-closed or right-closed breaks?

    Value

    chop_* functions return a factor of the same length as x.

    brk_* functions return a function to create breaks.

    tab_* functions return a contingency table.

    Details

    If width is negative, chop_width() sets left = FALSE and intervals will go downwards from start.

    Examples

    chop_width(1:10, 2)
    #>  [1] [1, 3)  [1, 3)  [3, 5)  [3, 5)  [5, 7)  [5, 7)  [7, 9)  [7, 9)  [9, 11]
    #> [10] [9, 11]
    #> Levels: [1, 3) [3, 5) [5, 7) [7, 9) [9, 11]
    
    chop_width(1:10, 2, start = 0)
    #>  [1] [0, 2)  [2, 4)  [2, 4)  [4, 6)  [4, 6)  [6, 8)  [6, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10]
    
    chop_width(1:9, -2)
    #> [1] [1, 3] [1, 3] [1, 3] (3, 5] (3, 5] (5, 7] (5, 7] (7, 9] (7, 9]
    #> Levels: [1, 3] (3, 5] (5, 7] (7, 9]
    
    chop(1:10, brk_width(2, 0))
    #>  [1] [0, 2)  [2, 4)  [2, 4)  [4, 6)  [4, 6)  [6, 8)  [6, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10]
    
    tab_width(1:10, 2, start = 0)
    #>  [0, 2)  [2, 4)  [4, 6)  [6, 8) [8, 10] 
    #>       1       2       2       2       3 
    
    
    ================================================ FILE: docs/reference/chop_width.md ================================================ # Chop into fixed-width intervals `chop_width()` chops `x` into intervals of fixed `width`. ## Usage ``` r chop_width(x, width, start, ..., left = sign(width) > 0) brk_width(width, start) # Default S3 method brk_width(width, start) tab_width(x, width, start, ..., left = sign(width) > 0) ``` ## Arguments - x: A vector. - width: Width of intervals. - start: Starting point for intervals. By default the smallest finite `x` (largest if `width` is negative). - ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - left: Logical. Left-closed or right-closed breaks? ## Value `chop_*` functions return a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`. `brk_*` functions return a [`function`](https://rdrr.io/r/base/function.html) to create `breaks`. `tab_*` functions return a contingency [`table`](https://rdrr.io/r/base/table.html). ## Details If `width` is negative, `chop_width()` sets `left = FALSE` and intervals will go downwards from `start`. ## See also [brk_width-for-datetime](https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.md) Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) ## Examples ``` r chop_width(1:10, 2) #> [1] [1, 3) [1, 3) [3, 5) [3, 5) [5, 7) [5, 7) [7, 9) [7, 9) [9, 11] #> [10] [9, 11] #> Levels: [1, 3) [3, 5) [5, 7) [7, 9) [9, 11] chop_width(1:10, 2, start = 0) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] chop_width(1:9, -2) #> [1] [1, 3] [1, 3] [1, 3] (3, 5] (3, 5] (5, 7] (5, 7] (7, 9] (7, 9] #> Levels: [1, 3] (3, 5] (5, 7] (7, 9] chop(1:10, brk_width(2, 0)) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] tab_width(1:10, 2, start = 0) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] #> 1 2 2 2 3 ``` ================================================ FILE: docs/reference/dissect.html ================================================ Cut data into intervals, separating out common values — dissect • santoku Skip to contents

    Sometimes it's useful to separate out common elements of x. dissect() chops x, but puts common elements of x ("spikes") into separate categories.

    Usage

    dissect(
      x,
      breaks,
      ...,
      n = NULL,
      prop = NULL,
      spike_labels = "{{{l}}}",
      exclude_spikes = FALSE
    )
    
    tab_dissect(x, breaks, ..., n = NULL, prop = NULL)

    Arguments

    x, breaks, ...

    Passed to chop().

    n, prop

    Scalar. Provide either n, a number of values, or prop, a proportion of length(x). Values of x which occur at least this often will get their own singleton break.

    spike_labels

    Glue string for spike labels. Use "{l}" for the spike value.

    exclude_spikes

    Logical. Exclude spikes before chopping x? This can affect the location of data-dependent breaks.

    Value

    dissect() returns the result of chop(), but with common values put into separate factor levels.

    tab_dissect() returns a contingency table().

    Details

    Unlike chop_spikes(), dissect() doesn't break up intervals which contain a spike. As a result, unlike chop_* functions, dissect() does not chop x into disjoint intervals. See the examples.

    If breaks are data-dependent, their labels may be misleading after common elements have been removed. See the example below. To get round this, set exclude_spikes to TRUE. Then breaks will be calculated after removing spikes from the data.

    Levels of the result are ordered by the minimum element in each level. As a result, if drop = FALSE, empty levels will be placed last.

    This function is [Experimental].

    See also

    chop_spikes() for a different approach.

    Examples

    x <- c(2, 3, 3, 3, 4)
    dissect(x, c(2, 4), n = 3)
    #> [1] [2, 4] {3}    {3}    {3}    [2, 4]
    #> Levels: [2, 4] {3}
    dissect(x, brk_width(2), prop = 0.5)
    #> [1] [2, 4] {3}    {3}    {3}    [2, 4]
    #> Levels: [2, 4] {3}
    
    set.seed(42)
    x <- runif(40, 0, 10)
    x <- sample(x, 200, replace = TRUE)
    # Compare:
    table(dissect(x, brk_width(2, 0), prop = 0.05))
    #> 
    #>  [0, 2)  [2, 4)  [4, 6)  [6, 8) [8, 10] {9.057} 
    #>      30      24      36      40      59      11 
    # Versus:
    tab_spikes(x, brk_width(2, 0), prop = 0.05)
    #>      [0, 2)      [2, 4)      [4, 6)      [6, 8)  [8, 9.057)     {9.057} 
    #>          30          24          36          40          22          11 
    #> (9.057, 10] 
    #>          37 
    
    # Potentially confusing data-dependent breaks:
    set.seed(42)
    x <- rnorm(99)
    x[1:9] <- x[1]
    tab_quantiles(x, 1:2/3)
    #>     [0%, 33.33%) [33.33%, 66.67%)   [66.67%, 100%] 
    #>               33               33               33 
    tab_dissect(x, brk_quantiles(1:2/3), n = 9)
    #>     [0%, 33.33%) [33.33%, 66.67%)   [66.67%, 100%]          {1.371} 
    #>               33               33               24                9 
    # Calculate quantiles excluding spikes:
    tab_dissect(x, brk_quantiles(1:2/3), n = 9, exclude_spikes = TRUE)
    #>     [0%, 33.33%) [33.33%, 66.67%)   [66.67%, 100%]          {1.371} 
    #>               30               30               30                9 
    
    ================================================ FILE: docs/reference/dissect.md ================================================ # Cut data into intervals, separating out common values Sometimes it's useful to separate out common elements of `x`. `dissect()` chops `x`, but puts common elements of `x` ("spikes") into separate categories. ## Usage ``` r dissect( x, breaks, ..., n = NULL, prop = NULL, spike_labels = "{{{l}}}", exclude_spikes = FALSE ) tab_dissect(x, breaks, ..., n = NULL, prop = NULL) ``` ## Arguments - x, breaks, ...: Passed to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). - n, prop: Scalar. Provide either `n`, a number of values, or `prop`, a proportion of `length(x)`. Values of `x` which occur at least this often will get their own singleton break. - spike_labels: [Glue](https://glue.tidyverse.org/reference/glue.html) string for spike labels. Use `"{l}"` for the spike value. - exclude_spikes: Logical. Exclude spikes before chopping `x`? This can affect the location of data-dependent breaks. ## Value `dissect()` returns the result of [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), but with common values put into separate factor levels. `tab_dissect()` returns a contingency [table()](https://rdrr.io/r/base/table.html). ## Details Unlike [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), `dissect()` doesn't break up intervals which contain a spike. As a result, unlike `chop_*` functions, `dissect()` does not chop `x` into disjoint intervals. See the examples. If breaks are data-dependent, their labels may be misleading after common elements have been removed. See the example below. To get round this, set `exclude_spikes` to `TRUE`. Then breaks will be calculated after removing spikes from the data. Levels of the result are ordered by the minimum element in each level. As a result, if `drop = FALSE`, empty levels will be placed last. This function is **\[experimental\]**. ## See also [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) for a different approach. ## Examples ``` r x <- c(2, 3, 3, 3, 4) dissect(x, c(2, 4), n = 3) #> [1] [2, 4] {3} {3} {3} [2, 4] #> Levels: [2, 4] {3} dissect(x, brk_width(2), prop = 0.5) #> [1] [2, 4] {3} {3} {3} [2, 4] #> Levels: [2, 4] {3} set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) # Compare: table(dissect(x, brk_width(2, 0), prop = 0.05)) #> #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] {9.057} #> 30 24 36 40 59 11 # Versus: tab_spikes(x, brk_width(2, 0), prop = 0.05) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 9.057) {9.057} #> 30 24 36 40 22 11 #> (9.057, 10] #> 37 # Potentially confusing data-dependent breaks: set.seed(42) x <- rnorm(99) x[1:9] <- x[1] tab_quantiles(x, 1:2/3) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] #> 33 33 33 tab_dissect(x, brk_quantiles(1:2/3), n = 9) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] {1.371} #> 33 33 24 9 # Calculate quantiles excluding spikes: tab_dissect(x, brk_quantiles(1:2/3), n = 9, exclude_spikes = TRUE) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] {1.371} #> 30 30 30 9 ``` ================================================ FILE: docs/reference/exactly.html ================================================ Define singleton intervals explicitly — exactly • santoku Skip to contents

    exactly() duplicates its input. It lets you define singleton intervals like this: chop(x, c(1, exactly(2), 3)). This is the same as chop(x, c(1, 2, 2, 3)) but conveys your intent more clearly.

    Usage

    exactly(x)

    Arguments

    x

    A numeric vector.

    Value

    The same as rep(x, each = 2).

    Examples

    chop(1:10, c(2, exactly(5), 8))
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  {5}     (5, 8)  (5, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10]
    
    # same:
    chop(1:10, c(2, 5, 5, 8))
    #>  [1] [1, 2)  [2, 5)  [2, 5)  [2, 5)  {5}     (5, 8)  (5, 8)  [8, 10] [8, 10]
    #> [10] [8, 10]
    #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10]
    
    ================================================ FILE: docs/reference/exactly.md ================================================ # Define singleton intervals explicitly `exactly()` duplicates its input. It lets you define singleton intervals like this: `chop(x, c(1, exactly(2), 3))`. This is the same as `chop(x, c(1, 2, 2, 3))` but conveys your intent more clearly. ## Usage ``` r exactly(x) ``` ## Arguments - x: A numeric vector. ## Value The same as `rep(x, each = 2)`. ## Examples ``` r chop(1:10, c(2, exactly(5), 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} (5, 8) (5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10] # same: chop(1:10, c(2, 5, 5, 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} (5, 8) (5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10] ``` ================================================ FILE: docs/reference/fillet.html ================================================ Chop data precisely (for programmers) — fillet • santoku Skip to contents

    fillet() calls chop() with extend = FALSE and drop = FALSE. This ensures that you get only the breaks and labels you ask for. When programming, consider using fillet() instead of chop().

    Usage

    fillet(
      x,
      breaks,
      labels = lbl_intervals(),
      left = TRUE,
      close_end = TRUE,
      raw = NULL
    )

    Arguments

    x

    A vector.

    breaks

    A numeric vector of cut-points, or a function to create cut-points from x.

    labels

    A character vector of labels or a function to create labels.

    left

    Logical. Left-closed or right-closed breaks?

    close_end

    Logical. Close last break at right? (If left is FALSE, close first break at left?)

    raw

    Logical. Use raw values in labels?

    Value

    fillet() returns a factor of the same length as x, representing the intervals containing the value of x.

    Examples

    fillet(1:10, c(2, 5, 8))
    #>  [1] <NA>   [2, 5) [2, 5) [2, 5) [5, 8] [5, 8] [5, 8] [5, 8] <NA>   <NA>  
    #> Levels: [2, 5) [5, 8]
    
    ================================================ FILE: docs/reference/fillet.md ================================================ # Chop data precisely (for programmers) `fillet()` calls [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) with `extend = FALSE` and `drop = FALSE`. This ensures that you get only the `breaks` and `labels` you ask for. When programming, consider using `fillet()` instead of [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). ## Usage ``` r fillet( x, breaks, labels = lbl_intervals(), left = TRUE, close_end = TRUE, raw = NULL ) ``` ## Arguments - x: A vector. - breaks: A numeric vector of cut-points, or a function to create cut-points from `x`. - labels: A character vector of labels or a function to create labels. - left: Logical. Left-closed or right-closed breaks? - close_end: Logical. Close last break at right? (If `left` is `FALSE`, close first break at left?) - raw: Logical. Use raw values in labels? ## Value `fillet()` returns a [`factor`](https://rdrr.io/r/base/factor.html) of the same length as `x`, representing the intervals containing the value of `x`. ## See also Other chopping functions: [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md), [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md), [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md), [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md), [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md), [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md), [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md), [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md), [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) ## Examples ``` r fillet(1:10, c(2, 5, 8)) #> [1] [2, 5) [2, 5) [2, 5) [5, 8] [5, 8] [5, 8] [5, 8] #> Levels: [2, 5) [5, 8] ``` ================================================ FILE: docs/reference/format.breaks.html ================================================ ================================================ FILE: docs/reference/index.html ================================================ Package index • santoku Skip to contents

    Package overview

    santoku santoku-package
    A versatile cutting tool for R: package overview and options

    Basic chop functions

    Cut a vector into intervals

    chop() kiru() tab()
    Cut data into intervals
    fillet()
    Chop data precisely (for programmers)

    Chopping by width

    Cut a vector into intervals defined by width

    brk_width(<Duration>)
    Equal-width intervals for dates or datetimes
    chop_width() brk_width() tab_width()
    Chop into fixed-width intervals
    chop_proportions() brk_proportions() tab_proportions()
    Chop into proportions of the range of x
    chop_evenly() brk_evenly() tab_evenly()
    Chop into equal-width intervals

    Chopping by n

    Cut a vector into intervals defined by number of elements

    Chopping and separating

    Cut a vector into intervals, separating out common values

    chop_spikes() brk_spikes() tab_spikes()
    Chop common values into singleton intervals
    dissect() tab_dissect()
    Cut data into intervals, separating out common values

    Other chop functions

    Miscellaneous ways to cut a vector into intervals

    chop_mean_sd() brk_mean_sd() tab_mean_sd()
    Chop by standard deviations
    chop_pretty() brk_pretty() tab_pretty()
    Chop using pretty breakpoints
    chop_fn() brk_fn() tab_fn()
    Chop using an existing function
    brk_default()
    Create a standard set of breaks
    brk_manual()
    Create a breaks object manually

    Label functions

    Specify how to label the chopped intervals

    lbl_dash()
    Label chopped intervals like 1-4, 4-5, ...
    lbl_date() lbl_datetime() experimental
    Label dates and datetimes
    lbl_discrete()
    Label discrete data
    lbl_endpoints() lbl_endpoint()
    Label chopped intervals by their left or right endpoints
    lbl_glue()
    Label chopped intervals using the glue package
    lbl_intervals()
    Label chopped intervals using set notation
    lbl_midpoints()
    Label chopped intervals by their midpoints
    lbl_seq()
    Label chopped intervals in sequence

    Miscellaneous

    Other helper functions

    format(<breaks>) print(<breaks>) is.breaks()
    Class representing a set of intervals
    exactly()
    Define singleton intervals explicitly
    non-standard-types
    Tips for chopping non-standard types
    percent()
    Simple percentage formatter
    ================================================ FILE: docs/reference/index.md ================================================ # Package index ## Package overview - [`santoku`](https://hughjonesd.github.io/santoku/reference/santoku-package.md) [`santoku-package`](https://hughjonesd.github.io/santoku/reference/santoku-package.md) : A versatile cutting tool for R: package overview and options ## Basic chop functions Cut a vector into intervals - [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) [`kiru()`](https://hughjonesd.github.io/santoku/reference/chop.md) [`tab()`](https://hughjonesd.github.io/santoku/reference/chop.md) : Cut data into intervals - [`fillet()`](https://hughjonesd.github.io/santoku/reference/fillet.md) : Chop data precisely (for programmers) ## Chopping by width Cut a vector into intervals defined by width - [`brk_width(`*``*`)`](https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.md) : Equal-width intervals for dates or datetimes - [`chop_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) [`brk_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) [`tab_width()`](https://hughjonesd.github.io/santoku/reference/chop_width.md) : Chop into fixed-width intervals - [`chop_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) [`brk_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) [`tab_proportions()`](https://hughjonesd.github.io/santoku/reference/chop_proportions.md) : Chop into proportions of the range of x - [`chop_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) [`brk_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) [`tab_evenly()`](https://hughjonesd.github.io/santoku/reference/chop_evenly.md) : Chop into equal-width intervals ## Chopping by n Cut a vector into intervals defined by number of elements - [`chop_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) [`brk_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) [`tab_n()`](https://hughjonesd.github.io/santoku/reference/chop_n.md) : Chop into fixed-sized groups - [`chop_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`chop_deciles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`brk_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`tab_quantiles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) [`tab_deciles()`](https://hughjonesd.github.io/santoku/reference/chop_quantiles.md) : Chop by quantiles - [`chop_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) [`brk_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) [`tab_equally()`](https://hughjonesd.github.io/santoku/reference/chop_equally.md) : Chop equal-sized groups ## Chopping and separating Cut a vector into intervals, separating out common values - [`chop_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) [`brk_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) [`tab_spikes()`](https://hughjonesd.github.io/santoku/reference/chop_spikes.md) : Chop common values into singleton intervals - [`dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) [`tab_dissect()`](https://hughjonesd.github.io/santoku/reference/dissect.md) : Cut data into intervals, separating out common values ## Other chop functions Miscellaneous ways to cut a vector into intervals - [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) [`brk_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) [`tab_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) : Chop by standard deviations - [`chop_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) [`brk_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) [`tab_pretty()`](https://hughjonesd.github.io/santoku/reference/chop_pretty.md) : Chop using pretty breakpoints - [`chop_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) [`brk_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) [`tab_fn()`](https://hughjonesd.github.io/santoku/reference/chop_fn.md) : Chop using an existing function - [`brk_default()`](https://hughjonesd.github.io/santoku/reference/brk_default.md) : Create a standard set of breaks - [`brk_manual()`](https://hughjonesd.github.io/santoku/reference/brk_manual.md) : Create a `breaks` object manually ## Label functions Specify how to label the chopped intervals - [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) : Label chopped intervals like 1-4, 4-5, ... - [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) [`lbl_datetime()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md) **\[experimental\]** : Label dates and datetimes - [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md) : Label discrete data - [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) [`lbl_endpoint()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md) : Label chopped intervals by their left or right endpoints - [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) : Label chopped intervals using the `glue` package - [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md) : Label chopped intervals using set notation - [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md) : Label chopped intervals by their midpoints - [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) : Label chopped intervals in sequence ## Miscellaneous Other helper functions - [`format(`*``*`)`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) [`print(`*``*`)`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) [`is.breaks()`](https://hughjonesd.github.io/santoku/reference/breaks-class.md) : Class representing a set of intervals - [`exactly()`](https://hughjonesd.github.io/santoku/reference/exactly.md) : Define singleton intervals explicitly - [`non-standard-types`](https://hughjonesd.github.io/santoku/reference/non-standard-types.md) : Tips for chopping non-standard types - [`percent()`](https://hughjonesd.github.io/santoku/reference/percent.md) : Simple percentage formatter ================================================ FILE: docs/reference/is.breaks.html ================================================ ================================================ FILE: docs/reference/kiru.html ================================================ ================================================ FILE: docs/reference/knife.html ================================================ Deprecated — knife • santoku Skip to contents

    [Soft-deprecated] knife() is deprecated in favour of purrr::partial().

    Usage

    knife(...)

    Arguments

    ...

    Parameters for chop().

    Value

    A function.

    ================================================ FILE: docs/reference/knife.md ================================================ # Deprecated **\[soft-deprecated\]** `knife()` is deprecated in favour of [`purrr::partial()`](https://purrr.tidyverse.org/reference/partial.html). ## Usage ``` r knife(...) ``` ## Arguments - ...: Parameters for [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). ## Value A function. ================================================ FILE: docs/reference/lbl_dash.html ================================================ Label chopped intervals like 1-4, 4-5, ... — lbl_dash • santoku Skip to contents

    This label style is user-friendly, but doesn't distinguish between left- and right-closed intervals. It's good for continuous data where you don't expect points to be exactly on the breaks.

    Usage

    lbl_dash(
      symbol = em_dash(),
      fmt = NULL,
      single = "{l}",
      first = NULL,
      last = NULL,
      raw = deprecated()
    )

    Arguments

    symbol

    String: symbol to use for the dash.

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    raw

    [Deprecated]. Throws an error. Use the raw argument to chop() instead.

    Value

    A function that creates a vector of labels.

    Details

    If you don't want unicode output, use lbl_dash("-").

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    chop(1:10, c(2, 5, 8), lbl_dash())
    #>  [1] 1—2  2—5  2—5  2—5  5—8  5—8  5—8  8—10 8—10 8—10
    #> Levels: 1—2 2—5 5—8 8—10
    
    chop(1:10, c(2, 5, 8), lbl_dash(" to ", fmt = "%.1f"))
    #>  [1] 1.0 to 2.0  2.0 to 5.0  2.0 to 5.0  2.0 to 5.0  5.0 to 8.0  5.0 to 8.0 
    #>  [7] 5.0 to 8.0  8.0 to 10.0 8.0 to 10.0 8.0 to 10.0
    #> Levels: 1.0 to 2.0 2.0 to 5.0 5.0 to 8.0 8.0 to 10.0
    
    chop(1:10, c(2, 5, 8), lbl_dash(first = "<{r}"))
    #>  [1] <2   2—5  2—5  2—5  5—8  5—8  5—8  8—10 8—10 8—10
    #> Levels: <2 2—5 5—8 8—10
    
    pretty <- function (x) prettyNum(x, big.mark = ",", digits = 1)
    chop(runif(10) * 10000, c(3000, 7000), lbl_dash(" to ", fmt = pretty))
    #>  [1] 7,000 to 9,677 7,000 to 9,677 7,000 to 9,677 3,000 to 7,000 7,000 to 9,677
    #>  [6] 3,000 to 7,000 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677 3,000 to 7,000
    #> Levels: 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677
    
    ================================================ FILE: docs/reference/lbl_dash.md ================================================ # Label chopped intervals like 1-4, 4-5, ... This label style is user-friendly, but doesn't distinguish between left- and right-closed intervals. It's good for continuous data where you don't expect points to be exactly on the breaks. ## Usage ``` r lbl_dash( symbol = em_dash(), fmt = NULL, single = "{l}", first = NULL, last = NULL, raw = deprecated() ) ``` ## Arguments - symbol: String: symbol to use for the dash. - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - raw: **\[deprecated\]**. Throws an error. Use the `raw` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) instead. ## Value A function that creates a vector of labels. ## Details If you don't want unicode output, use `lbl_dash("-")`. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r chop(1:10, c(2, 5, 8), lbl_dash()) #> [1] 1—2 2—5 2—5 2—5 5—8 5—8 5—8 8—10 8—10 8—10 #> Levels: 1—2 2—5 5—8 8—10 chop(1:10, c(2, 5, 8), lbl_dash(" to ", fmt = "%.1f")) #> [1] 1.0 to 2.0 2.0 to 5.0 2.0 to 5.0 2.0 to 5.0 5.0 to 8.0 5.0 to 8.0 #> [7] 5.0 to 8.0 8.0 to 10.0 8.0 to 10.0 8.0 to 10.0 #> Levels: 1.0 to 2.0 2.0 to 5.0 5.0 to 8.0 8.0 to 10.0 chop(1:10, c(2, 5, 8), lbl_dash(first = "<{r}")) #> [1] <2 2—5 2—5 2—5 5—8 5—8 5—8 8—10 8—10 8—10 #> Levels: <2 2—5 5—8 8—10 pretty <- function (x) prettyNum(x, big.mark = ",", digits = 1) chop(runif(10) * 10000, c(3000, 7000), lbl_dash(" to ", fmt = pretty)) #> [1] 7,000 to 9,677 7,000 to 9,677 7,000 to 9,677 3,000 to 7,000 7,000 to 9,677 #> [6] 3,000 to 7,000 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677 3,000 to 7,000 #> Levels: 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677 ``` ================================================ FILE: docs/reference/lbl_datetime.html ================================================ Label dates and datetimes — lbl_date • santoku Skip to contents

    [Experimental]

    lbl_date() and lbl_datetime() produce nice labels for dates and datetimes. Where possible ranges are simplified, like like "13-14 Jul 2026" or "11:15-12:15 1 Dec 2025".

    Usage

    lbl_date(
      fmt = "%e %b %Y",
      symbol = "-",
      unit = as.difftime(1, units = "days"),
      single = "{l}",
      first = NULL,
      last = NULL
    )
    
    lbl_datetime(
      fmt = "%H:%M:%S %b %e %Y",
      symbol = "-",
      unit = NULL,
      single = "{l}",
      first = NULL,
      last = NULL
    )

    Arguments

    fmt

    String, list or function. A format for break endpoints.

    symbol

    String: separator to use for full ranges.

    unit

    Optional interval unit for non-overlapping labels. If not NULL, . endpoints are adjusted in the style of lbl_discrete().

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    Value

    A function that creates a vector of labels.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    winter <- as.Date("2025-12-01") + 0:89
    tab(winter, as.Date(c("2025-12-25", "2026-01-06")),
        labels = lbl_date())
    #>             1-24 Dec 2025 25 Dec 2025 -  5 Jan 2026       6 Jan - 28 Feb 2026 
    #>                        24                        12                        54 
    new_year <- as.POSIXct("2025-12-31 23:00") + 0:120 * 60
    round_midnight <- as.POSIXct(c("2025-12-31 23:59", "2026-01-01 00:05"))
    tab(new_year, round_midnight,
        labels = lbl_datetime())
    #>               23:00:00-23:59:00 Dec 31 2025 
    #>                                          59 
    #> 23:59:00 Dec 31 2025 - 00:05:00 Jan  1 2026 
    #>                                           6 
    #>               00:05:00-01:00:00 Jan  1 2026 
    #>                                          56 
    tab(new_year, round_midnight,
        labels = lbl_datetime(unit = as.difftime(1, units = "mins")))
    #>               23:00:00-23:58:00 Dec 31 2025 
    #>                                          59 
    #> 23:59:00 Dec 31 2025 - 00:04:00 Jan  1 2026 
    #>                                           6 
    #>               00:05:00-01:00:00 Jan  1 2026 
    #>                                          56 
    
    ================================================ FILE: docs/reference/lbl_datetime.md ================================================ # Label dates and datetimes **\[experimental\]** `lbl_date()` and `lbl_datetime()` produce nice labels for dates and datetimes. Where possible ranges are simplified, like like "13-14 Jul 2026" or "11:15-12:15 1 Dec 2025". ## Usage ``` r lbl_date( fmt = "%e %b %Y", symbol = "-", unit = as.difftime(1, units = "days"), single = "{l}", first = NULL, last = NULL ) lbl_datetime( fmt = "%H:%M:%S %b %e %Y", symbol = "-", unit = NULL, single = "{l}", first = NULL, last = NULL ) ``` ## Arguments - fmt: String, list or function. A format for break endpoints. - symbol: String: separator to use for full ranges. - unit: Optional interval unit for non-overlapping labels. If not `NULL`, . endpoints are adjusted in the style of [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md). - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. ## Value A function that creates a vector of labels. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r winter <- as.Date("2025-12-01") + 0:89 tab(winter, as.Date(c("2025-12-25", "2026-01-06")), labels = lbl_date()) #> 1-24 Dec 2025 25 Dec 2025 - 5 Jan 2026 6 Jan - 28 Feb 2026 #> 24 12 54 new_year <- as.POSIXct("2025-12-31 23:00") + 0:120 * 60 round_midnight <- as.POSIXct(c("2025-12-31 23:59", "2026-01-01 00:05")) tab(new_year, round_midnight, labels = lbl_datetime()) #> 23:00:00-23:59:00 Dec 31 2025 #> 59 #> 23:59:00 Dec 31 2025 - 00:05:00 Jan 1 2026 #> 6 #> 00:05:00-01:00:00 Jan 1 2026 #> 56 tab(new_year, round_midnight, labels = lbl_datetime(unit = as.difftime(1, units = "mins"))) #> 23:00:00-23:58:00 Dec 31 2025 #> 59 #> 23:59:00 Dec 31 2025 - 00:04:00 Jan 1 2026 #> 6 #> 00:05:00-01:00:00 Jan 1 2026 #> 56 ``` ================================================ FILE: docs/reference/lbl_discrete.html ================================================ Label discrete data — lbl_discrete • santoku Skip to contents

    lbl_discrete() creates labels for discrete data, such as integers. For example, breaks c(1, 3, 4, 6, 7) are labelled: "1-2", "3", "4-5", "6-7".

    Usage

    lbl_discrete(
      symbol = em_dash(),
      unit = 1L,
      fmt = NULL,
      single = NULL,
      first = NULL,
      last = NULL
    )

    Arguments

    symbol

    String: symbol to use for the dash.

    unit

    Minimum difference between distinct values of data. For integers, 1.

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    Value

    A function that creates a vector of labels.

    Details

    No check is done that the data are discrete-valued. If they are not, then these labels may be misleading. Here, discrete-valued means that if x < y, then x <= y - unit.

    Be aware that Date objects may have non-integer values. See Date.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    tab(1:7, c(1, 3, 5), lbl_discrete())
    #> 1—2 3—4 5—7 
    #>   2   2   3 
    
    tab(1:7, c(3, 5), lbl_discrete(first = "<= {r}"))
    #> <= 2  3—4  5—7 
    #>    2    2    3 
    
    tab(1:7 * 1000, c(1, 3, 5) * 1000, lbl_discrete(unit = 1000))
    #> 1000—2000 3000—4000 5000—7000 
    #>         2         2         3 
    
    # Misleading labels for non-integer data
    chop(2.5, c(1, 3, 5), lbl_discrete())
    #> [1] 1—2
    #> Levels: 1—2
    
    
    ================================================ FILE: docs/reference/lbl_discrete.md ================================================ # Label discrete data `lbl_discrete()` creates labels for discrete data, such as integers. For example, breaks `c(1, 3, 4, 6, 7)` are labelled: `"1-2", "3", "4-5", "6-7"`. ## Usage ``` r lbl_discrete( symbol = em_dash(), unit = 1L, fmt = NULL, single = NULL, first = NULL, last = NULL ) ``` ## Arguments - symbol: String: symbol to use for the dash. - unit: Minimum difference between distinct values of data. For integers, 1. - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. ## Value A function that creates a vector of labels. ## Details No check is done that the data are discrete-valued. If they are not, then these labels may be misleading. Here, discrete-valued means that if `x < y`, then `x <= y - unit`. Be aware that Date objects may have non-integer values. See [Date](https://rdrr.io/r/base/Dates.html). ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r tab(1:7, c(1, 3, 5), lbl_discrete()) #> 1—2 3—4 5—7 #> 2 2 3 tab(1:7, c(3, 5), lbl_discrete(first = "<= {r}")) #> <= 2 3—4 5—7 #> 2 2 3 tab(1:7 * 1000, c(1, 3, 5) * 1000, lbl_discrete(unit = 1000)) #> 1000—2000 3000—4000 5000—7000 #> 2 2 3 # Misleading labels for non-integer data chop(2.5, c(1, 3, 5), lbl_discrete()) #> [1] 1—2 #> Levels: 1—2 ``` ================================================ FILE: docs/reference/lbl_endpoint.html ================================================ ================================================ FILE: docs/reference/lbl_endpoints.html ================================================ Label chopped intervals by their left or right endpoints — lbl_endpoints • santoku Skip to contents

    This is useful when the left endpoint unambiguously indicates the interval. In other cases it may give errors due to duplicate labels.

    Usage

    lbl_endpoints(
      left = TRUE,
      fmt = NULL,
      single = NULL,
      first = NULL,
      last = NULL,
      raw = deprecated()
    )
    
    lbl_endpoint(fmt = NULL, raw = FALSE, left = TRUE)

    Arguments

    left

    Flag. Use left endpoint or right endpoint?

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    raw

    [Deprecated]. Throws an error. Use the raw argument to chop() instead.

    Value

    A function that creates a vector of labels.

    Details

    lbl_endpoint() is [Defunct] and gives an error since santoku 1.0.0.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    chop(1:10, c(2, 5, 8), lbl_endpoints(left = TRUE))
    #>  [1] 1 2 2 2 5 5 5 8 8 8
    #> Levels: 1 2 5 8
    chop(1:10, c(2, 5, 8), lbl_endpoints(left = FALSE))
    #>  [1] 2  5  5  5  8  8  8  10 10 10
    #> Levels: 2 5 8 10
    if (requireNamespace("lubridate")) {
      tab_width(
              as.Date("2000-01-01") + 0:365,
             months(1),
             labels = lbl_endpoints(fmt = "%b")
           )
    }
    #> Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 
    #>  31  29  31  30  31  30  31  31  30  31  30  31 
    
    if (FALSE) { # \dontrun{
      # This gives breaks `[1, 2) [2, 3) {3}` which lead to
      # duplicate labels `"2", "3", "3"`:
      chop(1:3, 1:3, lbl_endpoints(left = FALSE))
    } # }
    
    ================================================ FILE: docs/reference/lbl_endpoints.md ================================================ # Label chopped intervals by their left or right endpoints This is useful when the left endpoint unambiguously indicates the interval. In other cases it may give errors due to duplicate labels. ## Usage ``` r lbl_endpoints( left = TRUE, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) lbl_endpoint(fmt = NULL, raw = FALSE, left = TRUE) ``` ## Arguments - left: Flag. Use left endpoint or right endpoint? - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - raw: **\[deprecated\]**. Throws an error. Use the `raw` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) instead. ## Value A function that creates a vector of labels. ## Details `lbl_endpoint()` is **\[defunct\]** and gives an error since santoku 1.0.0. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r chop(1:10, c(2, 5, 8), lbl_endpoints(left = TRUE)) #> [1] 1 2 2 2 5 5 5 8 8 8 #> Levels: 1 2 5 8 chop(1:10, c(2, 5, 8), lbl_endpoints(left = FALSE)) #> [1] 2 5 5 5 8 8 8 10 10 10 #> Levels: 2 5 8 10 if (requireNamespace("lubridate")) { tab_width( as.Date("2000-01-01") + 0:365, months(1), labels = lbl_endpoints(fmt = "%b") ) } #> Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec #> 31 29 31 30 31 30 31 31 30 31 30 31 if (FALSE) { # \dontrun{ # This gives breaks `[1, 2) [2, 3) {3}` which lead to # duplicate labels `"2", "3", "3"`: chop(1:3, 1:3, lbl_endpoints(left = FALSE)) } # } ``` ================================================ FILE: docs/reference/lbl_format.html ================================================ Label chopped intervals with arbitrary formatting — lbl_format • santoku Skip to contents

    [Questioning]

    Usage

    lbl_format(fmt, fmt1 = "%.3g", raw = FALSE)

    Arguments

    fmt

    A format. Can be a string, passed into base::sprintf() or format() methods; or a one-argument formatting function.

    fmt1

    Format for breaks consisting of a single value.

    raw

    Logical. Always use raw breaks in labels, rather than e.g. quantiles or standard deviations?

    Value

    A vector of labels for chop, or a function that creates labels.

    Details

    These labels let you format breaks arbitrarily, using either a string (passed to sprintf()) or a function.

    If fmt is a function, it must accept two arguments, representing the left and right endpoints of each interval.

    If breaks are non-numeric, you can only use "%s" in a string fmt. breaks will be converted to character in this case.

    lbl_format() is in the "questioning" stage. As an alternative, consider using lbl_dash() or lbl_intervals() with the fmt argument.

    See also

    Other labelling functions: lbl_dash(), lbl_discrete(), lbl_intervals(), lbl_manual(), lbl_seq()

    Examples

    
    tab(1:10, c(1,3, 3, 7),
          label = lbl_format("%.3g to %.3g"))
    #>  1 to 3       3  3 to 7 7 to 10 
    #>       2       1       3       4 
    tab(1:10, c(1,3, 3, 7),
          label = lbl_format("%.3g to %.3g", "Exactly %.3g"))
    #>    1 to 3 Exactly 3    3 to 7   7 to 10 
    #>         2         1         3         4 
    
    percent2 <- function (x, y) {
      sprintf("%.2f%% - %.2f%%", x*100, y*100)
    }
    tab(runif(100), c(0.25, 0.5, .75),
          labels = lbl_format(percent2))
    #>  0.64% - 25.00% 25.00% - 50.00% 50.00% - 75.00% 75.00% - 98.41% 
    #>              21              20              33              26 
    
    ================================================ FILE: docs/reference/lbl_format.md ================================================ # Label chopped intervals with arbitrary formatting **\[questioning\]** ## Usage ``` r lbl_format(fmt, fmt1 = "%.3g", raw = FALSE) ``` ## Arguments - fmt: A format. Can be a string, passed into [`base::sprintf()`](https://rdrr.io/r/base/sprintf.html) or [`format()`](https://rdrr.io/r/base/format.html) methods; or a one-argument formatting function. - fmt1: Format for breaks consisting of a single value. - raw: Logical. Always use raw `breaks` in labels, rather than e.g. quantiles or standard deviations? ## Value A vector of labels for `chop`, or a function that creates labels. ## Details These labels let you format breaks arbitrarily, using either a string (passed to [`sprintf()`](https://rdrr.io/r/base/sprintf.html)) or a function. If `fmt` is a function, it must accept two arguments, representing the left and right endpoints of each interval. If `breaks` are non-numeric, you can only use `"%s"` in a string `fmt`. `breaks` will be converted to character in this case. `lbl_format()` is in the "questioning" stage. As an alternative, consider using [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md) or [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md) with the `fmt` argument. ## See also Other labelling functions: [`lbl_dash`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md)`()`, [`lbl_discrete`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md)`()`, [`lbl_intervals`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md)`()`, [`lbl_manual`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md)`()`, [`lbl_seq`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md)`()` ## Examples ``` r tab(1:10, c(1,3, 3, 7), label = lbl_format("%.3g to %.3g")) #> 1 to 3 3 3 to 7 7 to 10 #> 2 1 3 4 tab(1:10, c(1,3, 3, 7), label = lbl_format("%.3g to %.3g", "Exactly %.3g")) #> 1 to 3 Exactly 3 3 to 7 7 to 10 #> 2 1 3 4 percent2 <- function (x, y) { sprintf("%.2f%% - %.2f%%", x*100, y*100) } tab(runif(100), c(0.25, 0.5, .75), labels = lbl_format(percent2)) #> 0.64% - 25.00% 25.00% - 50.00% 50.00% - 75.00% 75.00% - 98.41% #> 21 20 33 26 ``` ================================================ FILE: docs/reference/lbl_glue.html ================================================ Label chopped intervals using the glue package — lbl_glue • santoku Skip to contents

    Use "{l}" and "{r}" to show the left and right endpoints of the intervals.

    Usage

    lbl_glue(
      label,
      fmt = NULL,
      single = NULL,
      first = NULL,
      last = NULL,
      raw = deprecated(),
      ...
    )

    Arguments

    label

    A glue string passed to glue::glue().

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    raw

    [Deprecated]. Throws an error. Use the raw argument to chop() instead.

    ...

    Further arguments passed to glue::glue().

    Value

    A function that creates a vector of labels.

    Details

    The following variables are available in the glue string:

    • l is a character vector of left endpoints of intervals.

    • r is a character vector of right endpoints of intervals.

    • l_closed is a logical vector. Elements are TRUE when the left endpoint is closed.

    • r_closed is a logical vector, TRUE when the right endpoint is closed.

    Endpoints will be formatted by fmt before being passed to glue().

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    tab(1:10, c(1, 3, 3, 7),
        labels = lbl_glue("{l} to {r}", single = "Exactly {l}"))
    #>    1 to 3 Exactly 3    3 to 7   7 to 10 
    #>         2         1         3         4 
    
    tab(1:10 * 1000, c(1, 3, 5, 7) * 1000,
        labels = lbl_glue("{l}-{r}",
                          fmt = function(x) prettyNum(x, big.mark=',')))
    #>  1,000-3,000  3,000-5,000  5,000-7,000 7,000-10,000 
    #>            2            2            2            4 
    
    # reproducing lbl_intervals():
    interval_left <- "{ifelse(l_closed, '[', '(')}"
    interval_right <- "{ifelse(r_closed, ']', ')')}"
    glue_string <- paste0(interval_left, "{l}", ", ", "{r}", interval_right)
    tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(glue_string, single = "{{{l}}}"))
    #>  [1, 3)     {3}  (3, 7) [7, 10] 
    #>       2       1       3       4 
    
    
    ================================================ FILE: docs/reference/lbl_glue.md ================================================ # Label chopped intervals using the `glue` package Use `"{l}"` and `"{r}"` to show the left and right endpoints of the intervals. ## Usage ``` r lbl_glue( label, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated(), ... ) ``` ## Arguments - label: A glue string passed to [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html). - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See `lbl_glue()` for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See `lbl_glue()` for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See `lbl_glue()` for details. - raw: **\[deprecated\]**. Throws an error. Use the `raw` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) instead. - ...: Further arguments passed to [`glue::glue()`](https://glue.tidyverse.org/reference/glue.html). ## Value A function that creates a vector of labels. ## Details The following variables are available in the glue string: - `l` is a character vector of left endpoints of intervals. - `r` is a character vector of right endpoints of intervals. - `l_closed` is a logical vector. Elements are `TRUE` when the left endpoint is closed. - `r_closed` is a logical vector, `TRUE` when the right endpoint is closed. Endpoints will be formatted by `fmt` before being passed to `glue()`. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r tab(1:10, c(1, 3, 3, 7), labels = lbl_glue("{l} to {r}", single = "Exactly {l}")) #> 1 to 3 Exactly 3 3 to 7 7 to 10 #> 2 1 3 4 tab(1:10 * 1000, c(1, 3, 5, 7) * 1000, labels = lbl_glue("{l}-{r}", fmt = function(x) prettyNum(x, big.mark=','))) #> 1,000-3,000 3,000-5,000 5,000-7,000 7,000-10,000 #> 2 2 2 4 # reproducing lbl_intervals(): interval_left <- "{ifelse(l_closed, '[', '(')}" interval_right <- "{ifelse(r_closed, ']', ')')}" glue_string <- paste0(interval_left, "{l}", ", ", "{r}", interval_right) tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(glue_string, single = "{{{l}}}")) #> [1, 3) {3} (3, 7) [7, 10] #> 2 1 3 4 ``` ================================================ FILE: docs/reference/lbl_intervals.html ================================================ Label chopped intervals using set notation — lbl_intervals • santoku Skip to contents

    These labels are the most exact, since they show you whether intervals are "closed" or "open", i.e. whether they include their endpoints.

    Usage

    lbl_intervals(
      fmt = NULL,
      single = "{{{l}}}",
      first = NULL,
      last = NULL,
      raw = deprecated()
    )

    Arguments

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    raw

    [Deprecated]. Throws an error. Use the raw argument to chop() instead.

    Value

    A function that creates a vector of labels.

    Details

    Mathematical set notation looks like this:

    • [a, b]: all numbers x where a <= x <= b;

    • (a, b): all numbers where a < x < b;

    • [a, b): all numbers where a <= x < b;

    • (a, b]: all numbers where a < x <= b;

    • {a}: just the number a exactly.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    
    tab(-10:10, c(-3, 0, 0, 3),
          labels = lbl_intervals())
    #> [-10, -3)   [-3, 0)       {0}    (0, 3)   [3, 10] 
    #>         7         3         1         2         8 
    
    tab(-10:10, c(-3, 0, 0, 3),
          labels = lbl_intervals(fmt = list(nsmall = 1)))
    #> [-10.0,  -3.0) [ -3.0,   0.0)        {  0.0} (  0.0,   3.0) [  3.0,  10.0] 
    #>              7              3              1              2              8 
    
    tab_evenly(runif(20), 10,
          labels = lbl_intervals(fmt = percent))
    #>  [6.095%, 15.1%)  [15.1%, 24.11%) [24.11%, 33.11%) [33.11%, 42.12%) 
    #>                1                2                4                2 
    #> [42.12%, 51.12%) [51.12%, 60.13%) [60.13%, 69.13%) [78.14%, 87.14%) 
    #>                2                4                1                2 
    #> [87.14%, 96.15%] 
    #>                2 
    
    
    ================================================ FILE: docs/reference/lbl_intervals.md ================================================ # Label chopped intervals using set notation These labels are the most exact, since they show you whether intervals are "closed" or "open", i.e. whether they include their endpoints. ## Usage ``` r lbl_intervals( fmt = NULL, single = "{{{l}}}", first = NULL, last = NULL, raw = deprecated() ) ``` ## Arguments - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - raw: **\[deprecated\]**. Throws an error. Use the `raw` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) instead. ## Value A function that creates a vector of labels. ## Details Mathematical set notation looks like this: - `[a, b]`: all numbers `x` where `a <= x <= b`; - `(a, b)`: all numbers where `a < x < b`; - `[a, b)`: all numbers where `a <= x < b`; - `(a, b]`: all numbers where `a < x <= b`; - `{a}`: just the number `a` exactly. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals()) #> [-10, -3) [-3, 0) {0} (0, 3) [3, 10] #> 7 3 1 2 8 tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals(fmt = list(nsmall = 1))) #> [-10.0, -3.0) [ -3.0, 0.0) { 0.0} ( 0.0, 3.0) [ 3.0, 10.0] #> 7 3 1 2 8 tab_evenly(runif(20), 10, labels = lbl_intervals(fmt = percent)) #> [6.095%, 15.1%) [15.1%, 24.11%) [24.11%, 33.11%) [33.11%, 42.12%) #> 1 2 4 2 #> [42.12%, 51.12%) [51.12%, 60.13%) [60.13%, 69.13%) [78.14%, 87.14%) #> 2 4 1 2 #> [87.14%, 96.15%] #> 2 ``` ================================================ FILE: docs/reference/lbl_manual.html ================================================ Defunct: label chopped intervals in a user-defined sequence — lbl_manual • santoku Skip to contents

    [Defunct]

    Usage

    lbl_manual(sequence, fmt = "%s")

    Arguments

    sequence

    A character vector of labels.

    fmt

    String, list or function. A format for break endpoints.

    Value

    A function that creates a vector of labels.

    Details

    lbl_manual() is defunct since santoku 1.0.0. It is little used and is not closely related to the rest of the package. It also risks mislabelling intervals, e.g. if intervals are extended. Use of lbl_manual() will give an error.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    if (FALSE) { # \dontrun{
    chop(1:10, c(2, 5, 8), lbl_manual(c("w", "x", "y", "z")))
    # ->
    chop(1:10, c(2, 5, 8), labels = c("w", "x", "y", "z"))
    } # }
    
    ================================================ FILE: docs/reference/lbl_manual.md ================================================ # Defunct: label chopped intervals in a user-defined sequence **\[defunct\]** ## Usage ``` r lbl_manual(sequence, fmt = "%s") ``` ## Arguments - sequence: A character vector of labels. - fmt: String, list or function. A format for break endpoints. ## Value A function that creates a vector of labels. ## Details `lbl_manual()` is defunct since santoku 1.0.0. It is little used and is not closely related to the rest of the package. It also risks mislabelling intervals, e.g. if intervals are extended. Use of `lbl_manual()` will give an error. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r if (FALSE) { # \dontrun{ chop(1:10, c(2, 5, 8), lbl_manual(c("w", "x", "y", "z"))) # -> chop(1:10, c(2, 5, 8), labels = c("w", "x", "y", "z")) } # } ``` ================================================ FILE: docs/reference/lbl_midpoints.html ================================================ Label chopped intervals by their midpoints — lbl_midpoints • santoku Skip to contents

    This uses the midpoint of each interval for its label.

    Usage

    lbl_midpoints(
      fmt = NULL,
      single = NULL,
      first = NULL,
      last = NULL,
      raw = deprecated()
    )

    Arguments

    fmt

    String, list or function. A format for break endpoints.

    single

    Glue string: label for singleton intervals. See lbl_glue() for details. If NULL, singleton intervals will be labelled the same way as other intervals.

    first

    Glue string: override label for the first category. Write e.g. first = "<{r}" to create a label like "<18". See lbl_glue() for details.

    last

    String: override label for the last category. Write e.g. last = ">{l}" to create a label like ">65". See lbl_glue() for details.

    raw

    [Deprecated]. Throws an error. Use the raw argument to chop() instead.

    Value

    A function that creates a vector of labels.

    Formatting endpoints

    If fmt is not NULL then it is used to format the endpoints.

    • If fmt is a string, then numeric endpoints will be formatted by sprintf(fmt, breaks); other endpoints, e.g. Date objects, will be formatted by format(breaks, fmt).

    • If fmt is a list, then it will be used as arguments to format.

    • If fmt is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the {scales} package, e.g. scales::label_comma().

    Examples

    chop(1:10, c(2, 5, 8), lbl_midpoints())
    #>  [1] 1.5 3.5 3.5 3.5 6.5 6.5 6.5 9   9   9  
    #> Levels: 1.5 3.5 6.5 9
    
    ================================================ FILE: docs/reference/lbl_midpoints.md ================================================ # Label chopped intervals by their midpoints This uses the midpoint of each interval for its label. ## Usage ``` r lbl_midpoints( fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) ``` ## Arguments - fmt: String, list or function. A format for break endpoints. - single: Glue string: label for singleton intervals. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. If `NULL`, singleton intervals will be labelled the same way as other intervals. - first: Glue string: override label for the first category. Write e.g. `first = "<{r}"` to create a label like `"<18"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - last: String: override label for the last category. Write e.g. `last = ">{l}"` to create a label like `">65"`. See [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md) for details. - raw: **\[deprecated\]**. Throws an error. Use the `raw` argument to [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) instead. ## Value A function that creates a vector of labels. ## Formatting endpoints If `fmt` is not `NULL` then it is used to format the endpoints. - If `fmt` is a string, then numeric endpoints will be formatted by `sprintf(fmt, breaks)`; other endpoints, e.g. [Date](https://rdrr.io/r/base/Dates.html) objects, will be formatted by `format(breaks, fmt)`. - If `fmt` is a list, then it will be used as arguments to [format](https://rdrr.io/r/base/format.html). - If `fmt` is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the `{scales}` package, e.g. [`scales::label_comma()`](https://scales.r-lib.org/reference/label_number.html). ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_seq()`](https://hughjonesd.github.io/santoku/reference/lbl_seq.md) ## Examples ``` r chop(1:10, c(2, 5, 8), lbl_midpoints()) #> [1] 1.5 3.5 3.5 3.5 6.5 6.5 6.5 9 9 9 #> Levels: 1.5 3.5 6.5 9 ``` ================================================ FILE: docs/reference/lbl_seq.html ================================================ Label chopped intervals in sequence — lbl_seq • santoku Skip to contents

    lbl_seq() labels intervals sequentially, using numbers or letters.

    Usage

    lbl_seq(start = "a")

    Arguments

    start

    String. A template for the sequence. See below.

    Value

    A function that creates a vector of labels.

    Details

    start shows the first element of the sequence. It must contain exactly one character out of the set "a", "A", "i", "I" or "1". For later elements:

    • "a" will be replaced by "a", "b", "c", ...

    • "A" will be replaced by "A", "B", "C", ...

    • "i" will be replaced by lower-case Roman numerals "i", "ii", "iii", ...

    • "I" will be replaced by upper-case Roman numerals "I", "II", "III", ...

    • "1" will be replaced by numbers "1", "2", "3", ...

    Other characters will be retained as-is.

    Examples

    chop(1:10, c(2, 5, 8), lbl_seq())
    #>  [1] a b b b c c c d d d
    #> Levels: a b c d
    
    chop(1:10, c(2, 5, 8), lbl_seq("i."))
    #>  [1] i.   ii.  ii.  ii.  iii. iii. iii. iv.  iv.  iv. 
    #> Levels: i. ii. iii. iv.
    
    chop(1:10, c(2, 5, 8), lbl_seq("(A)"))
    #>  [1] (A) (B) (B) (B) (C) (C) (C) (D) (D) (D)
    #> Levels: (A) (B) (C) (D)
    
    ================================================ FILE: docs/reference/lbl_seq.md ================================================ # Label chopped intervals in sequence `lbl_seq()` labels intervals sequentially, using numbers or letters. ## Usage ``` r lbl_seq(start = "a") ``` ## Arguments - start: String. A template for the sequence. See below. ## Value A function that creates a vector of labels. ## Details `start` shows the first element of the sequence. It must contain exactly *one* character out of the set "a", "A", "i", "I" or "1". For later elements: - "a" will be replaced by "a", "b", "c", ... - "A" will be replaced by "A", "B", "C", ... - "i" will be replaced by lower-case Roman numerals "i", "ii", "iii", ... - "I" will be replaced by upper-case Roman numerals "I", "II", "III", ... - "1" will be replaced by numbers "1", "2", "3", ... Other characters will be retained as-is. ## See also Other labelling functions: [`lbl_dash()`](https://hughjonesd.github.io/santoku/reference/lbl_dash.md), [`lbl_date()`](https://hughjonesd.github.io/santoku/reference/lbl_datetime.md), [`lbl_discrete()`](https://hughjonesd.github.io/santoku/reference/lbl_discrete.md), [`lbl_endpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_endpoints.md), [`lbl_glue()`](https://hughjonesd.github.io/santoku/reference/lbl_glue.md), [`lbl_intervals()`](https://hughjonesd.github.io/santoku/reference/lbl_intervals.md), [`lbl_manual()`](https://hughjonesd.github.io/santoku/reference/lbl_manual.md), [`lbl_midpoints()`](https://hughjonesd.github.io/santoku/reference/lbl_midpoints.md) ## Examples ``` r chop(1:10, c(2, 5, 8), lbl_seq()) #> [1] a b b b c c c d d d #> Levels: a b c d chop(1:10, c(2, 5, 8), lbl_seq("i.")) #> [1] i. ii. ii. ii. iii. iii. iii. iv. iv. iv. #> Levels: i. ii. iii. iv. chop(1:10, c(2, 5, 8), lbl_seq("(A)")) #> [1] (A) (B) (B) (B) (C) (C) (C) (D) (D) (D) #> Levels: (A) (B) (C) (D) ``` ================================================ FILE: docs/reference/non-standard-types.html ================================================ Tips for chopping non-standard types — non-standard-types • santoku Skip to contents

    Santoku can handle many non-standard types.

    Details

    • If objects can be compared using <, == etc. then they should be choppable.

    • Objects which can't be converted to numeric are handled within R code, which may be slower.

    • Character x and breaks are chopped with a warning.

    • If x and breaks are not the same type, they should be able to be cast to the same type, usually using vctrs::vec_cast_common().

    • Not all chopping operations make sense, for example, chop_mean_sd() on a character vector.

    • For indexed objects such as stats::ts() objects, indices will be dropped from the result.

    • If you get errors, try setting extend = FALSE (but also file a bug report).

    • To request support for a type, open an issue on Github.

    See also

    brk-width-for-Datetime

    ================================================ FILE: docs/reference/non-standard-types.md ================================================ # Tips for chopping non-standard types Santoku can handle many non-standard types. ## Details - If objects can be compared using `<`, `==` etc. then they should be choppable. - Objects which can't be converted to numeric are handled within R code, which may be slower. - Character `x` and `breaks` are chopped with a warning. - If `x` and `breaks` are not the same type, they should be able to be cast to the same type, usually using [`vctrs::vec_cast_common()`](https://vctrs.r-lib.org/reference/vec_cast.html). - Not all chopping operations make sense, for example, [`chop_mean_sd()`](https://hughjonesd.github.io/santoku/reference/chop_mean_sd.md) on a character vector. - For indexed objects such as [`stats::ts()`](https://rdrr.io/r/stats/ts.html) objects, indices will be dropped from the result. - If you get errors, try setting `extend = FALSE` (but also file a bug report). - To request support for a type, open an issue on Github. ## See also brk-width-for-Datetime ================================================ FILE: docs/reference/percent.html ================================================ Simple percentage formatter — percent • santoku Skip to contents

    percent() formats x as a percentage. For a wider range of formatters, consider the scales package.

    Usage

    percent(x)

    Arguments

    x

    Numeric values.

    Value

    x formatted as a percent.

    Examples

    percent(0.5)
    #> [1] "50%"
    
    ================================================ FILE: docs/reference/percent.md ================================================ # Simple percentage formatter `percent()` formats `x` as a percentage. For a wider range of formatters, consider the [`scales` package](https://cran.r-project.org/package=scales). ## Usage ``` r percent(x) ``` ## Arguments - x: Numeric values. ## Value `x` formatted as a percent. ## Examples ``` r percent(0.5) #> [1] "50%" ``` ================================================ FILE: docs/reference/print.breaks.html ================================================ ================================================ FILE: docs/reference/santoku-cast.html ================================================ Internal functions — santoku-cast • santoku Skip to contents

    Internal functions

    Usage

    # Default S3 method
    santoku_cast_common(x, y)
    
    # S3 method for class 'double'
    santoku_cast_common(x, y)
    
    # S3 method for class 'Date'
    santoku_cast_common(x, y)
    
    # S3 method for class 'POSIXct'
    santoku_cast_common(x, y)
    
    # S3 method for class 'ts'
    santoku_cast_common(x, y)
    
    # S3 method for class 'zoo'
    santoku_cast_common(x, y)
    
    # S3 method for class 'integer64'
    santoku_cast_common(x, y)
    
    # S3 method for class 'hexmode'
    santoku_cast_common(x, y)
    
    # S3 method for class 'octmode'
    santoku_cast_common(x, y)

    Arguments

    x, y

    Vectors to cast.

    Value

    A list.

    ================================================ FILE: docs/reference/santoku-cast.md ================================================ # Internal functions Internal functions ## Usage ``` r # Default S3 method santoku_cast_common(x, y) # S3 method for class 'double' santoku_cast_common(x, y) # S3 method for class 'Date' santoku_cast_common(x, y) # S3 method for class 'POSIXct' santoku_cast_common(x, y) # S3 method for class 'ts' santoku_cast_common(x, y) # S3 method for class 'zoo' santoku_cast_common(x, y) # S3 method for class 'integer64' santoku_cast_common(x, y) # S3 method for class 'hexmode' santoku_cast_common(x, y) # S3 method for class 'octmode' santoku_cast_common(x, y) ``` ## Arguments - x, y: Vectors to cast. ## Value A list. ================================================ FILE: docs/reference/santoku-package.html ================================================ A versatile cutting tool for R: package overview and options — santoku-package • santoku Skip to contents

    santoku is a tool for cutting data into intervals. It provides the function chop(), which is similar to base R's cut() or Hmisc::cut2(). chop(x, breaks) takes a vector x and returns a factor of the same length, coding which interval each element of x falls into.

    Details

    Here are some advantages of santoku:

    • By default, chop() always covers the whole range of the data, so you won't get unexpected NA values.

    • Unlike cut() or cut2(), chop() can handle single values as well as intervals. For example, chop(x, breaks = c(1, 2, 2, 3)) will create a separate factor level for values exactly equal to 2.

    • Flexible and easy labelling.

    • Convenience functions for creating quantile intervals, evenly-spaced intervals or equal-sized groups.

    • Convenience functions to quickly tabulate chopped data.

    • Can chop numbers, dates, date-times and other objects.

    These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance.

    To get started, read the vignette:

    vignette("santoku")

    For more details, start with the documentation for chop().

    Options

    Santoku has two options:

    • options("santoku.infinity") sets the symbol for infinity in breaks. The default is NULL, in which case the infinity symbol is used on platforms that support it, otherwise "Inf" is used.

    • options("santoku.warn_character") warns if you try to chop a character vector. Set to FALSE to turn off this warning.

    Author

    Maintainer: David Hugh-Jones davidhughjones@gmail.com

    Other contributors:

    ================================================ FILE: docs/reference/santoku-package.md ================================================ # A versatile cutting tool for R: package overview and options santoku is a tool for cutting data into intervals. It provides the function [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md), which is similar to base R's [`cut()`](https://rdrr.io/r/base/cut.html) or [`Hmisc::cut2()`](https://rdrr.io/pkg/Hmisc/man/cut2.html). `chop(x, breaks)` takes a vector `x` and returns a factor of the same length, coding which interval each element of `x` falls into. ## Details Here are some advantages of santoku: - By default, [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) always covers the whole range of the data, so you won't get unexpected `NA` values. - Unlike [`cut()`](https://rdrr.io/r/base/cut.html) or `cut2()`, [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md) can handle single values as well as intervals. For example, `chop(x, breaks = c(1, 2, 2, 3))` will create a separate factor level for values exactly equal to 2. - Flexible and easy labelling. - Convenience functions for creating quantile intervals, evenly-spaced intervals or equal-sized groups. - Convenience functions to quickly tabulate chopped data. - Can chop numbers, dates, date-times and other objects. These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. To get started, read the vignette: vignette("santoku") For more details, start with the documentation for [`chop()`](https://hughjonesd.github.io/santoku/reference/chop.md). ## Options Santoku has two options: - `options("santoku.infinity")` sets the symbol for infinity in breaks. The default is `NULL`, in which case the infinity symbol is used on platforms that support it, otherwise `"Inf"` is used. - `options("santoku.warn_character")` warns if you try to chop a character vector. Set to `FALSE` to turn off this warning. ## See also Useful links: - - - Report bugs at ## Author **Maintainer**: David Hugh-Jones Other contributors: - Daniel Possenriede \[contributor\] ================================================ FILE: docs/reference/santoku.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.Date.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.POSIXct.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.default.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.double.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.hexmode.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.integer64.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.octmode.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.ts.html ================================================ ================================================ FILE: docs/reference/santoku_cast_common.zoo.html ================================================ ================================================ FILE: docs/reference/sequence-labels.html ================================================ Label manually in sequence — sequence-labels • santoku

    lbl_manual() uses an arbitrary sequence to label intervals. If the sequence is too short, it will be pasted with itself and repeated.

    lbl_manual(sequence, fmt = "%s")

    Arguments

    fmt

    A sprintf()-style format.

    Value

    A vector of labels for chop, or a function that creates labels.

    See also

    Other labelling functions: lbl_dash, lbl_format, lbl_intervals, lbl_seq

    Examples

    chop(1:10, c(2, 5, 8), lbl_manual(c("w", "x", "y", "z")))
    #> Error in lbl_manual(c("w", "x", "y", "z")): could not find function "lbl_manual"
    # if labels need repeating: chop(1:10, 1:10, lbl_manual(c("x", "y", "z")))
    #> Error in lbl_manual(c("x", "y", "z")): could not find function "lbl_manual"

    Site built with pkgdown 1.3.0.

    ================================================ FILE: docs/reference/tab.html ================================================ ================================================ FILE: docs/reference/tab_deciles.html ================================================ ================================================ FILE: docs/reference/tab_dissect.html ================================================ ================================================ FILE: docs/reference/tab_equally.html ================================================ ================================================ FILE: docs/reference/tab_evenly.html ================================================ ================================================ FILE: docs/reference/tab_fn.html ================================================ ================================================ FILE: docs/reference/tab_mean_sd.html ================================================ ================================================ FILE: docs/reference/tab_n.html ================================================ ================================================ FILE: docs/reference/tab_pretty.html ================================================ ================================================ FILE: docs/reference/tab_proportions.html ================================================ ================================================ FILE: docs/reference/tab_quantiles.html ================================================ ================================================ FILE: docs/reference/tab_spikes.html ================================================ ================================================ FILE: docs/reference/tab_width.html ================================================ ================================================ FILE: docs/search.json ================================================ [{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":null,"dir":"","previous_headings":"","what":"AGENTS.md","title":"AGENTS.md","text":"file provides guidance agents working code repository.","code":""},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"project-overview","dir":"","previous_headings":"","what":"Project Overview","title":"AGENTS.md","text":"santoku R package provides chop(), versatile replacement base::cut() cutting data intervals. package handles numeric vectors, dates, times, comparable objects, support singleton intervals flexible labeling.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"testing","dir":"","previous_headings":"Common Commands","what":"Testing","title":"AGENTS.md","text":"","code":"# Run all tests devtools::test() # Run tests from command line R CMD check . # Run specific test file testthat::test_file(\"tests/testthat/test-chop.R\")"},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"development-workflow","dir":"","previous_headings":"Common Commands","what":"Development workflow","title":"AGENTS.md","text":"","code":"# Build package devtools::build() # Install package locally devtools::install() # Check package devtools::check() # Load package for interactive development devtools::load_all()"},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"documentation","dir":"","previous_headings":"Common Commands","what":"Documentation","title":"AGENTS.md","text":"","code":"# Update documentation devtools::document() # Build website pkgdown::build_site()"},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"core-components","dir":"","previous_headings":"Architecture","what":"Core Components","title":"AGENTS.md","text":"Main cutting function: chop() R/chop.R - primary interface calls functions Break creation: R/breaks*.R files contain functions create break points (brk_* functions) Labeling system: R/labels*.R files contain labeling functions (lbl_* functions) Convenience functions: R/chop-*.R files contain chop_* wrapper functions common use cases C++ backend: src/categorize.cpp provides fast interval categorization via Rcpp Tabulation: R/tab.R provides tab_* functions chop tabulate one step","code":""},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"key-design-patterns","dir":"","previous_headings":"Architecture","what":"Key Design Patterns","title":"AGENTS.md","text":"Function factories: Many functions return functions (e.g., brk_* functions return break-creation functions) Method dispatch: Uses S3 methods vctrs handling different data types (numbers, dates, etc.) Extensible labeling: Label functions can combined customized using lbl_* family Performance: Core categorization logic implemented C++ speed","code":""},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"file-organization","dir":"","previous_headings":"Architecture","what":"File Organization","title":"AGENTS.md","text":"R/chop.R - Main chop() function documentation R/breaks*.R - Break point creation (brk_default, brk_width, etc.) R/labels*.R - Label generation (lbl_intervals, lbl_dash, etc.) R/chop-*.R - Convenience functions (chop_quantiles, chop_width, etc.) R/tab.R - Tabulation functions R/utils.R - Utility functions like exactly() percent() src/categorize.cpp - Fast C++ categorization implementation tests/testthat/ - Comprehensive test suite","code":""},{"path":"https://hughjonesd.github.io/santoku/AGENTS.html","id":"development-notes","dir":"","previous_headings":"","what":"Development Notes","title":"AGENTS.md","text":"package uses Rcpp performance-critical categorization Tests extensive include systematic testing test-zzz-systematic.R package supports non-standard data types (dates, times, units) via vctrs package Documentation follows roxygen2 conventions extensive examples Uses lifecycle package function lifecycle management","code":""},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":null,"dir":"","previous_headings":"","what":"CLAUDE.md","title":"CLAUDE.md","text":"file provides guidance Claude Code (claude.ai/code) working code repository.","code":""},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"project-overview","dir":"","previous_headings":"","what":"Project Overview","title":"CLAUDE.md","text":"santoku R package provides chop(), versatile replacement base::cut() cutting data intervals. package handles numeric vectors, dates, times, comparable objects, support singleton intervals flexible labeling.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"testing","dir":"","previous_headings":"Common Commands","what":"Testing","title":"CLAUDE.md","text":"","code":"# Run all tests devtools::test() # Run tests from command line R CMD check . # Run specific test file testthat::test_file(\"tests/testthat/test-chop.R\")"},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"development-workflow","dir":"","previous_headings":"Common Commands","what":"Development workflow","title":"CLAUDE.md","text":"","code":"# Build package devtools::build() # Install package locally devtools::install() # Check package devtools::check() # Load package for interactive development devtools::load_all()"},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"documentation","dir":"","previous_headings":"Common Commands","what":"Documentation","title":"CLAUDE.md","text":"","code":"# Update documentation devtools::document() # Build website pkgdown::build_site()"},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"core-components","dir":"","previous_headings":"Architecture","what":"Core Components","title":"CLAUDE.md","text":"Main cutting function: chop() R/chop.R - primary interface calls functions Break creation: R/breaks*.R files contain functions create break points (brk_* functions) Labeling system: R/labels*.R files contain labeling functions (lbl_* functions) Convenience functions: R/chop-*.R files contain chop_* wrapper functions common use cases C++ backend: src/categorize.cpp provides fast interval categorization via Rcpp Tabulation: R/tab.R provides tab_* functions chop tabulate one step","code":""},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"key-design-patterns","dir":"","previous_headings":"Architecture","what":"Key Design Patterns","title":"CLAUDE.md","text":"Function factories: Many functions return functions (e.g., brk_* functions return break-creation functions) Method dispatch: Uses S3 methods vctrs handling different data types (numbers, dates, etc.) Extensible labeling: Label functions can combined customized using lbl_* family Performance: Core categorization logic implemented C++ speed","code":""},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"file-organization","dir":"","previous_headings":"Architecture","what":"File Organization","title":"CLAUDE.md","text":"R/chop.R - Main chop() function documentation R/breaks*.R - Break point creation (brk_default, brk_width, etc.) R/labels*.R - Label generation (lbl_intervals, lbl_dash, etc.) R/chop-*.R - Convenience functions (chop_quantiles, chop_width, etc.) R/tab.R - Tabulation functions R/utils.R - Utility functions like exactly() percent() src/categorize.cpp - Fast C++ categorization implementation tests/testthat/ - Comprehensive test suite","code":""},{"path":"https://hughjonesd.github.io/santoku/CLAUDE.html","id":"development-notes","dir":"","previous_headings":"","what":"Development Notes","title":"CLAUDE.md","text":"package uses Rcpp performance-critical categorization Tests extensive include systematic testing test-zzz-systematic.R package supports non-standard data types (dates, times, units) via vctrs package Documentation follows roxygen2 conventions extensive examples Uses lifecycle package function lifecycle management","code":""},{"path":"https://hughjonesd.github.io/santoku/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2019 David Hugh-Jones Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":null,"dir":"","previous_headings":"","what":"TODO","title":"TODO","text":"tests left close_end arguments tests brk_default brk_width() needs tests match guarantees documentation ditto brk_evenly() now uses implementation guarantee exactly intervals intervals systematic tests brk_* functions problem moment vec_cast() highly unreliable never know particular class accept Inf. infinity class fine, go existing breaks object underlying class? NB requires new way create labels, kinda sucks….","code":""},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":"thoughts-on-errors","dir":"","previous_headings":"","what":"Thoughts on errors","title":"TODO","text":"Exception: x wrong class type. e.g. brk_manual() extend set. cases, e.g. brk_evenly() don’t need make guarantee.","code":""},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":"questions","dir":"","previous_headings":"","what":"Questions","title":"TODO","text":"alternative x non-numeric. makes surprise rarer, rare surprises can worse… adds complexity since functions generic. another alternative: chop sets left = FALSE non-numeric x. Probably better. drop default ! isTRUE(extend) .e. FALSE extend = TRUE?","code":""},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":"questions-with-a-provisional-answer","dir":"","previous_headings":"","what":"Questions with a (provisional) answer","title":"TODO","text":", labels = NULL integer codes though. sense , unless someone asks. Oh, someone just ask; generally though. . don’t want people set position, distinguish initial arguments. Current answer: fuck ’em. (NB: just kidding. huge tidyverse fan.) provide kiru(). REPL, people can just use kiru() load santoku first. load santoku second, ’ll use tidyr::chop(), reading documentation, suspect rare. programming, people probably used fully qualified name anyway. think default “necessary” (extend = NULL); always extend Inf, -Inf break labels data-dependent Tension wanting something predictable new data, vs. something readable tab_*. E.g. surely return labels , b, c. means aren’t always extending. lets people e.g. chop(rnorm(100), -2:2, LETTERS) might hide errors overall ’m maybe label functions access x? informed breaks got extended? breaks object know extend labels? current solution: labels get extend think better: breaks objects include suggested labels user can override. way always info necessary. choosing break numbers (may actual values, e.g quantiles std errs 0) formatting numbers, dashes, set notation etc maybe brk_* functions always return break numbers; labels decide format ? warning? currently error throw error cases e.g. brk_quantiles, brk_width, data may work well e.g. NA. empty set breaks?","code":"tab_size(1:9, 3, lbl_seq())"},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":"possible-interfaces","dir":"","previous_headings":"","what":"Possible interfaces","title":"TODO","text":"hist_xxx functions histograms/barplots? (treat singletons?) grp_xxx group_by? Hmmm… New label interface replace lbl_sequence: lbl_style(\"1.\"), lbl_style(\"()\"), lbl_style(\"\") etc.? Still wonder, drop extend adds complexity just () extend() new breaks functions?","code":""},{"path":"https://hughjonesd.github.io/santoku/TODO.html","id":"other-ideas","dir":"","previous_headings":"","what":"Other ideas","title":"TODO","text":"Speedup categorize checking left intervals, add 1 past interval [: actually fewer checks end…] Speedup using pointers? hmm, magic…","code":""},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Introduction to santoku","text":"Santoku package cutting data intervals. provides chop(), replacement base R’s cut() function, well several convenience functions cut different kinds intervals. install santoku, run:","code":"install.packages(\"santoku\")"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"basic-usage","dir":"Articles","previous_headings":"","what":"Basic usage","title":"Introduction to santoku","text":"Use chop() like cut(), cut numeric data intervals set breaks. chop() returns factor. data beyond limits breaks, extended automatically: chop single number separate category, put number twice breaks: quickly produce table chopped data, use tab():","code":"library(santoku) x <- runif(10, 0, 10) (chopped <- chop(x, breaks = 0:10)) #> [1] [4, 5) [8, 9) [3, 4) [4, 5) [7, 8) [9, 10] [6, 7) [8, 9) [1, 2) #> [10] [4, 5) #> Levels: [1, 2) [3, 4) [4, 5) [6, 7) [7, 8) [8, 9) [9, 10] data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 5) #> 2 8.970 [8, 9) #> 3 3.392 [3, 4) #> 4 4.677 [4, 5) #> 5 7.057 [7, 8) #> 6 9.708 [9, 10] #> 7 6.714 [6, 7) #> 8 8.377 [8, 9) #> 9 1.086 [1, 2) #> 10 4.495 [4, 5) chopped <- chop(x, breaks = 3:7) data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 5) #> 2 8.970 [7, 9.708] #> 3 3.392 [3, 4) #> 4 4.677 [4, 5) #> 5 7.057 [7, 9.708] #> 6 9.708 [7, 9.708] #> 7 6.714 [6, 7) #> 8 8.377 [7, 9.708] #> 9 1.086 [1.086, 3) #> 10 4.495 [4, 5) x_fives <- x x_fives[1:5] <- 5 chopped <- chop(x_fives, c(2, 5, 5, 8)) data.frame(x_fives, chopped) #> x_fives chopped #> 1 5.000 {5} #> 2 5.000 {5} #> 3 5.000 {5} #> 4 5.000 {5} #> 5 5.000 {5} #> 6 9.708 [8, 9.708] #> 7 6.714 (5, 8) #> 8 8.377 [8, 9.708] #> 9 1.086 [1.086, 2) #> 10 4.495 [2, 5) tab(1:10, c(2, 5, 8)) #> [1, 2) [2, 5) [5, 8) [8, 10] #> 1 3 3 3"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"chopping-by-width-and-number-of-elements","dir":"Articles","previous_headings":"","what":"Chopping by width and number of elements","title":"Introduction to santoku","text":"chop fixed-width intervals, starting minimum value, use chop_width(): chop fixed number intervals, width, use chop_evenly(): chop groups fixed number elements, use chop_n(): chop fixed number groups, number elements, use chop_equally(): chop data quantiles, use chop_quantiles(): chop data proportions data range, use chop_proportions(): can think six functions logically arranged table. Different ways chop size","code":"chopped <- chop_width(x, 2) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.086, 5.086) #> 2 8.970 [7.086, 9.086) #> 3 3.392 [3.086, 5.086) #> 4 4.677 [3.086, 5.086) #> 5 7.057 [5.086, 7.086) #> 6 9.708 [9.086, 11.09] #> 7 6.714 [5.086, 7.086) #> 8 8.377 [7.086, 9.086) #> 9 1.086 [1.086, 3.086) #> 10 4.495 [3.086, 5.086) chopped <- chop_evenly(x, intervals = 3) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.96, 6.834) #> 2 8.970 [6.834, 9.708] #> 3 3.392 [1.086, 3.96) #> 4 4.677 [3.96, 6.834) #> 5 7.057 [6.834, 9.708] #> 6 9.708 [6.834, 9.708] #> 7 6.714 [3.96, 6.834) #> 8 8.377 [6.834, 9.708] #> 9 1.086 [1.086, 3.96) #> 10 4.495 [3.96, 6.834) chopped <- chop_n(x, 4) table(chopped) #> chopped #> [1.086, 4.978) [4.978, 8.97) [8.97, 9.708] #> 4 4 2 chopped <- chop_equally(x, groups = 5) table(chopped) #> chopped #> [1.086, 4.275) [4.275, 4.858) [4.858, 6.851) [6.851, 8.495) [8.495, 9.708] #> 2 2 2 2 2 chopped <- chop_quantiles(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) #> x chopped #> 1 4.978 [25%, 50%) #> 2 8.970 [75%, 100%] #> 3 3.392 [0%, 25%) #> 4 4.677 [25%, 50%) #> 5 7.057 [50%, 75%) #> 6 9.708 [75%, 100%] #> 7 6.714 [50%, 75%) #> 8 8.377 [75%, 100%] #> 9 1.086 [0%, 25%) #> 10 4.495 [0%, 25%) chopped <- chop_proportions(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) #> x chopped #> 1 4.978 [3.242, 5.397) #> 2 8.970 [7.552, 9.708] #> 3 3.392 [3.242, 5.397) #> 4 4.677 [3.242, 5.397) #> 5 7.057 [5.397, 7.552) #> 6 9.708 [7.552, 9.708] #> 7 6.714 [5.397, 7.552) #> 8 8.377 [7.552, 9.708] #> 9 1.086 [1.086, 3.242) #> 10 4.495 [3.242, 5.397)"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"even-more-ways-to-chop","dir":"Articles","previous_headings":"","what":"Even more ways to chop","title":"Introduction to santoku","text":"chop data standard deviations around mean, use chop_mean_sd(): chop data attractive intervals, use chop_pretty(). selects intervals multiple 2, 5 10. ’s useful producing bar plots.","code":"chopped <- chop_mean_sd(x) data.frame(x, chopped) #> x chopped #> 1 4.978 [-1 sd, 0 sd) #> 2 8.970 [1 sd, 2 sd) #> 3 3.392 [-1 sd, 0 sd) #> 4 4.677 [-1 sd, 0 sd) #> 5 7.057 [0 sd, 1 sd) #> 6 9.708 [1 sd, 2 sd) #> 7 6.714 [0 sd, 1 sd) #> 8 8.377 [0 sd, 1 sd) #> 9 1.086 [-2 sd, -1 sd) #> 10 4.495 [-1 sd, 0 sd) chopped <- chop_pretty(x) data.frame(x, chopped) #> x chopped #> 1 4.978 [4, 6) #> 2 8.970 [8, 10] #> 3 3.392 [2, 4) #> 4 4.677 [4, 6) #> 5 7.057 [6, 8) #> 6 9.708 [8, 10] #> 7 6.714 [6, 8) #> 8 8.377 [8, 10] #> 9 1.086 [0, 2) #> 10 4.495 [4, 6)"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"isolating-common-values","dir":"Articles","previous_headings":"","what":"Isolating common values","title":"Introduction to santoku","text":"exploratory work, ’s sometimes useful find common values treat differently. can use dissect() : prop = 0.2 put unique value x separate category makes least 20% data. Note unlike chop_* functions, dissect() doesn’t always categorize x ordered, connected intervals. remind , named differently. want create separate intervals left right common elements, use chop_spikes(): Compare table . two intervals either side common value, instead one interval surrounding .","code":"x_spike <- rnorm(100) x_spike[1:50] <- x_spike[1] chopped <- dissect(x_spike, -3:3, prop = 0.1) table(chopped) #> chopped #> [-3, -2) [-2, -1) [-1, 0) [0, 1) {0.6996} [1, 2) #> 2 5 15 18 50 10 chopped <- chop_spikes(x_spike, -3:3, prop = 0.1) table(chopped) #> chopped #> [-3, -2) [-2, -1) [-1, 0) [0, 0.6996) {0.6996} (0.6996, 1) #> 2 5 15 13 50 5 #> [1, 2) #> 10"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"quick-tables","dir":"Articles","previous_headings":"","what":"Quick tables","title":"Introduction to santoku","text":"tab_n(), tab_width(), friends act similarly tab(), calling related chop_* function table() result.","code":"tab_n(x, 4) #> [1.086, 4.978) [4.978, 8.97) [8.97, 9.708] #> 4 4 2 tab_width(x, 2) #> [1.086, 3.086) [3.086, 5.086) [5.086, 7.086) [7.086, 9.086) [9.086, 11.09] #> 1 4 2 2 1 tab_evenly(x, 5) #> [1.086, 2.81) [2.81, 4.535) [4.535, 6.259) [6.259, 7.983) [7.983, 9.708] #> 1 2 2 2 3 tab_mean_sd(x) #> [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) #> 1 4 3 2"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"specifying-labels","dir":"Articles","previous_headings":"","what":"Specifying labels","title":"Introduction to santoku","text":"default, santoku labels intervals using mathematical notation: [0, 1] means numbers 0 1 inclusive. (0, 1) means numbers strictly 0 1, including endpoints. [0, 1) means numbers 0 1, including 0 1. (0, 1] means numbers 0 1, including 1 0. {0} means just number 0. override labels, provide names breaks argument: , can specify factor labels labels argument: need many labels intervals - one fewer length(breaks) data doesn’t extend beyond breaks, one length(breaks) . label intervals dash, use lbl_dash(): label integer data, use lbl_discrete(). uses informative right endpoints: can customize first last labels: label intervals order use lbl_seq(): can use numerals even roman numerals: labelling functions include: lbl_endpoints() - use left endpoints labels lbl_midpoints() - use interval midpoints labels lbl_glue() - specify labels flexibly glue package","code":"chopped <- chop(x, c(Lowest = 1, Low = 2, Higher = 5, Highest = 8)) data.frame(x, chopped) #> x chopped #> 1 4.978 Low #> 2 8.970 Highest #> 3 3.392 Low #> 4 4.677 Low #> 5 7.057 Higher #> 6 9.708 Highest #> 7 6.714 Higher #> 8 8.377 Highest #> 9 1.086 Lowest #> 10 4.495 Low chopped <- chop(x, c(2, 5, 8), labels = c(\"Lowest\", \"Low\", \"Higher\", \"Highest\")) data.frame(x, chopped) #> x chopped #> 1 4.978 Low #> 2 8.970 Highest #> 3 3.392 Low #> 4 4.677 Low #> 5 7.057 Higher #> 6 9.708 Highest #> 7 6.714 Higher #> 8 8.377 Highest #> 9 1.086 Lowest #> 10 4.495 Low chopped <- chop(x, c(2, 5, 8), labels = lbl_dash()) data.frame(x, chopped) #> x chopped #> 1 4.978 2—5 #> 2 8.970 8—9.708 #> 3 3.392 2—5 #> 4 4.677 2—5 #> 5 7.057 5—8 #> 6 9.708 8—9.708 #> 7 6.714 5—8 #> 8 8.377 8—9.708 #> 9 1.086 1.086—2 #> 10 4.495 2—5 chopped <- chop(1:10, c(2, 5, 8), labels = lbl_discrete()) chopped2 <- chop(1:10, c(2, 5, 8), labels = lbl_dash()) data.frame(x = 1:10, lbl_discrete = chopped, lbl_dash = chopped2) #> x lbl_discrete lbl_dash #> 1 1 1 1—2 #> 2 2 2—4 2—5 #> 3 3 2—4 2—5 #> 4 4 2—4 2—5 #> 5 5 5—7 5—8 #> 6 6 5—7 5—8 #> 7 7 5—7 5—8 #> 8 8 8—10 8—10 #> 9 9 8—10 8—10 #> 10 10 8—10 8—10 chopped <- chop(x, c(2, 5, 8), labels = lbl_dash(first = \"< 2\", last = \"8+\")) data.frame(x, chopped) #> x chopped #> 1 4.978 2—5 #> 2 8.970 8+ #> 3 3.392 2—5 #> 4 4.677 2—5 #> 5 7.057 5—8 #> 6 9.708 8+ #> 7 6.714 5—8 #> 8 8.377 8+ #> 9 1.086 < 2 #> 10 4.495 2—5 chopped <- chop(x, c(2, 5, 8), labels = lbl_seq()) data.frame(x, chopped) #> x chopped #> 1 4.978 b #> 2 8.970 d #> 3 3.392 b #> 4 4.677 b #> 5 7.057 c #> 6 9.708 d #> 7 6.714 c #> 8 8.377 d #> 9 1.086 a #> 10 4.495 b chop(x, c(2, 5, 8), labels = lbl_seq(\"(1)\")) #> [1] (2) (4) (2) (2) (3) (4) (3) (4) (1) (2) #> Levels: (1) (2) (3) (4) chop(x, c(2, 5, 8), labels = lbl_seq(\"i.\")) #> [1] ii. iv. ii. ii. iii. iv. iii. iv. i. ii. #> Levels: i. ii. iii. iv."},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"specifying-breaks","dir":"Articles","previous_headings":"","what":"Specifying breaks","title":"Introduction to santoku","text":"default, chop() extends breaks necessary. don’t want , set extend = FALSE: Data outside range breaks become NA. default, intervals closed left, .e. include left endpoints. want right-closed intervals, set left = FALSE: default, last interval closed ends. want keep last interval open end, set close_end = FALSE:","code":"chopped <- chop(x, c(3, 5, 7), extend = FALSE) data.frame(x, chopped) #> x chopped #> 1 4.978 [3, 5) #> 2 8.970 #> 3 3.392 [3, 5) #> 4 4.677 [3, 5) #> 5 7.057 #> 6 9.708 #> 7 6.714 [5, 7] #> 8 8.377 #> 9 1.086 #> 10 4.495 [3, 5) y <- 1:5 data.frame( y = y, left_closed = chop(y, 1:5), right_closed = chop(y, 1:5, left = FALSE) ) #> y left_closed right_closed #> 1 1 [1, 2) [1, 2] #> 2 2 [2, 3) [1, 2] #> 3 3 [3, 4) (2, 3] #> 4 4 [4, 5] (3, 4] #> 5 5 [4, 5] (4, 5] data.frame( y = y, end_closed = chop(y, 1:5), end_open = chop(y, 1:5, close_end = FALSE) ) #> y end_closed end_open #> 1 1 [1, 2) [1, 2) #> 2 2 [2, 3) [2, 3) #> 3 3 [3, 4) [3, 4) #> 4 4 [4, 5] [4, 5) #> 5 5 [4, 5] {5}"},{"path":"https://hughjonesd.github.io/santoku/articles/santoku.html","id":"chopping-dates-times-and-other-vectors","dir":"Articles","previous_headings":"","what":"Chopping dates, times and other vectors","title":"Introduction to santoku","text":"can chop many kinds vectors santoku, including Date objects… … POSIXct (date-time) objects: Note santoku correctly handles different timezones. can use chop_width() objects lubridate package, chop irregular periods months: lbl_date() produces nicely formatted dates: can also chop vectors units, using units package: able chop anything comparison operator. can even chop character data using lexical ordering. default santoku emits warning case, avoid accidentally misinterpreting results: find type data can’t chop, please file issue.","code":"y2k <- as.Date(\"2000-01-01\") + 0:10 * 7 data.frame( y2k = y2k, chopped = chop(y2k, as.Date(c(\"2000-02-01\", \"2000-03-01\"))) ) #> y2k chopped #> 1 2000-01-01 [2000-01-01, 2000-02-01) #> 2 2000-01-08 [2000-01-01, 2000-02-01) #> 3 2000-01-15 [2000-01-01, 2000-02-01) #> 4 2000-01-22 [2000-01-01, 2000-02-01) #> 5 2000-01-29 [2000-01-01, 2000-02-01) #> 6 2000-02-05 [2000-02-01, 2000-03-01) #> 7 2000-02-12 [2000-02-01, 2000-03-01) #> 8 2000-02-19 [2000-02-01, 2000-03-01) #> 9 2000-02-26 [2000-02-01, 2000-03-01) #> 10 2000-03-04 [2000-03-01, 2000-03-11] #> 11 2000-03-11 [2000-03-01, 2000-03-11] # hours of the 2020 Crew Dragon flight: crew_dragon <- seq(as.POSIXct(\"2020-05-30 18:00\", tz = \"GMT\"), length.out = 24, by = \"hours\") liftoff <- as.POSIXct(\"2020-05-30 15:22\", tz = \"America/New_York\") dock <- as.POSIXct(\"2020-05-31 10:16\", tz = \"America/New_York\") data.frame( crew_dragon = crew_dragon, chopped = chop(crew_dragon, c(liftoff, dock), labels = c(\"pre-flight\", \"flight\", \"docked\")) ) #> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent #> Warning in .check_tzones(e1, e2): 'tzone' attributes are inconsistent #> crew_dragon chopped #> 1 2020-05-30 18:00:00 pre-flight #> 2 2020-05-30 19:00:00 pre-flight #> 3 2020-05-30 20:00:00 flight #> 4 2020-05-30 21:00:00 flight #> 5 2020-05-30 22:00:00 flight #> 6 2020-05-30 23:00:00 flight #> 7 2020-05-31 00:00:00 flight #> 8 2020-05-31 01:00:00 flight #> 9 2020-05-31 02:00:00 flight #> 10 2020-05-31 03:00:00 flight #> 11 2020-05-31 04:00:00 flight #> 12 2020-05-31 05:00:00 flight #> 13 2020-05-31 06:00:00 flight #> 14 2020-05-31 07:00:00 flight #> 15 2020-05-31 08:00:00 flight #> 16 2020-05-31 09:00:00 flight #> 17 2020-05-31 10:00:00 flight #> 18 2020-05-31 11:00:00 flight #> 19 2020-05-31 12:00:00 flight #> 20 2020-05-31 13:00:00 flight #> 21 2020-05-31 14:00:00 flight #> 22 2020-05-31 15:00:00 docked #> 23 2020-05-31 16:00:00 docked #> 24 2020-05-31 17:00:00 docked library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union data.frame( y2k = y2k, chopped = chop_width(y2k, months(1)) ) #> y2k chopped #> 1 2000-01-01 [2000-01-01, 2000-02-01) #> 2 2000-01-08 [2000-01-01, 2000-02-01) #> 3 2000-01-15 [2000-01-01, 2000-02-01) #> 4 2000-01-22 [2000-01-01, 2000-02-01) #> 5 2000-01-29 [2000-01-01, 2000-02-01) #> 6 2000-02-05 [2000-02-01, 2000-03-01) #> 7 2000-02-12 [2000-02-01, 2000-03-01) #> 8 2000-02-19 [2000-02-01, 2000-03-01) #> 9 2000-02-26 [2000-02-01, 2000-03-01) #> 10 2000-03-04 [2000-03-01, 2000-04-01) #> 11 2000-03-11 [2000-03-01, 2000-04-01) data.frame( y2k = y2k, chopped = chop_width(y2k, days(28), labels = lbl_date()) ) #> y2k chopped #> 1 2000-01-01 1-28 Jan 2000 #> 2 2000-01-08 1-28 Jan 2000 #> 3 2000-01-15 1-28 Jan 2000 #> 4 2000-01-22 1-28 Jan 2000 #> 5 2000-01-29 29 Jan - 25 Feb 2000 #> 6 2000-02-05 29 Jan - 25 Feb 2000 #> 7 2000-02-12 29 Jan - 25 Feb 2000 #> 8 2000-02-19 29 Jan - 25 Feb 2000 #> 9 2000-02-26 26 Feb - 24 Mar 2000 #> 10 2000-03-04 26 Feb - 24 Mar 2000 #> 11 2000-03-11 26 Feb - 24 Mar 2000 library(units) #> udunits database from /Users/davidhugh-jones/Library/R/arm64/4.6/library/units/share/udunits/udunits2.xml x <- set_units(1:10 * 10, cm) br <- set_units(1:3, ft) data.frame( x = x, chopped = chop(x, br) ) #> x chopped #> 1 10 [cm] [ 10.00 [cm], 30.48 [cm]) #> 2 20 [cm] [ 10.00 [cm], 30.48 [cm]) #> 3 30 [cm] [ 10.00 [cm], 30.48 [cm]) #> 4 40 [cm] [ 30.48 [cm], 60.96 [cm]) #> 5 50 [cm] [ 30.48 [cm], 60.96 [cm]) #> 6 60 [cm] [ 30.48 [cm], 60.96 [cm]) #> 7 70 [cm] [ 60.96 [cm], 91.44 [cm]) #> 8 80 [cm] [ 60.96 [cm], 91.44 [cm]) #> 9 90 [cm] [ 60.96 [cm], 91.44 [cm]) #> 10 100 [cm] [ 91.44 [cm], 100.00 [cm]] chop(letters[1:10], c(\"d\", \"f\")) #> Warning in categorize_non_numeric(x, breaks, left): `x` or `breaks` is of type #> character, using lexical sorting. To turn off this warning, run: #> options(santoku.warn_character = FALSE) #> [1] [a, d) [a, d) [a, d) [d, f) [d, f) [f, j] [f, j] [f, j] [f, j] [f, j] #> Levels: [a, d) [d, f) [f, j]"},{"path":"https://hughjonesd.github.io/santoku/articles/website-articles/performance.html","id":"speed","dir":"Articles > Website-articles","previous_headings":"","what":"Speed","title":"Performance","text":"core santoku written C++. reasonably fast:","code":"packageVersion(\"santoku\") #> [1] '1.2.0' set.seed(27101975) mb <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e5), -2:2), base::cut(rnorm(1e5), -2:2), Hmisc::cut2(rnorm(1e5), -2:2) ) mb #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 santoku::chop(rnorm(1e+05), -2:2) 6.34ms 6.54ms 150. 10.21MB 64.5 #> 2 base::cut(rnorm(1e+05), -2:2) 2.73ms 2.78ms 357. 2.35MB 31.5 #> 3 Hmisc::cut2(rnorm(1e+05), -2:2) 9.74ms 9.88ms 101. 19.5MB 223. autoplot(mb, type = \"violin\")"},{"path":"https://hughjonesd.github.io/santoku/articles/website-articles/performance.html","id":"many-breaks","dir":"Articles > Website-articles","previous_headings":"","what":"Many breaks","title":"Performance","text":"","code":"many_breaks <- seq(-2, 2, 0.001) mb_breaks <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e4), many_breaks), base::cut(rnorm(1e4), many_breaks), Hmisc::cut2(rnorm(1e4), many_breaks) ) mb_breaks #> # A tibble: 3 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 santoku::chop(rnorm(10000), many… 20.95ms 21.28ms 46.2 5.14MB 8.81 #> 2 base::cut(rnorm(10000), many_bre… 2.35ms 2.43ms 409. 1.39MB 17.7 #> 3 Hmisc::cut2(rnorm(10000), many_b… 7.03ms 7.2ms 138. 5.7MB 32.5 autoplot(mb_breaks, type = \"violin\")"},{"path":"https://hughjonesd.github.io/santoku/articles/website-articles/performance.html","id":"various-chops","dir":"Articles > Website-articles","previous_headings":"","what":"Various chops","title":"Performance","text":"","code":"x <- c(rnorm(9e4), sample(-2:2, 1e4, replace = TRUE)) mb_various <- bench::mark(min_iterations = 100, check = FALSE, chop(x, -2:2), chop_equally(x, groups = 20), chop_n(x, n = 2e4), chop_quantiles(x, c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_evenly(x, intervals = 20), chop_width(x, width = 0.25), chop_proportions(x, proportions = c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_mean_sd(x, sds = 1:4), chop_fn(x, scales::breaks_extended(10)), chop_pretty(x, n = 10), chop_spikes(x, -2:2, prop = 0.01), dissect(x, -2:2, prop = 0.01) ) mb_various #> # A tibble: 12 × 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> #> 1 chop(x, -2:2) 4.23ms 4.33ms 229. 8.63MB 118. #> 2 chop_equally(x, groups = 20) 11.04ms 11.2ms 88.5 12.18MB 72.4 #> 3 chop_n(x, n = 20000) 7.65ms 8.11ms 124. 23.5MB 507. #> 4 chop_quantiles(x, c(0.05, 0.25,… 6.63ms 6.75ms 147. 12.08MB 125. #> 5 chop_evenly(x, intervals = 20) 5.43ms 5.57ms 178. 12.48MB 152. #> 6 chop_width(x, width = 0.25) 5.96ms 6.08ms 163. 12.53MB 139. #> 7 chop_proportions(x, proportions… 4.75ms 5.37ms 190. 12.48MB 162. #> 8 chop_mean_sd(x, sds = 1:4) 4.9ms 5ms 198. 11.36MB 150. #> 9 chop_fn(x, scales::breaks_exten… 5.33ms 5.5ms 177. 11.47MB 123. #> 10 chop_pretty(x, n = 10) 4.87ms 5.02ms 197. 10.58MB 142. #> 11 chop_spikes(x, -2:2, prop = 0.0… 8.14ms 8.41ms 119. 14.62MB 137. #> 12 dissect(x, -2:2, prop = 0.01) 11.42ms 11.61ms 85.6 22.27MB 257. autoplot(mb_various, type = \"violin\")"},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"you-can-use-break-names-for-labels","dir":"Articles","previous_headings":"","what":"You can use break names for labels","title":"What's new in santoku 0.9.0","text":"command line, sometimes ’d like quickly add labels breaks. Now, can simply adding names breaks vector: Break names override labels argument, can still use unnamed breaks: Names can also used labels chop_quantiles() chop_proportions(): feature experimental now.","code":"library(santoku) chop(1:5, c(1,3,5)) #> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5] #> Levels: [1, 3) [3, 5] chop(1:5, c(Low = 1, High = 3, 5)) #> [1] Low Low High High High #> Levels: Low High ages <- sample(12:80, 20) tab(ages, c(\"Under 16\" = 0, 16, 25, 35, 45, 55, \"65 and over\" = 65), labels = lbl_discrete() ) #> Under 16 16—24 25—34 35—44 45—54 55—64 #> 1 1 2 3 3 4 #> 65 and over #> 6 x <- rnorm(10) chopped <- chop_quantiles(x, c(\"Lower tail\" = 0, 0.025, \"Upper tail\" = 0.975) ) data.frame(x, chopped) #> x chopped #> 1 -1.3888607 [2.5%, 97.5%) #> 2 -0.2787888 [2.5%, 97.5%) #> 3 -0.1333213 [2.5%, 97.5%) #> 4 0.6359504 [2.5%, 97.5%) #> 5 -0.2842529 [2.5%, 97.5%) #> 6 -2.6564554 Lower tail #> 7 -2.4404669 [2.5%, 97.5%) #> 8 1.3201133 Upper tail #> 9 -0.3066386 [2.5%, 97.5%) #> 10 -1.7813084 [2.5%, 97.5%)"},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"close_end-works-differently","dir":"Articles","previous_headings":"","what":"close_end works differently","title":"What's new in santoku 0.9.0","text":"close_end parameter used right-close last break. used applied breaks extended cover items beyond explicitly given breaks. think confusing users. now, close_end applied breaks extended - .e. last break. 0.8.0: Notice central break [2, 3] right-closed. (extended break [3, 4] right-closed , extended breaks always closed “outer” end.) 0.9.0: Now, close_end applied final, extended break [3, 4], explicit break [2, 3).","code":"chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3] [2, 3] (3, 4] #> Levels: [1, 2) [2, 3] (3, 4] chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3) [3, 4] [3, 4] #> Levels: [1, 2) [2, 3) [3, 4]"},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"close_end-is-true-by-default","dir":"Articles","previous_headings":"","what":"close_end is TRUE by default","title":"What's new in santoku 0.9.0","text":"think exploratory work, users typically want include data lowest highest break, inclusive. , close_end now TRUE default. 0.8.0: 0.9.0:","code":"chop(1:3, 2:3) #> [1] [1, 2) [2, 3) {3} #> Levels: [1, 2) [2, 3) {3} chop(1:3, 2:3) #> [1] [1, 2) [2, 3] [2, 3] #> Levels: [1, 2) [2, 3]"},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"new-raw-parameter-for-chop","dir":"Articles","previous_headings":"","what":"New raw parameter for chop()","title":"What's new in santoku 0.9.0","text":"lbl_* functions raw parameter use raw interval endpoints labels, rather e.g. percentiles standard deviations. ’ve moved main chop() function. makes easier use: raw parameter lbl_* functions deprecated.","code":"chop_mean_sd(x) #> [1] [-1 sd, 0 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [0 sd, 1 sd) #> [6] [-2 sd, -1 sd) [-2 sd, -1 sd) [1 sd, 2 sd) [0 sd, 1 sd) [-1 sd, 0 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) chop_mean_sd(x, raw = TRUE) #> [1] [-2.03, -0.7314) [-0.7314, 0.5674) [-0.7314, 0.5674) [0.5674, 1.866) #> [5] [-0.7314, 0.5674) [-3.329, -2.03) [-3.329, -2.03) [0.5674, 1.866) #> [9] [-0.7314, 0.5674) [-2.03, -0.7314) #> 4 Levels: [-3.329, -2.03) [-2.03, -0.7314) ... [0.5674, 1.866)"},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"other-changes","dir":"Articles","previous_headings":"","what":"Other changes","title":"What's new in santoku 0.9.0","text":"NEWS file lists changes, including new chop_fn() function creates breaks using arbitrary function.","code":""},{"path":"https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html","id":"feedback","dir":"Articles","previous_headings":"","what":"Feedback","title":"What's new in santoku 0.9.0","text":"expect last release 1.0, ’ll stabilize interface move santoku “experimental” “stable”. , problems suggestions regarding changes, please file issue.","code":""},{"path":"https://hughjonesd.github.io/santoku/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"David Hugh-Jones. Author, maintainer. Daniel Possenriede. Contributor.","code":""},{"path":"https://hughjonesd.github.io/santoku/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Hugh-Jones D (2026). santoku: Versatile Cutting Tool. R package version 1.2.0, https://github.com/hughjonesd/santoku.","code":"@Manual{, title = {santoku: A Versatile Cutting Tool}, author = {David Hugh-Jones}, year = {2026}, note = {R package version 1.2.0}, url = {https://github.com/hughjonesd/santoku}, }"},{"path":"https://hughjonesd.github.io/santoku/index.html","id":"santoku-","dir":"","previous_headings":"","what":"A Versatile Cutting Tool","title":"A Versatile Cutting Tool","text":"santoku versatile cutting tool R. provides chop(), replacement base::cut().","code":""},{"path":"https://hughjonesd.github.io/santoku/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"A Versatile Cutting Tool","text":"Install r-universe: CRAN: get development version github:","code":"install.packages(\"santoku\", repos = c(\"https://hughjonesd.r-universe.dev\", \"https://cloud.r-project.org\")) install.packages(\"santoku\") # install.packages(\"remotes\") remotes::install_github(\"hughjonesd/santoku\")"},{"path":"https://hughjonesd.github.io/santoku/index.html","id":"advantages","dir":"","previous_headings":"","what":"Advantages","title":"A Versatile Cutting Tool","text":"advantages santoku: default, chop() always covers whole range data, won’t get unexpected NA values. chop() can handle single values well intervals. example, chop(x, breaks = c(1, 2, 2, 3)) create separate factor level values exactly equal 2. chop() can handle many kinds data, including numbers, dates times, units. chop_* functions create intervals many ways, using quantiles data, standard deviations, fixed-width intervals, equal-sized groups, pretty intervals use graphs. ’s easy label intervals: use names breaks vector, use lbl_* function create interval notation like [1, 2), dash notation like 1-2, arbitrary styles using glue::glue(). tab_* functions quickly chop data, tabulate . advantages make santoku especially useful exploratory analysis, may know range data advance.","code":""},{"path":"https://hughjonesd.github.io/santoku/index.html","id":"examples","dir":"","previous_headings":"","what":"Examples","title":"A Versatile Cutting Tool","text":"chop returns factor: Include number twice match exactly: Use names breaks labels: use lbl_* functions: Chop fixed-width intervals: fixed-size groups: Chop dates calendar month, tabulate: information, see vignette.","code":"library(santoku) chop(1:5, c(2, 4)) #> [1] [1, 2) [2, 4) [2, 4) [4, 5] [4, 5] #> Levels: [1, 2) [2, 4) [4, 5] chop(1:5, c(2, 2, 4)) #> [1] [1, 2) {2} (2, 4) [4, 5] [4, 5] #> Levels: [1, 2) {2} (2, 4) [4, 5] chop(1:5, c(Low = 1, Mid = 2, High = 4)) #> [1] Low Mid Mid High High #> Levels: Low Mid High chop(1:5, c(2, 4), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—5 4—5 #> Levels: 1—2 2—4 4—5 chop_width(runif(10), 0.1) #> [1] [0.368, 0.468) [0.268, 0.368) [0.768, 0.868] [0.568, 0.668) #> [5] [0.668, 0.768) [0.768, 0.868] [0.06801, 0.168) [0.668, 0.768) #> [9] [0.06801, 0.168) [0.468, 0.568) #> 7 Levels: [0.06801, 0.168) [0.268, 0.368) [0.368, 0.468) ... [0.768, 0.868] chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] library(lubridate) #> #> Attaching package: 'lubridate' #> The following objects are masked from 'package:base': #> #> date, intersect, setdiff, union dates <- as.Date(\"2021-12-31\") + 1:90 tab_width(dates, months(1), labels = lbl_discrete(fmt = \"%d %b\")) #> 01 Jan—31 Jan 01 Feb—28 Feb 01 Mar—31 Mar #> 31 28 31"},{"path":"https://hughjonesd.github.io/santoku/reference/breaks-class.html","id":null,"dir":"Reference","previous_headings":"","what":"Class representing a set of intervals — breaks-class","title":"Class representing a set of intervals — breaks-class","text":"Class representing set intervals","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/breaks-class.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Class representing a set of intervals — breaks-class","text":"","code":"# S3 method for class 'breaks' format(x, ...) # S3 method for class 'breaks' print(x, ...) is.breaks(x, ...)"},{"path":"https://hughjonesd.github.io/santoku/reference/breaks-class.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Class representing a set of intervals — breaks-class","text":"x breaks object ... Unused","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/brk-left-right.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Left- or right-closed breaks — brk-left-right","text":"","code":"brk_left(breaks) brk_right(breaks)"},{"path":"https://hughjonesd.github.io/santoku/reference/brk-left-right.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Left- or right-closed breaks — brk-left-right","text":"breaks numeric vector.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk-left-right.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Left- or right-closed breaks — brk-left-right","text":"(function returns ) object class breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk-left-right.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Left- or right-closed breaks — brk-left-right","text":"functions \"questioning\" stage clash left argument chop() friends. functions override left argument chop().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk-left-right.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Left- or right-closed breaks — brk-left-right","text":"","code":"chop(5:7, brk_left(5:7)) #> Warning: `brk_left()` was deprecated in santoku 0.4.0. #> Please use the `left` argument to `chop()` instead. #> This warning is displayed once every 8 hours. #> Call `lifecycle::last_warnings()` to see where this warning was generated. #> [1] [5, 6) [6, 7) {7} #> Levels: [5, 6) [6, 7) {7} chop(5:7, brk_right(5:7)) #> Warning: `brk_right()` was deprecated in santoku 0.4.0. #> Please use the `left` argument to `chop()` instead. #> This warning is displayed once every 8 hours. #> Call `lifecycle::last_warnings()` to see where this warning was generated. #> Warning: `left` argument to `brk_right()` ignored #> [1] {5} (5, 6] (6, 7] #> Levels: {5} (5, 6] (6, 7] chop(5:7, brk_left(5:7)) #> [1] [5, 6) [6, 7) {7} #> Levels: [5, 6) [6, 7) {7}"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_default.html","id":null,"dir":"Reference","previous_headings":"","what":"Create a standard set of breaks — brk_default","title":"Create a standard set of breaks — brk_default","text":"Create standard set breaks","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_default.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create a standard set of breaks — brk_default","text":"","code":"brk_default(breaks)"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_default.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create a standard set of breaks — brk_default","text":"breaks numeric vector.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_default.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create a standard set of breaks — brk_default","text":"function returns object class breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_default.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Create a standard set of breaks — brk_default","text":"","code":"chop(1:10, c(2, 5, 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) [5, 8) [5, 8) [5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) [5, 8) [8, 10] chop(1:10, brk_default(c(2, 5, 8))) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) [5, 8) [5, 8) [5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) [5, 8) [8, 10]"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":null,"dir":"Reference","previous_headings":"","what":"Create a breaks object manually — brk_manual","title":"Create a breaks object manually — brk_manual","text":"Create breaks object manually","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create a breaks object manually — brk_manual","text":"","code":"brk_manual(breaks, left_vec)"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create a breaks object manually — brk_manual","text":"breaks vector, must sorted. left_vec logical vector, length breaks. Specifies whether break left-closed right-closed.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create a breaks object manually — brk_manual","text":"function returns object class breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Create a breaks object manually — brk_manual","text":"breaks must closed exactly one side, like ..., x) [x, ... (left-closed) ..., x) [x, ... (right-closed). example, breaks = 1:3 left = c(TRUE, FALSE, TRUE), resulting intervals Singleton breaks created repeating number breaks. Singletons must closed sides, repeated number indices , +1, left[] must TRUE left[+1] must FALSE. brk_manual() ignores left close_end arguments passed chop(), since left_vec sets manually. extend drop arguments respected usual.","code":"T F T [ 1, 2 ] ( 2, 3 )"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_manual.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Create a breaks object manually — brk_manual","text":"","code":"lbrks <- brk_manual(1:3, rep(TRUE, 3)) chop(1:3, lbrks, extend = FALSE) #> [1] [1, 2) [2, 3) #> Levels: [1, 2) [2, 3) rbrks <- brk_manual(1:3, rep(FALSE, 3)) chop(1:3, rbrks, extend = FALSE) #> [1] (1, 2] (2, 3] #> Levels: (1, 2] (2, 3] brks_singleton <- brk_manual( c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE)) chop(1:3, brks_singleton, extend = FALSE) #> [1] [1, 2) {2} #> Levels: [1, 2) {2}"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html","id":null,"dir":"Reference","previous_headings":"","what":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","title":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","text":"brk_width() can used time interval classes base R lubridate package.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","text":"","code":"# S3 method for class 'Duration' brk_width(width, start)"},{"path":"https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","text":"width scalar difftime, Period Duration object. start scalar class Date POSIXct. Can omitted.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","text":"width Period, lubridate::add_with_rollback() used calculate widths. can useful e.g. calendar months.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Equal-width intervals for dates or datetimes — brk_width-for-datetime","text":"","code":"if (requireNamespace(\"lubridate\")) { year2001 <- as.Date(\"2001-01-01\") + 0:364 tab_width(year2001, months(1), labels = lbl_discrete(\" to \", fmt = \"%e %b %y\")) } #> 1 Jan 01 to 31 Jan 01 1 Feb 01 to 28 Feb 01 1 Mar 01 to 31 Mar 01 #> 31 28 31 #> 1 Apr 01 to 30 Apr 01 1 May 01 to 31 May 01 1 Jun 01 to 30 Jun 01 #> 30 31 30 #> 1 Jul 01 to 31 Jul 01 1 Aug 01 to 31 Aug 01 1 Sep 01 to 30 Sep 01 #> 31 31 30 #> 1 Oct 01 to 31 Oct 01 1 Nov 01 to 30 Nov 01 1 Dec 01 to 31 Dec 01 #> 31 30 31"},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":null,"dir":"Reference","previous_headings":"","what":"Cut data into intervals — chop","title":"Cut data into intervals — chop","text":"chop() cuts x intervals. returns factor length x, representing interval contains element x. kiru() alias chop. tab() calls chop() returns contingency table result.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Cut data into intervals — chop","text":"","code":"chop( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) kiru( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) tab( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE )"},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Cut data into intervals — chop","text":"x vector. breaks numeric vector cut-points, function create cut-points x. labels character vector labels function create labels. extend Logical. TRUE, always extend breaks +/-Inf. NULL, extend breaks min(x) /max(x) necessary. FALSE, never extend. left Logical. Left-closed right-closed breaks? close_end Logical. Close last break right? (left FALSE, close first break left?) raw Logical. Use raw values labels? drop Logical. Drop unused levels result?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Cut data into intervals — chop","text":"chop() returns factor length x, representing intervals containing value x. tab() returns contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Cut data into intervals — chop","text":"x may numeric vector, generally, vector can compared < == (see Ops). particular Date date-time objects supported. Character vectors supported warning.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"breaks","dir":"Reference","previous_headings":"","what":"Breaks","title":"Cut data into intervals — chop","text":"breaks may vector function. vector, breaks gives interval endpoints. Repeating value creates \"singleton\" interval, contains value. example breaks = c(1, 3, 3, 5) creates 3 intervals: [1, 3), {3} (3, 5]. breaks function, called x, extend, left close_end arguments, return object class breaks. Use brk_* functions create variety data-dependent breaks. Names breaks may used labels. See \"Labels\" .","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"options-for-breaks","dir":"Reference","previous_headings":"","what":"Options for breaks","title":"Cut data into intervals — chop","text":"default, left-closed intervals created. left FALSE, right-closed intervals created. close_end TRUE final break (first break left FALSE) closed ends. guarantees values x min(breaks) <= x <= max(breaks) included intervals. version 0.9.0, close_end FALSE default, also behaved differently respect extended breaks: see \"Extending intervals\" . Using mathematical set notation: left TRUE close_end TRUE, breaks look like [b1, b2), [b2, b3) ... [b_(n-1), b_n]. left FALSE close_end TRUE, breaks look like [b1, b2], (b2, b3] ... (b_(n-1), b_n]. left TRUE close_end FALSE, breaks look like ... [b1, b2) .... left FALSE close_end FALSE, breaks look like ... (b1, b2] ....","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"extending-intervals","dir":"Reference","previous_headings":"","what":"Extending intervals","title":"Cut data into intervals — chop","text":"extend TRUE, intervals extended [-Inf, min(breaks)) (max(breaks), Inf]. extend NULL (default), intervals extended [min(x), min(breaks)) (max(breaks), max(x)], necessary, .e. elements x outside unextended breaks. extend FALSE, intervals never extended. Note even extend = TRUE, extended intervals dropped factor levels contain elements drop = TRUE. close_end relevant intervals extended; extended intervals always closed outside. change previous behaviour. version 0.8.0, close_end applied last user-specified interval, extended intervals created. Since 1.1.0, infinity represented \\(\\infty\\) breaks unicode platforms. Set options(santoku.infinity = \"Inf\") get old behaviour.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"labels","dir":"Reference","previous_headings":"","what":"Labels","title":"Cut data into intervals — chop","text":"labels may character vector. length (possibly extended) number intervals. Alternatively, labels may lbl_* function lbl_dash(). breaks named vector, names breaks used labels interval starting corresponding element. overrides labels argument (unnamed breaks still use labels). feature . labels NULL, integer codes returned instead factor. raw TRUE, labels show actual interval endpoints, usually numbers. raw FALSE labels may show objects, quantiles chop_quantiles() friends, proportions range chop_proportions(), standard deviations chop_mean_sd(). raw NULL lbl_* functions use default (usually FALSE). Otherwise, raw argument chop() overrides raw arguments passed lbl_* functions directly.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"miscellaneous","dir":"Reference","previous_headings":"","what":"Miscellaneous","title":"Cut data into intervals — chop","text":"NA values x, values outside extended endpoints, return NA. kiru() synonym chop(). load {tidyr}, can use avoid confusion tidyr::chop(). Note chop(), like R, uses binary arithmetic. Thus, numbers may exactly equal think . example .","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Cut data into intervals — chop","text":"","code":"chop(1:7, c(2, 4, 6)) #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) [4, 6) [6, 7] chop(1:7, c(2, 4, 6), extend = FALSE) #> [1] [2, 4) [2, 4) [4, 6] [4, 6] [4, 6] #> Levels: [2, 4) [4, 6] # Repeat a number for a singleton break: chop(1:7, c(2, 4, 4, 6)) #> [1] [1, 2) [2, 4) [2, 4) {4} (4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) {4} (4, 6) [6, 7] chop(1:7, c(2, 4, 6), left = FALSE) #> [1] [1, 2] [1, 2] (2, 4] (2, 4] (4, 6] (4, 6] (6, 7] #> Levels: [1, 2] (2, 4] (4, 6] (6, 7] chop(1:7, c(2, 4, 6), close_end = FALSE) #> [1] [1, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 7] [6, 7] #> Levels: [1, 2) [2, 4) [4, 6) [6, 7] chop(1:7, brk_quantiles(c(0.25, 0.75))) #> [1] [0%, 25%) [0%, 25%) [25%, 75%) [25%, 75%) [25%, 75%) [75%, 100%] #> [7] [75%, 100%] #> Levels: [0%, 25%) [25%, 75%) [75%, 100%] # A single break is fine if `extend` is not `FALSE`: chop(1:7, 4) #> [1] [1, 4) [1, 4) [1, 4) [4, 7] [4, 7] [4, 7] [4, 7] #> Levels: [1, 4) [4, 7] # Floating point inaccuracy: chop(0.3/3, c(0, 0.1, 0.1, 1), labels = c(\"< 0.1\", \"0.1\", \"> 0.1\")) #> [1] < 0.1 #> Levels: < 0.1 # -- Labels -- chop(1:7, c(Lowest = 1, Low = 2, Mid = 4, High = 6)) #> [1] Lowest Low Low Mid Mid High High #> Levels: Lowest Low Mid High chop(1:7, c(2, 4, 6), labels = c(\"Lowest\", \"Low\", \"Mid\", \"High\")) #> [1] Lowest Low Low Mid Mid High High #> Levels: Lowest Low Mid High chop(1:7, c(2, 4, 6), labels = lbl_dash()) #> [1] 1—2 2—4 2—4 4—6 4—6 6—7 6—7 #> Levels: 1—2 2—4 4—6 6—7 # Mixing names and other labels: chop(1:7, c(\"<2\" = 1, 2, 4, \">=6\" = 6), labels = lbl_dash()) #> [1] <2 2—4 2—4 4—6 4—6 >=6 >=6 #> Levels: <2 2—4 4—6 >=6 # -- Non-standard types -- chop(as.Date(\"2001-01-01\") + 1:7, as.Date(\"2001-01-04\")) #> [1] [2001-01-02, 2001-01-04) [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08] #> [4] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08] [2001-01-04, 2001-01-08] #> [7] [2001-01-04, 2001-01-08] #> Levels: [2001-01-02, 2001-01-04) [2001-01-04, 2001-01-08] suppressWarnings(chop(LETTERS[1:7], \"D\")) #> [1] [A, D) [A, D) [A, D) [D, G] [D, G] [D, G] [D, G] #> Levels: [A, D) [D, G] tab(1:10, c(2, 5, 8)) #> [1, 2) [2, 5) [5, 8) [8, 10] #> 1 3 3 3"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop equal-sized groups — chop_equally","title":"Chop equal-sized groups — chop_equally","text":"chop_equally() chops x groups equal number elements.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop equal-sized groups — chop_equally","text":"","code":"chop_equally( x, groups, ..., labels = lbl_intervals(), left = is.numeric(x), raw = TRUE ) brk_equally(groups) tab_equally(x, groups, ..., left = is.numeric(x), raw = TRUE)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop equal-sized groups — chop_equally","text":"x vector. groups Number groups. ... Passed chop(). labels character vector labels function create labels. left Logical. Left-closed right-closed breaks? raw Logical. Use raw values labels?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop equal-sized groups — chop_equally","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop equal-sized groups — chop_equally","text":"chop_equally() uses brk_quantiles() hood. x duplicate elements, may get fewer groups requested. , warning emitted. See examples.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_equally.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop equal-sized groups — chop_equally","text":"","code":"chop_equally(1:10, 5) #> [1] [1, 2.8) [1, 2.8) [2.8, 4.6) [2.8, 4.6) [4.6, 6.4) [4.6, 6.4) #> [7] [6.4, 8.2) [6.4, 8.2) [8.2, 10] [8.2, 10] #> Levels: [1, 2.8) [2.8, 4.6) [4.6, 6.4) [6.4, 8.2) [8.2, 10] # You can't always guarantee equal-sized groups: dupes <- c(1, 1, 1, 2, 3, 4, 4, 4) quantile(dupes, 0:4/4) #> 0% 25% 50% 75% 100% #> 1.0 1.0 2.5 4.0 4.0 chop_equally(dupes, 4) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [1] {1} {1} {1} (1, 2.5) [2.5, 4) {4} {4} {4} #> Levels: {1} (1, 2.5) [2.5, 4) {4} # Or as many groups as you ask for: chop_equally(c(1, 1, 2, 2), 3) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [1] {1} {1} {2} {2} #> Levels: {1} {2}"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_evenly.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop into equal-width intervals — chop_evenly","title":"Chop into equal-width intervals — chop_evenly","text":"chop_evenly() chops x intervals intervals equal width.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_evenly.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop into equal-width intervals — chop_evenly","text":"","code":"chop_evenly(x, intervals, ...) brk_evenly(intervals) tab_evenly(x, intervals, ...)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_evenly.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop into equal-width intervals — chop_evenly","text":"x vector. intervals Integer: number intervals create. ... Passed chop().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_evenly.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop into equal-width intervals — chop_evenly","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_evenly.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop into equal-width intervals — chop_evenly","text":"","code":"chop_evenly(0:10, 5) #> [1] [0, 2) [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] #> [10] [8, 10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10]"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_fn.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop using an existing function — chop_fn","title":"Chop using an existing function — chop_fn","text":"chop_fn() convenience wrapper: chop_fn(x, foo, ...) chop(x, foo(x, ...)).","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_fn.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop using an existing function — chop_fn","text":"","code":"chop_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) brk_fn(fn, ...) tab_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE )"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_fn.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop using an existing function — chop_fn","text":"x vector. fn function returns numeric vector breaks. ... arguments fn extend Logical. TRUE, always extend breaks +/-Inf. NULL, extend breaks min(x) /max(x) necessary. FALSE, never extend. left Logical. Left-closed right-closed breaks? close_end Logical. Close last break right? (left FALSE, close first break left?) raw Logical. Use raw values labels? drop Logical. Drop unused levels result?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_fn.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop using an existing function — chop_fn","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_fn.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop using an existing function — chop_fn","text":"","code":"if (requireNamespace(\"scales\")) { chop_fn(rlnorm(10), scales::breaks_log(5)) # same as # x <- rlnorm(10) # chop(x, scales::breaks_log(5)(x)) } #> Loading required namespace: scales #> [1] [0.1, 0.3) [1, 3) [0.03, 0.1) [0.3, 1) [1, 3) [3, 10] #> [7] [0.1, 0.3) [0.3, 1) [0.3, 1) [0.3, 1) #> Levels: [0.03, 0.1) [0.1, 0.3) [0.3, 1) [1, 3) [3, 10]"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop by standard deviations — chop_mean_sd","title":"Chop by standard deviations — chop_mean_sd","text":"Intervals measured standard deviations either side mean.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop by standard deviations — chop_mean_sd","text":"","code":"chop_mean_sd(x, sds = 1:3, ..., raw = FALSE, sd = deprecated()) brk_mean_sd(sds = 1:3, sd = deprecated()) tab_mean_sd(x, sds = 1:3, ..., raw = FALSE)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop by standard deviations — chop_mean_sd","text":"x vector. sds Positive numeric vector standard deviations. ... Passed chop(). raw Logical. Use raw values labels? sd","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop by standard deviations — chop_mean_sd","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop by standard deviations — chop_mean_sd","text":"version 0.7.0, functions changed specifying sds vector. chop 1, 2 3 standard deviations around mean, write chop_mean_sd(x, sds = 1:3) instead chop_mean_sd(x, sd = 3).","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop by standard deviations — chop_mean_sd","text":"","code":"chop_mean_sd(1:10) #> [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) #> [6] [0 sd, 1 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [1 sd, 2 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) chop(1:10, brk_mean_sd()) #> [1] [-2 sd, -1 sd) [-2 sd, -1 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) [-1 sd, 0 sd) #> [6] [0 sd, 1 sd) [0 sd, 1 sd) [0 sd, 1 sd) [1 sd, 2 sd) [1 sd, 2 sd) #> Levels: [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) tab_mean_sd(1:10) #> [-2 sd, -1 sd) [-1 sd, 0 sd) [0 sd, 1 sd) [1 sd, 2 sd) #> 2 3 3 2"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop into fixed-sized groups — chop_n","title":"Chop into fixed-sized groups — chop_n","text":"chop_n() creates intervals containing fixed number elements.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop into fixed-sized groups — chop_n","text":"","code":"chop_n(x, n, ..., tail = \"split\") brk_n(n, tail = \"split\") tab_n(x, n, ..., tail = \"split\")"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop into fixed-sized groups — chop_n","text":"x vector. n Integer. Number elements interval. ... Passed chop(). tail String. final interval fewer n elements? \"split\" keep separate. \"merge\" merge neighbouring interval.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop into fixed-sized groups — chop_n","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop into fixed-sized groups — chop_n","text":"algorithm guarantees intervals contain n elements, long duplicates x tail = \"split\". also guarantees intervals contain fewer n elements, except possibly last interval (first interval left FALSE). ensure intervals contain least n elements (long least n elements x!) set tail = \"merge\". tail = \"split\" intervals containing duplicates n elements, warning given.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_n.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop into fixed-sized groups — chop_n","text":"","code":"chop_n(1:10, 5) #> [1] [1, 6) [1, 6) [1, 6) [1, 6) [1, 6) [6, 10] [6, 10] [6, 10] [6, 10] #> [10] [6, 10] #> Levels: [1, 6) [6, 10] chop_n(1:5, 2) #> [1] [1, 3) [1, 3) [3, 5) [3, 5) {5} #> Levels: [1, 3) [3, 5) {5} chop_n(1:5, 2, tail = \"merge\") #> [1] [1, 3) [1, 3) [3, 5] [3, 5] [3, 5] #> Levels: [1, 3) [3, 5] # too many duplicates x <- rep(1:2, each = 3) chop_n(x, 2) #> Warning: Some intervals contain more than 2 elements #> [1] [1, 2) [1, 2) [1, 2) {2} {2} {2} #> Levels: [1, 2) {2} tab_n(1:10, 5) #> [1, 6) [6, 10] #> 5 5 # fewer elements in one group tab_n(1:10, 4) #> [1, 5) [5, 9) [9, 10] #> 4 4 2"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop using pretty breakpoints — chop_pretty","title":"Chop using pretty breakpoints — chop_pretty","text":"chop_pretty() uses base::pretty() calculate breakpoints 1, 2 5 times power 10. look nice graphs.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop using pretty breakpoints — chop_pretty","text":"","code":"chop_pretty(x, n = 5, ...) brk_pretty(n = 5, ...) tab_pretty(x, n = 5, ...)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop using pretty breakpoints — chop_pretty","text":"x vector. n Positive integer passed base::pretty(). many intervals chop ? ... Passed chop() chop_pretty() tab_pretty(); passed base::pretty() brk_pretty().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop using pretty breakpoints — chop_pretty","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop using pretty breakpoints — chop_pretty","text":"base::pretty() tries return n+1 breakpoints, .e. n intervals, note guaranteed. methods Date POSIXct objects. fine-grained control base::pretty() parameters, use chop(x, brk_pretty(...)).","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_pretty.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop using pretty breakpoints — chop_pretty","text":"","code":"chop_pretty(1:10) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] chop(1:10, brk_pretty(n = 5, high.u.bias = 0)) #> [1] [1, 2) [2, 3) [3, 4) [4, 5) [5, 6) [6, 7) [7, 8) [8, 9) [9, 10] #> [10] [9, 10] #> Levels: [1, 2) [2, 3) [3, 4) [4, 5) [5, 6) [6, 7) [7, 8) [8, 9) [9, 10] tab_pretty(1:10) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] #> 1 2 2 2 3"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop into proportions of the range of x — chop_proportions","title":"Chop into proportions of the range of x — chop_proportions","text":"chop_proportions() chops x proportions range, excluding infinite values.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop into proportions of the range of x — chop_proportions","text":"","code":"chop_proportions(x, proportions, ..., raw = TRUE) brk_proportions(proportions) tab_proportions(x, proportions, ..., raw = TRUE)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop into proportions of the range of x — chop_proportions","text":"x vector. proportions Numeric vector 0 1: proportions x's range. proportions names, used labels. ... Passed chop(). raw Logical. Use raw values labels?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop into proportions of the range of x — chop_proportions","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop into proportions of the range of x — chop_proportions","text":"default, labels show raw numeric endpoints. label intervals proportions, use raw = FALSE.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_proportions.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop into proportions of the range of x — chop_proportions","text":"","code":"chop_proportions(0:10, c(0.2, 0.8)) #> [1] [0, 2) [0, 2) [2, 8) [2, 8) [2, 8) [2, 8) [2, 8) [2, 8) [8, 10] #> [10] [8, 10] [8, 10] #> Levels: [0, 2) [2, 8) [8, 10] chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8)) #> [1] Low Low Mid Mid Mid Mid Mid Mid High High High #> Levels: Low Mid High"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop by quantiles — chop_quantiles","title":"Chop by quantiles — chop_quantiles","text":"chop_quantiles() chops data quantiles. chop_deciles() convenience function chops deciles.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop by quantiles — chop_quantiles","text":"","code":"chop_quantiles( x, probs, ..., labels = if (raw) lbl_intervals() else lbl_intervals(single = NULL), left = is.numeric(x), raw = FALSE, weights = NULL, recalc_probs = FALSE ) chop_deciles(x, ...) brk_quantiles(probs, ..., weights = NULL, recalc_probs = FALSE) tab_quantiles(x, probs, ..., left = is.numeric(x), raw = FALSE) tab_deciles(x, ...)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop by quantiles — chop_quantiles","text":"x vector. probs vector probabilities quantiles. probs names, used labels. ... chop_quantiles, passed chop(). brk_quantiles(), passed stats::quantile() Hmisc::wtd.quantile(). labels character vector labels function create labels. left Logical. Left-closed right-closed breaks? raw Logical. Use raw values labels? weights NULL numeric vector length x. NULL, Hmisc::wtd.quantile() used calculate weighted quantiles. recalc_probs Logical. Recalculate probabilities quantiles using ecdf(x)? See .","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop by quantiles — chop_quantiles","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop by quantiles — chop_quantiles","text":"non-numeric x, left set FALSE default. works better calculating \"type 1\" quantiles, since round . See stats::quantile(). default, chop_quantiles() shows requested probabilities labels. show numeric quantiles , set raw = TRUE. x contains duplicates, consecutive quantiles may number. , interval labels may misleading, recalc_probs = FALSE warning emitted. Set recalc_probs = TRUE recalculate probabilities quantiles using empirical cumulative distribution function x. may give different labels expect, remove names probs, never changes actual quantiles used breaks. present, recalc_probs = TRUE incompatible non-null weights. See example .","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_quantiles.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop by quantiles — chop_quantiles","text":"","code":"chop_quantiles(1:10, 1:3/4) #> [1] [0%, 25%) [0%, 25%) [0%, 25%) [25%, 50%) [25%, 50%) [50%, 75%) #> [7] [50%, 75%) [75%, 100%] [75%, 100%] [75%, 100%] #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%] chop_quantiles(1:10, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75)) #> [1] Q1 Q1 Q1 Q2 Q2 Q3 Q3 Q4 Q4 Q4 #> Levels: Q1 Q2 Q3 Q4 chop(1:10, brk_quantiles(1:3/4)) #> [1] [0%, 25%) [0%, 25%) [0%, 25%) [25%, 50%) [25%, 50%) [50%, 75%) #> [7] [50%, 75%) [75%, 100%] [75%, 100%] [75%, 100%] #> Levels: [0%, 25%) [25%, 50%) [50%, 75%) [75%, 100%] chop_deciles(1:10) #> [1] [0%, 10%) [10%, 20%) [20%, 30%) [30%, 40%) [40%, 50%) [50%, 60%) #> [7] [60%, 70%) [70%, 80%) [80%, 90%) [90%, 100%] #> 10 Levels: [0%, 10%) [10%, 20%) [20%, 30%) [30%, 40%) [40%, 50%) ... [90%, 100%] # to label by the quantiles themselves: chop_quantiles(1:10, 1:3/4, raw = TRUE) #> [1] [1, 3.25) [1, 3.25) [1, 3.25) [3.25, 5.5) [3.25, 5.5) [5.5, 7.75) #> [7] [5.5, 7.75) [7.75, 10] [7.75, 10] [7.75, 10] #> Levels: [1, 3.25) [3.25, 5.5) [5.5, 7.75) [7.75, 10] # duplicate quantiles: x <- c(1, 1, 1, 2, 3) quantile(x, 1:5/5) #> 20% 40% 60% 80% 100% #> 1.0 1.0 1.4 2.2 3.0 tab_quantiles(x, 1:5/5) #> Warning: `x` has duplicate quantiles: break labels may be misleading #> [20%, 40%] [60%, 80%) [80%, 100%] #> 3 1 1 tab_quantiles(x, 1:5/5, recalc_probs = TRUE) #> [0%, 60%] [60%, 80%) [80%, 100%] #> 3 1 1 set.seed(42) tab_quantiles(rnorm(100), probs = 1:3/4, raw = TRUE) #> [-2.993, -0.6167) [-0.6167, 0.0898) [0.0898, 0.6616) [0.6616, 2.287] #> 25 25 25 25"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop common values into singleton intervals — chop_spikes","title":"Chop common values into singleton intervals — chop_spikes","text":"chop_spikes() lets chop common values x singleton intervals. can help make unusual values visible.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop common values into singleton intervals — chop_spikes","text":"","code":"chop_spikes(x, breaks, ..., n = NULL, prop = NULL) brk_spikes(breaks, n = NULL, prop = NULL) tab_spikes(x, breaks, ..., n = NULL, prop = NULL)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop common values into singleton intervals — chop_spikes","text":"x vector. breaks numeric vector cut-points call brk_* function. resulting breaks object modified add singleton breaks. ... Passed chop(). n, prop Scalar. Provide either n, number values, prop, proportion length(x). Values x occur least often get singleton break.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop common values into singleton intervals — chop_spikes","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop common values into singleton intervals — chop_spikes","text":"function .","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_spikes.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop common values into singleton intervals — chop_spikes","text":"","code":"x <- c(1:4, rep(5, 5), 6:10) chop_spikes(x, c(2, 7), n = 5) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} {5} {5} {5} {5} #> [10] (5, 7) [7, 10] [7, 10] [7, 10] [7, 10] #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10] chop_spikes(x, c(2, 7), prop = 0.25) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} {5} {5} {5} {5} #> [10] (5, 7) [7, 10] [7, 10] [7, 10] [7, 10] #> Levels: [1, 2) [2, 5) {5} (5, 7) [7, 10] chop_spikes(x, brk_width(5), n = 5) #> [1] [1, 5) [1, 5) [1, 5) [1, 5) {5} {5} {5} {5} {5} #> [10] [6, 11] [6, 11] [6, 11] [6, 11] [6, 11] #> Levels: [1, 5) {5} [6, 11] set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) tab_spikes(x, brk_width(2, 0), prop = 0.05) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 9.057) {9.057} #> 30 24 36 40 22 11 #> (9.057, 10] #> 37"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop into fixed-width intervals — chop_width","title":"Chop into fixed-width intervals — chop_width","text":"chop_width() chops x intervals fixed width.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop into fixed-width intervals — chop_width","text":"","code":"chop_width(x, width, start, ..., left = sign(width) > 0) brk_width(width, start) # Default S3 method brk_width(width, start) tab_width(x, width, start, ..., left = sign(width) > 0)"},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop into fixed-width intervals — chop_width","text":"x vector. width Width intervals. start Starting point intervals. default smallest finite x (largest width negative). ... Passed chop(). left Logical. Left-closed right-closed breaks?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop into fixed-width intervals — chop_width","text":"chop_* functions return factor length x. brk_* functions return function create breaks. tab_* functions return contingency table.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Chop into fixed-width intervals — chop_width","text":"width negative, chop_width() sets left = FALSE intervals go downwards start.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/chop_width.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop into fixed-width intervals — chop_width","text":"","code":"chop_width(1:10, 2) #> [1] [1, 3) [1, 3) [3, 5) [3, 5) [5, 7) [5, 7) [7, 9) [7, 9) [9, 11] #> [10] [9, 11] #> Levels: [1, 3) [3, 5) [5, 7) [7, 9) [9, 11] chop_width(1:10, 2, start = 0) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] chop_width(1:9, -2) #> [1] [1, 3] [1, 3] [1, 3] (3, 5] (3, 5] (5, 7] (5, 7] (7, 9] (7, 9] #> Levels: [1, 3] (3, 5] (5, 7] (7, 9] chop(1:10, brk_width(2, 0)) #> [1] [0, 2) [2, 4) [2, 4) [4, 6) [4, 6) [6, 8) [6, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] tab_width(1:10, 2, start = 0) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] #> 1 2 2 2 3"},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":null,"dir":"Reference","previous_headings":"","what":"Cut data into intervals, separating out common values — dissect","title":"Cut data into intervals, separating out common values — dissect","text":"Sometimes useful separate common elements x. dissect() chops x, puts common elements x (\"spikes\") separate categories.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Cut data into intervals, separating out common values — dissect","text":"","code":"dissect( x, breaks, ..., n = NULL, prop = NULL, spike_labels = \"{{{l}}}\", exclude_spikes = FALSE ) tab_dissect(x, breaks, ..., n = NULL, prop = NULL)"},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Cut data into intervals, separating out common values — dissect","text":"x, breaks, ... Passed chop(). n, prop Scalar. Provide either n, number values, prop, proportion length(x). Values x occur least often get singleton break. spike_labels Glue string spike labels. Use \"{l}\" spike value. exclude_spikes Logical. Exclude spikes chopping x? can affect location data-dependent breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Cut data into intervals, separating out common values — dissect","text":"dissect() returns result chop(), common values put separate factor levels. tab_dissect() returns contingency table().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Cut data into intervals, separating out common values — dissect","text":"Unlike chop_spikes(), dissect() break intervals contain spike. result, unlike chop_* functions, dissect() chop x disjoint intervals. See examples. breaks data-dependent, labels may misleading common elements removed. See example . get round , set exclude_spikes TRUE. breaks calculated removing spikes data. Levels result ordered minimum element level. result, drop = FALSE, empty levels placed last. function .","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/dissect.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Cut data into intervals, separating out common values — dissect","text":"","code":"x <- c(2, 3, 3, 3, 4) dissect(x, c(2, 4), n = 3) #> [1] [2, 4] {3} {3} {3} [2, 4] #> Levels: [2, 4] {3} dissect(x, brk_width(2), prop = 0.5) #> [1] [2, 4] {3} {3} {3} [2, 4] #> Levels: [2, 4] {3} set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) # Compare: table(dissect(x, brk_width(2, 0), prop = 0.05)) #> #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 10] {9.057} #> 30 24 36 40 59 11 # Versus: tab_spikes(x, brk_width(2, 0), prop = 0.05) #> [0, 2) [2, 4) [4, 6) [6, 8) [8, 9.057) {9.057} #> 30 24 36 40 22 11 #> (9.057, 10] #> 37 # Potentially confusing data-dependent breaks: set.seed(42) x <- rnorm(99) x[1:9] <- x[1] tab_quantiles(x, 1:2/3) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] #> 33 33 33 tab_dissect(x, brk_quantiles(1:2/3), n = 9) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] {1.371} #> 33 33 24 9 # Calculate quantiles excluding spikes: tab_dissect(x, brk_quantiles(1:2/3), n = 9, exclude_spikes = TRUE) #> [0%, 33.33%) [33.33%, 66.67%) [66.67%, 100%] {1.371} #> 30 30 30 9"},{"path":"https://hughjonesd.github.io/santoku/reference/exactly.html","id":null,"dir":"Reference","previous_headings":"","what":"Define singleton intervals explicitly — exactly","title":"Define singleton intervals explicitly — exactly","text":"exactly() duplicates input. lets define singleton intervals like : chop(x, c(1, exactly(2), 3)). chop(x, c(1, 2, 2, 3)) conveys intent clearly.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/exactly.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define singleton intervals explicitly — exactly","text":"","code":"exactly(x)"},{"path":"https://hughjonesd.github.io/santoku/reference/exactly.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define singleton intervals explicitly — exactly","text":"x numeric vector.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/exactly.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define singleton intervals explicitly — exactly","text":"rep(x, = 2).","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/exactly.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define singleton intervals explicitly — exactly","text":"","code":"chop(1:10, c(2, exactly(5), 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} (5, 8) (5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10] # same: chop(1:10, c(2, 5, 5, 8)) #> [1] [1, 2) [2, 5) [2, 5) [2, 5) {5} (5, 8) (5, 8) [8, 10] [8, 10] #> [10] [8, 10] #> Levels: [1, 2) [2, 5) {5} (5, 8) [8, 10]"},{"path":"https://hughjonesd.github.io/santoku/reference/fillet.html","id":null,"dir":"Reference","previous_headings":"","what":"Chop data precisely (for programmers) — fillet","title":"Chop data precisely (for programmers) — fillet","text":"fillet() calls chop() extend = FALSE drop = FALSE. ensures get breaks labels ask . programming, consider using fillet() instead chop().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/fillet.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Chop data precisely (for programmers) — fillet","text":"","code":"fillet( x, breaks, labels = lbl_intervals(), left = TRUE, close_end = TRUE, raw = NULL )"},{"path":"https://hughjonesd.github.io/santoku/reference/fillet.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Chop data precisely (for programmers) — fillet","text":"x vector. breaks numeric vector cut-points, function create cut-points x. labels character vector labels function create labels. left Logical. Left-closed right-closed breaks? close_end Logical. Close last break right? (left FALSE, close first break left?) raw Logical. Use raw values labels?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/fillet.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Chop data precisely (for programmers) — fillet","text":"fillet() returns factor length x, representing intervals containing value x.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/fillet.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Chop data precisely (for programmers) — fillet","text":"","code":"fillet(1:10, c(2, 5, 8)) #> [1] [2, 5) [2, 5) [2, 5) [5, 8] [5, 8] [5, 8] [5, 8] #> Levels: [2, 5) [5, 8]"},{"path":"https://hughjonesd.github.io/santoku/reference/knife.html","id":null,"dir":"Reference","previous_headings":"","what":"Deprecated — knife","title":"Deprecated — knife","text":"knife() deprecated favour purrr::partial().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/knife.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Deprecated — knife","text":"","code":"knife(...)"},{"path":"https://hughjonesd.github.io/santoku/reference/knife.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Deprecated — knife","text":"... Parameters chop().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/knife.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Deprecated — knife","text":"function.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"label style user-friendly, distinguish left- right-closed intervals. good continuous data expect points exactly breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"","code":"lbl_dash( symbol = em_dash(), fmt = NULL, single = \"{l}\", first = NULL, last = NULL, raw = deprecated() )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"symbol String: symbol use dash. fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details. raw . Throws error. Use raw argument chop() instead.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"want unicode output, use lbl_dash(\"-\").","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_dash.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals like 1-4, 4-5, ... — lbl_dash","text":"","code":"chop(1:10, c(2, 5, 8), lbl_dash()) #> [1] 1—2 2—5 2—5 2—5 5—8 5—8 5—8 8—10 8—10 8—10 #> Levels: 1—2 2—5 5—8 8—10 chop(1:10, c(2, 5, 8), lbl_dash(\" to \", fmt = \"%.1f\")) #> [1] 1.0 to 2.0 2.0 to 5.0 2.0 to 5.0 2.0 to 5.0 5.0 to 8.0 5.0 to 8.0 #> [7] 5.0 to 8.0 8.0 to 10.0 8.0 to 10.0 8.0 to 10.0 #> Levels: 1.0 to 2.0 2.0 to 5.0 5.0 to 8.0 8.0 to 10.0 chop(1:10, c(2, 5, 8), lbl_dash(first = \"<{r}\")) #> [1] <2 2—5 2—5 2—5 5—8 5—8 5—8 8—10 8—10 8—10 #> Levels: <2 2—5 5—8 8—10 pretty <- function (x) prettyNum(x, big.mark = \",\", digits = 1) chop(runif(10) * 10000, c(3000, 7000), lbl_dash(\" to \", fmt = pretty)) #> [1] 7,000 to 9,677 7,000 to 9,677 7,000 to 9,677 3,000 to 7,000 7,000 to 9,677 #> [6] 3,000 to 7,000 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677 3,000 to 7,000 #> Levels: 1,579 to 3,000 3,000 to 7,000 7,000 to 9,677"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":null,"dir":"Reference","previous_headings":"","what":"Label dates and datetimes — lbl_date","title":"Label dates and datetimes — lbl_date","text":"lbl_date() lbl_datetime() produce nice labels dates datetimes. possible ranges simplified, like like \"13-14 Jul 2026\" \"11:15-12:15 1 Dec 2025\".","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label dates and datetimes — lbl_date","text":"","code":"lbl_date( fmt = \"%e %b %Y\", symbol = \"-\", unit = as.difftime(1, units = \"days\"), single = \"{l}\", first = NULL, last = NULL ) lbl_datetime( fmt = \"%H:%M:%S %b %e %Y\", symbol = \"-\", unit = NULL, single = \"{l}\", first = NULL, last = NULL )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label dates and datetimes — lbl_date","text":"fmt String, list function. format break endpoints. symbol String: separator use full ranges. unit Optional interval unit non-overlapping labels. NULL, . endpoints adjusted style lbl_discrete(). single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label dates and datetimes — lbl_date","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label dates and datetimes — lbl_date","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_datetime.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label dates and datetimes — lbl_date","text":"","code":"winter <- as.Date(\"2025-12-01\") + 0:89 tab(winter, as.Date(c(\"2025-12-25\", \"2026-01-06\")), labels = lbl_date()) #> 1-24 Dec 2025 25 Dec 2025 - 5 Jan 2026 6 Jan - 28 Feb 2026 #> 24 12 54 new_year <- as.POSIXct(\"2025-12-31 23:00\") + 0:120 * 60 round_midnight <- as.POSIXct(c(\"2025-12-31 23:59\", \"2026-01-01 00:05\")) tab(new_year, round_midnight, labels = lbl_datetime()) #> 23:00:00-23:59:00 Dec 31 2025 #> 59 #> 23:59:00 Dec 31 2025 - 00:05:00 Jan 1 2026 #> 6 #> 00:05:00-01:00:00 Jan 1 2026 #> 56 tab(new_year, round_midnight, labels = lbl_datetime(unit = as.difftime(1, units = \"mins\"))) #> 23:00:00-23:58:00 Dec 31 2025 #> 59 #> 23:59:00 Dec 31 2025 - 00:04:00 Jan 1 2026 #> 6 #> 00:05:00-01:00:00 Jan 1 2026 #> 56"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":null,"dir":"Reference","previous_headings":"","what":"Label discrete data — lbl_discrete","title":"Label discrete data — lbl_discrete","text":"lbl_discrete() creates labels discrete data, integers. example, breaks c(1, 3, 4, 6, 7) labelled: \"1-2\", \"3\", \"4-5\", \"6-7\".","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label discrete data — lbl_discrete","text":"","code":"lbl_discrete( symbol = em_dash(), unit = 1L, fmt = NULL, single = NULL, first = NULL, last = NULL )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label discrete data — lbl_discrete","text":"symbol String: symbol use dash. unit Minimum difference distinct values data. integers, 1. fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label discrete data — lbl_discrete","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label discrete data — lbl_discrete","text":"check done data discrete-valued. , labels may misleading. , discrete-valued means x < y, x <= y - unit. aware Date objects may non-integer values. See Date.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label discrete data — lbl_discrete","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_discrete.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label discrete data — lbl_discrete","text":"","code":"tab(1:7, c(1, 3, 5), lbl_discrete()) #> 1—2 3—4 5—7 #> 2 2 3 tab(1:7, c(3, 5), lbl_discrete(first = \"<= {r}\")) #> <= 2 3—4 5—7 #> 2 2 3 tab(1:7 * 1000, c(1, 3, 5) * 1000, lbl_discrete(unit = 1000)) #> 1000—2000 3000—4000 5000—7000 #> 2 2 3 # Misleading labels for non-integer data chop(2.5, c(1, 3, 5), lbl_discrete()) #> [1] 1—2 #> Levels: 1—2"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals by their left or right endpoints — lbl_endpoints","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"useful left endpoint unambiguously indicates interval. cases may give errors due duplicate labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"","code":"lbl_endpoints( left = TRUE, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) lbl_endpoint(fmt = NULL, raw = FALSE, left = TRUE)"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"left Flag. Use left endpoint right endpoint? fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details. raw . Throws error. Use raw argument chop() instead.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"lbl_endpoint() gives error since santoku 1.0.0.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals by their left or right endpoints — lbl_endpoints","text":"","code":"chop(1:10, c(2, 5, 8), lbl_endpoints(left = TRUE)) #> [1] 1 2 2 2 5 5 5 8 8 8 #> Levels: 1 2 5 8 chop(1:10, c(2, 5, 8), lbl_endpoints(left = FALSE)) #> [1] 2 5 5 5 8 8 8 10 10 10 #> Levels: 2 5 8 10 if (requireNamespace(\"lubridate\")) { tab_width( as.Date(\"2000-01-01\") + 0:365, months(1), labels = lbl_endpoints(fmt = \"%b\") ) } #> Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec #> 31 29 31 30 31 30 31 31 30 31 30 31 if (FALSE) { # \\dontrun{ # This gives breaks `[1, 2) [2, 3) {3}` which lead to # duplicate labels `\"2\", \"3\", \"3\"`: chop(1:3, 1:3, lbl_endpoints(left = FALSE)) } # }"},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_format.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals with arbitrary formatting — lbl_format","text":"","code":"lbl_format(fmt, fmt1 = \"%.3g\", raw = FALSE)"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_format.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals with arbitrary formatting — lbl_format","text":"fmt format. Can string, passed base::sprintf() format() methods; one-argument formatting function. fmt1 Format breaks consisting single value. raw Logical. Always use raw breaks labels, rather e.g. quantiles standard deviations?","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_format.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals with arbitrary formatting — lbl_format","text":"vector labels chop, function creates labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_format.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals with arbitrary formatting — lbl_format","text":"labels let format breaks arbitrarily, using either string (passed sprintf()) function. fmt function, must accept two arguments, representing left right endpoints interval. breaks non-numeric, can use \"%s\" string fmt. breaks converted character case. lbl_format() \"questioning\" stage. alternative, consider using lbl_dash() lbl_intervals() fmt argument.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_format.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals with arbitrary formatting — lbl_format","text":"","code":"tab(1:10, c(1,3, 3, 7), label = lbl_format(\"%.3g to %.3g\")) #> 1 to 3 3 3 to 7 7 to 10 #> 2 1 3 4 tab(1:10, c(1,3, 3, 7), label = lbl_format(\"%.3g to %.3g\", \"Exactly %.3g\")) #> 1 to 3 Exactly 3 3 to 7 7 to 10 #> 2 1 3 4 percent2 <- function (x, y) { sprintf(\"%.2f%% - %.2f%%\", x*100, y*100) } tab(runif(100), c(0.25, 0.5, .75), labels = lbl_format(percent2)) #> 0.64% - 25.00% 25.00% - 50.00% 50.00% - 75.00% 75.00% - 98.41% #> 21 20 33 26"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals using the glue package — lbl_glue","title":"Label chopped intervals using the glue package — lbl_glue","text":"Use \"{l}\" \"{r}\" show left right endpoints intervals.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals using the glue package — lbl_glue","text":"","code":"lbl_glue( label, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated(), ... )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals using the glue package — lbl_glue","text":"label glue string passed glue::glue(). fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details. raw . Throws error. Use raw argument chop() instead. ... arguments passed glue::glue().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals using the glue package — lbl_glue","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals using the glue package — lbl_glue","text":"following variables available glue string: l character vector left endpoints intervals. r character vector right endpoints intervals. l_closed logical vector. Elements TRUE left endpoint closed. r_closed logical vector, TRUE right endpoint closed. Endpoints formatted fmt passed glue().","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label chopped intervals using the glue package — lbl_glue","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_glue.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals using the glue package — lbl_glue","text":"","code":"tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(\"{l} to {r}\", single = \"Exactly {l}\")) #> 1 to 3 Exactly 3 3 to 7 7 to 10 #> 2 1 3 4 tab(1:10 * 1000, c(1, 3, 5, 7) * 1000, labels = lbl_glue(\"{l}-{r}\", fmt = function(x) prettyNum(x, big.mark=','))) #> 1,000-3,000 3,000-5,000 5,000-7,000 7,000-10,000 #> 2 2 2 4 # reproducing lbl_intervals(): interval_left <- \"{ifelse(l_closed, '[', '(')}\" interval_right <- \"{ifelse(r_closed, ']', ')')}\" glue_string <- paste0(interval_left, \"{l}\", \", \", \"{r}\", interval_right) tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(glue_string, single = \"{{{l}}}\")) #> [1, 3) {3} (3, 7) [7, 10] #> 2 1 3 4"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals using set notation — lbl_intervals","title":"Label chopped intervals using set notation — lbl_intervals","text":"labels exact, since show whether intervals \"closed\" \"open\", .e. whether include endpoints.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals using set notation — lbl_intervals","text":"","code":"lbl_intervals( fmt = NULL, single = \"{{{l}}}\", first = NULL, last = NULL, raw = deprecated() )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals using set notation — lbl_intervals","text":"fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details. raw . Throws error. Use raw argument chop() instead.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals using set notation — lbl_intervals","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals using set notation — lbl_intervals","text":"Mathematical set notation looks like : [, b]: numbers x <= x <= b; (, b): numbers < x < b; [, b): numbers <= x < b; (, b]: numbers < x <= b; {}: just number exactly.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label chopped intervals using set notation — lbl_intervals","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_intervals.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals using set notation — lbl_intervals","text":"","code":"tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals()) #> [-10, -3) [-3, 0) {0} (0, 3) [3, 10] #> 7 3 1 2 8 tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals(fmt = list(nsmall = 1))) #> [-10.0, -3.0) [ -3.0, 0.0) { 0.0} ( 0.0, 3.0) [ 3.0, 10.0] #> 7 3 1 2 8 tab_evenly(runif(20), 10, labels = lbl_intervals(fmt = percent)) #> [6.095%, 15.1%) [15.1%, 24.11%) [24.11%, 33.11%) [33.11%, 42.12%) #> 1 2 4 2 #> [42.12%, 51.12%) [51.12%, 60.13%) [60.13%, 69.13%) [78.14%, 87.14%) #> 2 4 1 2 #> [87.14%, 96.15%] #> 2"},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"","code":"lbl_manual(sequence, fmt = \"%s\")"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"sequence character vector labels. fmt String, list function. format break endpoints.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"lbl_manual() defunct since santoku 1.0.0. little used closely related rest package. also risks mislabelling intervals, e.g. intervals extended. Use lbl_manual() give error.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_manual.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Defunct: label chopped intervals in a user-defined sequence — lbl_manual","text":"","code":"if (FALSE) { # \\dontrun{ chop(1:10, c(2, 5, 8), lbl_manual(c(\"w\", \"x\", \"y\", \"z\"))) # -> chop(1:10, c(2, 5, 8), labels = c(\"w\", \"x\", \"y\", \"z\")) } # }"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals by their midpoints — lbl_midpoints","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"uses midpoint interval label.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"","code":"lbl_midpoints( fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() )"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"fmt String, list function. format break endpoints. single Glue string: label singleton intervals. See lbl_glue() details. NULL, singleton intervals labelled way intervals. first Glue string: override label first category. Write e.g. first = \"<{r}\" create label like \"<18\". See lbl_glue() details. last String: override label last category. Write e.g. last = \">{l}\" create label like \">65\". See lbl_glue() details. raw . Throws error. Use raw argument chop() instead.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":"formatting-endpoints","dir":"Reference","previous_headings":"","what":"Formatting endpoints","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"fmt NULL used format endpoints. fmt string, numeric endpoints formatted sprintf(fmt, breaks); endpoints, e.g. Date objects, formatted format(breaks, fmt). fmt list, used arguments format. fmt function, take vector numbers (objects can used breaks) return character vector. may helpful use functions {scales} package, e.g. scales::label_comma().","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals by their midpoints — lbl_midpoints","text":"","code":"chop(1:10, c(2, 5, 8), lbl_midpoints()) #> [1] 1.5 3.5 3.5 3.5 6.5 6.5 6.5 9 9 9 #> Levels: 1.5 3.5 6.5 9"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":null,"dir":"Reference","previous_headings":"","what":"Label chopped intervals in sequence — lbl_seq","title":"Label chopped intervals in sequence — lbl_seq","text":"lbl_seq() labels intervals sequentially, using numbers letters.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Label chopped intervals in sequence — lbl_seq","text":"","code":"lbl_seq(start = \"a\")"},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Label chopped intervals in sequence — lbl_seq","text":"start String. template sequence. See .","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Label chopped intervals in sequence — lbl_seq","text":"function creates vector labels.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Label chopped intervals in sequence — lbl_seq","text":"start shows first element sequence. must contain exactly one character set \"\", \"\", \"\", \"\" \"1\". later elements: \"\" replaced \"\", \"b\", \"c\", ... \"\" replaced \"\", \"B\", \"C\", ... \"\" replaced lower-case Roman numerals \"\", \"ii\", \"iii\", ... \"\" replaced upper-case Roman numerals \"\", \"II\", \"III\", ... \"1\" replaced numbers \"1\", \"2\", \"3\", ... characters retained -.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/lbl_seq.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Label chopped intervals in sequence — lbl_seq","text":"","code":"chop(1:10, c(2, 5, 8), lbl_seq()) #> [1] a b b b c c c d d d #> Levels: a b c d chop(1:10, c(2, 5, 8), lbl_seq(\"i.\")) #> [1] i. ii. ii. ii. iii. iii. iii. iv. iv. iv. #> Levels: i. ii. iii. iv. chop(1:10, c(2, 5, 8), lbl_seq(\"(A)\")) #> [1] (A) (B) (B) (B) (C) (C) (C) (D) (D) (D) #> Levels: (A) (B) (C) (D)"},{"path":"https://hughjonesd.github.io/santoku/reference/non-standard-types.html","id":null,"dir":"Reference","previous_headings":"","what":"Tips for chopping non-standard types — non-standard-types","title":"Tips for chopping non-standard types — non-standard-types","text":"Santoku can handle many non-standard types.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/non-standard-types.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Tips for chopping non-standard types — non-standard-types","text":"objects can compared using <, == etc. choppable. Objects converted numeric handled within R code, may slower. Character x breaks chopped warning. x breaks type, able cast type, usually using vctrs::vec_cast_common(). chopping operations make sense, example, chop_mean_sd() character vector. indexed objects stats::ts() objects, indices dropped result. get errors, try setting extend = FALSE (also file bug report). request support type, open issue Github.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/percent.html","id":null,"dir":"Reference","previous_headings":"","what":"Simple percentage formatter — percent","title":"Simple percentage formatter — percent","text":"percent() formats x percentage. wider range formatters, consider scales package.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/percent.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simple percentage formatter — percent","text":"","code":"percent(x)"},{"path":"https://hughjonesd.github.io/santoku/reference/percent.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simple percentage formatter — percent","text":"x Numeric values.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/percent.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simple percentage formatter — percent","text":"x formatted percent.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/percent.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Simple percentage formatter — percent","text":"","code":"percent(0.5) #> [1] \"50%\""},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-cast.html","id":null,"dir":"Reference","previous_headings":"","what":"Internal functions — santoku-cast","title":"Internal functions — santoku-cast","text":"Internal functions","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-cast.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Internal functions — santoku-cast","text":"","code":"# Default S3 method santoku_cast_common(x, y) # S3 method for class 'double' santoku_cast_common(x, y) # S3 method for class 'Date' santoku_cast_common(x, y) # S3 method for class 'POSIXct' santoku_cast_common(x, y) # S3 method for class 'ts' santoku_cast_common(x, y) # S3 method for class 'zoo' santoku_cast_common(x, y) # S3 method for class 'integer64' santoku_cast_common(x, y) # S3 method for class 'hexmode' santoku_cast_common(x, y) # S3 method for class 'octmode' santoku_cast_common(x, y)"},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-cast.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Internal functions — santoku-cast","text":"x, y Vectors cast.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-cast.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Internal functions — santoku-cast","text":"list.","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-package.html","id":null,"dir":"Reference","previous_headings":"","what":"A versatile cutting tool for R: package overview and options — santoku-package","title":"A versatile cutting tool for R: package overview and options — santoku-package","text":"santoku tool cutting data intervals. provides function chop(), similar base R's cut() Hmisc::cut2(). chop(x, breaks) takes vector x returns factor length, coding interval element x falls .","code":""},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-package.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"A versatile cutting tool for R: package overview and options — santoku-package","text":"advantages santoku: default, chop() always covers whole range data, get unexpected NA values. Unlike cut() cut2(), chop() can handle single values well intervals. example, chop(x, breaks = c(1, 2, 2, 3)) create separate factor level values exactly equal 2. Flexible easy labelling. Convenience functions creating quantile intervals, evenly-spaced intervals equal-sized groups. Convenience functions quickly tabulate chopped data. Can chop numbers, dates, date-times objects. advantages make santoku especially useful exploratory analysis, may know range data advance. get started, read vignette: details, start documentation chop().","code":"vignette(\"santoku\")"},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-package.html","id":"options","dir":"Reference","previous_headings":"","what":"Options","title":"A versatile cutting tool for R: package overview and options — santoku-package","text":"Santoku two options: options(\"santoku.infinity\") sets symbol infinity breaks. default NULL, case infinity symbol used platforms support , otherwise \"Inf\" used. options(\"santoku.warn_character\") warns try chop character vector. Set FALSE turn warning.","code":""},{"path":[]},{"path":"https://hughjonesd.github.io/santoku/reference/santoku-package.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"A versatile cutting tool for R: package overview and options — santoku-package","text":"Maintainer: David Hugh-Jones davidhughjones@gmail.com contributors: Daniel Possenriede possenriede@gmail.com [contributor]","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-120","dir":"Changelog","previous_headings":"","what":"santoku 1.2.0","title":"santoku 1.2.0","text":"New experimental lbl_date() lbl_datetime() functions pretty formatting dates date-times. Bugfix: extended breaks failing haven::labelled objects. raw argument lbl_* functions, deprecated since 0.9.0, now throws error.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-110","dir":"Changelog","previous_headings":"","what":"santoku 1.1.0","title":"santoku 1.1.0","text":"CRAN release: 2025-09-11 Core logic speeded using raw pointers. vibe-coded Claude Code. breaks, please file bug report. experimental chop_spikes() dissect() functions give common values x singleton intervals. Unicode platforms, infinity represented ∞ breaks. Set options(santoku.infinity = \"Inf\") use old behaviour. Singleton breaks labelled specially default chop_quantiles(..., raw = FALSE). means e.g. 10th 20th percentiles number, label still [10%, 20%]. multiple quantiles , santoku warns returns leftmost quantile interval. merge intervals, creating labels might different user asked . chop_quantiles() gains recalc_probs argument. recalc_probs = TRUE recalculates probabilities using ecdf(x), may give accurate interval labels. single = NULL documented explicitly lbl_* functions. Bugfix: brk_manual() longer warns close_end = TRUE (default).","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-100","dir":"Changelog","previous_headings":"","what":"santoku 1.0.0","title":"santoku 1.0.0","text":"CRAN release: 2024-06-04 santoku now considered stable. chop_quantiles() brk_quantiles() gain new weights argument, letting chop weighted quantiles using Hmisc::wtd.quantile(). brk_quantiles() may now return singleton breaks, producing accurate results x duplicate elements. deprecated functions removed, raw argument lbl_* functions now always gives deprecation warning.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-0100","dir":"Changelog","previous_headings":"","what":"santoku 0.10.0","title":"santoku 0.10.0","text":"CRAN release: 2023-10-12 List arguments fmt lbl_* functions taken arguments base::format. gives flexibility formatting, e.g., units breaks. chop_n() gains tail argument, deal last interval containing less n elements. Set tail = \"merge\" merge previous interval. guarantees intervals contain least n elements. chop_equally() may return fewer groups groups duplicate elements. now warn happens. Bugfix: chop_n() return intervals fewer n elements duplicate elements. new algorithm avoids , may slower case.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-091","dir":"Changelog","previous_headings":"","what":"santoku 0.9.1","title":"santoku 0.9.1","text":"CRAN release: 2023-03-08 endpoint_labels() methods gain unused ... argument satisfy R CMD CHECK.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-090","dir":"Changelog","previous_headings":"","what":"santoku 0.9.0","title":"santoku 0.9.0","text":"CRAN release: 2022-11-01","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"breaking-changes-0-9-0","dir":"Changelog","previous_headings":"","what":"Breaking changes","title":"santoku 0.9.0","text":"important changes close_end. close_end now TRUE default chop() fillet(). previous versions: Whereas now: close_end now always applied extend. example, previous versions: Whereas now: changed behaviour line user expectations. breaks names, used labels: Names can also used labels probs chop_quantiles() proportions chop_proportions(). new raw parameter chop(). replaces parameter raw lbl_* functions, now soft-deprecated. lbl_manual() deprecated. Just use vector argument labels instead. labels argument chop_quantiles() now needs explicitly named. expect last important breaking changes release version 1.0 mark package “stable”. cause problems , please file issue.","code":"chop(1:2, 1:2) ## [1] [1, 2) {2} ## Levels: [1, 2) {2} chop(1:2, 1:2) ## [1] [1, 2] [1, 2] ## Levels: [1, 2] chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3] [2, 3] (3, 4] ## Levels: [1, 2) [2, 3] (3, 4] chop(1:4, 2:3, close_end = TRUE) ## [1] [1, 2) [2, 3) [3, 4] [3, 4] ## Levels: [1, 2) [2, 3) [3, 4] chop(1:5, c(Low = 1, Mid = 2, High = 4)) ## [1] Low Mid Mid High High ## Levels: Low Mid High"},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"other-changes-0-9-0","dir":"Changelog","previous_headings":"","what":"Other changes","title":"santoku 0.9.0","text":"New chop_fn(), brk_fn() tab_fn() chop using arbitrary function. Added section non-standard objects vignette.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-080","dir":"Changelog","previous_headings":"","what":"santoku 0.8.0","title":"santoku 0.8.0","text":"CRAN release: 2022-06-08","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"breaking-changes-0-8-0","dir":"Changelog","previous_headings":"","what":"Breaking changes","title":"santoku 0.8.0","text":"lbl_endpoint() renamed lbl_endpoints(). old version trigger deprecation warning. lbl_endpoints() gains first, last single arguments like labelling functions.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"other-changes-0-8-0","dir":"Changelog","previous_headings":"","what":"Other changes","title":"santoku 0.8.0","text":"New chop_pretty(), brk_pretty() tab_pretty() functions use base::pretty() calculate attractive breakpoints. Thanks @davidhodge931. New chop_proportions(), brk_proportions() tab_proportions() functions chop x proportions range. chop_equally() now uses lbl_intervals(raw = TRUE) default, bringing line chop_evenly(), chop_width() chop_n(). New lbl_midpoints() function labels breaks midpoints. lbl_discrete() gains single argument. can now chop ts, xts::xts zoo::zoo objects. Date objects POSIXct breaks, vice versa bit64::integer64 doubles Bugfix: lbl_discrete() sometimes ugly label formatting.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-070","dir":"Changelog","previous_headings":"","what":"santoku 0.7.0","title":"santoku 0.7.0","text":"CRAN release: 2022-03-18","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"breaking-changes-0-7-0","dir":"Changelog","previous_headings":"","what":"Breaking changes","title":"santoku 0.7.0","text":"labelling functions, first last arguments now passed glue::glue(). Variables l r represent left right endpoints intervals. chop_mean_sd() now takes vector sds standard deviations, rather single maximum number sd standard deviations. Write e.g.  chop_mean_sd(sds = 1:3) rather chop_mean_sd(sd = 3). sd argument deprecated. groups argument chop_evenly(), deprecated 0.4.0, removed. brk_left() brk_right(), deprecated 0.4.0, removed. knife(), deprecated 0.4.0, removed. lbl_format(), questioning since 0.4.0, removed. Arguments lbl_dash() lbl_intervals() reordered consistency labelling functions.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"other-changes-0-7-0","dir":"Changelog","previous_headings":"","what":"Other changes","title":"santoku 0.7.0","text":"Character vectors chopped lexicographic order, optional warning. problems chopping vector type, file bug report. glue package become hard dependency. used many places format labels. new lbl_glue() function using glue package. Thanks @dpprdan. can now set labels = NULL return integer codes. Arguments first, last single can used lbl_intervals() lbl_dash(), override first last interval labels, label singleton intervals. lbl_dash() lbl_discrete() use unicode em-dash possible. brk_default() throws error breaks sorted.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"bugfixes-0-7-0","dir":"Changelog","previous_headings":"","what":"Bugfixes","title":"santoku 0.7.0","text":"Bugfix: tab() friends longer display x variable name. Bugfix: lbl_endpoint() erroring types breaks.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-060","dir":"Changelog","previous_headings":"","what":"santoku 0.6.0","title":"santoku 0.6.0","text":"CRAN release: 2021-11-04 New arguments first last lbl_dash() lbl_discrete() allow override first last interval labels. Fixes CRAN.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-050","dir":"Changelog","previous_headings":"","what":"santoku 0.5.0","title":"santoku 0.5.0","text":"CRAN release: 2020-08-27 sets left = FALSE default. Also works negative time intervals.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-041","dir":"Changelog","previous_headings":"","what":"santoku 0.4.1","title":"santoku 0.4.1","text":"CRAN release: 2020-06-16 Bugfix: chop(1:4, 1) erroring.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-040","dir":"Changelog","previous_headings":"","what":"santoku 0.4.0","title":"santoku 0.4.0","text":"CRAN release: 2020-06-09","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"interface-changes-0-4-0","dir":"Changelog","previous_headings":"","what":"Interface changes","title":"santoku 0.4.0","text":"new version interface changes. based user experience, designed make using chop() intuitive predictable. chop() two new arguments, left close_end. Using left = FALSE simpler intuitive wrapping breaks brk_right(). brk_left() brk_right() kept now, used wrap break functions. Using close_end simpler passing close_end brk_left() brk_right() (longer accept argument directly). left = TRUE default, except non-numeric objects chop_quantiles() chop_equally(), left = FALSE works better. close_end now FALSE default. prevents user surprises e.g. chop(3, 1:3) puts 3 different category chop(3, 1:4). close_end TRUE default chop_quantiles(), chop_n() similar functions. ensures e.g.  chop_quantiles(x, c(0, 1/3, 2/3, 1)) expect. groups argument chop_evenly() renamed groups intervals. make easier remember difference chop_evenly() chop_equally(). (Chop evenly n equal-width intervals, chop equally n equal-sized groups.) knife() deprecated keep interface slim focused. Use purrr::partial() instead.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"other-changes-0-4-0","dir":"Changelog","previous_headings":"","what":"Other changes","title":"santoku 0.4.0","text":"Date datetime (POSIXct) objects can now chopped. chop_width() accepts difftime, lubridate::period lubridate::duration objects chop_ functions work well. Many labelling functions new fmt argument. can string interpreted sprintf() format(), 1-argument formatting function break endpoints, e.g. scales::label_percent(). Experimental: lbl_discrete() discrete data integers () dates. new lbl_endpoint() function labelling intervals solely left right endpoint. brk_mean_sd() now accepts non-integer positive numbers. Add brk_equally() symmetry chop_equally(). Minor tweaks chop_deciles(). Bugfix: lbl_format() wasn’t accepting numeric formats, even raw = TRUE. Thanks Sharla Gelfand.","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-030","dir":"Changelog","previous_headings":"","what":"santoku 0.3.0","title":"santoku 0.3.0","text":"CRAN release: 2020-01-24 First CRAN release. Changed kut() kiru(). kiru() alternative spelling chop(), use tidyr package loaded. lbl_sequence() become lbl_manual(). lbl_letters() friends replaced lbl_seq(): replace lbl_letters() use lbl_seq() replace lbl_LETTERS() use lbl_seq(\"\") replace lbl_roman() use lbl_seq(\"\") replace lbl_ROMAN() use lbl_seq(\"\") replace lbl_numerals() use lbl_seq(\"1\") complex formatting use e.g. lbl_seq(\":\"), lbl_seq(\"()\")","code":""},{"path":"https://hughjonesd.github.io/santoku/news/index.html","id":"santoku-020","dir":"Changelog","previous_headings":"","what":"santoku 0.2.0","title":"santoku 0.2.0","text":"Added NEWS.md file track changes package. Default labels extend = NULL changed, [-Inf, ... ..., Inf] [min(x), ... ..., max(x)].","code":""}] ================================================ FILE: docs/sitemap.xml ================================================ https://hughjonesd.github.io/santoku/404.html https://hughjonesd.github.io/santoku/AGENTS.html https://hughjonesd.github.io/santoku/CLAUDE.html https://hughjonesd.github.io/santoku/LICENSE-text.html https://hughjonesd.github.io/santoku/LICENSE.html https://hughjonesd.github.io/santoku/TODO.html https://hughjonesd.github.io/santoku/articles/index.html https://hughjonesd.github.io/santoku/articles/santoku.html https://hughjonesd.github.io/santoku/articles/website-articles/performance.html https://hughjonesd.github.io/santoku/articles/whats-new-in-0-9-0.html https://hughjonesd.github.io/santoku/authors.html https://hughjonesd.github.io/santoku/index.html https://hughjonesd.github.io/santoku/news/index.html https://hughjonesd.github.io/santoku/reference/breaks-class.html https://hughjonesd.github.io/santoku/reference/brk-left-right.html https://hughjonesd.github.io/santoku/reference/brk_default.html https://hughjonesd.github.io/santoku/reference/brk_manual.html https://hughjonesd.github.io/santoku/reference/brk_width-for-datetime.html https://hughjonesd.github.io/santoku/reference/chop.html https://hughjonesd.github.io/santoku/reference/chop_equally.html https://hughjonesd.github.io/santoku/reference/chop_evenly.html https://hughjonesd.github.io/santoku/reference/chop_fn.html https://hughjonesd.github.io/santoku/reference/chop_mean_sd.html https://hughjonesd.github.io/santoku/reference/chop_n.html https://hughjonesd.github.io/santoku/reference/chop_pretty.html https://hughjonesd.github.io/santoku/reference/chop_proportions.html https://hughjonesd.github.io/santoku/reference/chop_quantiles.html https://hughjonesd.github.io/santoku/reference/chop_spikes.html https://hughjonesd.github.io/santoku/reference/chop_width.html https://hughjonesd.github.io/santoku/reference/dissect.html https://hughjonesd.github.io/santoku/reference/exactly.html https://hughjonesd.github.io/santoku/reference/fillet.html https://hughjonesd.github.io/santoku/reference/index.html https://hughjonesd.github.io/santoku/reference/knife.html https://hughjonesd.github.io/santoku/reference/lbl_dash.html https://hughjonesd.github.io/santoku/reference/lbl_datetime.html https://hughjonesd.github.io/santoku/reference/lbl_discrete.html https://hughjonesd.github.io/santoku/reference/lbl_endpoints.html https://hughjonesd.github.io/santoku/reference/lbl_format.html https://hughjonesd.github.io/santoku/reference/lbl_glue.html https://hughjonesd.github.io/santoku/reference/lbl_intervals.html https://hughjonesd.github.io/santoku/reference/lbl_manual.html https://hughjonesd.github.io/santoku/reference/lbl_midpoints.html https://hughjonesd.github.io/santoku/reference/lbl_seq.html https://hughjonesd.github.io/santoku/reference/non-standard-types.html https://hughjonesd.github.io/santoku/reference/percent.html https://hughjonesd.github.io/santoku/reference/santoku-cast.html https://hughjonesd.github.io/santoku/reference/santoku-package.html https://hughjonesd.github.io/santoku/reference/sequence-labels.html https://hughjonesd.github.io/santoku/tutorials/00-visualintroduction.html https://hughjonesd.github.io/santoku/tutorials/01-chopping-dates.html https://hughjonesd.github.io/santoku/tutorials/index.html ================================================ FILE: docs/tutorials/00-visualintroduction.html ================================================ A visual introduction to santoku • santoku Skip to contents
    ================================================ FILE: docs/tutorials/00-visualintroduction.md ================================================ # A visual introduction to santoku ================================================ FILE: docs/tutorials/01-chopping-dates.html ================================================ Chopping dates with santoku • santoku Skip to contents
    ================================================ FILE: docs/tutorials/01-chopping-dates.md ================================================ # Chopping dates with santoku ================================================ FILE: docs/tutorials/index.html ================================================ Tutorials • santoku Skip to contents
    ================================================ FILE: docs/tutorials/index.md ================================================ # Tutorials - [A visual introduction to santoku](https://hughjonesd.github.io/santoku/tutorials/00-visualintroduction.md) - [Chopping dates with santoku](https://hughjonesd.github.io/santoku/tutorials/01-chopping-dates.md) ================================================ FILE: man/breaks-class.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/breaks.R \name{breaks-class} \alias{breaks-class} \alias{format.breaks} \alias{print.breaks} \alias{is.breaks} \title{Class representing a set of intervals} \usage{ \method{format}{breaks}(x, ...) \method{print}{breaks}(x, ...) is.breaks(x, ...) } \arguments{ \item{x}{A breaks object} \item{...}{Unused} } \description{ Class representing a set of intervals } ================================================ FILE: man/brk_default.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/breaks.R \name{brk_default} \alias{brk_default} \title{Create a standard set of breaks} \usage{ brk_default(breaks) } \arguments{ \item{breaks}{A numeric vector.} } \value{ A function which returns an object of class \code{breaks}. } \description{ Create a standard set of breaks } \examples{ chop(1:10, c(2, 5, 8)) chop(1:10, brk_default(c(2, 5, 8))) } ================================================ FILE: man/brk_manual.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/breaks-misc.R \name{brk_manual} \alias{brk_manual} \title{Create a \code{breaks} object manually} \usage{ brk_manual(breaks, left_vec) } \arguments{ \item{breaks}{A vector, which must be sorted.} \item{left_vec}{A logical vector, the same length as \code{breaks}. Specifies whether each break is left-closed or right-closed.} } \value{ A function which returns an object of class \code{breaks}. } \description{ Create a \code{breaks} object manually } \details{ All breaks must be closed on exactly one side, like \verb{..., x) [x, ...} (left-closed) or \verb{..., x) [x, ...} (right-closed). For example, if \code{breaks = 1:3} and \code{left = c(TRUE, FALSE, TRUE)}, then the resulting intervals are \preformatted{ T F T [ 1, 2 ] ( 2, 3 ) } Singleton breaks are created by repeating a number in \code{breaks}. Singletons must be closed on both sides, so if there is a repeated number at indices \code{i}, \code{i+1}, \code{left[i]} \emph{must} be \code{TRUE} and \code{left[i+1]} must be \code{FALSE}. \code{brk_manual()} ignores \code{left} and \code{close_end} arguments passed in from \code{\link[=chop]{chop()}}, since \code{left_vec} sets these manually. \code{extend} and \code{drop} arguments are respected as usual. } \examples{ lbrks <- brk_manual(1:3, rep(TRUE, 3)) chop(1:3, lbrks, extend = FALSE) rbrks <- brk_manual(1:3, rep(FALSE, 3)) chop(1:3, rbrks, extend = FALSE) brks_singleton <- brk_manual( c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE)) chop(1:3, brks_singleton, extend = FALSE) } ================================================ FILE: man/brk_width-for-datetime.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/breaks-by-width.R \name{brk_width-for-datetime} \alias{brk_width-for-datetime} \alias{brk_width.Duration} \title{Equal-width intervals for dates or datetimes} \usage{ \method{brk_width}{Duration}(width, start) } \arguments{ \item{width}{A scalar \link{difftime}, \link[lubridate:Period-class]{Period} or \link[lubridate:Duration-class]{Duration} object.} \item{start}{A scalar of class \link[base:Dates]{Date} or \link[=DateTimeClasses]{POSIXct}. Can be omitted.} } \description{ \code{brk_width()} can be used with time interval classes from base R or the \code{lubridate} package. } \details{ If \code{width} is a Period, \code{\link[lubridate:mplus]{lubridate::add_with_rollback()}} is used to calculate the widths. This can be useful for e.g. calendar months. } \examples{ if (requireNamespace("lubridate")) { year2001 <- as.Date("2001-01-01") + 0:364 tab_width(year2001, months(1), labels = lbl_discrete(" to ", fmt = "\%e \%b \%y")) } } ================================================ FILE: man/chop.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop.R, R/tab.R \name{chop} \alias{chop} \alias{kiru} \alias{tab} \title{Cut data into intervals} \usage{ chop( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) kiru( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) tab( x, breaks, labels = lbl_intervals(), extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) } \arguments{ \item{x}{A vector.} \item{breaks}{A numeric vector of cut-points, or a function to create cut-points from \code{x}.} \item{labels}{A character vector of labels or a function to create labels.} \item{extend}{Logical. If \code{TRUE}, always extend breaks to \verb{+/-Inf}. If \code{NULL}, extend breaks to \code{min(x)} and/or \code{max(x)} only if necessary. If \code{FALSE}, never extend.} \item{left}{Logical. Left-closed or right-closed breaks?} \item{close_end}{Logical. Close last break at right? (If \code{left} is \code{FALSE}, close first break at left?)} \item{raw}{Logical. Use raw values in labels?} \item{drop}{Logical. Drop unused levels from the result?} } \value{ \code{chop()} returns a \code{\link[base:factor]{factor}} of the same length as \code{x}, representing the intervals containing the value of \code{x}. \code{tab()} returns a contingency \code{\link[base:table]{table}}. } \description{ \code{chop()} cuts \code{x} into intervals. It returns a \code{\link[base:factor]{factor}} of the same length as \code{x}, representing which interval contains each element of \code{x}. \code{kiru()} is an alias for \code{chop}. \code{tab()} calls \code{chop()} and returns a contingency \code{\link[base:table]{table}} from the result. } \details{ \code{x} may be a numeric vector, or more generally, any vector which can be compared with \code{<} and \code{==} (see \link[=groupGeneric]{Ops}). In particular \link[base:Dates]{Date} and \link[=DateTimeClasses]{date-time} objects are supported. Character vectors are supported with a warning. \subsection{Breaks}{ \code{breaks} may be a vector or a function. If it is a vector, \code{breaks} gives the interval endpoints. Repeating a value creates a "singleton" interval, which contains only that value. For example \code{breaks = c(1, 3, 3, 5)} creates 3 intervals: \code{[1, 3)}, \code{{3}} and \code{(3, 5]}. If \code{breaks} is a function, it is called with the \code{x}, \code{extend}, \code{left} and \code{close_end} arguments, and should return an object of class \code{breaks}. Use \verb{brk_*} functions to create a variety of data-dependent breaks. Names of \code{breaks} may be used for labels. See "Labels" below. } \subsection{Options for breaks}{ By default, left-closed intervals are created. If \code{left} is \code{FALSE}, right-closed intervals are created. If \code{close_end} is \code{TRUE} the final break (or first break if \code{left} is \code{FALSE}) will be closed at both ends. This guarantees that all values \code{x} with \verb{min(breaks) <= x <= max(breaks)} are included in the intervals. Before version 0.9.0, \code{close_end} was \code{FALSE} by default, and also behaved differently with respect to extended breaks: see "Extending intervals" below. Using \link[=lbl_intervals]{mathematical set notation}: \itemize{ \item If \code{left} is \code{TRUE} and \code{close_end} is \code{TRUE}, breaks will look like \code{[b1, b2), [b2, b3) ... [b_(n-1), b_n]}. \item If \code{left} is \code{FALSE} and \code{close_end} is \code{TRUE}, breaks will look like \code{[b1, b2], (b2, b3] ... (b_(n-1), b_n]}. \item If \code{left} is \code{TRUE} and \code{close_end} is \code{FALSE}, all breaks will look like \code{... [b1, b2) ...}. \item If \code{left} is \code{FALSE} and \code{close_end} is \code{FALSE}, all breaks will look like \code{... (b1, b2] ...}. } } \subsection{Extending intervals}{ If \code{extend} is \code{TRUE}, intervals will be extended to \code{[-Inf, min(breaks))} and \code{(max(breaks), Inf]}. If \code{extend} is \code{NULL} (the default), intervals will be extended to \code{[min(x), min(breaks))} and \code{(max(breaks), max(x)]}, only if necessary, i.e. only if elements of \code{x} would be outside the unextended breaks. If \code{extend} is \code{FALSE}, intervals are never extended. Note that even when \code{extend = TRUE}, extended intervals will be dropped from the factor levels if they contain no elements and \code{drop = TRUE}. \code{close_end} is only relevant if intervals are not extended; extended intervals are always closed on the outside. This is a change from previous behaviour. Up to version 0.8.0, \code{close_end} was applied to the last user-specified interval, before any extended intervals were created. Since 1.1.0, infinity is represented as \eqn{\infty}{the infinity symbol} in breaks on unicode platforms. Set \code{options(santoku.infinity = "Inf")} to get the old behaviour. } \subsection{Labels}{ \code{labels} may be a character vector. It should have the same length as the (possibly extended) number of intervals. Alternatively, \code{labels} may be a \verb{lbl_*} function such as \code{\link[=lbl_dash]{lbl_dash()}}. If \code{breaks} is a named vector, then names of \code{breaks} will be used as labels for the interval starting at the corresponding element. This overrides the \code{labels} argument (but unnamed breaks will still use \code{labels}). This feature is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}. If \code{labels} is \code{NULL}, then integer codes will be returned instead of a factor. If \code{raw} is \code{TRUE}, labels will show the actual interval endpoints, usually numbers. If \code{raw} is \code{FALSE} then labels may show other objects, such as quantiles for \code{\link[=chop_quantiles]{chop_quantiles()}} and friends, proportions of the range for \code{\link[=chop_proportions]{chop_proportions()}}, or standard deviations for \code{\link[=chop_mean_sd]{chop_mean_sd()}}. If \code{raw} is \code{NULL} then \verb{lbl_*} functions will use their default (usually \code{FALSE}). Otherwise, the \code{raw} argument to \code{chop()} overrides \code{raw} arguments passed into \verb{lbl_*} functions directly. } \subsection{Miscellaneous}{ \code{NA} values in \code{x}, and values which are outside the extended endpoints, return \code{NA}. \code{kiru()} is a synonym for \code{chop()}. If you load \code{{tidyr}}, you can use it to avoid confusion with \code{tidyr::chop()}. Note that \code{chop()}, like all of R, uses binary arithmetic. Thus, numbers may not be exactly equal to what you think they should be. There is an example below. } } \examples{ chop(1:7, c(2, 4, 6)) chop(1:7, c(2, 4, 6), extend = FALSE) # Repeat a number for a singleton break: chop(1:7, c(2, 4, 4, 6)) chop(1:7, c(2, 4, 6), left = FALSE) chop(1:7, c(2, 4, 6), close_end = FALSE) chop(1:7, brk_quantiles(c(0.25, 0.75))) # A single break is fine if `extend` is not `FALSE`: chop(1:7, 4) # Floating point inaccuracy: chop(0.3/3, c(0, 0.1, 0.1, 1), labels = c("< 0.1", "0.1", "> 0.1")) # -- Labels -- chop(1:7, c(Lowest = 1, Low = 2, Mid = 4, High = 6)) chop(1:7, c(2, 4, 6), labels = c("Lowest", "Low", "Mid", "High")) chop(1:7, c(2, 4, 6), labels = lbl_dash()) # Mixing names and other labels: chop(1:7, c("<2" = 1, 2, 4, ">=6" = 6), labels = lbl_dash()) # -- Non-standard types -- chop(as.Date("2001-01-01") + 1:7, as.Date("2001-01-04")) suppressWarnings(chop(LETTERS[1:7], "D")) tab(1:10, c(2, 5, 8)) } \seealso{ \code{\link[base:cut]{base::cut()}}, \code{\link{non-standard-types}} for chopping objects that aren't numbers. Other chopping functions: \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_equally.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-group-size.R, R/breaks-by-group-size.R, % R/tab.R \name{chop_equally} \alias{chop_equally} \alias{brk_equally} \alias{tab_equally} \title{Chop equal-sized groups} \usage{ chop_equally( x, groups, ..., labels = lbl_intervals(), left = is.numeric(x), raw = TRUE ) brk_equally(groups) tab_equally(x, groups, ..., left = is.numeric(x), raw = TRUE) } \arguments{ \item{x}{A vector.} \item{groups}{Number of groups.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{labels}{A character vector of labels or a function to create labels.} \item{left}{Logical. Left-closed or right-closed breaks?} \item{raw}{Logical. Use raw values in labels?} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_equally()} chops \code{x} into groups with an equal number of elements. } \details{ \code{chop_equally()} uses \code{\link[=brk_quantiles]{brk_quantiles()}} under the hood. If \code{x} has duplicate elements, you may get fewer \code{groups} than requested. If so, a warning will be emitted. See the examples. } \examples{ chop_equally(1:10, 5) # You can't always guarantee equal-sized groups: dupes <- c(1, 1, 1, 2, 3, 4, 4, 4) quantile(dupes, 0:4/4) chop_equally(dupes, 4) # Or as many groups as you ask for: chop_equally(c(1, 1, 2, 2), 3) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_evenly.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-width.R, R/breaks-by-width.R, R/tab.R \name{chop_evenly} \alias{chop_evenly} \alias{brk_evenly} \alias{tab_evenly} \title{Chop into equal-width intervals} \usage{ chop_evenly(x, intervals, ...) brk_evenly(intervals) tab_evenly(x, intervals, ...) } \arguments{ \item{x}{A vector.} \item{intervals}{Integer: number of intervals to create.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_evenly()} chops \code{x} into \code{intervals} intervals of equal width. } \examples{ chop_evenly(0:10, 5) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_fn.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-misc.R, R/breaks-misc.R, R/tab.R \name{chop_fn} \alias{chop_fn} \alias{brk_fn} \alias{tab_fn} \title{Chop using an existing function} \usage{ chop_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) brk_fn(fn, ...) tab_fn( x, fn, ..., extend = NULL, left = TRUE, close_end = TRUE, raw = NULL, drop = TRUE ) } \arguments{ \item{x}{A vector.} \item{fn}{A function which returns a numeric vector of breaks.} \item{...}{Further arguments to \code{fn}} \item{extend}{Logical. If \code{TRUE}, always extend breaks to \verb{+/-Inf}. If \code{NULL}, extend breaks to \code{min(x)} and/or \code{max(x)} only if necessary. If \code{FALSE}, never extend.} \item{left}{Logical. Left-closed or right-closed breaks?} \item{close_end}{Logical. Close last break at right? (If \code{left} is \code{FALSE}, close first break at left?)} \item{raw}{Logical. Use raw values in labels?} \item{drop}{Logical. Drop unused levels from the result?} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_fn()} is a convenience wrapper: \code{chop_fn(x, foo, ...)} is the same as \code{chop(x, foo(x, ...))}. } \examples{ if (requireNamespace("scales")) { chop_fn(rlnorm(10), scales::breaks_log(5)) # same as # x <- rlnorm(10) # chop(x, scales::breaks_log(5)(x)) } } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_mean_sd.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-misc.R, R/breaks-misc.R, R/tab.R \name{chop_mean_sd} \alias{chop_mean_sd} \alias{brk_mean_sd} \alias{tab_mean_sd} \title{Chop by standard deviations} \usage{ chop_mean_sd(x, sds = 1:3, ..., raw = FALSE, sd = deprecated()) brk_mean_sd(sds = 1:3, sd = deprecated()) tab_mean_sd(x, sds = 1:3, ..., raw = FALSE) } \arguments{ \item{x}{A vector.} \item{sds}{Positive numeric vector of standard deviations.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{raw}{Logical. Use raw values in labels?} \item{sd}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ Intervals are measured in standard deviations on either side of the mean. } \details{ In version 0.7.0, these functions changed to specifying \code{sds} as a vector. To chop 1, 2 and 3 standard deviations around the mean, write \code{chop_mean_sd(x, sds = 1:3)} instead of \code{chop_mean_sd(x, sd = 3)}. } \examples{ chop_mean_sd(1:10) chop(1:10, brk_mean_sd()) tab_mean_sd(1:10) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_n.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-group-size.R, R/breaks-by-group-size.R, % R/tab.R \name{chop_n} \alias{chop_n} \alias{brk_n} \alias{tab_n} \title{Chop into fixed-sized groups} \usage{ chop_n(x, n, ..., tail = "split") brk_n(n, tail = "split") tab_n(x, n, ..., tail = "split") } \arguments{ \item{x}{A vector.} \item{n}{Integer. Number of elements in each interval.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{tail}{String. What to do if the final interval has fewer than \code{n} elements? \code{"split"} to keep it separate. \code{"merge"} to merge it with the neighbouring interval.} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_n()} creates intervals containing a fixed number of elements. } \details{ The algorithm guarantees that intervals contain no more than \code{n} elements, so long as there are no duplicates in \code{x} and \code{tail = "split"}. It also guarantees that intervals contain no fewer than \code{n} elements, except possibly the last interval (or first interval if \code{left} is \code{FALSE}). To ensure that all intervals contain at least \code{n} elements (so long as there are at least \code{n} elements in \code{x}!) set \code{tail = "merge"}. If \code{tail = "split"} and there are intervals containing duplicates with more than \code{n} elements, a warning is given. } \examples{ chop_n(1:10, 5) chop_n(1:5, 2) chop_n(1:5, 2, tail = "merge") # too many duplicates x <- rep(1:2, each = 3) chop_n(x, 2) tab_n(1:10, 5) # fewer elements in one group tab_n(1:10, 4) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_pretty.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-misc.R, R/breaks-misc.R, R/tab.R \name{chop_pretty} \alias{chop_pretty} \alias{brk_pretty} \alias{tab_pretty} \title{Chop using pretty breakpoints} \usage{ chop_pretty(x, n = 5, ...) brk_pretty(n = 5, ...) tab_pretty(x, n = 5, ...) } \arguments{ \item{x}{A vector.} \item{n}{Positive integer passed to \code{\link[base:pretty]{base::pretty()}}. How many intervals to chop into?} \item{...}{Passed to \code{\link[=chop]{chop()}} by \code{chop_pretty()} and \code{tab_pretty()}; passed to \code{\link[base:pretty]{base::pretty()}} by \code{brk_pretty()}.} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_pretty()} uses \code{\link[base:pretty]{base::pretty()}} to calculate breakpoints which are 1, 2 or 5 times a power of 10. These look nice in graphs. } \details{ \code{\link[base:pretty]{base::pretty()}} tries to return \code{n+1} breakpoints, i.e. \code{n} intervals, but note that this is not guaranteed. There are methods for Date and POSIXct objects. For fine-grained control over \code{\link[base:pretty]{base::pretty()}} parameters, use \code{chop(x, brk_pretty(...))}. } \examples{ chop_pretty(1:10) chop(1:10, brk_pretty(n = 5, high.u.bias = 0)) tab_pretty(1:10) } ================================================ FILE: man/chop_proportions.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-width.R, R/breaks-by-width.R, R/tab.R \name{chop_proportions} \alias{chop_proportions} \alias{brk_proportions} \alias{tab_proportions} \title{Chop into proportions of the range of x} \usage{ chop_proportions(x, proportions, ..., raw = TRUE) brk_proportions(proportions) tab_proportions(x, proportions, ..., raw = TRUE) } \arguments{ \item{x}{A vector.} \item{proportions}{Numeric vector between 0 and 1: proportions of x's range. If \code{proportions} has names, these will be used for labels.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{raw}{Logical. Use raw values in labels?} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_proportions()} chops \code{x} into \code{proportions} of its range, excluding infinite values. } \details{ By default, labels show the raw numeric endpoints. To label intervals by the proportions, use \code{raw = FALSE}. } \examples{ chop_proportions(0:10, c(0.2, 0.8)) chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8)) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_quantiles.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-group-size.R, R/breaks-by-group-size.R, % R/tab.R \name{chop_quantiles} \alias{chop_quantiles} \alias{chop_deciles} \alias{brk_quantiles} \alias{tab_quantiles} \alias{tab_deciles} \title{Chop by quantiles} \usage{ chop_quantiles( x, probs, ..., labels = if (raw) lbl_intervals() else lbl_intervals(single = NULL), left = is.numeric(x), raw = FALSE, weights = NULL, recalc_probs = FALSE ) chop_deciles(x, ...) brk_quantiles(probs, ..., weights = NULL, recalc_probs = FALSE) tab_quantiles(x, probs, ..., left = is.numeric(x), raw = FALSE) tab_deciles(x, ...) } \arguments{ \item{x}{A vector.} \item{probs}{A vector of probabilities for the quantiles. If \code{probs} has names, these will be used for labels.} \item{...}{For \code{chop_quantiles}, passed to \code{\link[=chop]{chop()}}. For \code{brk_quantiles()}, passed to \code{\link[stats:quantile]{stats::quantile()}} or \code{\link[Hmisc:wtd.stats]{Hmisc::wtd.quantile()}}.} \item{labels}{A character vector of labels or a function to create labels.} \item{left}{Logical. Left-closed or right-closed breaks?} \item{raw}{Logical. Use raw values in labels?} \item{weights}{\code{NULL} or numeric vector of same length as \code{x}. If not \code{NULL}, \code{\link[Hmisc:wtd.stats]{Hmisc::wtd.quantile()}} is used to calculate weighted quantiles.} \item{recalc_probs}{Logical. Recalculate probabilities of quantiles using \code{\link[stats:ecdf]{ecdf(x)}}? See below.} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_quantiles()} chops data by quantiles. \code{chop_deciles()} is a convenience function which chops into deciles. } \details{ For non-numeric \code{x}, \code{left} is set to \code{FALSE} by default. This works better for calculating "type 1" quantiles, since they round down. See \code{\link[stats:quantile]{stats::quantile()}}. By default, \code{chop_quantiles()} shows the requested probabilities in the labels. To show the numeric quantiles themselves, set \code{raw = TRUE}. When \code{x} contains duplicates, consecutive quantiles may be the same number. If so, interval labels may be misleading, and if \code{recalc_probs = FALSE} a warning is emitted. Set \code{recalc_probs = TRUE} to recalculate the probabilities of the quantiles using the \link[stats:ecdf]{empirical cumulative distribution function} of \code{x}. Doing so may give you different labels from what you expect, and will remove any names from \code{probs}, but it never changes the actual quantiles used for breaks. At present, \code{recalc_probs = TRUE} is incompatible with non-null \code{weights}. See the example below. } \examples{ chop_quantiles(1:10, 1:3/4) chop_quantiles(1:10, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75)) chop(1:10, brk_quantiles(1:3/4)) chop_deciles(1:10) # to label by the quantiles themselves: chop_quantiles(1:10, 1:3/4, raw = TRUE) # duplicate quantiles: x <- c(1, 1, 1, 2, 3) quantile(x, 1:5/5) tab_quantiles(x, 1:5/5) tab_quantiles(x, 1:5/5, recalc_probs = TRUE) set.seed(42) tab_quantiles(rnorm(100), probs = 1:3/4, raw = TRUE) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_spikes.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-isolates.R, R/breaks.R, R/tab.R \name{chop_spikes} \alias{chop_spikes} \alias{brk_spikes} \alias{tab_spikes} \title{Chop common values into singleton intervals} \usage{ chop_spikes(x, breaks, ..., n = NULL, prop = NULL) brk_spikes(breaks, n = NULL, prop = NULL) tab_spikes(x, breaks, ..., n = NULL, prop = NULL) } \arguments{ \item{x}{A vector.} \item{breaks}{A numeric vector of cut-points or a call to a \verb{brk_*} function. The resulting \code{\link[=breaks-class]{breaks}} object will be modified to add singleton breaks.} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{n, prop}{Scalar. Provide either \code{n}, a number of values, or \code{prop}, a proportion of \code{length(x)}. Values of \code{x} which occur at least this often will get their own singleton break.} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_spikes()} lets you chop common values of \code{x} into their own singleton intervals. This can help make unusual values visible. } \details{ This function is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}. } \examples{ x <- c(1:4, rep(5, 5), 6:10) chop_spikes(x, c(2, 7), n = 5) chop_spikes(x, c(2, 7), prop = 0.25) chop_spikes(x, brk_width(5), n = 5) set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) tab_spikes(x, brk_width(2, 0), prop = 0.05) } \seealso{ \code{\link[=dissect]{dissect()}} for a different approach. Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_width}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/chop_width.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-by-width.R, R/breaks-by-width.R, R/tab.R \name{chop_width} \alias{chop_width} \alias{brk_width} \alias{brk_width.default} \alias{tab_width} \title{Chop into fixed-width intervals} \usage{ chop_width(x, width, start, ..., left = sign(width) > 0) brk_width(width, start) \method{brk_width}{default}(width, start) tab_width(x, width, start, ..., left = sign(width) > 0) } \arguments{ \item{x}{A vector.} \item{width}{Width of intervals.} \item{start}{Starting point for intervals. By default the smallest finite \code{x} (largest if \code{width} is negative).} \item{...}{Passed to \code{\link[=chop]{chop()}}.} \item{left}{Logical. Left-closed or right-closed breaks?} } \value{ \verb{chop_*} functions return a \code{\link[base:factor]{factor}} of the same length as \code{x}. \verb{brk_*} functions return a \code{\link{function}} to create \code{breaks}. \verb{tab_*} functions return a contingency \code{\link[base:table]{table}}. } \description{ \code{chop_width()} chops \code{x} into intervals of fixed \code{width}. } \details{ If \code{width} is negative, \code{chop_width()} sets \code{left = FALSE} and intervals will go downwards from \code{start}. } \examples{ chop_width(1:10, 2) chop_width(1:10, 2, start = 0) chop_width(1:9, -2) chop(1:10, brk_width(2, 0)) tab_width(1:10, 2, start = 0) } \seealso{ \link{brk_width-for-datetime} Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{fillet}()} } \concept{chopping functions} ================================================ FILE: man/dissect.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop-isolates.R, R/tab.R \name{dissect} \alias{dissect} \alias{tab_dissect} \title{Cut data into intervals, separating out common values} \usage{ dissect( x, breaks, ..., n = NULL, prop = NULL, spike_labels = "{{{l}}}", exclude_spikes = FALSE ) tab_dissect(x, breaks, ..., n = NULL, prop = NULL) } \arguments{ \item{x, breaks, ...}{Passed to \code{\link[=chop]{chop()}}.} \item{n, prop}{Scalar. Provide either \code{n}, a number of values, or \code{prop}, a proportion of \code{length(x)}. Values of \code{x} which occur at least this often will get their own singleton break.} \item{spike_labels}{\link[glue:glue]{Glue} string for spike labels. Use \code{"{l}"} for the spike value.} \item{exclude_spikes}{Logical. Exclude spikes before chopping \code{x}? This can affect the location of data-dependent breaks.} } \value{ \code{dissect()} returns the result of \code{\link[=chop]{chop()}}, but with common values put into separate factor levels. \code{tab_dissect()} returns a contingency \link[base:table]{table()}. } \description{ Sometimes it's useful to separate out common elements of \code{x}. \code{dissect()} chops \code{x}, but puts common elements of \code{x} ("spikes") into separate categories. } \details{ Unlike \code{\link[=chop_spikes]{chop_spikes()}}, \code{dissect()} doesn't break up intervals which contain a spike. As a result, unlike \verb{chop_*} functions, \code{dissect()} does not chop \code{x} into disjoint intervals. See the examples. If breaks are data-dependent, their labels may be misleading after common elements have been removed. See the example below. To get round this, set \code{exclude_spikes} to \code{TRUE}. Then breaks will be calculated after removing spikes from the data. Levels of the result are ordered by the minimum element in each level. As a result, if \code{drop = FALSE}, empty levels will be placed last. This function is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}. } \examples{ x <- c(2, 3, 3, 3, 4) dissect(x, c(2, 4), n = 3) dissect(x, brk_width(2), prop = 0.5) set.seed(42) x <- runif(40, 0, 10) x <- sample(x, 200, replace = TRUE) # Compare: table(dissect(x, brk_width(2, 0), prop = 0.05)) # Versus: tab_spikes(x, brk_width(2, 0), prop = 0.05) # Potentially confusing data-dependent breaks: set.seed(42) x <- rnorm(99) x[1:9] <- x[1] tab_quantiles(x, 1:2/3) tab_dissect(x, brk_quantiles(1:2/3), n = 9) # Calculate quantiles excluding spikes: tab_dissect(x, brk_quantiles(1:2/3), n = 9, exclude_spikes = TRUE) } \seealso{ \code{\link[=chop_spikes]{chop_spikes()}} for a different approach. } ================================================ FILE: man/exactly.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{exactly} \alias{exactly} \title{Define singleton intervals explicitly} \usage{ exactly(x) } \arguments{ \item{x}{A numeric vector.} } \value{ The same as \code{rep(x, each = 2)}. } \description{ \code{exactly()} duplicates its input. It lets you define singleton intervals like this: \code{chop(x, c(1, exactly(2), 3))}. This is the same as \code{chop(x, c(1, 2, 2, 3))} but conveys your intent more clearly. } \examples{ chop(1:10, c(2, exactly(5), 8)) # same: chop(1:10, c(2, 5, 5, 8)) } ================================================ FILE: man/fillet.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/chop.R \name{fillet} \alias{fillet} \title{Chop data precisely (for programmers)} \usage{ fillet( x, breaks, labels = lbl_intervals(), left = TRUE, close_end = TRUE, raw = NULL ) } \arguments{ \item{x}{A vector.} \item{breaks}{A numeric vector of cut-points, or a function to create cut-points from \code{x}.} \item{labels}{A character vector of labels or a function to create labels.} \item{left}{Logical. Left-closed or right-closed breaks?} \item{close_end}{Logical. Close last break at right? (If \code{left} is \code{FALSE}, close first break at left?)} \item{raw}{Logical. Use raw values in labels?} } \value{ \code{fillet()} returns a \code{\link[base:factor]{factor}} of the same length as \code{x}, representing the intervals containing the value of \code{x}. } \description{ \code{fillet()} calls \code{\link[=chop]{chop()}} with \code{extend = FALSE} and \code{drop = FALSE}. This ensures that you get only the \code{breaks} and \code{labels} you ask for. When programming, consider using \code{fillet()} instead of \code{chop()}. } \examples{ fillet(1:10, c(2, 5, 8)) } \seealso{ Other chopping functions: \code{\link{chop}()}, \code{\link{chop_equally}()}, \code{\link{chop_evenly}()}, \code{\link{chop_fn}()}, \code{\link{chop_mean_sd}()}, \code{\link{chop_n}()}, \code{\link{chop_proportions}()}, \code{\link{chop_quantiles}()}, \code{\link{chop_spikes}()}, \code{\link{chop_width}()} } \concept{chopping functions} ================================================ FILE: man/lbl_dash.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels.R \name{lbl_dash} \alias{lbl_dash} \title{Label chopped intervals like 1-4, 4-5, ...} \usage{ lbl_dash( symbol = em_dash(), fmt = NULL, single = "{l}", first = NULL, last = NULL, raw = deprecated() ) } \arguments{ \item{symbol}{String: symbol to use for the dash.} \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{raw}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Throws an error. Use the \code{raw} argument to \code{\link[=chop]{chop()}} instead.} } \value{ A function that creates a vector of labels. } \description{ This label style is user-friendly, but doesn't distinguish between left- and right-closed intervals. It's good for continuous data where you don't expect points to be exactly on the breaks. } \details{ If you don't want unicode output, use \code{lbl_dash("-")}. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ chop(1:10, c(2, 5, 8), lbl_dash()) chop(1:10, c(2, 5, 8), lbl_dash(" to ", fmt = "\%.1f")) chop(1:10, c(2, 5, 8), lbl_dash(first = "<{r}")) pretty <- function (x) prettyNum(x, big.mark = ",", digits = 1) chop(runif(10) * 10000, c(3000, 7000), lbl_dash(" to ", fmt = pretty)) } \seealso{ Other labelling functions: \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_datetime.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-datetime.R \name{lbl_date} \alias{lbl_date} \alias{lbl_datetime} \title{Label dates and datetimes} \usage{ lbl_date( fmt = "\%e \%b \%Y", symbol = "-", unit = as.difftime(1, units = "days"), single = "{l}", first = NULL, last = NULL ) lbl_datetime( fmt = "\%H:\%M:\%S \%b \%e \%Y", symbol = "-", unit = NULL, single = "{l}", first = NULL, last = NULL ) } \arguments{ \item{fmt}{String, list or function. A format for break endpoints.} \item{symbol}{String: separator to use for full ranges.} \item{unit}{Optional interval unit for non-overlapping labels. If not \code{NULL}, . endpoints are adjusted in the style of \code{\link[=lbl_discrete]{lbl_discrete()}}.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} } \value{ A function that creates a vector of labels. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} \code{lbl_date()} and \code{lbl_datetime()} produce nice labels for dates and datetimes. Where possible ranges are simplified, like like "13-14 Jul 2026" or "11:15-12:15 1 Dec 2025". } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ winter <- as.Date("2025-12-01") + 0:89 tab(winter, as.Date(c("2025-12-25", "2026-01-06")), labels = lbl_date()) new_year <- as.POSIXct("2025-12-31 23:00") + 0:120 * 60 round_midnight <- as.POSIXct(c("2025-12-31 23:59", "2026-01-01 00:05")) tab(new_year, round_midnight, labels = lbl_datetime()) tab(new_year, round_midnight, labels = lbl_datetime(unit = as.difftime(1, units = "mins"))) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_discrete.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels.R \name{lbl_discrete} \alias{lbl_discrete} \title{Label discrete data} \usage{ lbl_discrete( symbol = em_dash(), unit = 1L, fmt = NULL, single = NULL, first = NULL, last = NULL ) } \arguments{ \item{symbol}{String: symbol to use for the dash.} \item{unit}{Minimum difference between distinct values of data. For integers, 1.} \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} } \value{ A function that creates a vector of labels. } \description{ \code{lbl_discrete()} creates labels for discrete data, such as integers. For example, breaks \code{c(1, 3, 4, 6, 7)} are labelled: \verb{"1-2", "3", "4-5", "6-7"}. } \details{ No check is done that the data are discrete-valued. If they are not, then these labels may be misleading. Here, discrete-valued means that if \code{x < y}, then \code{x <= y - unit}. Be aware that Date objects may have non-integer values. See \link[base:Dates]{Date}. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ tab(1:7, c(1, 3, 5), lbl_discrete()) tab(1:7, c(3, 5), lbl_discrete(first = "<= {r}")) tab(1:7 * 1000, c(1, 3, 5) * 1000, lbl_discrete(unit = 1000)) # Misleading labels for non-integer data chop(2.5, c(1, 3, 5), lbl_discrete()) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_endpoints.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-single.R \name{lbl_endpoints} \alias{lbl_endpoints} \alias{lbl_endpoint} \title{Label chopped intervals by their left or right endpoints} \usage{ lbl_endpoints( left = TRUE, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) lbl_endpoint(fmt = NULL, raw = FALSE, left = TRUE) } \arguments{ \item{left}{Flag. Use left endpoint or right endpoint?} \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{raw}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Throws an error. Use the \code{raw} argument to \code{\link[=chop]{chop()}} instead.} } \value{ A function that creates a vector of labels. } \description{ This is useful when the left endpoint unambiguously indicates the interval. In other cases it may give errors due to duplicate labels. } \details{ \code{lbl_endpoint()} is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#defunct}{\figure{lifecycle-defunct.svg}{options: alt='[Defunct]'}}}{\strong{[Defunct]}} and gives an error since santoku 1.0.0. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ chop(1:10, c(2, 5, 8), lbl_endpoints(left = TRUE)) chop(1:10, c(2, 5, 8), lbl_endpoints(left = FALSE)) if (requireNamespace("lubridate")) { tab_width( as.Date("2000-01-01") + 0:365, months(1), labels = lbl_endpoints(fmt = "\%b") ) } \dontrun{ # This gives breaks `[1, 2) [2, 3) {3}` which lead to # duplicate labels `"2", "3", "3"`: chop(1:3, 1:3, lbl_endpoints(left = FALSE)) } } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_glue.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-glue.R \name{lbl_glue} \alias{lbl_glue} \title{Label chopped intervals using the \code{glue} package} \usage{ lbl_glue( label, fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated(), ... ) } \arguments{ \item{label}{A glue string passed to \code{\link[glue:glue]{glue::glue()}}.} \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{raw}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Throws an error. Use the \code{raw} argument to \code{\link[=chop]{chop()}} instead.} \item{...}{Further arguments passed to \code{\link[glue:glue]{glue::glue()}}.} } \value{ A function that creates a vector of labels. } \description{ Use \code{"{l}"} and \code{"{r}"} to show the left and right endpoints of the intervals. } \details{ The following variables are available in the glue string: \itemize{ \item \code{l} is a character vector of left endpoints of intervals. \item \code{r} is a character vector of right endpoints of intervals. \item \code{l_closed} is a logical vector. Elements are \code{TRUE} when the left endpoint is closed. \item \code{r_closed} is a logical vector, \code{TRUE} when the right endpoint is closed. } Endpoints will be formatted by \code{fmt} before being passed to \code{glue()}. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ tab(1:10, c(1, 3, 3, 7), labels = lbl_glue("{l} to {r}", single = "Exactly {l}")) tab(1:10 * 1000, c(1, 3, 5, 7) * 1000, labels = lbl_glue("{l}-{r}", fmt = function(x) prettyNum(x, big.mark=','))) # reproducing lbl_intervals(): interval_left <- "{ifelse(l_closed, '[', '(')}" interval_right <- "{ifelse(r_closed, ']', ')')}" glue_string <- paste0(interval_left, "{l}", ", ", "{r}", interval_right) tab(1:10, c(1, 3, 3, 7), labels = lbl_glue(glue_string, single = "{{{l}}}")) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_intervals.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels.R \name{lbl_intervals} \alias{lbl_intervals} \title{Label chopped intervals using set notation} \usage{ lbl_intervals( fmt = NULL, single = "{{{l}}}", first = NULL, last = NULL, raw = deprecated() ) } \arguments{ \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{raw}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Throws an error. Use the \code{raw} argument to \code{\link[=chop]{chop()}} instead.} } \value{ A function that creates a vector of labels. } \description{ These labels are the most exact, since they show you whether intervals are "closed" or "open", i.e. whether they include their endpoints. } \details{ Mathematical set notation looks like this: \itemize{ \item \code{[a, b]}: all numbers \code{x} where \verb{a <= x <= b}; \item \code{(a, b)}: all numbers where \verb{a < x < b}; \item \code{[a, b)}: all numbers where \verb{a <= x < b}; \item \code{(a, b]}: all numbers where \verb{a < x <= b}; \item \code{{a}}: just the number \code{a} exactly. } } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals()) tab(-10:10, c(-3, 0, 0, 3), labels = lbl_intervals(fmt = list(nsmall = 1))) tab_evenly(runif(20), 10, labels = lbl_intervals(fmt = percent)) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_manual.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-single.R \name{lbl_manual} \alias{lbl_manual} \title{Defunct: label chopped intervals in a user-defined sequence} \usage{ lbl_manual(sequence, fmt = "\%s") } \arguments{ \item{sequence}{A character vector of labels.} \item{fmt}{String, list or function. A format for break endpoints.} } \value{ A function that creates a vector of labels. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#defunct}{\figure{lifecycle-defunct.svg}{options: alt='[Defunct]'}}}{\strong{[Defunct]}} } \details{ \code{lbl_manual()} is defunct since santoku 1.0.0. It is little used and is not closely related to the rest of the package. It also risks mislabelling intervals, e.g. if intervals are extended. Use of \code{lbl_manual()} will give an error. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ \dontrun{ chop(1:10, c(2, 5, 8), lbl_manual(c("w", "x", "y", "z"))) # -> chop(1:10, c(2, 5, 8), labels = c("w", "x", "y", "z")) } } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_midpoints}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} \keyword{internal} ================================================ FILE: man/lbl_midpoints.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-single.R \name{lbl_midpoints} \alias{lbl_midpoints} \title{Label chopped intervals by their midpoints} \usage{ lbl_midpoints( fmt = NULL, single = NULL, first = NULL, last = NULL, raw = deprecated() ) } \arguments{ \item{fmt}{String, list or function. A format for break endpoints.} \item{single}{Glue string: label for singleton intervals. See \code{\link[=lbl_glue]{lbl_glue()}} for details. If \code{NULL}, singleton intervals will be labelled the same way as other intervals.} \item{first}{Glue string: override label for the first category. Write e.g. \code{first = "<{r}"} to create a label like \code{"<18"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{last}{String: override label for the last category. Write e.g. \code{last = ">{l}"} to create a label like \code{">65"}. See \code{\link[=lbl_glue]{lbl_glue()}} for details.} \item{raw}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}. Throws an error. Use the \code{raw} argument to \code{\link[=chop]{chop()}} instead.} } \value{ A function that creates a vector of labels. } \description{ This uses the midpoint of each interval for its label. } \section{Formatting endpoints}{ If \code{fmt} is not \code{NULL} then it is used to format the endpoints. \itemize{ \item If \code{fmt} is a string, then numeric endpoints will be formatted by \code{sprintf(fmt, breaks)}; other endpoints, e.g. \link[base:Dates]{Date} objects, will be formatted by \code{format(breaks, fmt)}. \item If \code{fmt} is a list, then it will be used as arguments to \link{format}. \item If \code{fmt} is a function, it should take a vector of numbers (or other objects that can be used as breaks) and return a character vector. It may be helpful to use functions from the \code{{scales}} package, e.g. \code{\link[scales:label_number]{scales::label_comma()}}. } } \examples{ chop(1:10, c(2, 5, 8), lbl_midpoints()) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_seq}()} } \concept{labelling functions} ================================================ FILE: man/lbl_seq.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/labels-single.R \name{lbl_seq} \alias{lbl_seq} \title{Label chopped intervals in sequence} \usage{ lbl_seq(start = "a") } \arguments{ \item{start}{String. A template for the sequence. See below.} } \value{ A function that creates a vector of labels. } \description{ \code{lbl_seq()} labels intervals sequentially, using numbers or letters. } \details{ \code{start} shows the first element of the sequence. It must contain exactly \emph{one} character out of the set "a", "A", "i", "I" or "1". For later elements: \itemize{ \item "a" will be replaced by "a", "b", "c", ... \item "A" will be replaced by "A", "B", "C", ... \item "i" will be replaced by lower-case Roman numerals "i", "ii", "iii", ... \item "I" will be replaced by upper-case Roman numerals "I", "II", "III", ... \item "1" will be replaced by numbers "1", "2", "3", ... } Other characters will be retained as-is. } \examples{ chop(1:10, c(2, 5, 8), lbl_seq()) chop(1:10, c(2, 5, 8), lbl_seq("i.")) chop(1:10, c(2, 5, 8), lbl_seq("(A)")) } \seealso{ Other labelling functions: \code{\link{lbl_dash}()}, \code{\link{lbl_date}()}, \code{\link{lbl_discrete}()}, \code{\link{lbl_endpoints}()}, \code{\link{lbl_glue}()}, \code{\link{lbl_intervals}()}, \code{\link{lbl_manual}()}, \code{\link{lbl_midpoints}()} } \concept{labelling functions} ================================================ FILE: man/non-standard-types.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/non-standard-types-doc.R \name{non-standard-types} \alias{non-standard-types} \title{Tips for chopping non-standard types} \description{ Santoku can handle many non-standard types. } \details{ \itemize{ \item If objects can be compared using \code{<}, \code{==} etc. then they should be choppable. \item Objects which can't be converted to numeric are handled within R code, which may be slower. \item Character \code{x} and \code{breaks} are chopped with a warning. \item If \code{x} and \code{breaks} are not the same type, they should be able to be cast to the same type, usually using \code{\link[vctrs:vec_cast]{vctrs::vec_cast_common()}}. \item Not all chopping operations make sense, for example, \code{\link[=chop_mean_sd]{chop_mean_sd()}} on a character vector. \item For indexed objects such as \code{\link[stats:ts]{stats::ts()}} objects, indices will be dropped from the result. \item If you get errors, try setting \code{extend = FALSE} (but also file a bug report). \item To request support for a type, open an issue on Github. } } \seealso{ brk-width-for-Datetime } ================================================ FILE: man/percent.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{percent} \alias{percent} \title{Simple percentage formatter} \usage{ percent(x) } \arguments{ \item{x}{Numeric values.} } \value{ \code{x} formatted as a percent. } \description{ \code{percent()} formats \code{x} as a percentage. For a wider range of formatters, consider the \href{https://cran.r-project.org/package=scales}{\code{scales} package}. } \examples{ percent(0.5) } ================================================ FILE: man/santoku-cast.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/santoku-cast.R \name{santoku-cast} \alias{santoku-cast} \alias{santoku_cast_common.default} \alias{santoku_cast_common.double} \alias{santoku_cast_common.Date} \alias{santoku_cast_common.POSIXct} \alias{santoku_cast_common.ts} \alias{santoku_cast_common.zoo} \alias{santoku_cast_common.integer64} \alias{santoku_cast_common.hexmode} \alias{santoku_cast_common.octmode} \title{Internal functions} \usage{ \method{santoku_cast_common}{default}(x, y) \method{santoku_cast_common}{double}(x, y) \method{santoku_cast_common}{Date}(x, y) \method{santoku_cast_common}{POSIXct}(x, y) \method{santoku_cast_common}{ts}(x, y) \method{santoku_cast_common}{zoo}(x, y) \method{santoku_cast_common}{integer64}(x, y) \method{santoku_cast_common}{hexmode}(x, y) \method{santoku_cast_common}{octmode}(x, y) } \arguments{ \item{x, y}{Vectors to cast.} } \value{ A list. } \description{ Internal functions } \keyword{Do} \keyword{These} \keyword{are} \keyword{functions.} \keyword{internal} \keyword{not} \keyword{use.} ================================================ FILE: man/santoku-package.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/santoku-package.R \docType{package} \name{santoku-package} \alias{santoku} \alias{santoku-package} \title{A versatile cutting tool for R: package overview and options} \description{ santoku is a tool for cutting data into intervals. It provides the function \code{\link[=chop]{chop()}}, which is similar to base R's \code{\link[=cut]{cut()}} or \code{Hmisc::cut2()}. \code{chop(x, breaks)} takes a vector \code{x} and returns a factor of the same length, coding which interval each element of \code{x} falls into. } \details{ Here are some advantages of santoku: \itemize{ \item By default, \code{chop()} always covers the whole range of the data, so you won't get unexpected \code{NA} values. \item Unlike \code{cut()} or \code{cut2()}, \code{chop()} can handle single values as well as intervals. For example, \code{chop(x, breaks = c(1, 2, 2, 3))} will create a separate factor level for values exactly equal to 2. \item Flexible and easy labelling. \item Convenience functions for creating quantile intervals, evenly-spaced intervals or equal-sized groups. \item Convenience functions to quickly tabulate chopped data. \item Can chop numbers, dates, date-times and other objects. } These advantages make santoku especially useful for exploratory analysis, where you may not know the range of your data in advance. To get started, read the vignette: \if{html}{\out{
    }}\preformatted{vignette("santoku") }\if{html}{\out{
    }} For more details, start with the documentation for \code{\link[=chop]{chop()}}. } \section{Options}{ Santoku has two options: \itemize{ \item \code{options("santoku.infinity")} sets the symbol for infinity in breaks. The default is \code{NULL}, in which case the infinity symbol is used on platforms that support it, otherwise \code{"Inf"} is used. \item \code{options("santoku.warn_character")} warns if you try to chop a character vector. Set to \code{FALSE} to turn off this warning. } } \seealso{ Useful links: \itemize{ \item \url{https://github.com/hughjonesd/santoku} \item \url{https://hughjonesd.github.io/santoku/} \item Report bugs at \url{https://github.com/hughjonesd/santoku/issues} } } \author{ \strong{Maintainer}: David Hugh-Jones \email{davidhughjones@gmail.com} Other contributors: \itemize{ \item Daniel Possenriede \email{possenriede@gmail.com} [contributor] } } ================================================ FILE: pkgdown/extra.css ================================================ pre code span.fu {color: #F9EA6B;} /* function */ pre code span.kw {color: #F9EA6B;} /* keyword */ pre code span.va {color: #89b0dd;} /* keyword */ pre code span.op {color: white;} /* ? */ ================================================ FILE: release-process.R ================================================ # ensure version is correct, then: # run line by line (some commands require command line input) devtools::check_win_devel() devtools::check_win_release() devtools::check_mac_release() devtools::check() devtools::spell_check() revdepcheck::revdep_check() # make sure you've put comments in cran-comments.md # install the new version # IF YOU WANT TO KEEP WEBSITE ON THE CRAN VERSION: # do this on branch website-x.y.z. Then you can commit and push changes without # devtools complaining about uncommitted changes. # OTHERWISE, JUST DO THIS ON MASTER pkgdown::build_site() my_home <- "~/hughjonesd.github.io/" rmarkdown::render("vignettes/tutorials/visual-introduction.Rmd", output_dir = my_home) file.copy("vignettes/tutorials/chopping-dates-with-santoku.Rmd", my_home, overwrite = TRUE) file.copy("vignettes/tutorials/figures", my_home, recursive = TRUE) withr::with_dir(my_home, rmarkdown::render("chopping-dates-with-santoku.Rmd") ) # but don't push yet! it affects what is seen from the website... # NB you may need to update the README on master, as this is part # of the package. # Now back to master: devtools::release() # when it's accepted: # - merge website-x.y.z into master # - push hughjonesd.github.io # - usethis::use_github_release() ================================================ FILE: santoku.Rproj ================================================ Version: 1.0 RestoreWorkspace: Default SaveWorkspace: Default AlwaysSaveHistory: Default EnableCodeIndexing: Yes UseSpacesForTab: Yes NumSpacesForTab: 2 Encoding: UTF-8 RnwWeave: knitr LaTeX: pdfLaTeX StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source PackageRoxygenize: rd,collate,namespace ================================================ FILE: src/.gitignore ================================================ *.o *.so *.dll ================================================ FILE: src/RcppExports.cpp ================================================ // Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include using namespace Rcpp; #ifdef RCPP_USE_GLOBAL_ROSTREAM Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif // categorize_impl IntegerVector categorize_impl(NumericVector x, NumericVector breaks, LogicalVector left); RcppExport SEXP _santoku_categorize_impl(SEXP xSEXP, SEXP breaksSEXP, SEXP leftSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); Rcpp::traits::input_parameter< NumericVector >::type breaks(breaksSEXP); Rcpp::traits::input_parameter< LogicalVector >::type left(leftSEXP); rcpp_result_gen = Rcpp::wrap(categorize_impl(x, breaks, left)); return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { {"_santoku_categorize_impl", (DL_FUNC) &_santoku_categorize_impl, 3}, {NULL, NULL, 0} }; RcppExport void R_init_santoku(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } ================================================ FILE: src/categorize.cpp ================================================ #include using namespace Rcpp; // [[Rcpp::export]] IntegerVector categorize_impl(NumericVector x, NumericVector breaks, LogicalVector left) { int xs = x.size(); int bs = breaks.size(); if (left.size() != bs) Rcpp::stop("`left` of different size to `breaks`"); IntegerVector codes(xs, NA_INTEGER); // Handle empty vectors if (xs == 0) return codes; // Use raw pointers for direct memory access - provides ~5-10% speedup double* x_ptr = &x[0]; double* breaks_ptr = &breaks[0]; int* left_ptr = &left[0]; int* codes_ptr = &codes[0]; for (int i = 0; i < xs; ++i) { double val = x_ptr[i]; for (int j = 0; j < bs - 1; ++j) { if ((val > breaks_ptr[j] || (left_ptr[j] && val == breaks_ptr[j])) && (val < breaks_ptr[j + 1] || (! left_ptr[j + 1] && val == breaks_ptr[j + 1]))) { codes_ptr[i] = j + 1; break; } } } return codes; } ================================================ FILE: tests/testthat/test-Date-DateTime.R ================================================ d1 <- seq(as.Date("1975-10-27"), as.Date("1975-11-27"), by = "day") db1 <- as.Date(c("1975-11-01", "1975-11-15")) dt1 <- seq(as.POSIXct("2000-01-01 15:00"), length = 20, by = "1 min") dtb1 <- dt1[c(5, 15)] table_vals <- function (x) unclass(table(x)) test_that("Basic chop", { expect_silent(chop(d1, db1)) expect_silent(chop(dt1, dtb1)) }) test_that("Chop with conversion", { lb <- lbl_seq() withr::with_timezone("UTC", { expect_equal(chop(d1, db1, lb), chop(as.POSIXct(d1), db1, lb), ignore_attr = TRUE) expect_equal(chop(d1, db1, lb), chop(d1, as.POSIXct(db1), lb), ignore_attr = TRUE) }) }) test_that("Basic breaks", { expect_silent(brk_res(brk_default(db1), d1, extend = FALSE)) expect_silent(brk_res(brk_default(db1), d1, extend = NULL)) expect_silent(brk_res(brk_default(db1), d1, extend = TRUE)) expect_silent(brk_res(brk_default(db1), d1, left = FALSE)) expect_silent(brk_res(brk_default(db1), d1, close_end = TRUE)) expect_silent(brk_res(brk_default(db1), d1, left = FALSE, close_end = TRUE)) }) test_that("chop_equally", { expect_silent(res <- chop_equally(d1, groups = 4)) expect_equal(table_vals(res), rep(8, 4), ignore_attr = TRUE) expect_silent(res2 <- chop_equally(dt1, groups = 4)) expect_equal(table_vals(res2), rep(5, 4), ignore_attr = TRUE) }) test_that("chop_n", { expect_silent(res <- chop_n(d1, 4)) expect_equal(table_vals(res), rep(4, 8), ignore_attr = TRUE) expect_silent(res2 <- chop_n(dt1, 5)) expect_equal(table_vals(res2), rep(5, 4), ignore_attr = TRUE) }) test_that("chop_quantiles", { # `left = FALSE` works better with type 1 quantiles, which round down. expect_silent(res1 <- chop_quantiles(d1, 0:4/4, left = FALSE)) expect_equal(table_vals(res1), rep(8, 4), ignore_attr = TRUE) expect_silent(res2 <- chop_quantiles(dt1, 0:5/5, left = FALSE)) expect_equal(table_vals(res2), rep(4, 5), ignore_attr = TRUE) }) test_that("chop_mean_sd", { expect_silent(res <- chop_mean_sd(d1)) cmp <- cut(d1, mean(d1) + (-2:2) * sd(d1), right = FALSE) expect_equal(table_vals(res), table_vals(cmp), ignore_attr = TRUE) expect_silent(res2 <- chop_mean_sd(dt1)) cmp2 <- cut(dt1, mean(dt1) + (-2:2) * sd(dt1), right = FALSE) expect_equal(table_vals(res2), table_vals(cmp2), ignore_attr = TRUE) expect_silent(res3 <- chop_mean_sd(d1, c(1, 1.4))) # the -10 and 10 capture values outside 1.4 sds: cmp3 <- cut(d1, mean(d1) + c(-10, -1.4, -1, 0, 1, 1.4, 10) * sd(d1), right = FALSE) expect_equal(table_vals(res3), table_vals(cmp3), ignore_attr = TRUE) }) test_that("chop_pretty", { expect_silent(res <- chop_pretty(d1)) cmp <- chop(d1, base::pretty(d1)) expect_equal( table_vals(res), table_vals(cmp), ignore_attr = TRUE ) }) test_that("chop_width: difftime", { difftime_w1 <- as.difftime(4, units = "days") expect_silent(res1 <- chop_width(d1, width = difftime_w1)) expect_equal(table_vals(res1), rep(4, 8), ignore_attr = TRUE) expect_silent( res2 <- chop_width(d1, width = difftime_w1, start = as.Date("1975-11-01")) ) tv <- table_vals(res2) expect_true(all(tv[c(-1, -length(tv))] == 4)) difftime_w2 <- as.difftime(5, units = "mins") expect_silent(res3 <- chop_width(dt1, width = difftime_w2)) expect_equal(table_vals(res3), rep(5, 4), ignore_attr = TRUE) expect_silent( res4 <- chop_width(dt1, width = difftime_w2, start = as.POSIXct("2000-01-01 15:10")) ) expect_equal(table_vals(res4), c(10, 5, 5), ignore_attr = TRUE) expect_silent( res5 <- chop_width(d1, width = as.difftime(-4, units = "days")) ) expect_equal(table_vals(res5), rep(4, 8), ignore_attr = TRUE) expect_silent( res6 <- chop_width(d1, width = as.difftime(-4, units = "days"), start = as.Date("1975-11-25")) ) tv <- table_vals(res6) expect_true(all(tv[c(-1, -length(tv))] == 4)) expect_silent( res7 <- chop_width(dt1, width = as.difftime(-5, units = "mins")) ) expect_equal(table_vals(res7), rep(5, 4), ignore_attr = TRUE) expect_silent(chop_width(d1, as.difftime(7, units = "days"))) expect_silent(chop_width(dt1, as.difftime(7, units = "mins"))) }) test_that("chop_width: Duration", { skip_if_not_installed("lubridate") library(lubridate) duration_w1 <- ddays(4) expect_silent(res1 <- chop_width(d1, width = duration_w1)) expect_equal(table_vals(res1), rep(4, 8), ignore_attr = TRUE) expect_silent( res2 <- chop_width(d1, width = duration_w1, start = as.Date("1975-11-16")) ) expect_equal(table_vals(res2), c(20, 4, 4, 4), ignore_attr = TRUE) duration_w2 <- dminutes(5) expect_silent(res3 <- chop_width(dt1, width = duration_w2)) expect_equal(table_vals(res3), rep(5, 4), ignore_attr = TRUE) expect_silent( res4 <- chop_width(dt1, duration_w2, start = as.POSIXct("2000-01-01 15:10")) ) expect_equal(table_vals(res4), c(10, 5, 5), ignore_attr = TRUE) expect_silent(chop_width(d1, ddays(7))) expect_silent(chop_width(dt1, dminutes(7))) }) test_that("chop_width: Period", { skip_if_not_installed("lubridate") library(lubridate) period_w1 <- days(8) expect_silent(res1 <- chop_width(d1, width = period_w1)) expect_equal(table_vals(res1), rep(8, 4), ignore_attr = TRUE) expect_silent( res2 <- chop_width(d1, period_w1, start = as.Date("1975-11-12")) ) expect_equal(table_vals(res2), c(16, 8, 8), ignore_attr = TRUE) period_w2 <- minutes(5) expect_silent(res3 <- chop_width(dt1, width = period_w2)) expect_equal(table_vals(res3), rep(5, 4), ignore_attr = TRUE) expect_silent( res4 <- chop_width(dt1, period_w2, start = as.POSIXct("2000-01-01 15:07")) ) expect_equal(table_vals(res4), c(7, 5, 5, 3), ignore_attr = TRUE) expect_silent(chop_width(d1, days(7))) expect_silent(chop_width(dt1, minutes(7))) # TODO: include tests that Period deals with quirks }) test_that("chop_width: Period quirks", { skip_if_not_installed("lubridate") library(lubridate) noughties <- seq(as.Date("2000-01-01"), as.Date("2009-12-31"), by = "day") res1 <- chop_width(noughties, years(1)) expect_equal( table_vals(res1), c(366, 365, 365, 365, 366, 365, 365, 365, 366, 365), ignore_attr = TRUE ) y2k <- seq(as.Date("2000-01-01"), as.Date("2000-12-31"), by = "day") res2 <- chop_width(y2k, months(1)) expect_equal( table_vals(res2), c(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), ignore_attr = TRUE ) }) test_that("chop_evenly", { expect_silent(res1 <- chop_evenly(d1, 8)) expect_equal(table_vals(res1), rep(4, 8), ignore_attr = TRUE) expect_silent(res2 <- chop_evenly(dt1, 4)) expect_equal(table_vals(res2), rep(5, 4), ignore_attr = TRUE) expect_silent(chop_evenly(d1, 7)) expect_silent(chop_evenly(dt1, 7)) }) test_that("brk_spikes", { d_reps <- c(rep(d1[3], 5), d1[-3]) expect_silent( res <- chop(d_reps, brk_spikes(db1, n = 5)) ) expect_in("{1975-10-29}", levels(res)) expect_equal(sum(res == "{1975-10-29}"), 5, ignore_attr = TRUE) }) test_that("chop timezones", { dt_z1 <- seq(as.POSIXct("2000-01-01 09:00:00", tz = "GMT"), by = "hour", length.out = 24) # 8 hours behind. Hi Tom and Dan! dtb_z2 <- as.POSIXct("2000-01-01 12:30:00", tz = "America/Los_Angeles") res1 <- chop(dt_z1, dtb_z2) expect_equal(table_vals(res1), c(12, 12), ignore_attr = TRUE) # we convert breaks to the timezone of x expect_match(levels(res1), "20:30", fixed = TRUE) }) test_that("Date labels", { li <- lbl_intervals() b <- brk_res(brk_default(db1), close_end = FALSE) expect_equal( li(b), "[1975-11-01, 1975-11-15)" , ignore_attr = TRUE) withr::local_options(santoku.infinity = "Inf") b2 <- brk_res(brk_default(db1), x = as.Date("1975-01-01"), extend = TRUE, close_end = FALSE) expect_equal( li(b2), c("[-Inf, 1975-11-01)", "[1975-11-01, 1975-11-15)", "[1975-11-15, Inf]") , ignore_attr = TRUE) expect_equal( lbl_intervals(fmt = "%y %m %d")(b), "[75 11 01, 75 11 15)" , ignore_attr = TRUE) expect_equal( lbl_dash(" to ")(b), "1975-11-01 to 1975-11-15" , ignore_attr = TRUE) expect_equal( lbl_dash(" to ", fmt = "%d/%m")(b), "01/11 to 15/11" , ignore_attr = TRUE) }) test_that("POSIXct labels", { li <- lbl_intervals() b <- brk_res(brk_default(dtb1), close_end = FALSE) expect_equal( li(b), "[2000-01-01 15:04:00, 2000-01-01 15:14:00)" , ignore_attr = TRUE) withr::local_options(santoku.infinity = "Inf") b2 <- brk_res(brk_default(dtb1), x = as.POSIXct("2000-01-01 15:00:00"), extend = TRUE) expect_equal( li(b2), c("[-Inf, 2000-01-01 15:04:00)", "[2000-01-01 15:04:00, 2000-01-01 15:14:00)", "[2000-01-01 15:14:00, Inf]") , ignore_attr = TRUE) expect_equal( lbl_intervals(fmt = "%H:%M")(b), "[15:04, 15:14)" , ignore_attr = TRUE) expect_equal( lbl_dash(" to ")(b), "2000-01-01 15:04:00 to 2000-01-01 15:14:00" , ignore_attr = TRUE) expect_equal( lbl_dash(" to ", fmt = "%H.%M")(b), "15.04 to 15.14" , ignore_attr = TRUE) }) ================================================ FILE: tests/testthat/test-breaks.R ================================================ test_that("brk_manual", { for (l in c(TRUE, FALSE)) for (r in c(TRUE, FALSE)) { expect_silent(x <- brk_res(brk_manual(1:2, c(l, r)))) expect_s3_class(x, "breaks") } expect_error(brk_res(brk_manual(c(2, 2), c(TRUE, TRUE)))) expect_error(brk_res(brk_manual(c(2, 2), c(FALSE, TRUE)))) expect_error(brk_res(brk_manual(c(2, 2), c(FALSE, FALSE)))) expect_silent(brk_res(brk_manual(c(2, 2), c(TRUE, FALSE)))) expect_error(brk_res(brk_manual(1, c(TRUE, FALSE)))) expect_error(brk_res(brk_manual(1:2, c(TRUE)))) expect_error(brk_res(brk_manual("a", TRUE))) expect_error(brk_res(brk_manual(1, "c"))) expect_error(brk_res(brk_manual(c(1, NA), c(TRUE, TRUE)))) expect_error(brk_res(brk_manual(2:1, c(TRUE, TRUE)))) expect_error(brk_res(brk_manual(c(1, 2, 2, 2, 3), rep(TRUE, 5))), regexp = "equal") expect_warning(brk_res(brk_manual(1:3, rep(TRUE, 3)), left = FALSE)) expect_warning(brk_res(brk_manual(1:3, rep(TRUE, 3)), close_end = FALSE)) }) test_that("brk_n", { for (i in 1:10) { x <- rnorm(sample(10:20, 1L)) b <- sample(5L, 1L) expect_true(all(tab(!!x, brk_n(!!b), drop = TRUE) <= !!b), info = sprintf("length(x) %s b %s", length(x), b)) # right-closed breaks expect_true(all(tab(!!x, brk_n(!!b), drop = TRUE, left = FALSE) <= !!b), info = sprintf("length(x) %s b %s left = FALSE", length(x), b)) } # test with duplicates in x for (i in 1:10) { x <- rnorm(10) x <- sample(x, replace = TRUE) b <- sample(5L, 1L) tbl <- tab(x, brk_n(b), drop = TRUE) # all but the last category should have size >= b expect_true(all(tbl[-length(tbl)] >= b), info = sprintf("length(x) %s b %s", length(x), b)) # right-closed breaks tbl <- tab(x, brk_n(b), drop = TRUE, left = FALSE) expect_true(all(tbl[-1] >= b), info = sprintf("length(x) %s b %s", length(x), b)) } }) test_that("brk_n, tail = 'merge'", { x <- 1:5 res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), 5) x <- 1:6 res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), c(3, 3)) x <- 1:7 res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), c(3, 4)) x <- c(1, 1, 1, 2, 2) res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), 5) x <- c(1, 1, 1, 2, 2, 2) res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), c(3, 3)) x <- c(1, 1, 1, 2, 2, 2, 2) res <- brk_res(brk_n(3, tail = "merge"), x = x) expect_equal(as.vector(tab(x, res)), c(3, 4)) }) test_that("bugfix: brk_n shouldn't error with too many non-unique values", { expect_error( brk_res(brk_n(2), x = c(1, 1, 1, 1, 5, 5, 5, 5)), regexp = NA ) }) test_that("bugfix: brk_n shouldn't take too few elems after non-unique values", { x <- c(1, 1, 1, 1, 2, 3, 4) res <- brk_res(brk_n(3), x = x) expect_equal(as.vector(tab(x, res)), c(4, 3)) x <- c(1, 2, 3, 3, 4, 5, 6) res <- brk_res(brk_n(3), x = x) expect_equal(as.vector(tab(x, res)), c(4, 3)) x <- c(1, 2, 3, 3, 4) res <- brk_res(brk_n(2), x = x) expect_equal(as.vector(tab(x, res)), c(2, 2, 1)) }) test_that("brk_width", { b <- brk_res(brk_width(1), 0.5:1.5) expect_equal(diff(as.vector(b)), 1) width <- runif(1) b <- brk_res(brk_width(width), 0.5:1.5) bvec <- as.vector(b) expect_equal(diff(bvec)[1], width) expect_equal(bvec[1], 0.5) b <- brk_res(brk_width(1), rep(NA, 2)) expect_identical(as.vector(b), c(-Inf, Inf)) b <- brk_res(brk_width(1), c(Inf, -Inf, NA)) expect_identical(as.vector(b), c(-Inf, Inf)) b <- brk_res(brk_width(1), c(NA, 2, 4, NA)) expect_equal(diff(as.vector(b))[1], 1) }) test_that("brk_width, negative width", { b <- brk_res(brk_width(-1), 0.5:1.5) expect_equal(diff(as.vector(b)), 1) width <- runif(1, min = -1, max = 0) b <- brk_res(brk_width(width), 0.5:1.5) bvec <- as.vector(b) expect_equal(diff(bvec)[1], -width) expect_equal(bvec[length(bvec)], 1.5) b <- brk_res(brk_width(-2, start = 2.5), 0:4) expect_identical(as.vector(b), c(-1.5, 0.5, 2.5)) }) test_that("brk_evenly", { b <- brk_res(brk_evenly(5), 0:10) expect_identical(as.vector(b), c(0, 2, 4, 6, 8, 10)) }) test_that("brk_proportions", { b <- brk_res(brk_proportions(c(0.2, 0.8)), 0:10) expect_identical(as.vector(b), c(2, 8)) expect_error(brk_proportions(c(0, 1, 2))) expect_error(brk_proportions(c(-1, 0.5))) expect_error(brk_proportions(c(0.5, NA))) }) test_that("brk_mean_sd", { x <- rnorm(100) expect_silent(b <- brk_res(brk_mean_sd(1:3), x = x)) m <- mean(x) sd <- sd(x) sd_ints <- seq(m - 3 * sd, m + 3 * sd, sd) expect_equal(as.numeric(b), sd_ints) expect_silent(brk_res(brk_mean_sd(1:3), x = rep(NA, 2))) expect_silent(brk_res(brk_mean_sd(1:3), x = rep(1, 3))) expect_silent(brk_res(brk_mean_sd(1:3), x = 1)) lifecycle::expect_deprecated(res <- brk_res(brk_mean_sd(sd = 3))) expect_equal( res, brk_res(brk_mean_sd(1:3)) ) }) test_that("brk_quantiles", { expect_silent(brk_res(brk_quantiles(1:3/4))) x <- 1:10 brks <- brk_quantiles(1:3/4)(x, FALSE, TRUE, FALSE) expect_equal(c(brks), quantile(x, 1:3/4), ignore_attr = TRUE) expect_silent(brks <- brk_quantiles(numeric(0))(x, TRUE, TRUE, FALSE)) expect_equal(c(brks), c(-Inf, Inf)) x <- 1:10 brks <- brk_quantiles(1:3/4, weights = 1:10)(x, FALSE, TRUE, FALSE) expect_equal( c(brks), Hmisc::wtd.quantile(x, weights = 1:10, probs = 1:3/4), ignore_attr = TRUE ) }) test_that("brk_quantiles with duplicate quantiles", { x <- rep(1, 5) expect_warning(brks <- brk_quantiles(1:3/4)(x, FALSE, TRUE, FALSE)) expect_equal(c(brks), c(1, 1)) x <- c(1, 1, 2, 3, 4) expect_warning(brks <- brk_quantiles(0:5/5)(x, FALSE, TRUE, FALSE)) expect_equal(c(brks), c(1.0, 1.0, 1.6, 2.4, 3.2, 4.0)) x <- c(1, 1, 1, 2, 3) expect_warning(brks <- brk_quantiles(0:5/5)(x, FALSE, TRUE, FALSE)) expect_equal(c(brks), c(1.0, 1.0, 1.4, 2.2, 3.0)) expect_silent( brks <- brk_quantiles(0:5/5, recalc_probs = TRUE)(x, FALSE, TRUE, FALSE) ) expect_equal(c(brks), c(1.0, 1.0, 1.4, 2.2, 3.0)) }) test_that("brk_equally", { expect_silent(brk_res(brk_equally(5))) expect_error(brk_equally(4.5)) brks <- brk_res(brk_equally(3)) expect_equal(brks, brk_res(brk_quantiles(0:3/3))) }) test_that("brk_equally warns when too few breaks created", { dupes <- rep(1, 4) expect_warning(brk_res(brk_equally(4), x = dupes)) }) test_that("brk_pretty", { expect_silent(brks <- brk_res(brk_pretty(5), x = 1:10)) expect_equal(brks, brk_res(brk_default(pretty(1:10)), x = 1:10)) expect_silent(brks2 <- brk_res(brk_pretty(5, high.u.bias = 0), x = 1:10)) expect_equal( brks2, brk_res(brk_default(pretty(1:10, high.u.bias = 0)), x = 1:10) ) }) test_that("brk_fn", { x <- 1:10 expect_silent( brks <- brk_res(brk_fn(scales::breaks_extended(5)), x = x) ) expect_equal( brks, brk_res(brk_default(scales::breaks_extended(5)(x))) ) expect_silent( brks2 <- brk_res(brk_fn(pretty, n = 10), x = x) ) expect_equal( brks2, brk_res(brk_default(pretty(x, n = 10)), x = x) ) }) test_that("brk_spikes", { x <- c(rep(5, 5), 1:10) expect_silent( brks <- brk_res(brk_spikes(c(2, 8), n = 5), x = x) ) expect_equal( c(brks), c(2, 5, 5, 8) ) expect_silent( brks2 <- brk_res(brk_spikes(c(2, 5, 8), n = 5), x = x) ) expect_equal( c(brks2), c(2, 5, 5, 8) ) expect_silent( brks3 <- brk_res(brk_spikes(c(2, 5, 5, 8), n = 5), x = x) ) expect_equal( c(brks3), c(2, 5, 5, 8) ) x2 <- c(1, 1, 1, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8, 9) expect_silent( brks3.1 <- brk_res(brk_spikes(c(2, 8), n = 3), x = x2) ) expect_equal( c(brks3.1), c(1, 1, 2, 4, 4, 8, 8) ) expect_silent( brks4 <- brk_res(brk_spikes(brk_width(5), n = 5), x = x) ) expect_equal( c(brks4), c(1, 5, 5, 6, 11) ) expect_silent( brks5 <- brk_res(brk_spikes(c(2, 8), prop = 0.4), x = x) ) expect_equal( c(brks5), c(2, 5, 5, 8) ) expect_error( brk_spikes(c(2, 8), n = 5, prop = 0.5) ) expect_error( brk_spikes(c(2, 8)) ) }) test_that("printing", { b <- brk_res(brk_default(1:3)) expect_output(print(b)) expect_silent(format(b)) b_empty <- brk_res(brk_default(1)) expect_output(print(b_empty)) }) ================================================ FILE: tests/testthat/test-categorize.R ================================================ test_that("brk_manual", { expect_silent(brk_res(brk_manual(1:3, c(TRUE, TRUE, TRUE)))) expect_silent(brk_res(brk_manual(c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE)))) expect_silent(brk_res(brk_manual(c(-Inf, 1, Inf), rep(TRUE, 3)))) # singletons must have FALSE, TRUE expect_error(brk_res(brk_manual(c(1, 2, 2, 3), c(TRUE, FALSE, FALSE, TRUE)))) # out of order: expect_error(brk_res(brk_manual(c(0, 3, 1), c(TRUE, TRUE, TRUE)))) }) test_that("categorize works", { x <- seq(0.5, 3.5, 0.5) breaks <- brk_res(brk_manual(1:3, c(TRUE, TRUE, TRUE))) r <- categorize(x, breaks) expect_equal(r, c(NA, 1, 1, 2, 2, NA, NA)) breaks <- brk_res(brk_manual(1:3, c(TRUE, TRUE, FALSE))) r <- categorize(x, breaks) expect_equal(r, c(NA, 1, 1, 2, 2, 2, NA)) breaks <- brk_res(brk_manual(c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE))) r <- categorize(x, breaks) expect_equal(r, c(NA, 1, 1, 2, 3, NA, NA)) x <- c(Inf, 1, -Inf, NA, NaN) breaks <- brk_res(brk_manual(1:3, c(TRUE, TRUE, TRUE))) r <- categorize(x, breaks) expect_equal(r, c(NA, 1, NA, NA, NA)) }) test_that("categorize_impl/categorize_non_numeric equivalence", { replicate(100, { n <- 10 x <- rnorm(n) * 10 breaks_pop <- c(-Inf, -5:5, Inf) b <- sort(sample(breaks_pop, n, replace = FALSE)) left <- sample(c(TRUE, FALSE), n, replace = TRUE) x[c(3,5,7)] <- sample(breaks_pop, 3) b[3] <- b[2] b[8] <- b[7] left[2:3] <- left[7:8] <- c(TRUE, FALSE) ci <- categorize_impl(x, b, left) cnn <- categorize_non_numeric(x, b, left) expect_equal(ci, cnn) }) }) ================================================ FILE: tests/testthat/test-chop.R ================================================ test_that("basic functionality", { x <- 1:3 lbrks <- brk_manual(1:3, rep(TRUE, 3)) rbrks <- brk_manual(1:3, rep(FALSE, 3)) rc_brks <- brk_manual(1:3, c(TRUE, TRUE, FALSE)) expect_equal( chop(x, lbrks, lbl_seq("1"), extend = FALSE), factor(c(1, 2, NA)) ) expect_equal( chop(x, rbrks, lbl_seq("1"), extend = FALSE), factor(c(NA, 1, 2)) ) expect_equal( chop(x, rc_brks, lbl_seq("1"), extend = FALSE), factor(c(1, 2, 2)) ) }) test_that("NA, NaN and Inf", { y <- c(1:3, NA, NaN) expect_equal( chop(y, 1:3, lbl_seq("1"), extend = FALSE, close_end = FALSE), factor(c(1, 2, NA, NA, NA)), ignore_attr = TRUE ) x <- c(-Inf, 1, Inf) r <- chop(x, 1:2, labels = letters[1:3]) expect_equal( r, factor(c("a", "b", "c"), levels = letters[1:3], ordered = FALSE), ignore_attr = TRUE ) x <- c(-Inf, 1, Inf) # if extend is NULL, we should ensure even Inf is included r <- chop(x, -Inf, left = FALSE, labels = c("-Inf", "a"), close_end = FALSE) expect_equal(r, factor(c("-Inf", "a", "a"), levels = c("-Inf", "a")), ignore_attr = TRUE) r <- chop(x, Inf, labels = c("a", "Inf"), close_end = FALSE) expect_equal(r, factor(c("a", "a", "Inf"), levels = c("a", "Inf")), ignore_attr = TRUE) # otherwise, we respect close_end = FALSE r <- chop(x, brk_default(c(-Inf, Inf)), labels = "a", extend = FALSE, left = FALSE, close_end = FALSE) expect_equal(r, factor(c(NA, "a", "a"), levels = c("a")), ignore_attr = TRUE) r <- chop(x, c(-Inf, Inf), labels = "a", extend = FALSE, close_end = FALSE) expect_equal(r, factor(c("a", "a", NA), levels = c("a")), ignore_attr = TRUE) all_na <- rep(NA_real_, 5) expect_silent(chop(all_na, 1:2)) # not sure if this should be OK or not... # expect_silent(chop_quantiles(all_na, c(.25, .75))) all_na[1] <- NaN expect_silent(chop(all_na, 1:2)) }) test_that("singleton breaks", { expect_silent(chop(1:4, 2)) expect_silent(chop(1:4, 1)) expect_silent(chop(1:4, 4)) expect_silent(chop(1:4, 0)) expect_silent(chop(1:4, 5)) expect_silent(chop(1, 1)) }) test_that("labels", { x <- seq(0.5, 2.5, 0.5) expect_equal( chop(x, 1:2, labels = letters[1:3]), factor(c("a", "b", "b", "c", "c"), levels = letters[1:3]) ) expect_error(chop(1:10, 3:4, labels = c("a", "a", "a"))) expect_error(chop(1:10, 3:4, labels = c("a", "b"))) expect_error(chop(1:10, 3:4, labels = c("a", "b", "c", "d"))) expect_equal( chop(x, 1:2, labels = NULL), c(1, 2, 2, 3, 3) ) }) test_that("break names as labels", { expect_equal( chop(1:4, c(Low = 1, High = 3, 4)), factor(c("Low", "Low", "High", "High"), levels = c("Low", "High")), ignore_attr = TRUE ) expect_equal( chop(1:5, c(Low = 1, Mid = 3, High = 4)), factor(c("Low", "Low", "Mid", "High", "High"), levels = c("Low", "Mid", "High")), ignore_attr = TRUE ) expect_equal( chop(0:4, c(Low = 1, High = 3)), factor(c("[0, 1)", "Low", "Low", "High", "High"), levels = c("[0, 1)", "Low", "High")), ignore_attr = TRUE ) expect_equal( chop(1:4, c(Low = 1, Mid = 2, 3, 4), labels = lbl_endpoints()), factor(c("Low", "Mid", "3", "3"), levels = c("Low", "Mid", "3")), ignore_attr = TRUE ) }) test_that("extend", { expect_equal( chop(c(1, 4), 2:3, labels = lbl_seq("1"), extend = TRUE), factor(c(1, 3)) ) expect_equal( chop(c(1, 4), 2:3, labels = lbl_seq("1"), extend = FALSE), factor(c(NA, NA)) ) }) test_that("close_end", { res <- chop(1:4, 2:3, close_end = TRUE, drop = FALSE) expect_equal( levels(res), c("[1, 2)", "[2, 3)", "[3, 4]") ) res <- chop(1:4, 2:3, close_end = FALSE, extend = FALSE, drop = FALSE) expect_equal( levels(res), c("[2, 3)") ) res <- chop(1:4, 2:3, close_end = TRUE, extend = FALSE, drop = FALSE) expect_equal( levels(res), c("[2, 3]") ) }) test_that("raw", { x <- 1:10 expect_silent( res <- chop(x, brk_quantiles(c(0.25, 0.75)), raw = TRUE) ) expect_equal( levels(res), c("[1, 3.25)", "[3.25, 7.75)", "[7.75, 10]") ) expect_silent( res <- chop(x, brk_quantiles(c(0.25, 0.75)), raw = FALSE) ) expect_equal( levels(res), c("[0%, 25%)", "[25%, 75%)", "[75%, 100%]") ) }) test_that("drop", { x <- c(1, 3) expect_equal( levels(chop(x, 1:3, labels = lbl_seq("1"), extend = TRUE, drop = TRUE)), as.character(c(2, 4)) ) expect_equal( levels(chop(x, 1:3, labels = lbl_seq("1"), extend = TRUE, drop = FALSE)), as.character(1:4) ) }) test_that("chop_width", { x <- 1:10 expect_equal( chop_width(x, 2, labels = lbl_seq("1")), factor(rep(1:5, each = 2)) ) expect_equal( chop_width(x, 2, 0, labels = lbl_seq("1")), factor(c(1, rep(2:4, each = 2), 5, 5, 5)) ) }) test_that("chop_evenly", { x <- 1:10 expect_equal( chop_evenly(x, 2, labels = lbl_seq("1")), factor(rep(1:2, each = 5)) ) expect_error(r <- chop_evenly(x, groups = 2)) }) test_that("chop_proportions", { expect_equal( chop_proportions(0:10, c(0.2, 0.8), labels = lbl_seq("1")), factor(rep(1:3, c(2, 6, 3))) ) expect_equal( chop_proportions(0:10, c(Low = 0, Mid = 0.2, High = 0.8)), factor(c(rep("Low", 2), rep("Mid", 6), rep("High", 3)), levels = c("Low", "Mid", "High")), ignore_attr = TRUE ) expect_equal( chop_proportions(0:10, c(0.2, 0.8), labels = lbl_intervals(), raw = FALSE), factor(c(1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3), labels = c("[0, 0.2)", "[0.2, 0.8)", "[0.8, 1]")) ) }) test_that("chop_quantiles", { expect_equal( chop_quantiles(1:6, c(.25, .5, .75), labels = lbl_seq("1")), as.factor(c(1, 1, 2, 3, 4, 4)) ) expect_equal( chop_quantiles(1:6, c(Q1 = 0, Q2 = 0.25, Q3 = 0.5, Q4 = 0.75)), factor(c("Q1", "Q1", "Q2", "Q3", "Q4", "Q4")) ) x <- c(1, 1, 1, 2, 3) expect_equal( chop_quantiles(x, 1:4/5, recalc_probs = TRUE), factor(c("[0%, 60%]", "[0%, 60%]", "[0%, 60%]", "[60%, 80%)", "[80%, 100%]")) ) withr::local_options(lifecycle_verbosity = "quiet") expect_equal( chop_quantiles(1:6, c(.25, .5, .75), raw = TRUE), factor(c(1, 1, 2, 3, 4, 4), labels = c("[1, 2.25)", "[2.25, 3.5)", "[3.5, 4.75)", "[4.75, 6]")) ) }) test_that("chop_equally", { x <- 1:6 expect_equal( chop_equally(x, 2, labels = lbl_seq("1")), as.factor(rep(1:2, each = 3)) ) expect_equal( chop_equally(x, 2, raw = FALSE), factor(c(1, 1, 1, 2, 2, 2), labels = c("[0%, 50%)", "[50%, 100%]")) ) expect_equal( chop_equally(x, 2, raw = TRUE), factor(c(1, 1, 1, 2, 2, 2), labels = c("[1, 3.5)", "[3.5, 6]")) ) expect_warning( chop_equally(c(1, 1, 1, 1), 4), "Fewer" ) }) test_that("chop_deciles", { x <- rnorm(100) expect_identical( chop_quantiles(x, 0:10/10), chop_deciles(x) ) }) test_that("chop_n", { expect_silent(res <- chop_n(rnorm(100), 10)) expect_equal(as.vector(table(res)), rep(10, 10)) # chop_n should give accurate answers even when left = FALSE res <- chop_n(1:4, 2, left = FALSE) expect_equal(as.vector(table(res)), rep(2, 2)) expect_warning(chop_n(rep(1:3, each = 3), 2)) }) test_that("Bugfix: chop_n(tail = 'merge') works with n > length(x)", { expect_silent( chop_n(1:3, 4, tail = "merge", extend = FALSE) ) }) test_that("chop_mean_sd", { x <- -1:1 # mean 0, sd 1 expect_silent(res <- chop_mean_sd(x)) expect_equal(as.vector(table(res)), c(1, 1, 1)) expect_silent(res2 <- chop_mean_sd(x, sds = 1:2)) expect_silent(chop_mean_sd(x, sds = c(1, 1.96))) lifecycle::expect_deprecated(res3 <- chop_mean_sd(x, sd = 2)) expect_equal(res2, res3) expect_equal( chop_mean_sd(x, raw = TRUE), factor(c("[-1, 0)", "[0, 1)", "[1, 2)"), levels = c("[-1, 0)", "[0, 1)", "[1, 2)")) ) }) test_that("chop_pretty", { expect_silent(res <- chop_pretty(1:10)) expect_silent(res <- chop_pretty(1:10, 3)) expect_silent(res <- chop_pretty(1:10, 3)) }) test_that("chop_fn", { expect_silent(res <- chop_fn(1:10, pretty)) expect_silent(res <- chop_fn(1:10, quantile, c(.2, .8))) expect_equal( chop_fn(1:5, median), factor(c("[1, 3)", "[1, 3)", "[3, 5]", "[3, 5]", "[3, 5]")) ) expect_equal( chop_fn(1:5, median, left = FALSE), factor(c("[1, 3]", "[1, 3]", "[1, 3]", "(3, 5]", "(3, 5]"), levels = c("[1, 3]", "(3, 5]")), ignore_attr = TRUE ) }) test_that("chop_spikes", { x <- c(1:2, rep(3, 3), 4:5) expect_silent(res <- chop_spikes(x, breaks = 2, n = 2)) expect_equal( res, factor(c("[1, 2)", "[2, 3)", rep("{3}", 3), rep("(3, 5]", 2)), levels = c("[1, 2)", "[2, 3)", "{3}", "(3, 5]")), ignore_attr = TRUE ) expect_silent(res2 <- chop_spikes(x, breaks = 2, prop = 0.3)) expect_equal(res, res2) expect_silent(res3 <- chop_spikes(x, breaks = brk_width(4), n = 2)) expect_equal( res3, factor(c(rep("[1, 3)", 2), rep("{3}", 3), rep("(3, 5]", 2)), levels = c("[1, 3)", "{3}", "(3, 5]")), ignore_attr = TRUE ) }) test_that("dissect", { x <- c(1:2, rep(3, 3), 4:6) expect_silent(res <- dissect(x, breaks = c(2, 5), n = 2)) expect_equal( res, factor(c("[1, 2)", "[2, 5)", rep("{3}", 3), "[2, 5)", "[5, 6]", "[5, 6]"), levels = c("[1, 2)", "[2, 5)", "{3}", "[5, 6]")), ignore_attr = TRUE ) expect_silent(res2 <- dissect(x, breaks = c(2, 5), prop = 0.25)) expect_equal(res, res2) x <- c(1, 2, 3, 4, 5, 5, 5, 5) expect_silent(res3 <- dissect(x, breaks = brk_equally(2), n = 2, exclude_spikes = TRUE)) expect_equal( res3, factor(c("[0%, 50%)", "[0%, 50%)", "[50%, 100%]", "[50%, 100%]", rep("{5}", 4))) ) }) test_that("fillet", { x <- -2:2 expect_silent(sole <- fillet(x, -1:1)) expect_identical(sole, chop(x, -1:1, extend = FALSE, drop = FALSE)) }) ================================================ FILE: tests/testthat/test-labels.R ================================================ test_that("lbl_manual and lbl_endpoint are defunct", { lifecycle::expect_defunct(lbl_manual(letters)) lifecycle::expect_defunct(lbl_endpoint()(lbrk)) }) test_that("lbl_seq", { brk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) expect_error(lbl_seq("b")) expect_error(lbl_seq("a1")) expect_error(lbl_seq(c("a", "b"))) expect_equal(lbl_seq()(brk), c("a", "b")) expect_equal(lbl_seq("A")(brk), c("A", "B")) expect_equal(lbl_seq("i")(brk), c("i", "ii")) expect_equal(lbl_seq("I")(brk), c("I", "II")) expect_equal(lbl_seq("1")(brk), c("1", "2")) expect_equal(lbl_seq("(a)")(brk), c("(a)", "(b)")) expect_equal(lbl_seq("i.")(brk), c("i.", "ii.")) expect_equal(lbl_seq("I:")(brk), c("I:", "II:")) expect_equal(lbl_seq("1)")(brk), c("1)", "2)")) brk_many <- brk_res(brk_manual(1:28, rep(TRUE, 28))) expect_error(lbl_seq("a")(brk_many)) expect_error(lbl_seq("A)")(brk_many)) }) test_that("lbl_dash", { brk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) em_dash <- em_dash() expect_equal(lbl_dash()(brk), paste0(1:2, em_dash, 2:3)) expect_equal(lbl_dash("/")(brk), c("1/2", "2/3")) }) test_that("lbl_dash arguments", { brk <- brk_res(brk_default(1:3), 1:2) expect_equal(lbl_dash("-", fmt = "%.2f")(brk), c("1.00-2.00", "2.00-3.00")) expect_equal(lbl_dash("-", first = "< 2")(brk), c("< 2", "2-3")) expect_equal(lbl_dash("-", last = "> 2")(brk), c("1-2", "> 2")) expect_equal(lbl_dash("-", first = "< {r}")(brk), c("< 2", "2-3")) expect_equal(lbl_dash("-", last = "> {l}")(brk), c("1-2", "> 2")) brackets <- function (x) paste0("(", x, ")") expect_equal( lbl_dash("-", fmt = brackets)(brk), c("(1)-(2)", "(2)-(3)") ) expect_equal( lbl_dash("-", fmt = list(width = 2))(brk), c(" 1- 2", " 2- 3") ) brk2 <- brk_res(brk_default(c(1, 2, 2, 3)), 1:2) expect_equal( lbl_dash("-", single = "Just {l}")(brk2), c("1-2", "Just 2", "2-3") ) qbrk <- brk_res(brk_quantiles(c(0, .5, 1)), x = 0:10) expect_equal(lbl_dash("-")(qbrk), c("0%-50%", "50%-100%")) expect_equal( lbl_dash("-", fmt = "%.3f")(qbrk), c("0.000-0.500", "0.500-1.000") ) lifecycle::expect_defunct(lbl_dash(raw = TRUE)) }) test_that("lbl_glue", { brk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) expect_equal( lbl_glue("{l} to {r}")(brk), c("1 to 2", "2 to 3") ) expect_equal( lbl_glue("{ifelse(l_closed, '[', '(')}{l},{r}{ifelse(r_closed, ']', ')')}")(brk), c("[1,2)", "[2,3)") ) }) test_that("lbl_glue arguments", { brk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) expect_equal( lbl_glue("{l} to {r}", first = "Up to {r}", last = "Beyond {l}")(brk), c("Up to 2", "Beyond 2") ) expect_equal( lbl_glue("<{l} to {r}>", fmt = "%.1f")(brk), c("<1.0 to 2.0>", "<2.0 to 3.0>") ) expect_equal( lbl_glue("{l} to {r}", fmt = percent)(brk), c("100% to 200%", "200% to 300%") ) expect_equal( lbl_glue("{l}/{r}", fmt = list(width = 2))(brk), c(" 1/ 2", " 2/ 3") ) brk2 <- brk_res(brk_manual(c(1,2,2,3), c(TRUE, TRUE, FALSE, TRUE))) expect_equal( lbl_glue("{l} to {r}", single = "{{{l}}}")(brk2), c("1 to 2", "{2}", "2 to 3") ) expect_equal( lbl_glue(" to ", single = "{}", .open = "<", .close = ">")(brk2), c("1 to 2", "{2}", "2 to 3") ) expect_equal( lbl_glue("{l} to {r}")(brk2), c("1 to 2", "2 to 2", "2 to 3") ) expect_equal( lbl_glue("<{l} to {r}>", fmt = '%.1f', single = "|{sprintf('%.3f', as.numeric(l))}|")(brk2), c("<1.0 to 2.0>", "|2.000|", "<2.0 to 3.0>") ) qbrk <- brk_res(brk_quantiles(c(0, .5, 1)), x = 0:10) lifecycle::expect_defunct(lbl_glue("{l} / {r}", raw = TRUE)) }) test_that("lbl_endpoints", { lbrk <- brk_res(brk_default(c(1, 3, 5)), extend = FALSE) expect_equal( lbl_endpoints()(lbrk), c("1", "3") ) expect_equal( lbl_endpoints(left = FALSE)(lbrk), c("3", "5") ) dates <- as.Date("2000-01-01") + c(3, 5) dbrk <- brk_res(brk_default(dates), x = as.Date("2000-01-01") + 1:10) expect_equal( lbl_endpoints()(dbrk), as.character(dates[1]) ) }) test_that("lbl_endpoints arguments", { lbrk <- brk_res(brk_default(c(1, 3, 5)), extend = FALSE) expect_equal( lbl_endpoints(fmt = "%.2f")(lbrk), c("1.00", "3.00") ) expect_equal( lbl_endpoints(fmt = percent)(lbrk), c("100%", "300%") ) expect_equal( lbl_endpoints(fmt = list(nsmall = 2, decimal.mark = ","))(lbrk), c("1,00", "3,00") ) }) test_that("lbl_midpoints", { lbrk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) expect_equal(lbl_midpoints()(lbrk), c("1.5", "2.5")) dates <- as.Date("2000-01-01") + c(3, 5) dbrk <- brk_res(brk_default(dates), x = as.Date("2000-01-01") + 1:10) expect_equal( lbl_endpoints()(dbrk), c("2000-01-04") ) }) test_that("lbl_midpoints arguments", { lbrk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) expect_equal(lbl_midpoints(first = "{r}")(lbrk), c("2", "2.5")) expect_equal(lbl_midpoints(last = "{l}")(lbrk), c("1.5", "2")) sbrk <- brk_res(brk_manual(c(1, 2, 2, 3), c(TRUE, TRUE, FALSE, TRUE))) expect_equal(lbl_midpoints(single = "[{l}]")(sbrk), c("1.5", "[2]", "2.5")) qbrk <- brk_res(brk_quantiles(c(0, 0.5, 1)), x = 0:10) expect_equal(lbl_midpoints(fmt = percent)(qbrk), c("25%", "75%")) expect_equal( lbl_midpoints(fmt = list(decimal.mark = ","))(qbrk), c("0,25", "0,75") ) lifecycle::expect_defunct(lbl_midpoints(raw = TRUE)) }) test_that("lbl_intervals", { lbrk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) rbrk <- brk_res(brk_manual(1:3, rep(FALSE, 3))) expect_equal(lbl_intervals()(lbrk), c("[1, 2)", "[2, 3)")) expect_equal(lbl_intervals()(rbrk), c("(1, 2]", "(2, 3]")) lbrk <- brk_res(brk_default(1:3), close_end = TRUE) expect_equal(lbl_intervals()(lbrk), c("[1, 2)", "[2, 3]")) rbrk <- brk_res(brk_default(1:3), close_end = TRUE, left = FALSE) expect_equal(lbl_intervals()(rbrk), c("[1, 2]", "(2, 3]")) sbrk <- brk_res(brk_default(c(1, 2, 2, 3))) expect_equal(lbl_intervals()(sbrk), c("[1, 2)", "{2}", "(2, 3]")) mbrk <- brk_res(brk_manual(1:4, c(FALSE, TRUE, FALSE, TRUE))) expect_equal(lbl_intervals()(mbrk), c("(1, 2)", "[2, 3]", "(3, 4)")) }) test_that("lbl_intervals arguments", { lbrk <- brk_res(brk_default(c(1, 2, 2, 3) + 0.5)) expect_equal( lbl_intervals(fmt = "%.2f")(lbrk), c("[1.50, 2.50)", "{2.50}", "(2.50, 3.50]") ) expect_equal( lbl_intervals(fmt = list(digits = 2))(lbrk), c("[1.5, 2.5)", "{2.5}", "(2.5, 3.5]") ) lbrk <- brk_res(brk_default(1:3 * 10000)) expect_equal( lbl_intervals(fmt = "%2g")(lbrk), c("[10000, 20000)", "[20000, 30000]") ) qbrk <- brk_res(brk_quantiles(c(0, 0.5, 1)), x = 0:10) expect_equal( lbl_intervals()(qbrk), c("[0%, 50%)", "[50%, 100%]") ) expect_equal( lbl_intervals(fmt = "%.2f")(qbrk), c("[0.00, 0.50)", "[0.50, 1.00]") ) expect_equal( lbl_intervals(fmt = percent)(qbrk), c("[0%, 50%)", "[50%, 100%]") ) expect_equal( lbl_intervals(fmt = list(digits = 2))(qbrk), c("[0.0, 0.5)", "[0.5, 1.0]") ) lbrk <- brk_res(brk_default(c(1, 2, 2, 3))) expect_equal( lbl_intervals(first = "< {r}")(lbrk), c("< 2", "{2}", "(2, 3]") ) expect_equal( lbl_intervals(last = "> {l}")(lbrk), c("[1, 2)", "{2}", "> 2") ) expect_equal( lbl_intervals(single = "[{l}]")(lbrk), c("[1, 2)", "[2]", "(2, 3]") ) lifecycle::expect_defunct(lbl_intervals(raw = TRUE)) }) test_that("lbl_discrete", { lbrk <- brk_res(brk_manual(1:3, rep(TRUE, 3))) rbrk <- brk_res(brk_manual(1:3, rep(FALSE, 3))) expect_equal(lbl_discrete()(lbrk), c("1", "2")) expect_equal(lbl_discrete()(rbrk), c("2", "3")) lbrk2 <- brk_res(brk_manual(c(1, 3, 5), rep(TRUE, 3))) em_dash <- em_dash() expect_equal(lbl_discrete()(lbrk2), paste0(c(1, 3), em_dash, c(2, 4))) expect_equal(lbl_discrete(" to ")(lbrk2), c("1 to 2", "3 to 4")) lbrk3 <- brk_res(brk_default(c(1, 3, 3, 5)), close_end = TRUE) expect_equal(lbl_discrete("-")(lbrk3), c("1-2", "3", "4-5")) # break containing (1,2) which has no integer in it: open_brk <- brk_res(brk_manual(1:3, c(FALSE, TRUE, FALSE))) expect_warning(l <- lbl_discrete()(open_brk)) expect_equal(l[1], "--") }) test_that("lbl_discrete arguments", { lbrk <- brk_res(brk_default(c(1, 3, 5))) expect_equal( lbl_discrete("-", fmt = "(%s)")(lbrk), c("(1)-(2)", "(3)-(5)") ) brackets <- function (x) paste0("(", x, ")") expect_equal( lbl_discrete("-", fmt = brackets)(lbrk), c("(1)-(2)", "(3)-(5)") ) expect_equal( lbl_discrete("-", fmt = list(nsmall = 1))(lbrk), c("1.0-2.0", "3.0-5.0") ) expect_equal( lbl_discrete("-", first = "<= {r}")(lbrk), c("<= 2", "3-5") ) expect_equal( lbl_discrete("-", last = ">= {l}")(lbrk), c("1-2", ">= 3") ) sbrk <- brk_res(brk_default(c(1, 3, 3, 6))) expect_equal( lbl_discrete("-", single = "[{l}]")(sbrk), c("1-2", "[3]", "4-6") ) brk1000 <- brk_res(brk_default(c(1, 3, 5) * 1000)) expect_equal( lbl_discrete("-", unit = 1000)(brk1000), c("1000-2000", "3000-5000") ) }) test_that("santoku.infinity", { withr::local_options(santoku.infinity = NULL) brk12 <- brk_res(brk_default(1:2), extend = TRUE) if (l10n_info()[["UTF-8"]]) { expect_match(format(brk12), "\u221e") } else { expect_match(format(brk12), "Inf") } withr::with_options(list(santoku.infinity = "oo"), { expect_match(format(brk12), "oo") }) }) test_that("bug: breaks labels don't produce duplicates", { brk <- brk_res(brk_default(c(1.333333335, 1.333333336, 1.333333337, 5))) lbls <- lbl_intervals()(brk) expect_equal(anyDuplicated(lbls), 0) lbls <- lbl_dash()(brk) expect_equal(anyDuplicated(lbls), 0) brk <- brk_res(brk_quantiles(seq(0, 1, 0.0001)), x = rnorm(10000)) lbls <- lbl_intervals()(brk) expect_equal(anyDuplicated(lbls), 0) lbls <- lbl_dash()(brk) expect_equal(anyDuplicated(lbls), 0) }) test_that("bug: lbl_endpoints() works with no format and non-standard breaks", { expect_error( chop_quantiles(0:10, 0.5, labels = lbl_endpoints()) , NA) expect_error( chop_mean_sd(0:10, labels = lbl_endpoints()) , NA) }) test_that("lbl_date collapses shared date components", { withr::local_locale(c(LC_TIME = "C")) brk_same_month <- brk_res(brk_default(as.Date(c("2000-01-13", "2000-01-15")))) expect_equal( lbl_date(fmt = "%d %b %Y")(brk_same_month), "13-15 Jan 2000" ) brk_same_year <- brk_res(brk_default(as.Date(c("2000-01-13", "2000-02-15")))) expect_equal( lbl_date(fmt = "%d %b %Y")(brk_same_year), "13 Jan - 15 Feb 2000" ) brk_diff_year <- brk_res(brk_default(as.Date(c("2000-01-13", "2001-01-15")))) expect_equal( lbl_date(fmt = "%d %b %Y")(brk_diff_year), "13 Jan 2000 - 15 Jan 2001" ) }) test_that("lbl_datetime collapses shared datetime components", { withr::local_locale(c(LC_TIME = "C")) brk_same_day <- brk_res(brk_default(as.POSIXlt(c( "2000-01-12 11:15:00", "2000-01-12 11:45:00" ), tz = "UTC"))) expect_equal( lbl_datetime(fmt = "%I.%M %b %d %Y")(brk_same_day), "11.15-11.45 Jan 12 2000" ) }) test_that("lbl_date can apply discrete non-overlapping labels", { withr::local_locale(c(LC_TIME = "C")) brk <- brk_res(brk_default(as.Date(c("2000-01-13", "2000-01-15", "2000-01-17")))) expect_equal( lbl_date(fmt = "%d %b %Y")(brk), c("13-14 Jan 2000", "15-17 Jan 2000") ) expect_equal( lbl_date(fmt = "%d %b %Y", unit = NULL)(brk), c("13-15 Jan 2000", "15-17 Jan 2000") ) }) test_that("lbl_datetime can apply discrete non-overlapping labels", { withr::local_locale(c(LC_TIME = "C")) brk <- brk_res(brk_default(as.POSIXct(c( "2000-01-12 11:00:00", "2000-01-12 12:00:00", "2000-01-12 13:00:00" ), tz = "UTC"))) expect_equal( lbl_datetime(fmt = "%H:%M", unit = as.difftime(1, units = "mins"))(brk), c("11:00-11:59", "12:00-13:00") ) expect_equal( lbl_datetime(fmt = "%H:%M")(brk), c("11:00-12:00", "12:00-13:00") ) }) test_that("lbl_date collapses shared greater components even when they are prefixes", { withr::local_locale(c(LC_TIME = "C")) brk <- brk_res(brk_default(as.Date(c( "2026-05-01", "2026-05-15", "2026-05-29", "2026-06-13" )))) expect_equal( lbl_date(fmt = "%b %e", unit = as.difftime(1, units = "days"))(brk), c("May 1-14", "May 15-28", "May 29 - Jun 13") ) }) test_that("lbl_date collapses around first differing component", { withr::local_locale(c(LC_TIME = "C")) brk <- brk_res(brk_default(as.Date(c("2006-05-13", "2006-05-14")))) expect_equal( lbl_date(fmt = "%d %b", unit = NULL)(brk), "13-14 May" ) expect_equal( lbl_date(fmt = "%b %d, %Y", unit = NULL)(brk), "May 13-14, 2006" ) brk <- brk_res(brk_default(as.Date(c("2006-05-13", "2006-06-14")))) expect_equal( lbl_date(fmt = "%b %d, %Y", unit = NULL)(brk), "May 13 - Jun 14, 2006" ) }) test_that("lbl_datetime keeps month when both day and time differ", { withr::local_locale(c(LC_TIME = "C")) brk <- brk_res(brk_default(as.POSIXct(c( "2020-05-30 22:00:00", "2020-05-31 02:00:00" ), tz = "UTC"))) expect_equal( lbl_datetime(fmt = "%H:%M %d %b", unit = NULL)(brk), "22:00 30 May - 02:00 31 May" ) }) ================================================ FILE: tests/testthat/test-nonstandard.R ================================================ test_that("character", { x <- LETTERS br <- c("F", "M") expect_warning( chop(x, br) ) withr::local_options(santoku.warn_character = FALSE) expect_silent( chop(x, br) ) # here, we think there should *always* be a warning expect_warning( chop(x, br, extend = TRUE) ) expect_silent( chop_equally(x, 13) ) expect_silent( chop_n(x, 13) ) }) test_that("ordered", { x <- ordered(1:10) br <- ordered(c(5, 8), levels = levels(x)) expect_silent( chop(x, br) ) # here, we think there should *always* be a warning expect_warning( chop(x, br, extend = TRUE) ) expect_silent( chop_n(x, 5) ) expect_silent( chop_equally(x, groups = 2) ) }) test_that("hexmode", { x <- as.hexmode(1:10 + 10) br <- as.hexmode(c(13, 15, 15, 18)) expect_silent( chop(x, br, extend = FALSE) ) expect_silent( chop(x, br) ) # here, there happens to be a warning as of 0.7.0.9000, # but we'd be happy if we could represent +/- Inf as hexmode suppressWarnings(expect_error( chop(x, br, extend = TRUE), regexp = NA )) }) test_that("octmode", { x <- as.octmode(1:10 + 10) br <- as.octmode(c(13, 15, 15, 18)) expect_silent( chop(x, br, extend = FALSE) ) expect_silent( chop(x, br) ) expect_silent( chop(x, c(12, 15, 15, 18)) ) expect_silent( chop(1:10 + 10, br) ) suppressWarnings(expect_error( chop(x, br, extend = TRUE), regexp = NA )) }) test_that("stat::ts", { x <- ts(1:10) # note: we need to specify integer breaks # vec_cast can't cope with ts() and ts() br <- c(5L, 8L) expect_silent( chop(x, br) ) x <- ts(c(1.0, 3.0, 5.0)) br <- c(2.0, 4.0) expect_silent( chop(x, br) ) expect_silent( chop(x, br, extend = TRUE) ) expect_silent( chop_equally(x, groups = 3) ) expect_silent( chop_width(x, width = 2) ) x <- ts(1:10) br <- ts(c(5.0, 8.0)) expect_silent( chop(x, br) ) }) test_that("zoo::zoo", { skip_if_not_installed("zoo") x <- zoo::zoo(1:10, 1:10) expect_silent( chop(x, c(3, 5, 5, 7)) ) suppressWarnings(expect_error( # gives a warning but no error as of 0.7.0.9000 chop(x, c(3, 5, 5, 7), extend = TRUE), regexp = NA )) expect_silent( chop_width(x, 2) ) expect_silent( chop_equally(x, 2) ) }) test_that("xts::xts", { skip_if_not_installed("xts") x <- xts::xts(1:10, Sys.Date() + 1:10) expect_silent( chop(x, c(3, 5, 5, 7)) ) suppressWarnings(expect_error( # gives a warning but no error as of 0.7.0.9000 chop(x, c(3, 5, 5, 7), extend = TRUE), regexp = NA )) expect_silent( chop_width(x, 2) ) expect_silent( chop_equally(x, 2) ) }) test_that("units::units", { skip_if_not_installed("units") x <- units::set_units(1:10, cm) br <- units::set_units(c(3, 5, 5, 8), cm) br_mm <- units::set_units(c(30, 50, 50, 80), mm) expect_silent( chop(x, br) ) expect_silent( chop(x, br, extend = TRUE) ) expect_equal( as.numeric(chop(x, br_mm)), c(1, 1, 2, 2, 3, 4, 4, 5, 5, 5) ) br_m2 <- units::set_units(c(3,5,5,8), m^2) expect_error( chop(x, br_m2) ) expect_silent( chopped <- chop_width(x, units::set_units(0.05, m)) ) expect_equal( as.numeric(chopped), c(rep(1, 5), rep(2, 5)) ) start <- units::set_units(20, mm) expect_silent( chopped <- chop_width(x, units::set_units(0.05, m), start) ) expect_equal( as.numeric(chopped), c(1, rep(2, 5), rep(3, 4)) ) expect_silent( chopped <- chop_evenly(x, intervals = 2) ) expect_equal( as.numeric(chopped), c(rep(1, 5), rep(2, 5)) ) expect_silent( chop_equally(x, 5) ) expect_silent( chop_n(x, 3) ) expect_silent( chop(x, br, labels = lbl_discrete(unit = units::set_units(1, cm))) ) # we don't support mixed units, since units doesn't support # comparison operators on those }) test_that("package_version", { x <- as.package_version(c("0.5", "0.5.1", "1.0", "1.1.1", "1.2.0")) br <- as.package_version(c("0.7", "1.1", "1.2.0")) expect_silent( chop(x, br) ) expect_warning( chop(x, br, extend = TRUE) ) }) test_that("difftime", { days <- as.Date("1970-01-01") + 0:30 difftimes_d <- days[10:15] - days[12:7] difftimes_h <- difftimes_d units(difftimes_h) <- "hours" expect_silent( chop(difftimes_d, difftimes_d[c(3,5)]) ) expect_silent( chop(difftimes_d, difftimes_h) ) expect_silent( chop(difftimes_d, difftimes_d[c(3,5)], extend = TRUE) ) }) test_that("bit64", { skip_if_not_installed("bit64") x64 <- bit64::as.integer64(1:10) b64 <- bit64::as.integer64(c(3, 5, 5, 7)) expect_silent( chop(x64, b64) ) expect_silent( chopped <- chop(x64, b64, extend = TRUE) ) expect_equal( as.numeric(chopped), c(1, 1, 2, 2, 3, 4, 5, 5, 5, 5) ) expect_silent( chop(1:10, b64) ) expect_silent( chop(x64, as.integer(c(3, 5, 5, 7))) ) expect_silent( chop(x64, c(3, 5, 5, 7)) ) expect_silent( chop(c(1, 3, 5, 7), b64) ) expect_equal( chop(x64, c(2.5, 7.5), labels = letters[1:3]), factor(c(1, 1, 2, 2, 2, 2, 2, 3, 3, 3), labels = letters[1:3]) ) x64_big <- bit64::as.integer64("1000000000000000000") + 1:10 b64_big <- bit64::as.integer64("1000000000000000000") + c(3, 5, 5, 7) expect_silent( chop(x64_big, b64_big) ) expect_warning( chop(c(bit64::as.integer64(1), x64_big), 2.5) ) }) test_that("hms::hms", { skip_if_not_installed("hms") x <- hms::hms(minutes = 1:180) br <- hms::hms(hours = 1:2) expect_silent( chopped <- chop(x, br) ) expect_equal( as.numeric(chopped), rep(1:3, c(59, 60, 61)), ignore_attr = TRUE ) expect_silent( chop(x, br, extend = TRUE) ) }) test_that("haven::labelled", { skip_if_not_installed("haven") x <- haven::labelled(as.double(1:10), c("Lo" = 1, "Hi" = 10)) br <- haven::labelled(c(3, 5), c("Mid" = 3, "Mid2" = 5)) expect_silent( chop(x, c(2, 5, 5, 8)) ) expect_silent( chop(x, br) ) expect_silent( chop(x, br, extend = TRUE) ) }) test_that("haven::labelled works with lbl_discrete() when breaks are extended", { skip_if_not_installed("haven") x <- haven::labelled(1:5) expect_silent( chop(x, 2:3, labels = lbl_discrete()) ) }) ================================================ FILE: tests/testthat/test-tab.R ================================================ test_that("tab", { expect_identical( tab(1:5, c(2, 4), letters[1:3]), table(x = c("a", "b", "b", "c", "c"), useNA = "ifany", dnn = NULL) ) expect_identical( tab(1:5, c(2, 4), letters[1:3]), table(x = chop(1:5, c(2, 4), letters[1:3], drop = FALSE), useNA = "ifany", dnn = NULL) ) }) test_that("tab_n", { expect_identical( tab_n(1:9, 3, lbl_seq()), table(x = rep(c("a", "b", "c"), 3), useNA = "ifany", dnn = NULL) ) }) test_that("tab_width", { expect_identical( tab_width(0:7, 2), table(x = c(rep(c("[0, 2)", "[2, 4)", "[4, 6)", "[6, 8]"), 2)), dnn = NULL) ) }) test_that("tab_evenly", { expect_identical( tab_evenly(0:10, 5), table(x = c(rep(c("[0, 2)", "[2, 4)", "[4, 6)", "[6, 8)", "[8, 10]"), 2), "[8, 10]"), dnn = NULL) ) }) test_that("tab_proportions", { expect_identical( tab_proportions(0:10, c(0.2, 0.8)), table(rep(c("[0, 2)", "[2, 8)", "[8, 10]"), c(2, 6, 3)), dnn = NULL) ) }) test_that("tab_mean_sd", { expect_silent( tb <- tab_mean_sd(rnorm(100), sds = 1:3, extend = TRUE, drop = FALSE) ) expect_equal(length(tb), 8) }) test_that("tab_pretty", { expect_silent( tb <- tab_pretty(1:9) ) expect_equal(length(tb), 5) }) test_that("tab_quantiles", { expect_identical( tab_quantiles(1:4, c(0, .25, .5, .75, 1)), table(x = c("[0%, 25%)", "[25%, 50%)", "[50%, 75%)", "[75%, 100%]"), dnn = NULL) ) }) test_that("tab_deciles", { expect_identical( tab_deciles(0:9), table(x = c("[0%, 10%)", "[10%, 20%)", "[20%, 30%)", "[30%, 40%)", "[40%, 50%)", "[50%, 60%)", "[60%, 70%)", "[70%, 80%)", "[80%, 90%)", "[90%, 100%]"), dnn = NULL) ) }) test_that("tab_equally", { expect_identical( tab_equally(1:4, 4), table(x = c("[1, 1.75)", "[1.75, 2.5)", "[2.5, 3.25)", "[3.25, 4]"), dnn = NULL) ) }) test_that("tab_fn", { expect_equal( tab_fn(1:5, median), table(factor(c(rep("[1, 3)", 2), rep("[3, 5]", 3)))), ignore_attr = TRUE ) }) test_that("tab_spikes", { expect_equal( tab_spikes(c(1:5, rep(3, 2)), breaks = c(2, 4), n = 3), table(factor( c("[1, 2)", "[2, 3)", rep("{3}", 3), rep("[4, 5]", 2)), levels = c("[1, 2)", "[2, 3)", "{3}", "[4, 5]") )), ignore_attr = TRUE ) }) test_that("tab_dissect", { expect_equal( tab_dissect(c(1:5, rep(3, 2)), breaks = c(2, 4), n = 3), table(factor( c("[1, 2)", "[2, 4)", rep("{3}", 3), rep("[4, 5]", 2)), levels = c("[1, 2)", "[2, 4)", "{3}", "[4, 5]") )), ignore_attr = TRUE ) }) ================================================ FILE: tests/testthat/test-zzz-systematic.R ================================================ test_that("systematic tests", { x_vals <- list( ordinary = 4:1, real = 4:1 + 0.5, NAs = c(NA, 1:3), all_NAs = c(NA_real_, NA_real_, NA_real_), inf = c(-Inf, Inf, 1:3), inf_lo = c(-Inf, 1:3), inf_hi = c(Inf, 1:3), "NaN" = c(NaN, 1:3), same = rep(1, 3), one = 3, none = numeric(0), char = letters[1:3], complex = 1:3 + 1i, Date = as.Date("1950-01-01") + 0:20, POSIXct = as.POSIXct("2000-01-01") + 0:30 ) brk_funs <- list( brk_evenly = expression(brk_evenly(2)), brk_proportions = expression(brk_proportions(c(0.25, 0.6))), brk_manual = expression(brk_manual(1:3, c(FALSE, TRUE, FALSE))), brk_mean_sd = expression(brk_mean_sd()), brk_pretty = expression(brk_pretty()), brk_n = expression(brk_n(5)), brk_n_merge = expression(brk_n(5, tail = "merge")), brk_quantiles = expression(brk_quantiles(1:3/4)), brk_default = expression(brk_default(1:3)), brk_default2 = expression(brk_default(c(1, 2, 2, 3))), brk_default_lo = expression(brk_default(1)), brk_default_hi = expression(brk_default(5)), brk_width = expression(brk_width(1)), brk_width2 = expression(brk_width(1, 0)), brk_spikes = expression(brk_spikes(1:3, n = 2)), brk_w_difft_day = expression(brk_width(as.difftime(5, units = "days"))), brk_w_difft_sec = expression(brk_width(as.difftime(5, units = "secs"))), brk_def_Date = expression(brk_default(as.Date("1950-01-05") + c(0, 5))), brk_def_POSIXct = expression(brk_default(as.POSIXct("2000-01-01") + c(10, 20))) ) lbl_funs <- list( lbl_dash = expression(lbl_dash()), lbl_intervals = expression(lbl_intervals()), lbl_seq = expression(lbl_seq("a")), lbl_endpoints = expression(lbl_endpoints()), lbl_midpoints = expression(lbl_midpoints()), lbl_date = expression(lbl_date()), lbl_datetime = expression(lbl_datetime()) ) # how to investigate a failure: # 1. Note the seed and row. # 2. Manually run from here to END TEST DATA CREATION below # 3. Look at test_df[row,] or test_df[test_df$row == row, ] # 4. Go to the for loop below, set r to the row and seed to the seed # and run relevant lines. test_df <- expand.grid( x = x_vals, brk_fun = names(brk_funs), lbl_fun = names(lbl_funs), # we translate NA to NULL in chop(); doing this means we don't need a list(): extend = c(TRUE, FALSE, NA), left = c(TRUE, FALSE), close_end = c(TRUE, FALSE), # ditto: raw = c(TRUE, FALSE, NA), drop = c(TRUE, FALSE), stringsAsFactors = FALSE ) # remove some pointless conditions: skip_test <- function (cond) { cond <- substitute(cond) test_df <<- test_df[with(test_df, ! eval(cond)), ] } skip_test(! left & brk_fun == "brk_manual") skip_test(! close_end & brk_fun == "brk_manual") POSIXct_breaks <- c("brk_def_POSIXct", "brk_w_difft_sec") Date_breaks <- c("brk_def_Date", "brk_w_difft_day") skip_test(names(x) %in% c("Date", "POSIXct") & ! brk_fun %in% c(Date_breaks, POSIXct_breaks)) skip_test(! names(x) %in% c("Date", "POSIXct") & brk_fun %in% c(Date_breaks, POSIXct_breaks)) # don't try to break dates by 1 second width (very slow!) skip_test(names(x) != "POSIXct" & brk_fun == "brk_w_difft_sec") skip_test(names(x) != "Date" & lbl_fun == "lbl_date") skip_test(names(x) != "POSIXct" & lbl_fun == "lbl_datetime") test_df$expect <- "succeed" test_df$row <- seq_len(nrow(test_df)) # END TEST DATA CREATION # some things should fail should_fail <- function (cond) test_df$expect[cond] <<- "error" should_warn <- function (cond) test_df$expect[cond] <<- "warn" should_either <- function (cond) test_df$expect[cond] <<- "either" dont_care <- function (cond) test_df <<- test_df[! cond, ] should_fail(names(test_df$x) == "char") # but if we break by quantities, OK... char_by_quantities <- names(test_df$x) == "char" & test_df$brk_fun %in% c("brk_equally", "brk_quantiles", "brk_n", "brk_n_merge") # so long as we aren't trying raw midpoints raw <- ! is.na(test_df$raw) & test_df$raw should_warn(char_by_quantities & ! (test_df$lbl_fun == "lbl_midpoints" & raw) ) # ... or midpoints with brk_n() should_fail(char_by_quantities & test_df$lbl_fun == "lbl_midpoints" & test_df$brk_fun %in% c("brk_n", "brk_n_merge")) # brk_default_hi and _lo have a single break, so if you can't # extend it, there are no possible intervals: should_fail(with(test_df, brk_fun %in% c("brk_default_hi", "brk_default_lo") & extend == FALSE )) # ditto when extend is NULL and there's no non-NA data # here we have to fail even though with some data we'd be OK should_fail(with(test_df, brk_fun %in% c("brk_default_hi", "brk_default_lo") & names(x) %in% c("all_NAs", "none") & is.na(extend) )) # raw endpoints get duplicated if multiple quantiles are infinite: dont_care(with(test_df, names(x) %in% c("inf_lo", "inf_hi") & brk_fun == "brk_quantiles" & lbl_fun == "lbl_midpoints" & raw == TRUE & extend == TRUE & close_end == FALSE )) dont_care(with(test_df, names(x) == "inf_lo" & brk_fun == "brk_quantiles" & lbl_fun == "lbl_endpoints" & raw == TRUE & extend == TRUE & left == FALSE & close_end == FALSE )) # lbl_endpoints() can create duplicates # when you extend an open interval to add a singleton # e.g. {1}, (1, 2] dont_care(with(test_df, lbl_fun == "lbl_endpoints" & left == FALSE & is.na(extend) )) dont_care(with(test_df, lbl_fun == "lbl_endpoints" & brk_fun %in% c("brk_default_lo", "brk_manual") & left == TRUE & is.na(extend) )) # quantiles here likely to create duplicate endpoints dont_care(with(test_df, names(x) %in% c("one", "same", "char") & lbl_fun == "lbl_endpoints" & brk_fun == "brk_quantiles" & extend == TRUE & raw == TRUE )) # brk_quantiles() should warn on duplicate quantiles should_warn(with(test_df, names(x) %in% c("one", "same") & brk_fun == "brk_quantiles" )) # brk_default has breaks 1,2,2,3 # with lbl_endpoints, this may create duplicate left endpoints # ie the user asked for something we can't do dont_care(with(test_df, names(x) %in% c("ordinary", "inf", "inf_lo", "inf_hi", "NaN", "NAs") & brk_fun == "brk_default2" & lbl_fun == "lbl_endpoints" )) dont_care(with(test_df, brk_fun == "brk_default2" & lbl_fun == "lbl_endpoints" & drop == FALSE )) dont_care(with(test_df, brk_fun == "brk_spikes" & lbl_fun == "lbl_endpoints" & drop == FALSE )) dont_care(with(test_df, brk_fun %in% c("brk_n", "brk_n_merge") & lbl_fun == "lbl_endpoints" )) # lbl_midpoints struggles with Inf for obvious reasons dont_care(with(test_df, names(x) %in% c("inf", "inf_lo", "inf_hi") & brk_fun %in% c("brk_n", "brk_n_merge") & lbl_fun == "lbl_midpoints" )) should_fail(names(test_df$x) == "complex") # we sample the same 10000 rows every day seed <- as.numeric(Sys.Date()) set.seed(seed) test_everything <- isTRUE(as.logical(Sys.getenv("CI"))) || getOption("santoku.test_everything", FALSE) sample_rows <- if (test_everything) { seq_len(nrow(test_df)) } else { sort(sample(nrow(test_df), 10000, replace = FALSE)) } for (r in sample_rows) { # start manual debugging here with r and seed set tdata <- test_df[r, ] if (is.na(tdata$expect)) next if (is.na(tdata$extend)) tdata$extend <- NULL if (is.na(tdata$raw)) tdata$raw <- NULL x <- tdata$x[[1]] format_null <- function (x) if (is.null(x)) "NULL" else x info <- sprintf( "seed: %s row: %s command: chop(%s, %s, labels = %s, extend = %s, left = %s, close_end = %s, raw = %s, drop = %s)", seed, tdata$row, tdata$x, as.character(brk_funs[[tdata$brk_fun]]), as.character(lbl_funs[[tdata$lbl_fun]]), format_null(tdata$extend), tdata$left, tdata$close_end, format_null(tdata$raw), tdata$drop) # NA means "no error": regexp <- switch(tdata$expect, "succeed" = NA, NULL) err_class <- switch(tdata$expect, "warn" = "warning", "either" = NULL, "error") exp_fn <- if (tdata$expect == "error") expect_error else expect_condition # suppressWarnings or we drown in them: suppressWarnings(exp_fn( chop(!!x, breaks = eval(brk_funs[[!!tdata$brk_fun]]), labels = eval(lbl_funs[[!!tdata$lbl_fun]]), extend = !!tdata$extend, left = !!tdata$left, close_end = !!tdata$close_end, raw = !!tdata$raw, drop = !!tdata$drop ), regexp = regexp, class = err_class, info = info )) } }) ================================================ FILE: tests/testthat.R ================================================ library(testthat) library(santoku) test_check("santoku") ================================================ FILE: vignettes/.gitignore ================================================ *.html *.R ================================================ FILE: vignettes/santoku.Rmd ================================================ --- title: "Introduction to santoku" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{Introduction to santoku} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} set.seed(23479) knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(digits = 4) ``` ## Introduction Santoku is a package for cutting data into intervals. It provides `chop()`, a replacement for base R's `cut()` function, as well as several convenience functions to cut different kinds of intervals. To install santoku, run: ``` r install.packages("santoku") ``` ## Basic usage Use `chop()` like `cut()`, to cut numeric data into intervals between a set of `breaks`. ```{r} library(santoku) x <- runif(10, 0, 10) (chopped <- chop(x, breaks = 0:10)) data.frame(x, chopped) ``` `chop()` returns a factor. If data is beyond the limits of `breaks`, they will be extended automatically: ```{r} chopped <- chop(x, breaks = 3:7) data.frame(x, chopped) ``` To chop a single number into a separate category, put the number twice in `breaks`: ```{r} x_fives <- x x_fives[1:5] <- 5 chopped <- chop(x_fives, c(2, 5, 5, 8)) data.frame(x_fives, chopped) ``` To quickly produce a table of chopped data, use `tab()`: ```{r} tab(1:10, c(2, 5, 8)) ``` ## Chopping by width and number of elements To chop into fixed-width intervals, starting at the minimum value, use `chop_width()`: ```{r} chopped <- chop_width(x, 2) data.frame(x, chopped) ``` To chop into a fixed number of intervals, each with the same width, use `chop_evenly()`: ```{r} chopped <- chop_evenly(x, intervals = 3) data.frame(x, chopped) ``` To chop into groups with a fixed number of elements, use `chop_n()`: ```{r} chopped <- chop_n(x, 4) table(chopped) ``` To chop into a fixed number of groups, each with the same number of elements, use `chop_equally()`: ```{r} chopped <- chop_equally(x, groups = 5) table(chopped) ``` To chop data up by quantiles, use `chop_quantiles()`: ```{r} chopped <- chop_quantiles(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) ``` To chop data up by proportions of the data range, use `chop_proportions()`: ```{r} chopped <- chop_proportions(x, c(0.25, 0.5, 0.75)) data.frame(x, chopped) ``` You can think of these six functions as logically arranged in a table. To chop into... | Sizing intervals by... | :------------------------------|:--------------------------|:----------------------   | number of elements: | interval width: a specific number of equal intervals... | `chop_equally()` | `chop_evenly()` intervals of one specific size... | `chop_n()` | `chop_width()` intervals of different specific sizes... | `chop_quantiles()` | `chop_proportions()` : Different ways to chop by size ## Even more ways to chop To chop data by standard deviations around the mean, use `chop_mean_sd()`: ```{r} chopped <- chop_mean_sd(x) data.frame(x, chopped) ``` To chop data into attractive intervals, use `chop_pretty()`. This selects intervals which are a multiple of 2, 5 or 10. It's useful for producing bar plots. ```{r} chopped <- chop_pretty(x) data.frame(x, chopped) ``` ## Isolating common values In exploratory work, it's sometimes useful to find common values and treat them differently. You can use `dissect()` to do this: ```{r} x_spike <- rnorm(100) x_spike[1:50] <- x_spike[1] chopped <- dissect(x_spike, -3:3, prop = 0.1) table(chopped) ``` `prop = 0.2` will put any unique value of `x` into its own separate category if it makes up at least 20% of the data. Note that unlike all the other `chop_*` functions, `dissect()` doesn't always categorize `x` into ordered, connected intervals. To remind you of this, it is named differently. If you want to create separate intervals on the left and right of common elements, use `chop_spikes()`: ```{r} chopped <- chop_spikes(x_spike, -3:3, prop = 0.1) table(chopped) ``` Compare this to the table before. There are two intervals on either side of the common value, instead of one interval surrounding it. ## Quick tables `tab_n()`, `tab_width()`, and friends act similarly to `tab()`, calling the related `chop_*` function and then `table()` on the result. ```{r} tab_n(x, 4) tab_width(x, 2) tab_evenly(x, 5) tab_mean_sd(x) ``` ## Specifying labels By default, santoku labels intervals using mathematical notation: * `[0, 1]` means all numbers between 0 and 1 inclusive. * `(0, 1)` means all numbers _strictly_ between 0 and 1, not including the endpoints. * `[0, 1)` means all numbers between 0 and 1, including 0 but not 1. * `(0, 1]` means all numbers between 0 and 1, including 1 but not 0. * `{0}` means just the number 0. To override these labels, provide names to the `breaks` argument: ```{r} chopped <- chop(x, c(Lowest = 1, Low = 2, Higher = 5, Highest = 8)) data.frame(x, chopped) ``` Or, you can specify factor labels with the `labels` argument: ```{r} chopped <- chop(x, c(2, 5, 8), labels = c("Lowest", "Low", "Higher", "Highest")) data.frame(x, chopped) ``` You need as many labels as there are intervals - one fewer than `length(breaks)` if your data doesn't extend beyond `breaks`, one more than `length(breaks)` if it does. To label intervals with a dash, use `lbl_dash()`: ```{r} chopped <- chop(x, c(2, 5, 8), labels = lbl_dash()) data.frame(x, chopped) ``` To label integer data, use `lbl_discrete()`. It uses more informative right endpoints: ```{r} chopped <- chop(1:10, c(2, 5, 8), labels = lbl_discrete()) chopped2 <- chop(1:10, c(2, 5, 8), labels = lbl_dash()) data.frame(x = 1:10, lbl_discrete = chopped, lbl_dash = chopped2) ``` You can customize the first or last labels: ```{r} chopped <- chop(x, c(2, 5, 8), labels = lbl_dash(first = "< 2", last = "8+")) data.frame(x, chopped) ``` To label intervals in order use `lbl_seq()`: ```{r} chopped <- chop(x, c(2, 5, 8), labels = lbl_seq()) data.frame(x, chopped) ``` You can use numerals or even roman numerals: ```{r} chop(x, c(2, 5, 8), labels = lbl_seq("(1)")) chop(x, c(2, 5, 8), labels = lbl_seq("i.")) ``` Other labelling functions include: * `lbl_endpoints()` - use left endpoints as labels * `lbl_midpoints()` - use interval midpoints as labels * `lbl_glue()` - specify labels flexibly with the `{glue}` package ## Specifying breaks By default, `chop()` extends `breaks` if necessary. If you don't want that, set `extend = FALSE`: ```{r} chopped <- chop(x, c(3, 5, 7), extend = FALSE) data.frame(x, chopped) ``` Data outside the range of `breaks` will become `NA`. By default, intervals are closed on the left, i.e. they include their left endpoints. If you want right-closed intervals, set `left = FALSE`: ```{r} y <- 1:5 data.frame( y = y, left_closed = chop(y, 1:5), right_closed = chop(y, 1:5, left = FALSE) ) ``` By default, the last interval is closed on both ends. If you want to keep the last interval open at the end, set `close_end = FALSE`: ```{r} data.frame( y = y, end_closed = chop(y, 1:5), end_open = chop(y, 1:5, close_end = FALSE) ) ``` # Chopping dates, times and other vectors You can chop many kinds of vectors with santoku, including Date objects... ```{r} y2k <- as.Date("2000-01-01") + 0:10 * 7 data.frame( y2k = y2k, chopped = chop(y2k, as.Date(c("2000-02-01", "2000-03-01"))) ) ``` ... and POSIXct (date-time) objects: ```{r} # hours of the 2020 Crew Dragon flight: crew_dragon <- seq(as.POSIXct("2020-05-30 18:00", tz = "GMT"), length.out = 24, by = "hours") liftoff <- as.POSIXct("2020-05-30 15:22", tz = "America/New_York") dock <- as.POSIXct("2020-05-31 10:16", tz = "America/New_York") data.frame( crew_dragon = crew_dragon, chopped = chop(crew_dragon, c(liftoff, dock), labels = c("pre-flight", "flight", "docked")) ) ``` Note how santoku correctly handles the different timezones. You can use `chop_width()` with objects from the `lubridate` package, to chop by irregular periods such as months: ```{r} library(lubridate) data.frame( y2k = y2k, chopped = chop_width(y2k, months(1)) ) ``` `lbl_date()` produces nicely formatted dates: ```{r} data.frame( y2k = y2k, chopped = chop_width(y2k, days(28), labels = lbl_date()) ) ``` You can also chop vectors with units, using the `units` package: ```{r} library(units) x <- set_units(1:10 * 10, cm) br <- set_units(1:3, ft) data.frame( x = x, chopped = chop(x, br) ) ``` You should be able to chop anything that has a comparison operator. You can even chop character data using lexical ordering. By default santoku emits a warning in this case, to avoid accidentally misinterpreting results: ```{r} chop(letters[1:10], c("d", "f")) ``` If you find a type of data that you can't chop, please [file an issue](https://github.com/hughjonesd/santoku/issues). ================================================ FILE: vignettes/tutorials/chopping-dates-with-santoku.Rmd ================================================ --- title: "Chopping dates and times with santoku" author: "David Hugh-Jones" date: "`r Sys.Date()`" output: xaringan::moon_reader: css: [default, default-fonts] chakra: libs/remark-latest.min.js seal: false lib_dir: libs nature: highlightStyle: tomorrow-night highlightLines: true countIncrementalSlides: false --- class: center, middle # Chopping dates with santoku ## David Hugh-Jones ```{r setup, include = FALSE} library(ggplot2) library(santoku) library(lubridate) options(htmltools.dir.version = FALSE) knitr::opts_chunk$set( fig.height = 2, fig.width = 10, dpi = 144, dev = 'quartz_png', fig.align = "center", out.width = "90%", dev.args = "transparent" ) date_plot <- function (dates, chopped) { chopped_q <- rlang::enquo(chopped) dfr <- data.frame(dates = dates, chopped = chopped) dfr <- dplyr::rename(dfr, "{{chopped}}" := chopped) xscale <- if (inherits(dates, "Date")) { scale_x_date(expand = expansion(mult = .15)) } else { scale_x_datetime(expand = expansion(mult = .15)) } # xscale <- NULL # ggplot(dfr, aes(x = dates, y = 1, color = {{chopped_q}})) + geom_point(size = 4, shape = "circle filled", stroke = 2) + theme_void() + theme( text = element_text(colour = "white", size = 14), legend.text = element_text(colour = "white", size = 15), axis.text.y = element_blank(), axis.text.x = element_text(colour = "white", size = 14), axis.line.x = element_line(colour = "yellow"), axis.ticks.length.x = unit(0.3, "lines"), axis.ticks.x = element_line(colour = "yellow"), rect = element_rect(fill = "transparent", colour = NA), plot.margin = unit(c(4, 4, 4, 4), "mm") ) + labs(y = "") + xscale } ``` --- class: middle, center # Santoku is a package for chopping data. ## It provides `chop()`, a replacement for `base::cut()`. --- class: middle, center # Santoku 0.4.0 can chop dates. --- class: inverse, middle ```{r, results = "hide"} y2k <- as.Date("2000-01-01") + 0:365 y2k_sample <- sample(y2k, 60) brk_dates <- as.Date(c("2000-01-15", "2000-04-01", "2000-08-01")) chopped <- chop(y2k_sample, brk_dates) ``` ```{r, echo = FALSE} date_plot(y2k_sample, chopped) ``` --- class: inverse, top # You can use the usual `chop` functions. ```{r, results = "hide"} quartile <- chop_quantiles(y2k_sample, c(0.25, 0.75)) ``` ```{r, echo = FALSE} date_plot(y2k_sample, quartile) ``` --- class: inverse # `lbl_date()` produces nice labels. ```{r} x <- chop(y2k_sample, brk_dates, labels = lbl_date()) table(x) ``` --- class: inverse # `chop_width()` works. * It accepts `difftime`, `lubridate::Period` and `lubridate::Duration` widths. -- ```{r} x <- chop_width(y2k, months(1), labels = lbl_endpoints(fmt = "%b")) table(x) ``` -- * `Period` objects get the month right. --- class: center, middle # Chopping time works too. --- background-image: url(figures/crew-dragon.jpg) background-size: cover class: inverse, middle ```{r, results = "hide"} crew_dragon <- as.POSIXct(c( liftoff = "2020-05-30 15:22", dock = "2020-05-31 10:16"), tz = "America/New_York") uk_times <- seq(as.POSIXct("2020-05-30 18:00", tz = "GMT"), length.out = 24, by = "hours") stage <- chop(uk_times, crew_dragon, labels = c("pre-flight", "flight", "docked")) ``` ```{r, echo = FALSE, dev.args = list(bg = rgb(.5, .5, .5, .4))} date_plot(uk_times, stage) ``` --- class: inverse # `lbl_datetime()` labels datetimes. ```{r} x <- chop_width(uk_times, hours(4), labels = lbl_datetime(fmt = "%H:%M %b %d")) table(x) ``` --- class: middle center # Happy chopping! [CRAN](https://cran.r-project.org/package=santoku) [github](https://hughjonesd.github.io/santoku) ================================================ FILE: vignettes/tutorials/libs/Proj4Leaflet/proj4-compressed.js ================================================ !function(a){if("object"==typeof exports)module.exports=a();else if("function"==typeof define&&define.amd)define(a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.proj4=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gf;f++)if(!b||2!==f||void 0!==c.z)switch(0===f?(d=g,e="x"):1===f?(d=h,e="y"):(d=i,e="z"),a.axis[f]){case"e":c[e]=d;break;case"w":c[e]=-d;break;case"n":c[e]=d;break;case"s":c[e]=-d;break;case"u":void 0!==c[e]&&(c.z=d);break;case"d":void 0!==c[e]&&(c.z=-d);break;default:return null}return c}},{}],4:[function(a,b,c){var d=Math.PI/2,e=a("./sign");b.exports=function(a){return Math.abs(a)1&&(a=a>1?1:-1),Math.asin(a)}},{}],7:[function(a,b,c){b.exports=function(a){return 1-.25*a*(1+a/16*(3+1.25*a))}},{}],8:[function(a,b,c){b.exports=function(a){return.375*a*(1+.25*a*(1+.46875*a))}},{}],9:[function(a,b,c){b.exports=function(a){return.05859375*a*a*(1+.75*a)}},{}],10:[function(a,b,c){b.exports=function(a){return a*a*a*(35/3072)}},{}],11:[function(a,b,c){b.exports=function(a,b,c){var d=b*c;return a/Math.sqrt(1-d*d)}},{}],12:[function(a,b,c){b.exports=function(a,b,c,d,e){var f,g;f=a/b;for(var h=0;15>h;h++)if(g=(a-(b*f-c*Math.sin(2*f)+d*Math.sin(4*f)-e*Math.sin(6*f)))/(b-2*c*Math.cos(2*f)+4*d*Math.cos(4*f)-6*e*Math.cos(6*f)),f+=g,Math.abs(g)<=1e-10)return f;return NaN}},{}],13:[function(a,b,c){var d=Math.PI/2;b.exports=function(a,b){var c=1-(1-a*a)/(2*a)*Math.log((1-a)/(1+a));if(Math.abs(Math.abs(b)-c)<1e-6)return 0>b?-1*d:d;for(var e,f,g,h,i=Math.asin(.5*b),j=0;30>j;j++)if(f=Math.sin(i),g=Math.cos(i),h=a*f,e=Math.pow(1-h*h,2)/(2*g)*(b/(1-a*a)-f/(1-h*h)+.5/a*Math.log((1-h)/(1+h))),i+=e,Math.abs(e)<=1e-10)return i;return NaN}},{}],14:[function(a,b,c){b.exports=function(a,b,c,d,e){return a*e-b*Math.sin(2*e)+c*Math.sin(4*e)-d*Math.sin(6*e)}},{}],15:[function(a,b,c){b.exports=function(a,b,c){var d=a*b;return c/Math.sqrt(1-d*d)}},{}],16:[function(a,b,c){var d=Math.PI/2;b.exports=function(a,b){for(var c,e,f=.5*a,g=d-2*Math.atan(b),h=0;15>=h;h++)if(c=a*Math.sin(g),e=d-2*Math.atan(b*Math.pow((1-c)/(1+c),f))-g,g+=e,Math.abs(e)<=1e-10)return g;return-9999}},{}],17:[function(a,b,c){var d=1,e=.25,f=.046875,g=.01953125,h=.01068115234375,i=.75,j=.46875,k=.013020833333333334,l=.007120768229166667,m=.3645833333333333,n=.005696614583333333,o=.3076171875;b.exports=function(a){var b=[];b[0]=d-a*(e+a*(f+a*(g+a*h))),b[1]=a*(i-a*(f+a*(g+a*h)));var c=a*a;return b[2]=c*(j-a*(k+a*l)),c*=a,b[3]=c*(m-a*n),b[4]=c*a*o,b}},{}],18:[function(a,b,c){var d=a("./pj_mlfn"),e=1e-10,f=20;b.exports=function(a,b,c){for(var g=1/(1-b),h=a,i=f;i;--i){var j=Math.sin(h),k=1-b*j*j;if(k=(d(h,j,Math.cos(h),c)-a)*(k*Math.sqrt(k))*g,h-=k,Math.abs(k)1e-7?(c=a*b,(1-a*a)*(b/(1-c*c)-.5/a*Math.log((1-c)/(1+c)))):2*b}},{}],21:[function(a,b,c){b.exports=function(a){return 0>a?-1:1}},{}],22:[function(a,b,c){b.exports=function(a,b){return Math.pow((1-a)/(1+a),b)}},{}],23:[function(a,b,c){b.exports=function(a){var b={x:a[0],y:a[1]};return a.length>2&&(b.z=a[2]),a.length>3&&(b.m=a[3]),b}},{}],24:[function(a,b,c){var d=Math.PI/2;b.exports=function(a,b,c){var e=a*c,f=.5*a;return e=Math.pow((1-e)/(1+e),f),Math.tan(.5*(d-b))/e}},{}],25:[function(a,b,c){c.wgs84={towgs84:"0,0,0",ellipse:"WGS84",datumName:"WGS84"},c.ch1903={towgs84:"674.374,15.056,405.346",ellipse:"bessel",datumName:"swiss"},c.ggrs87={towgs84:"-199.87,74.79,246.62",ellipse:"GRS80",datumName:"Greek_Geodetic_Reference_System_1987"},c.nad83={towgs84:"0,0,0",ellipse:"GRS80",datumName:"North_American_Datum_1983"},c.nad27={nadgrids:"@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat",ellipse:"clrk66",datumName:"North_American_Datum_1927"},c.potsdam={towgs84:"606.0,23.0,413.0",ellipse:"bessel",datumName:"Potsdam Rauenberg 1950 DHDN"},c.carthage={towgs84:"-263.0,6.0,431.0",ellipse:"clark80",datumName:"Carthage 1934 Tunisia"},c.hermannskogel={towgs84:"653.0,-212.0,449.0",ellipse:"bessel",datumName:"Hermannskogel"},c.ire65={towgs84:"482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15",ellipse:"mod_airy",datumName:"Ireland 1965"},c.rassadiran={towgs84:"-133.63,-157.5,-158.62",ellipse:"intl",datumName:"Rassadiran"},c.nzgd49={towgs84:"59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993",ellipse:"intl",datumName:"New Zealand Geodetic Datum 1949"},c.osgb36={towgs84:"446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894",ellipse:"airy",datumName:"Airy 1830"},c.s_jtsk={towgs84:"589,76,480",ellipse:"bessel",datumName:"S-JTSK (Ferro)"},c.beduaram={towgs84:"-106,-87,188",ellipse:"clrk80",datumName:"Beduaram"},c.gunung_segara={towgs84:"-403,684,41",ellipse:"bessel",datumName:"Gunung Segara Jakarta"},c.rnb72={towgs84:"106.869,-52.2978,103.724,-0.33657,0.456955,-1.84218,1",ellipse:"intl",datumName:"Reseau National Belge 1972"}},{}],26:[function(a,b,c){c.MERIT={a:6378137,rf:298.257,ellipseName:"MERIT 1983"},c.SGS85={a:6378136,rf:298.257,ellipseName:"Soviet Geodetic System 85"},c.GRS80={a:6378137,rf:298.257222101,ellipseName:"GRS 1980(IUGG, 1980)"},c.IAU76={a:6378140,rf:298.257,ellipseName:"IAU 1976"},c.airy={a:6377563.396,b:6356256.91,ellipseName:"Airy 1830"},c.APL4={a:6378137,rf:298.25,ellipseName:"Appl. Physics. 1965"},c.NWL9D={a:6378145,rf:298.25,ellipseName:"Naval Weapons Lab., 1965"},c.mod_airy={a:6377340.189,b:6356034.446,ellipseName:"Modified Airy"},c.andrae={a:6377104.43,rf:300,ellipseName:"Andrae 1876 (Den., Iclnd.)"},c.aust_SA={a:6378160,rf:298.25,ellipseName:"Australian Natl & S. Amer. 1969"},c.GRS67={a:6378160,rf:298.247167427,ellipseName:"GRS 67(IUGG 1967)"},c.bessel={a:6377397.155,rf:299.1528128,ellipseName:"Bessel 1841"},c.bess_nam={a:6377483.865,rf:299.1528128,ellipseName:"Bessel 1841 (Namibia)"},c.clrk66={a:6378206.4,b:6356583.8,ellipseName:"Clarke 1866"},c.clrk80={a:6378249.145,rf:293.4663,ellipseName:"Clarke 1880 mod."},c.clrk58={a:6378293.645208759,rf:294.2606763692654,ellipseName:"Clarke 1858"},c.CPM={a:6375738.7,rf:334.29,ellipseName:"Comm. des Poids et Mesures 1799"},c.delmbr={a:6376428,rf:311.5,ellipseName:"Delambre 1810 (Belgium)"},c.engelis={a:6378136.05,rf:298.2566,ellipseName:"Engelis 1985"},c.evrst30={a:6377276.345,rf:300.8017,ellipseName:"Everest 1830"},c.evrst48={a:6377304.063,rf:300.8017,ellipseName:"Everest 1948"},c.evrst56={a:6377301.243,rf:300.8017,ellipseName:"Everest 1956"},c.evrst69={a:6377295.664,rf:300.8017,ellipseName:"Everest 1969"},c.evrstSS={a:6377298.556,rf:300.8017,ellipseName:"Everest (Sabah & Sarawak)"},c.fschr60={a:6378166,rf:298.3,ellipseName:"Fischer (Mercury Datum) 1960"},c.fschr60m={a:6378155,rf:298.3,ellipseName:"Fischer 1960"},c.fschr68={a:6378150,rf:298.3,ellipseName:"Fischer 1968"},c.helmert={a:6378200,rf:298.3,ellipseName:"Helmert 1906"},c.hough={a:6378270,rf:297,ellipseName:"Hough"},c.intl={a:6378388,rf:297,ellipseName:"International 1909 (Hayford)"},c.kaula={a:6378163,rf:298.24,ellipseName:"Kaula 1961"},c.lerch={a:6378139,rf:298.257,ellipseName:"Lerch 1979"},c.mprts={a:6397300,rf:191,ellipseName:"Maupertius 1738"},c.new_intl={a:6378157.5,b:6356772.2,ellipseName:"New International 1967"},c.plessis={a:6376523,rf:6355863,ellipseName:"Plessis 1817 (France)"},c.krass={a:6378245,rf:298.3,ellipseName:"Krassovsky, 1942"},c.SEasia={a:6378155,b:6356773.3205,ellipseName:"Southeast Asia"},c.walbeck={a:6376896,b:6355834.8467,ellipseName:"Walbeck"},c.WGS60={a:6378165,rf:298.3,ellipseName:"WGS 60"},c.WGS66={a:6378145,rf:298.25,ellipseName:"WGS 66"},c.WGS7={a:6378135,rf:298.26,ellipseName:"WGS 72"},c.WGS84={a:6378137,rf:298.257223563,ellipseName:"WGS 84"},c.sphere={a:6370997,b:6370997,ellipseName:"Normal Sphere (r=6370997)"}},{}],27:[function(a,b,c){c.greenwich=0,c.lisbon=-9.131906111111,c.paris=2.337229166667,c.bogota=-74.080916666667,c.madrid=-3.687938888889,c.rome=12.452333333333,c.bern=7.439583333333,c.jakarta=106.807719444444,c.ferro=-17.666666666667,c.brussels=4.367975,c.stockholm=18.058277777778,c.athens=23.7163375,c.oslo=10.722916666667},{}],28:[function(a,b,c){c.ft={to_meter:.3048},c["us-ft"]={to_meter:1200/3937}},{}],29:[function(a,b,c){function d(a,b,c){var d;return Array.isArray(c)?(d=g(a,b,c),3===c.length?[d.x,d.y,d.z]:[d.x,d.y]):g(a,b,c)}function e(a){return a instanceof f?a:a.oProj?a.oProj:f(a)}function proj4(a,b,c){a=e(a);var f,g=!1;return"undefined"==typeof b?(b=a,a=h,g=!0):("undefined"!=typeof b.x||Array.isArray(b))&&(c=b,b=a,a=h,g=!0),b=e(b),c?d(a,b,c):(f={forward:function(c){return d(a,b,c)},inverse:function(c){return d(b,a,c)}},g&&(f.oProj=b),f)}var f=a("./Proj"),g=a("./transform"),h=f("WGS84");b.exports=proj4},{"./Proj":2,"./transform":65}],30:[function(a,b,c){var d=Math.PI/2,e=1,f=2,g=3,h=4,i=5,j=484813681109536e-20,k=1.0026,l=.3826834323650898,m=function(a){return this instanceof m?(this.datum_type=h,void(a&&(a.datumCode&&"none"===a.datumCode&&(this.datum_type=i),a.datum_params&&(this.datum_params=a.datum_params.map(parseFloat),(0!==this.datum_params[0]||0!==this.datum_params[1]||0!==this.datum_params[2])&&(this.datum_type=e),this.datum_params.length>3&&(0!==this.datum_params[3]||0!==this.datum_params[4]||0!==this.datum_params[5]||0!==this.datum_params[6])&&(this.datum_type=f,this.datum_params[3]*=j,this.datum_params[4]*=j,this.datum_params[5]*=j,this.datum_params[6]=this.datum_params[6]/1e6+1)),this.datum_type=a.grids?g:this.datum_type,this.a=a.a,this.b=a.b,this.es=a.es,this.ep2=a.ep2,this.datum_type===g&&(this.grids=a.grids)))):new m(a)};m.prototype={compare_datums:function(a){return this.datum_type!==a.datum_type?!1:this.a!==a.a||Math.abs(this.es-a.es)>5e-11?!1:this.datum_type===e?this.datum_params[0]===a.datum_params[0]&&this.datum_params[1]===a.datum_params[1]&&this.datum_params[2]===a.datum_params[2]:this.datum_type===f?this.datum_params[0]===a.datum_params[0]&&this.datum_params[1]===a.datum_params[1]&&this.datum_params[2]===a.datum_params[2]&&this.datum_params[3]===a.datum_params[3]&&this.datum_params[4]===a.datum_params[4]&&this.datum_params[5]===a.datum_params[5]&&this.datum_params[6]===a.datum_params[6]:this.datum_type===g||a.datum_type===g?this.nadgrids===a.nadgrids:!0},geodetic_to_geocentric:function(a){var b,c,e,f,g,h,i,j=a.x,k=a.y,l=a.z?a.z:0,m=0;if(-d>k&&k>-1.001*d)k=-d;else if(k>d&&1.001*d>k)k=d;else if(-d>k||k>d)return null;return j>Math.PI&&(j-=2*Math.PI),g=Math.sin(k),i=Math.cos(k),h=g*g,f=this.a/Math.sqrt(1-this.es*h),b=(f+l)*i*Math.cos(j),c=(f+l)*i*Math.sin(j),e=(f*(1-this.es)+l)*g,a.x=b,a.y=c,a.z=e,m},geocentric_to_geodetic:function(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t=1e-12,u=t*t,v=30,w=a.x,x=a.y,y=a.z?a.z:0;if(o=!1,b=Math.sqrt(w*w+x*x),c=Math.sqrt(w*w+x*x+y*y),b/this.au&&v>p);return r=Math.atan(m/Math.abs(l)),a.x=q,a.y=r,a.z=s,a},geocentric_to_geodetic_noniter:function(a){var b,c,e,f,g,h,i,j,m,n,o,p,q,r,s,t,u,v=a.x,w=a.y,x=a.z?a.z:0;if(v=parseFloat(v),w=parseFloat(w),x=parseFloat(x),u=!1,0!==v)b=Math.atan2(w,v);else if(w>0)b=d;else if(0>w)b=-d;else if(u=!0,b=0,x>0)c=d;else{if(!(0>x))return c=d,void(e=-this.b);c=-d}return g=v*v+w*w,f=Math.sqrt(g),h=x*k,j=Math.sqrt(h*h+g),n=h/j,p=f/j,o=n*n*n,i=x+this.b*this.ep2*o,t=f-this.a*this.es*p*p*p,m=Math.sqrt(i*i+t*t),q=i/m,r=t/m,s=this.a/Math.sqrt(1-this.es*q*q),e=r>=l?f/r-s:-l>=r?f/-r-s:x/q+s*(this.es-1),u===!1&&(c=Math.atan(q/r)),a.x=b,a.y=c,a.z=e,a},geocentric_to_wgs84:function(a){if(this.datum_type===e)a.x+=this.datum_params[0],a.y+=this.datum_params[1],a.z+=this.datum_params[2];else if(this.datum_type===f){var b=this.datum_params[0],c=this.datum_params[1],d=this.datum_params[2],g=this.datum_params[3],h=this.datum_params[4],i=this.datum_params[5],j=this.datum_params[6],k=j*(a.x-i*a.y+h*a.z)+b,l=j*(i*a.x+a.y-g*a.z)+c,m=j*(-h*a.x+g*a.y+a.z)+d;a.x=k,a.y=l,a.z=m}},geocentric_from_wgs84:function(a){if(this.datum_type===e)a.x-=this.datum_params[0],a.y-=this.datum_params[1],a.z-=this.datum_params[2];else if(this.datum_type===f){var b=this.datum_params[0],c=this.datum_params[1],d=this.datum_params[2],g=this.datum_params[3],h=this.datum_params[4],i=this.datum_params[5],j=this.datum_params[6],k=(a.x-b)/j,l=(a.y-c)/j,m=(a.z-d)/j;a.x=k+i*l-h*m,a.y=-i*k+l+g*m,a.z=h*k-g*l+m}}},b.exports=m},{}],31:[function(a,b,c){var d=1,e=2,f=3,g=5,h=6378137,i=.006694379990141316;b.exports=function(a,b,c){function j(a){return a===d||a===e}var k,l,m;if(a.compare_datums(b))return c;if(a.datum_type===g||b.datum_type===g)return c;var n=a.a,o=a.es,p=b.a,q=b.es,r=a.datum_type;if(r===f)if(0===this.apply_gridshift(a,0,c))a.a=h,a.es=i;else{if(!a.datum_params)return a.a=n,a.es=a.es,c;for(k=1,l=0,m=a.datum_params.length;m>l;l++)k*=a.datum_params[l];if(0===k)return a.a=n,a.es=a.es,c;r=a.datum_params.length>3?e:d}return b.datum_type===f&&(b.a=h,b.es=i),(a.es!==b.es||a.a!==b.a||j(r)||j(b.datum_type))&&(a.geodetic_to_geocentric(c),j(a.datum_type)&&a.geocentric_to_wgs84(c),j(b.datum_type)&&b.geocentric_from_wgs84(c),b.geocentric_to_geodetic(c)),b.datum_type===f&&this.apply_gridshift(b,1,c),a.a=n,a.es=o,b.a=p,b.es=q,c}},{}],32:[function(a,b,c){function d(a){var b=this;if(2===arguments.length){var c=arguments[1];"string"==typeof c?"+"===c.charAt(0)?d[a]=f(arguments[1]):d[a]=g(arguments[1]):d[a]=c}else if(1===arguments.length){if(Array.isArray(a))return a.map(function(a){Array.isArray(a)?d.apply(b,a):d(a)});if("string"==typeof a){if(a in d)return d[a]}else"EPSG"in a?d["EPSG:"+a.EPSG]=a:"ESRI"in a?d["ESRI:"+a.ESRI]=a:"IAU2000"in a?d["IAU2000:"+a.IAU2000]=a:console.log(a);return}}var e=a("./global"),f=a("./projString"),g=a("./wkt");e(d),b.exports=d},{"./global":35,"./projString":38,"./wkt":66}],33:[function(a,b,c){var d=a("./constants/Datum"),e=a("./constants/Ellipsoid"),f=a("./extend"),g=a("./datum"),h=1e-10,i=.16666666666666666,j=.04722222222222222,k=.022156084656084655;b.exports=function(a){if(a.datumCode&&"none"!==a.datumCode){var b=d[a.datumCode];b&&(a.datum_params=b.towgs84?b.towgs84.split(","):null,a.ellps=b.ellipse,a.datumName=b.datumName?b.datumName:a.datumCode)}if(!a.a){var c=e[a.ellps]?e[a.ellps]:e.WGS84;f(a,c)}return a.rf&&!a.b&&(a.b=(1-1/a.rf)*a.a),(0===a.rf||Math.abs(a.a-a.b)d?this.ns0=(this.ms1*this.ms1-this.ms2*this.ms2)/(this.qs2-this.qs1):this.ns0=this.con,this.c=this.ms1*this.ms1+this.ns0*this.qs1,this.rh=this.a*Math.sqrt(this.c-this.ns0*this.qs0)/this.ns0)},c.forward=function(a){var b=a.x,c=a.y;this.sin_phi=Math.sin(c),this.cos_phi=Math.cos(c);var d=f(this.e3,this.sin_phi,this.cos_phi),e=this.a*Math.sqrt(this.c-this.ns0*d)/this.ns0,h=this.ns0*g(b-this.long0),i=e*Math.sin(h)+this.x0,j=this.rh-e*Math.cos(h)+this.y0;return a.x=i,a.y=j,a},c.inverse=function(a){var b,c,d,e,f,h;return a.x-=this.x0,a.y=this.rh-a.y+this.y0,this.ns0>=0?(b=Math.sqrt(a.x*a.x+a.y*a.y),d=1):(b=-Math.sqrt(a.x*a.x+a.y*a.y),d=-1),e=0,0!==b&&(e=Math.atan2(d*a.x,d*a.y)),d=b*this.ns0/this.a,this.sphere?h=Math.asin((this.c-d*d)/(2*this.ns0)):(c=(this.c-d*d)/this.ns0,h=this.phi1z(this.e3,c)),f=g(e/this.ns0+this.long0),a.x=f,a.y=h,a},c.phi1z=function(a,b){var c,e,f,g,i,j=h(.5*b);if(d>a)return j;for(var k=a*a,l=1;25>=l;l++)if(c=Math.sin(j),e=Math.cos(j),f=a*c,g=1-f*f,i=.5*g*g/e*(b/(1-k)-c/g+.5/a*Math.log((1-f)/(1+f))),j+=i,Math.abs(i)<=1e-7)return j;return null},c.names=["Albers_Conic_Equal_Area","Albers","aea"]},{"../common/adjust_lon":5,"../common/asinz":6,"../common/msfnz":15,"../common/qsfnz":20}],41:[function(a,b,c){var d=a("../common/adjust_lon"),e=Math.PI/2,f=1e-10,g=a("../common/mlfn"),h=a("../common/e0fn"),i=a("../common/e1fn"),j=a("../common/e2fn"),k=a("../common/e3fn"),l=a("../common/gN"),m=a("../common/asinz"),n=a("../common/imlfn");c.init=function(){this.sin_p12=Math.sin(this.lat0),this.cos_p12=Math.cos(this.lat0)},c.forward=function(a){var b,c,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H=a.x,I=a.y,J=Math.sin(a.y),K=Math.cos(a.y),L=d(H-this.long0);return this.sphere?Math.abs(this.sin_p12-1)<=f?(a.x=this.x0+this.a*(e-I)*Math.sin(L),a.y=this.y0-this.a*(e-I)*Math.cos(L),a):Math.abs(this.sin_p12+1)<=f?(a.x=this.x0+this.a*(e+I)*Math.sin(L),a.y=this.y0+this.a*(e+I)*Math.cos(L),a):(B=this.sin_p12*J+this.cos_p12*K*Math.cos(L),z=Math.acos(B),A=z/Math.sin(z),a.x=this.x0+this.a*A*K*Math.sin(L),a.y=this.y0+this.a*A*(this.cos_p12*J-this.sin_p12*K*Math.cos(L)),a):(b=h(this.es),c=i(this.es),m=j(this.es),n=k(this.es),Math.abs(this.sin_p12-1)<=f?(o=this.a*g(b,c,m,n,e),p=this.a*g(b,c,m,n,I),a.x=this.x0+(o-p)*Math.sin(L),a.y=this.y0-(o-p)*Math.cos(L),a):Math.abs(this.sin_p12+1)<=f?(o=this.a*g(b,c,m,n,e),p=this.a*g(b,c,m,n,I),a.x=this.x0+(o+p)*Math.sin(L),a.y=this.y0+(o+p)*Math.cos(L),a):(q=J/K,r=l(this.a,this.e,this.sin_p12),s=l(this.a,this.e,J),t=Math.atan((1-this.es)*q+this.es*r*this.sin_p12/(s*K)),u=Math.atan2(Math.sin(L),this.cos_p12*Math.tan(t)-this.sin_p12*Math.cos(L)),C=0===u?Math.asin(this.cos_p12*Math.sin(t)-this.sin_p12*Math.cos(t)):Math.abs(Math.abs(u)-Math.PI)<=f?-Math.asin(this.cos_p12*Math.sin(t)-this.sin_p12*Math.cos(t)):Math.asin(Math.sin(L)*Math.cos(t)/Math.sin(u)),v=this.e*this.sin_p12/Math.sqrt(1-this.es),w=this.e*this.cos_p12*Math.cos(u)/Math.sqrt(1-this.es),x=v*w,y=w*w,D=C*C,E=D*C,F=E*C,G=F*C,z=r*C*(1-D*y*(1-y)/6+E/8*x*(1-2*y)+F/120*(y*(4-7*y)-3*v*v*(1-7*y))-G/48*x),a.x=this.x0+z*Math.sin(u),a.y=this.y0+z*Math.cos(u),a))},c.inverse=function(a){a.x-=this.x0,a.y-=this.y0;var b,c,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I;if(this.sphere){if(b=Math.sqrt(a.x*a.x+a.y*a.y),b>2*e*this.a)return;return c=b/this.a,o=Math.sin(c),p=Math.cos(c),q=this.long0,Math.abs(b)<=f?r=this.lat0:(r=m(p*this.sin_p12+a.y*o*this.cos_p12/b),s=Math.abs(this.lat0)-e,q=d(Math.abs(s)<=f?this.lat0>=0?this.long0+Math.atan2(a.x,-a.y):this.long0-Math.atan2(-a.x,a.y):this.long0+Math.atan2(a.x*o,b*this.cos_p12*p-a.y*this.sin_p12*o))),a.x=q,a.y=r,a}return t=h(this.es),u=i(this.es),v=j(this.es),w=k(this.es),Math.abs(this.sin_p12-1)<=f?(x=this.a*g(t,u,v,w,e),b=Math.sqrt(a.x*a.x+a.y*a.y),y=x-b,r=n(y/this.a,t,u,v,w),q=d(this.long0+Math.atan2(a.x,-1*a.y)),a.x=q,a.y=r,a):Math.abs(this.sin_p12+1)<=f?(x=this.a*g(t,u,v,w,e),b=Math.sqrt(a.x*a.x+a.y*a.y),y=b-x,r=n(y/this.a,t,u,v,w),q=d(this.long0+Math.atan2(a.x,a.y)),a.x=q,a.y=r,a):(b=Math.sqrt(a.x*a.x+a.y*a.y),B=Math.atan2(a.x,a.y),z=l(this.a,this.e,this.sin_p12),C=Math.cos(B),D=this.e*this.cos_p12*C,E=-D*D/(1-this.es),F=3*this.es*(1-E)*this.sin_p12*this.cos_p12*C/(1-this.es),G=b/z,H=G-E*(1+E)*Math.pow(G,3)/6-F*(1+3*E)*Math.pow(G,4)/24,I=1-E*H*H/2-G*H*H*H/6,A=Math.asin(this.sin_p12*Math.cos(H)+this.cos_p12*Math.sin(H)*C),q=d(this.long0+Math.asin(Math.sin(B)*Math.sin(H)/Math.cos(A))),r=Math.atan((1-this.es*I*this.sin_p12/Math.sin(A))*Math.tan(A)/(1-this.es)),a.x=q,a.y=r,a)},c.names=["Azimuthal_Equidistant","aeqd"]},{"../common/adjust_lon":5,"../common/asinz":6,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/gN":11,"../common/imlfn":12,"../common/mlfn":14}],42:[function(a,b,c){var d=a("../common/mlfn"),e=a("../common/e0fn"),f=a("../common/e1fn"),g=a("../common/e2fn"),h=a("../common/e3fn"),i=a("../common/gN"),j=a("../common/adjust_lon"),k=a("../common/adjust_lat"),l=a("../common/imlfn"),m=Math.PI/2,n=1e-10;c.init=function(){this.sphere||(this.e0=e(this.es),this.e1=f(this.es),this.e2=g(this.es),this.e3=h(this.es),this.ml0=this.a*d(this.e0,this.e1,this.e2,this.e3,this.lat0))},c.forward=function(a){var b,c,e=a.x,f=a.y;if(e=j(e-this.long0),this.sphere)b=this.a*Math.asin(Math.cos(f)*Math.sin(e)),c=this.a*(Math.atan2(Math.tan(f),Math.cos(e))-this.lat0);else{var g=Math.sin(f),h=Math.cos(f),k=i(this.a,this.e,g),l=Math.tan(f)*Math.tan(f),m=e*Math.cos(f),n=m*m,o=this.es*h*h/(1-this.es),p=this.a*d(this.e0,this.e1,this.e2,this.e3,f);b=k*m*(1-n*l*(1/6-(8-l+8*o)*n/120)),c=p-this.ml0+k*g/h*n*(.5+(5-l+6*o)*n/24)}return a.x=b+this.x0,a.y=c+this.y0,a},c.inverse=function(a){a.x-=this.x0,a.y-=this.y0;var b,c,d=a.x/this.a,e=a.y/this.a;if(this.sphere){var f=e+this.lat0;b=Math.asin(Math.sin(f)*Math.cos(d)),c=Math.atan2(Math.tan(d),Math.cos(f))}else{var g=this.ml0/this.a+e,h=l(g,this.e0,this.e1,this.e2,this.e3);if(Math.abs(Math.abs(h)-m)<=n)return a.x=this.long0,a.y=m,0>e&&(a.y*=-1),a;var o=i(this.a,this.e,Math.sin(h)),p=o*o*o/this.a/this.a*(1-this.es),q=Math.pow(Math.tan(h),2),r=d*this.a/o,s=r*r;b=h-o*Math.tan(h)/p*r*r*(.5-(1+3*q)*r*r/24),c=r*(1-s*(q/3+(1+3*q)*q*s/15))/Math.cos(h)}return a.x=j(c+this.long0),a.y=k(b),a},c.names=["Cassini","Cassini_Soldner","cass"]},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/gN":11,"../common/imlfn":12,"../common/mlfn":14}],43:[function(a,b,c){var d=a("../common/adjust_lon"),e=a("../common/qsfnz"),f=a("../common/msfnz"),g=a("../common/iqsfnz");c.init=function(){this.sphere||(this.k0=f(this.e,Math.sin(this.lat_ts),Math.cos(this.lat_ts)))},c.forward=function(a){var b,c,f=a.x,g=a.y,h=d(f-this.long0);if(this.sphere)b=this.x0+this.a*h*Math.cos(this.lat_ts),c=this.y0+this.a*Math.sin(g)/Math.cos(this.lat_ts);else{var i=e(this.e,Math.sin(g));b=this.x0+this.a*this.k0*h,c=this.y0+this.a*i*.5/this.k0}return a.x=b,a.y=c,a},c.inverse=function(a){a.x-=this.x0,a.y-=this.y0;var b,c;return this.sphere?(b=d(this.long0+a.x/this.a/Math.cos(this.lat_ts)),c=Math.asin(a.y/this.a*Math.cos(this.lat_ts))):(c=g(this.e,2*a.y*this.k0/this.a),b=d(this.long0+a.x/(this.a*this.k0))),a.x=b,a.y=c,a},c.names=["cea"]},{"../common/adjust_lon":5,"../common/iqsfnz":13,"../common/msfnz":15,"../common/qsfnz":20}],44:[function(a,b,c){var d=a("../common/adjust_lon"),e=a("../common/adjust_lat");c.init=function(){this.x0=this.x0||0,this.y0=this.y0||0,this.lat0=this.lat0||0,this.long0=this.long0||0,this.lat_ts=this.lat_ts||0,this.title=this.title||"Equidistant Cylindrical (Plate Carre)",this.rc=Math.cos(this.lat_ts)},c.forward=function(a){var b=a.x,c=a.y,f=d(b-this.long0),g=e(c-this.lat0);return a.x=this.x0+this.a*f*this.rc,a.y=this.y0+this.a*g,a},c.inverse=function(a){var b=a.x,c=a.y;return a.x=d(this.long0+(b-this.x0)/(this.a*this.rc)),a.y=e(this.lat0+(c-this.y0)/this.a),a},c.names=["Equirectangular","Equidistant_Cylindrical","eqc"]},{"../common/adjust_lat":4,"../common/adjust_lon":5}],45:[function(a,b,c){var d=a("../common/e0fn"),e=a("../common/e1fn"),f=a("../common/e2fn"),g=a("../common/e3fn"),h=a("../common/msfnz"),i=a("../common/mlfn"),j=a("../common/adjust_lon"),k=a("../common/adjust_lat"),l=a("../common/imlfn"),m=1e-10;c.init=function(){Math.abs(this.lat1+this.lat2)=0?(c=Math.sqrt(a.x*a.x+a.y*a.y),b=1):(c=-Math.sqrt(a.x*a.x+a.y*a.y),b=-1);var f=0;if(0!==c&&(f=Math.atan2(b*a.x,b*a.y)),this.sphere)return e=j(this.long0+f/this.ns),d=k(this.g-c/this.a),a.x=e,a.y=d,a;var g=this.g-c/this.a;return d=l(g,this.e0,this.e1,this.e2,this.e3),e=j(this.long0+f/this.ns),a.x=e,a.y=d,a},c.names=["Equidistant_Conic","eqdc"]},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/e0fn":7,"../common/e1fn":8,"../common/e2fn":9,"../common/e3fn":10,"../common/imlfn":12,"../common/mlfn":14,"../common/msfnz":15}],46:[function(a,b,c){var d=Math.PI/4,e=a("../common/srat"),f=Math.PI/2,g=20;c.init=function(){var a=Math.sin(this.lat0),b=Math.cos(this.lat0);b*=b,this.rc=Math.sqrt(1-this.es)/(1-this.es*a*a),this.C=Math.sqrt(1+this.es*b*b/(1-this.es)),this.phic0=Math.asin(a/this.C),this.ratexp=.5*this.C*this.e,this.K=Math.tan(.5*this.phic0+d)/(Math.pow(Math.tan(.5*this.lat0+d),this.C)*e(this.e*a,this.ratexp))},c.forward=function(a){var b=a.x,c=a.y;return a.y=2*Math.atan(this.K*Math.pow(Math.tan(.5*c+d),this.C)*e(this.e*Math.sin(c),this.ratexp))-f,a.x=this.C*b,a},c.inverse=function(a){for(var b=1e-14,c=a.x/this.C,h=a.y,i=Math.pow(Math.tan(.5*h+d)/this.K,1/this.C),j=g;j>0&&(h=2*Math.atan(i*e(this.e*Math.sin(a.y),-.5*this.e))-f,!(Math.abs(h-a.y)0||Math.abs(i)<=e?(j=this.x0+this.a*h*c*Math.sin(f)/i,k=this.y0+this.a*h*(this.cos_p14*b-this.sin_p14*c*g)/i):(j=this.x0+this.infinity_dist*c*Math.sin(f),k=this.y0+this.infinity_dist*(this.cos_p14*b-this.sin_p14*c*g)),a.x=j,a.y=k,a},c.inverse=function(a){var b,c,e,g,h,i;return a.x=(a.x-this.x0)/this.a,a.y=(a.y-this.y0)/this.a,a.x/=this.k0,a.y/=this.k0,(b=Math.sqrt(a.x*a.x+a.y*a.y))?(g=Math.atan2(b,this.rc),c=Math.sin(g),e=Math.cos(g),i=f(e*this.sin_p14+a.y*c*this.cos_p14/b),h=Math.atan2(a.x*c,b*this.cos_p14*e-a.y*this.sin_p14*c),h=d(this.long0+h)):(i=this.phic0,h=0),a.x=h,a.y=i,a},c.names=["gnom"]},{"../common/adjust_lon":5,"../common/asinz":6}],48:[function(a,b,c){var d=a("../common/adjust_lon");c.init=function(){this.a=6377397.155,this.es=.006674372230614,this.e=Math.sqrt(this.es),this.lat0||(this.lat0=.863937979737193),this.long0||(this.long0=.4334234309119251),this.k0||(this.k0=.9999),this.s45=.785398163397448,this.s90=2*this.s45,this.fi0=this.lat0,this.e2=this.es,this.e=Math.sqrt(this.e2),this.alfa=Math.sqrt(1+this.e2*Math.pow(Math.cos(this.fi0),4)/(1-this.e2)),this.uq=1.04216856380474,this.u0=Math.asin(Math.sin(this.fi0)/this.alfa),this.g=Math.pow((1+this.e*Math.sin(this.fi0))/(1-this.e*Math.sin(this.fi0)),this.alfa*this.e/2),this.k=Math.tan(this.u0/2+this.s45)/Math.pow(Math.tan(this.fi0/2+this.s45),this.alfa)*this.g,this.k1=this.k0,this.n0=this.a*Math.sqrt(1-this.e2)/(1-this.e2*Math.pow(Math.sin(this.fi0),2)),this.s0=1.37008346281555,this.n=Math.sin(this.s0),this.ro0=this.k1*this.n0/Math.tan(this.s0),this.ad=this.s90-this.uq},c.forward=function(a){var b,c,e,f,g,h,i,j=a.x,k=a.y,l=d(j-this.long0);return b=Math.pow((1+this.e*Math.sin(k))/(1-this.e*Math.sin(k)),this.alfa*this.e/2),c=2*(Math.atan(this.k*Math.pow(Math.tan(k/2+this.s45),this.alfa)/b)-this.s45),e=-l*this.alfa,f=Math.asin(Math.cos(this.ad)*Math.sin(c)+Math.sin(this.ad)*Math.cos(c)*Math.cos(e)),g=Math.asin(Math.cos(c)*Math.sin(e)/Math.cos(f)),h=this.n*g,i=this.ro0*Math.pow(Math.tan(this.s0/2+this.s45),this.n)/Math.pow(Math.tan(f/2+this.s45),this.n),a.y=i*Math.cos(h)/1,a.x=i*Math.sin(h)/1,this.czech||(a.y*=-1,a.x*=-1),a},c.inverse=function(a){var b,c,d,e,f,g,h,i,j=a.x;a.x=a.y,a.y=j,this.czech||(a.y*=-1,a.x*=-1),g=Math.sqrt(a.x*a.x+a.y*a.y),f=Math.atan2(a.y,a.x),e=f/Math.sin(this.s0),d=2*(Math.atan(Math.pow(this.ro0/g,1/this.n)*Math.tan(this.s0/2+this.s45))-this.s45),b=Math.asin(Math.cos(this.ad)*Math.sin(d)-Math.sin(this.ad)*Math.cos(d)*Math.cos(e)),c=Math.asin(Math.cos(d)*Math.sin(e)/Math.cos(b)),a.x=this.long0-c/this.alfa,h=b,i=0;var k=0;do a.y=2*(Math.atan(Math.pow(this.k,-1/this.alfa)*Math.pow(Math.tan(b/2+this.s45),1/this.alfa)*Math.pow((1+this.e*Math.sin(h))/(1-this.e*Math.sin(h)),this.e/2))-this.s45),Math.abs(h-a.y)<1e-10&&(i=1),h=a.y,k+=1;while(0===i&&15>k);return k>=15?null:a},c.names=["Krovak","krovak"]},{"../common/adjust_lon":5}],49:[function(a,b,c){var d=Math.PI/2,e=Math.PI/4,f=1e-10,g=a("../common/qsfnz"),h=a("../common/adjust_lon");c.S_POLE=1,c.N_POLE=2,c.EQUIT=3,c.OBLIQ=4,c.init=function(){var a=Math.abs(this.lat0);if(Math.abs(a-d)0){var b;switch(this.qp=g(this.e,1),this.mmf=.5/(1-this.es),this.apa=this.authset(this.es),this.mode){case this.N_POLE:this.dd=1;break;case this.S_POLE:this.dd=1;break;case this.EQUIT:this.rq=Math.sqrt(.5*this.qp),this.dd=1/this.rq,this.xmf=1,this.ymf=.5*this.qp;break;case this.OBLIQ:this.rq=Math.sqrt(.5*this.qp),b=Math.sin(this.lat0),this.sinb1=g(this.e,b)/this.qp,this.cosb1=Math.sqrt(1-this.sinb1*this.sinb1),this.dd=Math.cos(this.lat0)/(Math.sqrt(1-this.es*b*b)*this.rq*this.cosb1),this.ymf=(this.xmf=this.rq)/this.dd,this.xmf*=this.dd}}else this.mode===this.OBLIQ&&(this.sinph0=Math.sin(this.lat0),this.cosph0=Math.cos(this.lat0))},c.forward=function(a){var b,c,i,j,k,l,m,n,o,p,q=a.x,r=a.y;if(q=h(q-this.long0),this.sphere){if(k=Math.sin(r),p=Math.cos(r),i=Math.cos(q),this.mode===this.OBLIQ||this.mode===this.EQUIT){if(c=this.mode===this.EQUIT?1+p*i:1+this.sinph0*k+this.cosph0*p*i,f>=c)return null;c=Math.sqrt(2/c),b=c*p*Math.sin(q),c*=this.mode===this.EQUIT?k:this.cosph0*k-this.sinph0*p*i}else if(this.mode===this.N_POLE||this.mode===this.S_POLE){if(this.mode===this.N_POLE&&(i=-i),Math.abs(r+this.phi0)=0?(b=(o=Math.sqrt(l))*j,c=i*(this.mode===this.S_POLE?o:-o)):b=c=0}}return a.x=this.a*b+this.x0,a.y=this.a*c+this.y0,a},c.inverse=function(a){a.x-=this.x0,a.y-=this.y0;var b,c,e,g,i,j,k,l=a.x/this.a,m=a.y/this.a;if(this.sphere){var n,o=0,p=0;if(n=Math.sqrt(l*l+m*m),c=.5*n,c>1)return null;switch(c=2*Math.asin(c),(this.mode===this.OBLIQ||this.mode===this.EQUIT)&&(p=Math.sin(c),o=Math.cos(c)),this.mode){case this.EQUIT:c=Math.abs(n)<=f?0:Math.asin(m*p/n),l*=p,m=o*n;break;case this.OBLIQ:c=Math.abs(n)<=f?this.phi0:Math.asin(o*this.sinph0+m*p*this.cosph0/n),l*=p*this.cosph0,m=(o-Math.sin(c)*this.sinph0)*n;break;case this.N_POLE:m=-m,c=d-c;break;case this.S_POLE:c-=d}b=0!==m||this.mode!==this.EQUIT&&this.mode!==this.OBLIQ?Math.atan2(l,m):0}else{if(k=0,this.mode===this.OBLIQ||this.mode===this.EQUIT){if(l/=this.dd,m*=this.dd,j=Math.sqrt(l*l+m*m),f>j)return a.x=0,a.y=this.phi0,a;g=2*Math.asin(.5*j/this.rq),e=Math.cos(g),l*=g=Math.sin(g),this.mode===this.OBLIQ?(k=e*this.sinb1+m*g*this.cosb1/j,i=this.qp*k,m=j*this.cosb1*e-m*this.sinb1*g):(k=m*g/j,i=this.qp*k,m=j*e)}else if(this.mode===this.N_POLE||this.mode===this.S_POLE){if(this.mode===this.N_POLE&&(m=-m),i=l*l+m*m,!i)return a.x=0,a.y=this.phi0,a;k=1-i/this.qp,this.mode===this.S_POLE&&(k=-k)}b=Math.atan2(l,m),c=this.authlat(Math.asin(k),this.apa)}return a.x=h(this.long0+b),a.y=c,a},c.P00=.3333333333333333,c.P01=.17222222222222222,c.P02=.10257936507936508,c.P10=.06388888888888888,c.P11=.0664021164021164,c.P20=.016415012942191543,c.authset=function(a){var b,c=[];return c[0]=a*this.P00,b=a*a,c[0]+=b*this.P01,c[1]=b*this.P10,b*=a,c[0]+=b*this.P02,c[1]+=b*this.P11,c[2]=b*this.P20,c},c.authlat=function(a,b){var c=a+a;return a+b[0]*Math.sin(c)+b[1]*Math.sin(c+c)+b[2]*Math.sin(c+c+c)},c.names=["Lambert Azimuthal Equal Area","Lambert_Azimuthal_Equal_Area","laea"]},{"../common/adjust_lon":5,"../common/qsfnz":20}],50:[function(a,b,c){var d=1e-10,e=a("../common/msfnz"),f=a("../common/tsfnz"),g=Math.PI/2,h=a("../common/sign"),i=a("../common/adjust_lon"),j=a("../common/phi2z");c.init=function(){if(this.lat2||(this.lat2=this.lat1),this.k0||(this.k0=1),this.x0=this.x0||0,this.y0=this.y0||0,!(Math.abs(this.lat1+this.lat2)d?this.ns=Math.log(g/k)/Math.log(h/l):this.ns=b,isNaN(this.ns)&&(this.ns=b),this.f0=g/(this.ns*Math.pow(h,this.ns)),this.rh=this.a*this.f0*Math.pow(m,this.ns),this.title||(this.title="Lambert Conformal Conic")}},c.forward=function(a){var b=a.x,c=a.y;Math.abs(2*Math.abs(c)-Math.PI)<=d&&(c=h(c)*(g-2*d));var e,j,k=Math.abs(Math.abs(c)-g);if(k>d)e=f(this.e,c,Math.sin(c)),j=this.a*this.f0*Math.pow(e,this.ns);else{if(k=c*this.ns,0>=k)return null;j=0}var l=this.ns*i(b-this.long0);return a.x=this.k0*(j*Math.sin(l))+this.x0,a.y=this.k0*(this.rh-j*Math.cos(l))+this.y0,a},c.inverse=function(a){var b,c,d,e,f,h=(a.x-this.x0)/this.k0,k=this.rh-(a.y-this.y0)/this.k0;this.ns>0?(b=Math.sqrt(h*h+k*k),c=1):(b=-Math.sqrt(h*h+k*k),c=-1);var l=0;if(0!==b&&(l=Math.atan2(c*h,c*k)),0!==b||this.ns>0){if(c=1/this.ns,d=Math.pow(b/(this.a*this.f0),c),e=j(this.e,d),-9999===e)return null}else e=-g;return f=i(l/this.ns+this.long0),a.x=f,a.y=e,a},c.names=["Lambert Tangential Conformal Conic Projection","Lambert_Conformal_Conic","Lambert_Conformal_Conic_2SP","lcc"]},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/sign":21,"../common/tsfnz":24}],51:[function(a,b,c){function d(a){return a}c.init=function(){},c.forward=d,c.inverse=d,c.names=["longlat","identity"]},{}],52:[function(a,b,c){var d=a("../common/msfnz"),e=Math.PI/2,f=1e-10,g=57.29577951308232,h=a("../common/adjust_lon"),i=Math.PI/4,j=a("../common/tsfnz"),k=a("../common/phi2z");c.init=function(){var a=this.b/this.a;this.es=1-a*a,"x0"in this||(this.x0=0),"y0"in this||(this.y0=0),this.e=Math.sqrt(this.es),this.lat_ts?this.sphere?this.k0=Math.cos(this.lat_ts):this.k0=d(this.e,Math.sin(this.lat_ts),Math.cos(this.lat_ts)):this.k0||(this.k?this.k0=this.k:this.k0=1)},c.forward=function(a){var b=a.x,c=a.y;if(c*g>90&&-90>c*g&&b*g>180&&-180>b*g)return null;var d,k;if(Math.abs(Math.abs(c)-e)<=f)return null;if(this.sphere)d=this.x0+this.a*this.k0*h(b-this.long0),k=this.y0+this.a*this.k0*Math.log(Math.tan(i+.5*c));else{var l=Math.sin(c),m=j(this.e,c,l);d=this.x0+this.a*this.k0*h(b-this.long0),k=this.y0-this.a*this.k0*Math.log(m)}return a.x=d,a.y=k,a},c.inverse=function(a){var b,c,d=a.x-this.x0,f=a.y-this.y0;if(this.sphere)c=e-2*Math.atan(Math.exp(-f/(this.a*this.k0)));else{var g=Math.exp(-f/(this.a*this.k0));if(c=k(this.e,g),-9999===c)return null}return b=h(this.long0+d/(this.a*this.k0)),a.x=b,a.y=c,a},c.names=["Mercator","Popular Visualisation Pseudo Mercator","Mercator_1SP","Mercator_Auxiliary_Sphere","merc"]},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/tsfnz":24}],53:[function(a,b,c){var d=a("../common/adjust_lon");c.init=function(){},c.forward=function(a){var b=a.x,c=a.y,e=d(b-this.long0),f=this.x0+this.a*e,g=this.y0+this.a*Math.log(Math.tan(Math.PI/4+c/2.5))*1.25;return a.x=f,a.y=g,a},c.inverse=function(a){a.x-=this.x0,a.y-=this.y0;var b=d(this.long0+a.x/this.a),c=2.5*(Math.atan(Math.exp(.8*a.y/this.a))-Math.PI/4);return a.x=b,a.y=c,a},c.names=["Miller_Cylindrical","mill"]},{"../common/adjust_lon":5}],54:[function(a,b,c){var d=a("../common/adjust_lon"),e=1e-10;c.init=function(){},c.forward=function(a){for(var b=a.x,c=a.y,f=d(b-this.long0),g=c,h=Math.PI*Math.sin(c),i=0;!0;i++){var j=-(g+Math.sin(g)-h)/(1+Math.cos(g));if(g+=j,Math.abs(j).999999999999&&(c=.999999999999),b=Math.asin(c);var e=d(this.long0+a.x/(.900316316158*this.a*Math.cos(b)));e<-Math.PI&&(e=-Math.PI),e>Math.PI&&(e=Math.PI),c=(2*b+Math.sin(2*b))/Math.PI,Math.abs(c)>1&&(c=1);var f=Math.asin(c);return a.x=e,a.y=f,a},c.names=["Mollweide","moll"]},{"../common/adjust_lon":5}],55:[function(a,b,c){var d=484813681109536e-20;c.iterations=1,c.init=function(){this.A=[],this.A[1]=.6399175073,this.A[2]=-.1358797613,this.A[3]=.063294409,this.A[4]=-.02526853,this.A[5]=.0117879,this.A[6]=-.0055161,this.A[7]=.0026906,this.A[8]=-.001333,this.A[9]=67e-5,this.A[10]=-34e-5,this.B_re=[],this.B_im=[],this.B_re[1]=.7557853228,this.B_im[1]=0,this.B_re[2]=.249204646,this.B_im[2]=.003371507,this.B_re[3]=-.001541739,this.B_im[3]=.04105856,this.B_re[4]=-.10162907,this.B_im[4]=.01727609,this.B_re[5]=-.26623489,this.B_im[5]=-.36249218,this.B_re[6]=-.6870983,this.B_im[6]=-1.1651967,this.C_re=[],this.C_im=[],this.C_re[1]=1.3231270439,this.C_im[1]=0,this.C_re[2]=-.577245789,this.C_im[2]=-.007809598,this.C_re[3]=.508307513,this.C_im[3]=-.112208952,this.C_re[4]=-.15094762,this.C_im[4]=.18200602,this.C_re[5]=1.01418179,this.C_im[5]=1.64497696,this.C_re[6]=1.9660549,this.C_im[6]=2.5127645,this.D=[],this.D[1]=1.5627014243,this.D[2]=.5185406398,this.D[3]=-.03333098,this.D[4]=-.1052906,this.D[5]=-.0368594,this.D[6]=.007317,this.D[7]=.0122,this.D[8]=.00394,this.D[9]=-.0013},c.forward=function(a){var b,c=a.x,e=a.y,f=e-this.lat0,g=c-this.long0,h=f/d*1e-5,i=g,j=1,k=0;for(b=1;10>=b;b++)j*=h,k+=this.A[b]*j;var l,m,n=k,o=i,p=1,q=0,r=0,s=0;for(b=1;6>=b;b++)l=p*n-q*o,m=q*n+p*o,p=l,q=m,r=r+this.B_re[b]*p-this.B_im[b]*q,s=s+this.B_im[b]*p+this.B_re[b]*q;return a.x=s*this.a+this.x0,a.y=r*this.a+this.y0,a},c.inverse=function(a){var b,c,e,f=a.x,g=a.y,h=f-this.x0,i=g-this.y0,j=i/this.a,k=h/this.a,l=1,m=0,n=0,o=0;for(b=1;6>=b;b++)c=l*j-m*k,e=m*j+l*k,l=c,m=e,n=n+this.C_re[b]*l-this.C_im[b]*m,o=o+this.C_im[b]*l+this.C_re[b]*m;for(var p=0;p=b;b++)q=s*n-t*o,r=t*n+s*o,s=q,t=r,u+=(b-1)*(this.B_re[b]*s-this.B_im[b]*t),v+=(b-1)*(this.B_im[b]*s+this.B_re[b]*t);s=1,t=0;var w=this.B_re[1],x=this.B_im[1];for(b=2;6>=b;b++)q=s*n-t*o,r=t*n+s*o,s=q,t=r,w+=b*(this.B_re[b]*s-this.B_im[b]*t),x+=b*(this.B_im[b]*s+this.B_re[b]*t);var y=w*w+x*x;n=(u*w+v*x)/y,o=(v*w-u*x)/y}var z=n,A=o,B=1,C=0;for(b=1;9>=b;b++)B*=z,C+=this.D[b]*B;var D=this.lat0+C*d*1e5,E=this.long0+A;return a.x=E,a.y=D,a},c.names=["New_Zealand_Map_Grid","nzmg"]},{}],56:[function(a,b,c){var d=a("../common/tsfnz"),e=a("../common/adjust_lon"),f=a("../common/phi2z"),g=Math.PI/2,h=Math.PI/4,i=1e-10;c.init=function(){this.no_off=this.no_off||!1,this.no_rot=this.no_rot||!1,isNaN(this.k0)&&(this.k0=1);var a=Math.sin(this.lat0),b=Math.cos(this.lat0),c=this.e*a;this.bl=Math.sqrt(1+this.es/(1-this.es)*Math.pow(b,4)),this.al=this.a*this.bl*this.k0*Math.sqrt(1-this.es)/(1-c*c);var f=d(this.e,this.lat0,a),g=this.bl/b*Math.sqrt((1-this.es)/(1-c*c));1>g*g&&(g=1);var h,i;if(isNaN(this.longc)){var j=d(this.e,this.lat1,Math.sin(this.lat1)),k=d(this.e,this.lat2,Math.sin(this.lat2));this.lat0>=0?this.el=(g+Math.sqrt(g*g-1))*Math.pow(f,this.bl):this.el=(g-Math.sqrt(g*g-1))*Math.pow(f,this.bl);var l=Math.pow(j,this.bl),m=Math.pow(k,this.bl);h=this.el/l,i=.5*(h-1/h);var n=(this.el*this.el-m*l)/(this.el*this.el+m*l),o=(m-l)/(m+l),p=e(this.long1-this.long2);this.long0=.5*(this.long1+this.long2)-Math.atan(n*Math.tan(.5*this.bl*p)/o)/this.bl,this.long0=e(this.long0);var q=e(this.long1-this.long0);this.gamma0=Math.atan(Math.sin(this.bl*q)/i),this.alpha=Math.asin(g*Math.sin(this.gamma0))}else h=this.lat0>=0?g+Math.sqrt(g*g-1):g-Math.sqrt(g*g-1),this.el=h*Math.pow(f,this.bl),i=.5*(h-1/h),this.gamma0=Math.asin(Math.sin(this.alpha)/g),this.long0=this.longc-Math.asin(i*Math.tan(this.gamma0))/this.bl;this.no_off?this.uc=0:this.lat0>=0?this.uc=this.al/this.bl*Math.atan2(Math.sqrt(g*g-1),Math.cos(this.alpha)):this.uc=-1*this.al/this.bl*Math.atan2(Math.sqrt(g*g-1),Math.cos(this.alpha))},c.forward=function(a){var b,c,f,j=a.x,k=a.y,l=e(j-this.long0);if(Math.abs(Math.abs(k)-g)<=i)f=k>0?-1:1,c=this.al/this.bl*Math.log(Math.tan(h+f*this.gamma0*.5)),b=-1*f*g*this.al/this.bl;else{var m=d(this.e,k,Math.sin(k)),n=this.el/Math.pow(m,this.bl),o=.5*(n-1/n),p=.5*(n+1/n),q=Math.sin(this.bl*l),r=(o*Math.sin(this.gamma0)-q*Math.cos(this.gamma0))/p;c=Math.abs(Math.abs(r)-1)<=i?Number.POSITIVE_INFINITY:.5*this.al*Math.log((1-r)/(1+r))/this.bl,b=Math.abs(Math.cos(this.bl*l))<=i?this.al*this.bl*l:this.al*Math.atan2(o*Math.cos(this.gamma0)+q*Math.sin(this.gamma0),Math.cos(this.bl*l))/this.bl}return this.no_rot?(a.x=this.x0+b,a.y=this.y0+c):(b-=this.uc,a.x=this.x0+c*Math.cos(this.alpha)+b*Math.sin(this.alpha),a.y=this.y0+b*Math.cos(this.alpha)-c*Math.sin(this.alpha)),a},c.inverse=function(a){var b,c;this.no_rot?(c=a.y-this.y0,b=a.x-this.x0):(c=(a.x-this.x0)*Math.cos(this.alpha)-(a.y-this.y0)*Math.sin(this.alpha),b=(a.y-this.y0)*Math.cos(this.alpha)+(a.x-this.x0)*Math.sin(this.alpha),b+=this.uc);var d=Math.exp(-1*this.bl*c/this.al),h=.5*(d-1/d),j=.5*(d+1/d),k=Math.sin(this.bl*b/this.al),l=(k*Math.cos(this.gamma0)+h*Math.sin(this.gamma0))/j,m=Math.pow(this.el/Math.sqrt((1+l)/(1-l)),1/this.bl);return Math.abs(l-1)g?(g=Math.sin(b),c=this.long0+a.x*Math.sqrt(1-this.es*g*g)/(this.a*Math.cos(b)),f=d(c)):j>g-k&&(f=this.long0)),a.x=f,a.y=b,a},c.names=["Sinusoidal","sinu"]},{"../common/adjust_lat":4,"../common/adjust_lon":5,"../common/asinz":6,"../common/pj_enfn":17,"../common/pj_inv_mlfn":18,"../common/pj_mlfn":19}],59:[function(a,b,c){c.init=function(){var a=this.lat0;this.lambda0=this.long0;var b=Math.sin(a),c=this.a,d=this.rf,e=1/d,f=2*e-Math.pow(e,2),g=this.e=Math.sqrt(f);this.R=this.k0*c*Math.sqrt(1-f)/(1-f*Math.pow(b,2)),this.alpha=Math.sqrt(1+f/(1-f)*Math.pow(Math.cos(a),4)),this.b0=Math.asin(b/this.alpha);var h=Math.log(Math.tan(Math.PI/4+this.b0/2)),i=Math.log(Math.tan(Math.PI/4+a/2)),j=Math.log((1+g*b)/(1-g*b));this.K=h-this.alpha*i+this.alpha*g/2*j},c.forward=function(a){var b=Math.log(Math.tan(Math.PI/4-a.y/2)),c=this.e/2*Math.log((1+this.e*Math.sin(a.y))/(1-this.e*Math.sin(a.y))),d=-this.alpha*(b+c)+this.K,e=2*(Math.atan(Math.exp(d))-Math.PI/4),f=this.alpha*(a.x-this.lambda0),g=Math.atan(Math.sin(f)/(Math.sin(this.b0)*Math.tan(e)+Math.cos(this.b0)*Math.cos(f))),h=Math.asin(Math.cos(this.b0)*Math.sin(e)-Math.sin(this.b0)*Math.cos(e)*Math.cos(f));return a.y=this.R/2*Math.log((1+Math.sin(h))/(1-Math.sin(h)))+this.y0,a.x=this.R*g+this.x0,a},c.inverse=function(a){for(var b=a.x-this.x0,c=a.y-this.y0,d=b/this.R,e=2*(Math.atan(Math.exp(c/this.R))-Math.PI/4),f=Math.asin(Math.cos(this.b0)*Math.sin(e)+Math.sin(this.b0)*Math.cos(e)*Math.cos(d)),g=Math.atan(Math.sin(d)/(Math.cos(this.b0)*Math.cos(d)-Math.sin(this.b0)*Math.tan(e))),h=this.lambda0+g/this.alpha,i=0,j=f,k=-1e3,l=0;Math.abs(j-k)>1e-7;){if(++l>20)return;i=1/this.alpha*(Math.log(Math.tan(Math.PI/4+f/2))-this.K)+this.e*Math.log(Math.tan(Math.PI/4+Math.asin(this.e*Math.sin(j))/2)),k=j,j=2*Math.atan(Math.exp(i))-Math.PI/2}return a.x=h,a.y=j,a},c.names=["somerc"]},{}],60:[function(a,b,c){var d=Math.PI/2,e=1e-10,f=a("../common/sign"),g=a("../common/msfnz"),h=a("../common/tsfnz"),i=a("../common/phi2z"),j=a("../common/adjust_lon");c.ssfn_=function(a,b,c){return b*=c,Math.tan(.5*(d+a))*Math.pow((1-b)/(1+b),.5*c)},c.init=function(){this.coslat0=Math.cos(this.lat0),this.sinlat0=Math.sin(this.lat0),this.sphere?1===this.k0&&!isNaN(this.lat_ts)&&Math.abs(this.coslat0)<=e&&(this.k0=.5*(1+f(this.lat0)*Math.sin(this.lat_ts))):(Math.abs(this.coslat0)<=e&&(this.lat0>0?this.con=1:this.con=-1),this.cons=Math.sqrt(Math.pow(1+this.e,1+this.e)*Math.pow(1-this.e,1-this.e)),1===this.k0&&!isNaN(this.lat_ts)&&Math.abs(this.coslat0)<=e&&(this.k0=.5*this.cons*g(this.e,Math.sin(this.lat_ts),Math.cos(this.lat_ts))/h(this.e,this.con*this.lat_ts,this.con*Math.sin(this.lat_ts))),this.ms1=g(this.e,this.sinlat0,this.coslat0),this.X0=2*Math.atan(this.ssfn_(this.lat0,this.sinlat0,this.e))-d,this.cosX0=Math.cos(this.X0),this.sinX0=Math.sin(this.X0))},c.forward=function(a){var b,c,f,g,i,k,l=a.x,m=a.y,n=Math.sin(m),o=Math.cos(m),p=j(l-this.long0);return Math.abs(Math.abs(l-this.long0)-Math.PI)<=e&&Math.abs(m+this.lat0)<=e?(a.x=NaN,a.y=NaN,a):this.sphere?(b=2*this.k0/(1+this.sinlat0*n+this.coslat0*o*Math.cos(p)),a.x=this.a*b*o*Math.sin(p)+this.x0,a.y=this.a*b*(this.coslat0*n-this.sinlat0*o*Math.cos(p))+this.y0,a):(c=2*Math.atan(this.ssfn_(m,n,this.e))-d,g=Math.cos(c),f=Math.sin(c),Math.abs(this.coslat0)<=e?(i=h(this.e,m*this.con,this.con*n),k=2*this.a*this.k0*i/this.cons,a.x=this.x0+k*Math.sin(l-this.long0),a.y=this.y0-this.con*k*Math.cos(l-this.long0),a):(Math.abs(this.sinlat0)=k?(a.x=b,a.y=c,a):(c=Math.asin(Math.cos(l)*this.sinlat0+a.y*Math.sin(l)*this.coslat0/k),b=j(Math.abs(this.coslat0)0?this.long0+Math.atan2(a.x,-1*a.y):this.long0+Math.atan2(a.x,a.y):this.long0+Math.atan2(a.x*Math.sin(l),k*this.coslat0*Math.cos(l)-a.y*this.sinlat0*Math.sin(l))),a.x=b,a.y=c,a)}if(Math.abs(this.coslat0)<=e){if(e>=k)return c=this.lat0,b=this.long0,a.x=b,a.y=c,a;a.x*=this.con,a.y*=this.con,f=k*this.cons/(2*this.a*this.k0),c=this.con*i(this.e,f),b=this.con*j(this.con*this.long0+Math.atan2(a.x,-1*a.y))}else g=2*Math.atan(k*this.cosX0/(2*this.a*this.k0*this.ms1)),b=this.long0,e>=k?h=this.X0:(h=Math.asin(Math.cos(g)*this.sinX0+a.y*Math.sin(g)*this.cosX0/k),b=j(this.long0+Math.atan2(a.x*Math.sin(g),k*this.cosX0*Math.cos(g)-a.y*this.sinX0*Math.sin(g)))),c=-1*i(this.e,Math.tan(.5*(d+h)));return a.x=b,a.y=c,a},c.names=["stere","Stereographic_South_Pole","Polar Stereographic (variant B)"]},{"../common/adjust_lon":5,"../common/msfnz":15,"../common/phi2z":16,"../common/sign":21,"../common/tsfnz":24}],61:[function(a,b,c){var d=a("./gauss"),e=a("../common/adjust_lon");c.init=function(){d.init.apply(this),this.rc&&(this.sinc0=Math.sin(this.phic0),this.cosc0=Math.cos(this.phic0),this.R2=2*this.rc,this.title||(this.title="Oblique Stereographic Alternative"))},c.forward=function(a){var b,c,f,g;return a.x=e(a.x-this.long0),d.forward.apply(this,[a]),b=Math.sin(a.y),c=Math.cos(a.y),f=Math.cos(a.x),g=this.k0*this.R2/(1+this.sinc0*b+this.cosc0*c*f),a.x=g*c*Math.sin(a.x),a.y=g*(this.cosc0*b-this.sinc0*c*f),a.x=this.a*a.x+this.x0,a.y=this.a*a.y+this.y0,a},c.inverse=function(a){var b,c,f,g,h;if(a.x=(a.x-this.x0)/this.a,a.y=(a.y-this.y0)/this.a,a.x/=this.k0,a.y/=this.k0,h=Math.sqrt(a.x*a.x+a.y*a.y)){var i=2*Math.atan2(h,this.R2);b=Math.sin(i),c=Math.cos(i),g=Math.asin(c*this.sinc0+a.y*b*this.cosc0/h),f=Math.atan2(a.x*b,h*this.cosc0*c-a.y*this.sinc0*b)}else g=this.phic0,f=0;return a.x=f,a.y=g,d.inverse.apply(this,[a]),a.x=e(a.x+this.long0),a},c.names=["Stereographic_North_Pole","Oblique_Stereographic","Polar_Stereographic","sterea","Oblique Stereographic Alternative"]},{"../common/adjust_lon":5,"./gauss":46}],62:[function(a,b,c){var d=a("../common/e0fn"),e=a("../common/e1fn"),f=a("../common/e2fn"),g=a("../common/e3fn"),h=a("../common/mlfn"),i=a("../common/adjust_lon"),j=Math.PI/2,k=1e-10,l=a("../common/sign"),m=a("../common/asinz");c.init=function(){this.e0=d(this.es),this.e1=e(this.es),this.e2=f(this.es),this.e3=g(this.es),this.ml0=this.a*h(this.e0,this.e1,this.e2,this.e3,this.lat0)},c.forward=function(a){var b,c,d,e=a.x,f=a.y,g=i(e-this.long0),j=Math.sin(f),k=Math.cos(f);if(this.sphere){var l=k*Math.sin(g);if(Math.abs(Math.abs(l)-1)<1e-10)return 93;c=.5*this.a*this.k0*Math.log((1+l)/(1-l)),b=Math.acos(k*Math.cos(g)/Math.sqrt(1-l*l)),0>f&&(b=-b),d=this.a*this.k0*(b-this.lat0)}else{var m=k*g,n=Math.pow(m,2),o=this.ep2*Math.pow(k,2),p=Math.tan(f),q=Math.pow(p,2);b=1-this.es*Math.pow(j,2);var r=this.a/Math.sqrt(b),s=this.a*h(this.e0,this.e1,this.e2,this.e3,f);c=this.k0*r*m*(1+n/6*(1-q+o+n/20*(5-18*q+Math.pow(q,2)+72*o-58*this.ep2)))+this.x0,d=this.k0*(s-this.ml0+r*p*(n*(.5+n/24*(5-q+9*o+4*Math.pow(o,2)+n/30*(61-58*q+Math.pow(q,2)+600*o-330*this.ep2)))))+this.y0}return a.x=c,a.y=d,a},c.inverse=function(a){var b,c,d,e,f,g,h=6;if(this.sphere){var n=Math.exp(a.x/(this.a*this.k0)),o=.5*(n-1/n),p=this.lat0+a.y/(this.a*this.k0),q=Math.cos(p);b=Math.sqrt((1-q*q)/(1+o*o)),f=m(b),0>p&&(f=-f),g=0===o&&0===q?this.long0:i(Math.atan2(o,q)+this.long0)}else{var r=a.x-this.x0,s=a.y-this.y0;for(b=(this.ml0+s/this.k0)/this.a,c=b,e=0;!0&&(d=(b+this.e1*Math.sin(2*c)-this.e2*Math.sin(4*c)+this.e3*Math.sin(6*c))/this.e0-c,c+=d,!(Math.abs(d)<=k));e++)if(e>=h)return 95;if(Math.abs(c)=0?this.y0+Math.PI*this.R*Math.tan(.5*k):this.y0+Math.PI*this.R*-Math.tan(.5*k));var l=.5*Math.abs(Math.PI/j-j/Math.PI),m=l*l,n=Math.sin(k),o=Math.cos(k),p=o/(n+o-1),q=p*p,r=p*(2/n-1),s=r*r,t=Math.PI*this.R*(l*(p-s)+Math.sqrt(m*(p-s)*(p-s)-(s+m)*(q-s)))/(s+m);0>j&&(t=-t),b=this.x0+t;var u=m+p;return t=Math.PI*this.R*(r*u-l*Math.sqrt((s+m)*(m+1)-u*u))/(s+m),c=i>=0?this.y0+t:this.y0-t,a.x=b,a.y=c,a},c.inverse=function(a){var b,c,e,g,h,i,j,k,l,m,n,o,p;return a.x-=this.x0,a.y-=this.y0,n=Math.PI*this.R,e=a.x/n,g=a.y/n,h=e*e+g*g,i=-Math.abs(g)*(1+h),j=i-2*g*g+e*e,k=-2*i+1+2*g*g+h*h,p=g*g/k+(2*j*j*j/k/k/k-9*i*j/k/k)/27,l=(i-j*j/3/k)/k,m=2*Math.sqrt(-l/3),n=3*p/l/m,Math.abs(n)>1&&(n=n>=0?1:-1),o=Math.acos(n)/3,c=a.y>=0?(-m*Math.cos(o+Math.PI/3)-j/3/k)*Math.PI:-(-m*Math.cos(o+Math.PI/3)-j/3/k)*Math.PI,b=Math.abs(e)-1?(b[c]={name:a[0].toLowerCase(),convert:a[1]},3===a.length&&(b[c].auth=a[2])):"SPHEROID"===c?(b[c]={name:a[0],a:a[1],rf:a[2]},4===a.length&&(b[c].auth=a[3])):["GEOGCS","GEOCCS","DATUM","VERT_CS","COMPD_CS","LOCAL_CS","FITTED_CS","LOCAL_DATUM"].indexOf(c)>-1?(a[0]=["name",a[0]],d(b,c,a)):a.every(function(a){return Array.isArray(a)})?d(b,c,a):e(a,b[c])):b[c]=!0,void 0):void(b[a]=!0)}function f(a,b){var c=b[0],d=b[1];!(c in a)&&d in a&&(a[c]=a[d],3===b.length&&(a[c]=b[2](a[c])))}function g(a){return a*i}function h(a){function b(b){var c=a.to_meter||1;return parseFloat(b,10)*c}"GEOGCS"===a.type?a.projName="longlat":"LOCAL_CS"===a.type?(a.projName="identity",a.local=!0):"object"==typeof a.PROJECTION?a.projName=Object.keys(a.PROJECTION)[0]:a.projName=a.PROJECTION,a.UNIT&&(a.units=a.UNIT.name.toLowerCase(),"metre"===a.units&&(a.units="meter"), a.UNIT.convert&&("GEOGCS"===a.type?a.DATUM&&a.DATUM.SPHEROID&&(a.to_meter=parseFloat(a.UNIT.convert,10)*a.DATUM.SPHEROID.a):a.to_meter=parseFloat(a.UNIT.convert,10))),a.GEOGCS&&(a.GEOGCS.DATUM?a.datumCode=a.GEOGCS.DATUM.name.toLowerCase():a.datumCode=a.GEOGCS.name.toLowerCase(),"d_"===a.datumCode.slice(0,2)&&(a.datumCode=a.datumCode.slice(2)),("new_zealand_geodetic_datum_1949"===a.datumCode||"new_zealand_1949"===a.datumCode)&&(a.datumCode="nzgd49"),"wgs_1984"===a.datumCode&&("Mercator_Auxiliary_Sphere"===a.PROJECTION&&(a.sphere=!0),a.datumCode="wgs84"),"_ferro"===a.datumCode.slice(-6)&&(a.datumCode=a.datumCode.slice(0,-6)),"_jakarta"===a.datumCode.slice(-8)&&(a.datumCode=a.datumCode.slice(0,-8)),~a.datumCode.indexOf("belge")&&(a.datumCode="rnb72"),a.GEOGCS.DATUM&&a.GEOGCS.DATUM.SPHEROID&&(a.ellps=a.GEOGCS.DATUM.SPHEROID.name.replace("_19","").replace(/[Cc]larke\_18/,"clrk"),"international"===a.ellps.toLowerCase().slice(0,13)&&(a.ellps="intl"),a.a=a.GEOGCS.DATUM.SPHEROID.a,a.rf=parseFloat(a.GEOGCS.DATUM.SPHEROID.rf,10)),~a.datumCode.indexOf("osgb_1936")&&(a.datumCode="osgb36")),a.b&&!isFinite(a.b)&&(a.b=a.a);var c=function(b){return f(a,b)},d=[["standard_parallel_1","Standard_Parallel_1"],["standard_parallel_2","Standard_Parallel_2"],["false_easting","False_Easting"],["false_northing","False_Northing"],["central_meridian","Central_Meridian"],["latitude_of_origin","Latitude_Of_Origin"],["latitude_of_origin","Central_Parallel"],["scale_factor","Scale_Factor"],["k0","scale_factor"],["latitude_of_center","Latitude_of_center"],["lat0","latitude_of_center",g],["longitude_of_center","Longitude_Of_Center"],["longc","longitude_of_center",g],["x0","false_easting",b],["y0","false_northing",b],["long0","central_meridian",g],["lat0","latitude_of_origin",g],["lat0","standard_parallel_1",g],["lat1","standard_parallel_1",g],["lat2","standard_parallel_2",g],["alpha","azimuth",g],["srsCode","name"]];d.forEach(c),a.long0||!a.longc||"Albers_Conic_Equal_Area"!==a.projName&&"Lambert_Azimuthal_Equal_Area"!==a.projName||(a.long0=a.longc),a.lat_ts||!a.lat1||"Stereographic_South_Pole"!==a.projName&&"Polar Stereographic (variant B)"!==a.projName||(a.lat0=g(a.lat1>0?90:-90),a.lat_ts=a.lat1)}var i=.017453292519943295,j=a("./extend");b.exports=function(a,b){var c=JSON.parse((","+a).replace(/\s*\,\s*([A-Z_0-9]+?)(\[)/g,',["$1",').slice(1).replace(/\s*\,\s*([A-Z_0-9]+?)\]/g,',"$1"]').replace(/,\["VERTCS".+/,"")),d=c.shift(),f=c.shift();c.unshift(["name",f]),c.unshift(["type",d]),c.unshift("output");var g={};return e(c,g),h(g.output),j(b,g.output)}},{"./extend":34}],67:[function(a,b,c){function d(a){return a*(Math.PI/180)}function e(a){return 180*(a/Math.PI)}function f(a){var b,c,e,f,g,i,j,k,l,m=a.lat,n=a.lon,o=6378137,p=.00669438,q=.9996,r=d(m),s=d(n);l=Math.floor((n+180)/6)+1,180===n&&(l=60),m>=56&&64>m&&n>=3&&12>n&&(l=32),m>=72&&84>m&&(n>=0&&9>n?l=31:n>=9&&21>n?l=33:n>=21&&33>n?l=35:n>=33&&42>n&&(l=37)),b=6*(l-1)-180+3,k=d(b),c=p/(1-p),e=o/Math.sqrt(1-p*Math.sin(r)*Math.sin(r)),f=Math.tan(r)*Math.tan(r),g=c*Math.cos(r)*Math.cos(r),i=Math.cos(r)*(s-k),j=o*((1-p/4-3*p*p/64-5*p*p*p/256)*r-(3*p/8+3*p*p/32+45*p*p*p/1024)*Math.sin(2*r)+(15*p*p/256+45*p*p*p/1024)*Math.sin(4*r)-35*p*p*p/3072*Math.sin(6*r));var t=q*e*(i+(1-f+g)*i*i*i/6+(5-18*f+f*f+72*g-58*c)*i*i*i*i*i/120)+5e5,u=q*(j+e*Math.tan(r)*(i*i/2+(5-f+9*g+4*g*g)*i*i*i*i/24+(61-58*f+f*f+600*g-330*c)*i*i*i*i*i*i/720));return 0>m&&(u+=1e7),{northing:Math.round(u),easting:Math.round(t),zoneNumber:l,zoneLetter:h(m)}}function g(a){var b=a.northing,c=a.easting,d=a.zoneLetter,f=a.zoneNumber;if(0>f||f>60)return null;var h,i,j,k,l,m,n,o,p,q,r=.9996,s=6378137,t=.00669438,u=(1-Math.sqrt(1-t))/(1+Math.sqrt(1-t)),v=c-5e5,w=b;"N">d&&(w-=1e7),o=6*(f-1)-180+3,h=t/(1-t),n=w/r,p=n/(s*(1-t/4-3*t*t/64-5*t*t*t/256)),q=p+(3*u/2-27*u*u*u/32)*Math.sin(2*p)+(21*u*u/16-55*u*u*u*u/32)*Math.sin(4*p)+151*u*u*u/96*Math.sin(6*p),i=s/Math.sqrt(1-t*Math.sin(q)*Math.sin(q)),j=Math.tan(q)*Math.tan(q),k=h*Math.cos(q)*Math.cos(q),l=s*(1-t)/Math.pow(1-t*Math.sin(q)*Math.sin(q),1.5),m=v/(i*r);var x=q-i*Math.tan(q)/l*(m*m/2-(5+3*j+10*k-4*k*k-9*h)*m*m*m*m/24+(61+90*j+298*k+45*j*j-252*h-3*k*k)*m*m*m*m*m*m/720);x=e(x);var y=(m-(1+2*j+k)*m*m*m/6+(5-2*k+28*j-3*k*k+8*h+24*j*j)*m*m*m*m*m/120)/Math.cos(q);y=o+e(y);var z;if(a.accuracy){var A=g({northing:a.northing+a.accuracy,easting:a.easting+a.accuracy,zoneLetter:a.zoneLetter,zoneNumber:a.zoneNumber});z={top:A.lat,right:A.lon,bottom:x,left:y}}else z={lat:x,lon:y};return z}function h(a){var b="Z";return 84>=a&&a>=72?b="X":72>a&&a>=64?b="W":64>a&&a>=56?b="V":56>a&&a>=48?b="U":48>a&&a>=40?b="T":40>a&&a>=32?b="S":32>a&&a>=24?b="R":24>a&&a>=16?b="Q":16>a&&a>=8?b="P":8>a&&a>=0?b="N":0>a&&a>=-8?b="M":-8>a&&a>=-16?b="L":-16>a&&a>=-24?b="K":-24>a&&a>=-32?b="J":-32>a&&a>=-40?b="H":-40>a&&a>=-48?b="G":-48>a&&a>=-56?b="F":-56>a&&a>=-64?b="E":-64>a&&a>=-72?b="D":-72>a&&a>=-80&&(b="C"),b}function i(a,b){var c="00000"+a.easting,d="00000"+a.northing;return a.zoneNumber+a.zoneLetter+j(a.easting,a.northing,a.zoneNumber)+c.substr(c.length-5,b)+d.substr(d.length-5,b)}function j(a,b,c){var d=k(c),e=Math.floor(a/1e5),f=Math.floor(b/1e5)%20;return l(e,f,d)}function k(a){var b=a%q;return 0===b&&(b=q),b}function l(a,b,c){var d=c-1,e=r.charCodeAt(d),f=s.charCodeAt(d),g=e+a-1,h=f+b,i=!1;g>x&&(g=g-x+t-1,i=!0),(g===u||u>e&&g>u||(g>u||u>e)&&i)&&g++,(g===v||v>e&&g>v||(g>v||v>e)&&i)&&(g++,g===u&&g++),g>x&&(g=g-x+t-1),h>w?(h=h-w+t-1,i=!0):i=!1,(h===u||u>f&&h>u||(h>u||u>f)&&i)&&h++,(h===v||v>f&&h>v||(h>v||v>f)&&i)&&(h++,h===u&&h++),h>w&&(h=h-w+t-1);var j=String.fromCharCode(g)+String.fromCharCode(h);return j}function m(a){if(a&&0===a.length)throw"MGRSPoint coverting from nothing";for(var b,c=a.length,d=null,e="",f=0;!/[A-Z]/.test(b=a.charAt(f));){if(f>=2)throw"MGRSPoint bad conversion from: "+a;e+=b,f++}var g=parseInt(e,10);if(0===f||f+3>c)throw"MGRSPoint bad conversion from: "+a;var h=a.charAt(f++);if("A">=h||"B"===h||"Y"===h||h>="Z"||"I"===h||"O"===h)throw"MGRSPoint zone letter "+h+" not handled: "+a;d=a.substring(f,f+=2);for(var i=k(g),j=n(d.charAt(0),i),l=o(d.charAt(1),i);l0&&(q=1e5/Math.pow(10,v),r=a.substring(f,f+v),w=parseFloat(r)*q,s=a.substring(f+v),x=parseFloat(s)*q),t=w+j,u=x+l,{easting:t,northing:u,zoneLetter:h,zoneNumber:g,accuracy:q}}function n(a,b){for(var c=r.charCodeAt(b-1),d=1e5,e=!1;c!==a.charCodeAt(0);){if(c++,c===u&&c++,c===v&&c++,c>x){if(e)throw"Bad character: "+a;c=t,e=!0}d+=1e5}return d}function o(a,b){if(a>"V")throw"MGRSPoint given invalid Northing "+a;for(var c=s.charCodeAt(b-1),d=0,e=!1;c!==a.charCodeAt(0);){if(c++,c===u&&c++,c===v&&c++,c>w){if(e)throw"Bad character: "+a;c=t,e=!0}d+=1e5}return d}function p(a){var b;switch(a){case"C":b=11e5;break;case"D":b=2e6;break;case"E":b=28e5;break;case"F":b=37e5;break;case"G":b=46e5;break;case"H":b=55e5;break;case"J":b=64e5;break;case"K":b=73e5;break;case"L":b=82e5;break;case"M":b=91e5;break;case"N":b=0;break;case"P":b=8e5;break;case"Q":b=17e5;break;case"R":b=26e5;break;case"S":b=35e5;break;case"T":b=44e5;break;case"U":b=53e5;break;case"V":b=62e5;break;case"W":b=7e6;break;case"X":b=79e5;break;default:b=-1}if(b>=0)return b;throw"Invalid zone letter: "+a}var q=6,r="AJSAJS",s="AFAFAF",t=65,u=73,v=79,w=86,x=90;c.forward=function(a,b){return b=b||5,i(f({lat:a[1],lon:a[0]}),b)},c.inverse=function(a){var b=g(m(a.toUpperCase()));return b.lat&&b.lon?[b.lon,b.lat,b.lon,b.lat]:[b.left,b.bottom,b.right,b.top]},c.toPoint=function(a){var b=g(m(a.toUpperCase()));return b.lat&&b.lon?[b.lon,b.lat]:[(b.left+b.right)/2,(b.top+b.bottom)/2]}},{}],68:[function(a,b,c){b.exports={name:"proj4",version:"2.3.14",description:"Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.",main:"lib/index.js",directories:{test:"test",doc:"docs"},scripts:{test:"./node_modules/istanbul/lib/cli.js test ./node_modules/mocha/bin/_mocha test/test.js"},repository:{type:"git",url:"git://github.com/proj4js/proj4js.git"},author:"",license:"MIT",jam:{main:"dist/proj4.js",include:["dist/proj4.js","README.md","AUTHORS","LICENSE.md"]},devDependencies:{"grunt-cli":"~0.1.13",grunt:"~0.4.2","grunt-contrib-connect":"~0.6.0","grunt-contrib-jshint":"~0.8.0",chai:"~1.8.1",mocha:"~1.17.1","grunt-mocha-phantomjs":"~0.4.0",browserify:"~12.0.1","grunt-browserify":"~4.0.1","grunt-contrib-uglify":"~0.11.1",curl:"git://github.com/cujojs/curl.git",istanbul:"~0.2.4",tin:"~0.4.0"},dependencies:{mgrs:"~0.0.2"}}},{}],"./includedProjections":[function(a,b,c){b.exports=a("hTEDpn")},{}],hTEDpn:[function(a,b,c){var d=[a("./lib/projections/tmerc"),a("./lib/projections/utm"),a("./lib/projections/sterea"),a("./lib/projections/stere"),a("./lib/projections/somerc"),a("./lib/projections/omerc"),a("./lib/projections/lcc"),a("./lib/projections/krovak"),a("./lib/projections/cass"),a("./lib/projections/laea"),a("./lib/projections/aea"),a("./lib/projections/gnom"),a("./lib/projections/cea"),a("./lib/projections/eqc"),a("./lib/projections/poly"),a("./lib/projections/nzmg"),a("./lib/projections/mill"),a("./lib/projections/sinu"),a("./lib/projections/moll"),a("./lib/projections/eqdc"),a("./lib/projections/vandg"),a("./lib/projections/aeqd")];b.exports=function(proj4){d.forEach(function(a){proj4.Proj.projections.add(a)})}},{"./lib/projections/aea":40,"./lib/projections/aeqd":41,"./lib/projections/cass":42,"./lib/projections/cea":43,"./lib/projections/eqc":44,"./lib/projections/eqdc":45,"./lib/projections/gnom":47,"./lib/projections/krovak":48,"./lib/projections/laea":49,"./lib/projections/lcc":50,"./lib/projections/mill":53,"./lib/projections/moll":54,"./lib/projections/nzmg":55,"./lib/projections/omerc":56,"./lib/projections/poly":57,"./lib/projections/sinu":58,"./lib/projections/somerc":59,"./lib/projections/stere":60,"./lib/projections/sterea":61,"./lib/projections/tmerc":62,"./lib/projections/utm":63,"./lib/projections/vandg":64}]},{},[36])(36)}); ================================================ FILE: vignettes/tutorials/libs/Proj4Leaflet/proj4leaflet.js ================================================ (function (factory) { var L, proj4; if (typeof define === 'function' && define.amd) { // AMD define(['leaflet', 'proj4'], factory); } else if (typeof module === 'object' && typeof module.exports === "object") { // Node/CommonJS L = require('leaflet'); proj4 = require('proj4'); module.exports = factory(L, proj4); } else { // Browser globals if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined') throw 'Leaflet and proj4 must be loaded first'; factory(window.L, window.proj4); } }(function (L, proj4) { if (proj4.__esModule && proj4.default) { // If proj4 was bundled as an ES6 module, unwrap it to get // to the actual main proj4 object. // See discussion in https://github.com/kartena/Proj4Leaflet/pull/147 proj4 = proj4.default; } L.Proj = {}; L.Proj._isProj4Obj = function(a) { return (typeof a.inverse !== 'undefined' && typeof a.forward !== 'undefined'); }; L.Proj.Projection = L.Class.extend({ initialize: function(code, def, bounds) { var isP4 = L.Proj._isProj4Obj(code); this._proj = isP4 ? code : this._projFromCodeDef(code, def); this.bounds = isP4 ? def : bounds; }, project: function (latlng) { var point = this._proj.forward([latlng.lng, latlng.lat]); return new L.Point(point[0], point[1]); }, unproject: function (point, unbounded) { var point2 = this._proj.inverse([point.x, point.y]); return new L.LatLng(point2[1], point2[0], unbounded); }, _projFromCodeDef: function(code, def) { if (def) { proj4.defs(code, def); } else if (proj4.defs[code] === undefined) { var urn = code.split(':'); if (urn.length > 3) { code = urn[urn.length - 3] + ':' + urn[urn.length - 1]; } if (proj4.defs[code] === undefined) { throw 'No projection definition for code ' + code; } } return proj4(code); } }); L.Proj.CRS = L.Class.extend({ includes: L.CRS, options: { transformation: new L.Transformation(1, 0, -1, 0) }, initialize: function(a, b, c) { var code, proj, def, options; if (L.Proj._isProj4Obj(a)) { proj = a; code = proj.srsCode; options = b || {}; this.projection = new L.Proj.Projection(proj, options.bounds); } else { code = a; def = b; options = c || {}; this.projection = new L.Proj.Projection(code, def, options.bounds); } L.Util.setOptions(this, options); this.code = code; this.transformation = this.options.transformation; if (this.options.origin) { this.transformation = new L.Transformation(1, -this.options.origin[0], -1, this.options.origin[1]); } if (this.options.scales) { this._scales = this.options.scales; } else if (this.options.resolutions) { this._scales = []; for (var i = this.options.resolutions.length - 1; i >= 0; i--) { if (this.options.resolutions[i]) { this._scales[i] = 1 / this.options.resolutions[i]; } } } this.infinite = !this.options.bounds; }, scale: function(zoom) { var iZoom = Math.floor(zoom), baseScale, nextScale, scaleDiff, zDiff; if (zoom === iZoom) { return this._scales[zoom]; } else { // Non-integer zoom, interpolate baseScale = this._scales[iZoom]; nextScale = this._scales[iZoom + 1]; scaleDiff = nextScale - baseScale; zDiff = (zoom - iZoom); return baseScale + scaleDiff * zDiff; } }, zoom: function(scale) { // Find closest number in this._scales, down var downScale = this._closestElement(this._scales, scale), downZoom = this._scales.indexOf(downScale), nextScale, nextZoom, scaleDiff; // Check if scale is downScale => return array index if (scale === downScale) { return downZoom; } if (downScale === undefined) { return -Infinity; } // Interpolate nextZoom = downZoom + 1; nextScale = this._scales[nextZoom]; if (nextScale === undefined) { return Infinity; } scaleDiff = nextScale - downScale; return (scale - downScale) / scaleDiff + downZoom; }, distance: L.CRS.Earth.distance, R: L.CRS.Earth.R, /* Get the closest lowest element in an array */ _closestElement: function(array, element) { var low; for (var i = array.length; i--;) { if (array[i] <= element && (low === undefined || low < array[i])) { low = array[i]; } } return low; } }); L.Proj.GeoJSON = L.GeoJSON.extend({ initialize: function(geojson, options) { this._callLevel = 0; L.GeoJSON.prototype.initialize.call(this, geojson, options); }, addData: function(geojson) { var crs; if (geojson) { if (geojson.crs && geojson.crs.type === 'name') { crs = new L.Proj.CRS(geojson.crs.properties.name); } else if (geojson.crs && geojson.crs.type) { crs = new L.Proj.CRS(geojson.crs.type + ':' + geojson.crs.properties.code); } if (crs !== undefined) { this.options.coordsToLatLng = function(coords) { var point = L.point(coords[0], coords[1]); return crs.projection.unproject(point); }; } } // Base class' addData might call us recursively, but // CRS shouldn't be cleared in that case, since CRS applies // to the whole GeoJSON, inluding sub-features. this._callLevel++; try { L.GeoJSON.prototype.addData.call(this, geojson); } finally { this._callLevel--; if (this._callLevel === 0) { delete this.options.coordsToLatLng; } } } }); L.Proj.geoJson = function(geojson, options) { return new L.Proj.GeoJSON(geojson, options); }; L.Proj.ImageOverlay = L.ImageOverlay.extend({ initialize: function (url, bounds, options) { L.ImageOverlay.prototype.initialize.call(this, url, null, options); this._projectedBounds = bounds; }, // Danger ahead: Overriding internal methods in Leaflet. // Decided to do this rather than making a copy of L.ImageOverlay // and doing very tiny modifications to it. // Future will tell if this was wise or not. _animateZoom: function (event) { var scale = this._map.getZoomScale(event.zoom); var northWest = L.point(this._projectedBounds.min.x, this._projectedBounds.max.y); var offset = this._projectedToNewLayerPoint(northWest, event.zoom, event.center); L.DomUtil.setTransform(this._image, offset, scale); }, _reset: function () { var zoom = this._map.getZoom(); var pixelOrigin = this._map.getPixelOrigin(); var bounds = L.bounds( this._transform(this._projectedBounds.min, zoom)._subtract(pixelOrigin), this._transform(this._projectedBounds.max, zoom)._subtract(pixelOrigin) ); var size = bounds.getSize(); L.DomUtil.setPosition(this._image, bounds.min); this._image.style.width = size.x + 'px'; this._image.style.height = size.y + 'px'; }, _projectedToNewLayerPoint: function (point, zoom, center) { var viewHalf = this._map.getSize()._divideBy(2); var newTopLeft = this._map.project(center, zoom)._subtract(viewHalf)._round(); var topLeft = newTopLeft.add(this._map._getMapPanePos()); return this._transform(point, zoom)._subtract(topLeft); }, _transform: function (point, zoom) { var crs = this._map.options.crs; var transformation = crs.transformation; var scale = crs.scale(zoom); return transformation.transform(point, scale); } }); L.Proj.imageOverlay = function (url, bounds, options) { return new L.Proj.ImageOverlay(url, bounds, options); }; return L.Proj; })); ================================================ FILE: vignettes/tutorials/libs/crosstalk/css/crosstalk.css ================================================ /* Adjust margins outwards, so column contents line up with the edges of the parent of container-fluid. */ .container-fluid.crosstalk-bscols { margin-left: -30px; margin-right: -30px; white-space: normal; } /* But don't adjust the margins outwards if we're directly under the body, i.e. we were the top-level of something at the console. */ body > .container-fluid.crosstalk-bscols { margin-left: auto; margin-right: auto; } .crosstalk-input-checkboxgroup .crosstalk-options-group .crosstalk-options-column { display: inline-block; padding-right: 12px; vertical-align: top; } @media only screen and (max-width:480px) { .crosstalk-input-checkboxgroup .crosstalk-options-group .crosstalk-options-column { display: block; padding-right: inherit; } } ================================================ FILE: vignettes/tutorials/libs/crosstalk/js/crosstalk.js ================================================ (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o b) { return 1; } } /** * @private */ var FilterSet = function () { function FilterSet() { _classCallCheck(this, FilterSet); this.reset(); } _createClass(FilterSet, [{ key: "reset", value: function reset() { // Key: handle ID, Value: array of selected keys, or null this._handles = {}; // Key: key string, Value: count of handles that include it this._keys = {}; this._value = null; this._activeHandles = 0; } }, { key: "update", value: function update(handleId, keys) { if (keys !== null) { keys = keys.slice(0); // clone before sorting keys.sort(naturalComparator); } var _diffSortedLists = (0, _util.diffSortedLists)(this._handles[handleId], keys), added = _diffSortedLists.added, removed = _diffSortedLists.removed; this._handles[handleId] = keys; for (var i = 0; i < added.length; i++) { this._keys[added[i]] = (this._keys[added[i]] || 0) + 1; } for (var _i = 0; _i < removed.length; _i++) { this._keys[removed[_i]]--; } this._updateValue(keys); } /** * @param {string[]} keys Sorted array of strings that indicate * a superset of possible keys. * @private */ }, { key: "_updateValue", value: function _updateValue() { var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._allKeys; var handleCount = Object.keys(this._handles).length; if (handleCount === 0) { this._value = null; } else { this._value = []; for (var i = 0; i < keys.length; i++) { var count = this._keys[keys[i]]; if (count === handleCount) { this._value.push(keys[i]); } } } } }, { key: "clear", value: function clear(handleId) { if (typeof this._handles[handleId] === "undefined") { return; } var keys = this._handles[handleId]; if (!keys) { keys = []; } for (var i = 0; i < keys.length; i++) { this._keys[keys[i]]--; } delete this._handles[handleId]; this._updateValue(); } }, { key: "value", get: function get() { return this._value; } }, { key: "_allKeys", get: function get() { var allKeys = Object.keys(this._keys); allKeys.sort(naturalComparator); return allKeys; } }]); return FilterSet; }(); exports.default = FilterSet; },{"./util":11}],4:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.default = group; var _var2 = require("./var"); var _var3 = _interopRequireDefault(_var2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Use a global so that multiple copies of crosstalk.js can be loaded and still // have groups behave as singletons across all copies. global.__crosstalk_groups = global.__crosstalk_groups || {}; var groups = global.__crosstalk_groups; function group(groupName) { if (groupName && typeof groupName === "string") { if (!groups.hasOwnProperty(groupName)) { groups[groupName] = new Group(groupName); } return groups[groupName]; } else if ((typeof groupName === "undefined" ? "undefined" : _typeof(groupName)) === "object" && groupName._vars && groupName.var) { // Appears to already be a group object return groupName; } else if (Array.isArray(groupName) && groupName.length == 1 && typeof groupName[0] === "string") { return group(groupName[0]); } else { throw new Error("Invalid groupName argument"); } } var Group = function () { function Group(name) { _classCallCheck(this, Group); this.name = name; this._vars = {}; } _createClass(Group, [{ key: "var", value: function _var(name) { if (!name || typeof name !== "string") { throw new Error("Invalid var name"); } if (!this._vars.hasOwnProperty(name)) this._vars[name] = new _var3.default(this, name); return this._vars[name]; } }, { key: "has", value: function has(name) { if (!name || typeof name !== "string") { throw new Error("Invalid var name"); } return this._vars.hasOwnProperty(name); } }]); return Group; }(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./var":12}],5:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _group = require("./group"); var _group2 = _interopRequireDefault(_group); var _selection = require("./selection"); var _filter = require("./filter"); var _input = require("./input"); require("./input_selectize"); require("./input_checkboxgroup"); require("./input_slider"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var defaultGroup = (0, _group2.default)("default"); function var_(name) { return defaultGroup.var(name); } function has(name) { return defaultGroup.has(name); } if (global.Shiny) { global.Shiny.addCustomMessageHandler("update-client-value", function (message) { if (typeof message.group === "string") { (0, _group2.default)(message.group).var(message.name).set(message.value); } else { var_(message.name).set(message.value); } }); } var crosstalk = { group: _group2.default, var: var_, has: has, SelectionHandle: _selection.SelectionHandle, FilterHandle: _filter.FilterHandle, bind: _input.bind }; /** * @namespace crosstalk */ exports.default = crosstalk; global.crosstalk = crosstalk; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./filter":2,"./group":4,"./input":6,"./input_checkboxgroup":7,"./input_selectize":8,"./input_slider":9,"./selection":10}],6:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.register = register; exports.bind = bind; var $ = global.jQuery; var bindings = {}; function register(reg) { bindings[reg.className] = reg; if (global.document && global.document.readyState !== "complete") { $(function () { bind(); }); } else if (global.document) { setTimeout(bind, 100); } } function bind() { Object.keys(bindings).forEach(function (className) { var binding = bindings[className]; $("." + binding.className).not(".crosstalk-input-bound").each(function (i, el) { bindInstance(binding, el); }); }); } // Escape jQuery identifier function $escape(val) { return val.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, "\\$1"); } function bindEl(el) { var $el = $(el); Object.keys(bindings).forEach(function (className) { if ($el.hasClass(className) && !$el.hasClass("crosstalk-input-bound")) { var binding = bindings[className]; bindInstance(binding, el); } }); } function bindInstance(binding, el) { var jsonEl = $(el).find("script[type='application/json'][data-for='" + $escape(el.id) + "']"); var data = JSON.parse(jsonEl[0].innerText); var instance = binding.factory(el, data); $(el).data("crosstalk-instance", instance); $(el).addClass("crosstalk-input-bound"); } if (global.Shiny) { var inputBinding = new global.Shiny.InputBinding(); var _$ = global.jQuery; _$.extend(inputBinding, { find: function find(scope) { return _$(scope).find(".crosstalk-input"); }, initialize: function initialize(el) { if (!_$(el).hasClass("crosstalk-input-bound")) { bindEl(el); } }, getId: function getId(el) { return el.id; }, getValue: function getValue(el) {}, setValue: function setValue(el, value) {}, receiveMessage: function receiveMessage(el, data) {}, subscribe: function subscribe(el, callback) { _$(el).data("crosstalk-instance").resume(); }, unsubscribe: function unsubscribe(el) { _$(el).data("crosstalk-instance").suspend(); } }); global.Shiny.inputBindings.register(inputBinding, "crosstalk.inputBinding"); } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],7:[function(require,module,exports){ (function (global){ "use strict"; var _input = require("./input"); var input = _interopRequireWildcard(_input); var _filter = require("./filter"); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var $ = global.jQuery; input.register({ className: "crosstalk-input-checkboxgroup", factory: function factory(el, data) { /* * map: {"groupA": ["keyA", "keyB", ...], ...} * group: "ct-groupname" */ var ctHandle = new _filter.FilterHandle(data.group); var lastKnownKeys = void 0; var $el = $(el); $el.on("change", "input[type='checkbox']", function () { var checked = $el.find("input[type='checkbox']:checked"); if (checked.length === 0) { lastKnownKeys = null; ctHandle.clear(); } else { var keys = {}; checked.each(function () { data.map[this.value].forEach(function (key) { keys[key] = true; }); }); var keyArray = Object.keys(keys); keyArray.sort(); lastKnownKeys = keyArray; ctHandle.set(keyArray); } }); return { suspend: function suspend() { ctHandle.clear(); }, resume: function resume() { if (lastKnownKeys) ctHandle.set(lastKnownKeys); } }; } }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./filter":2,"./input":6}],8:[function(require,module,exports){ (function (global){ "use strict"; var _input = require("./input"); var input = _interopRequireWildcard(_input); var _util = require("./util"); var util = _interopRequireWildcard(_util); var _filter = require("./filter"); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var $ = global.jQuery; input.register({ className: "crosstalk-input-select", factory: function factory(el, data) { /* * items: {value: [...], label: [...]} * map: {"groupA": ["keyA", "keyB", ...], ...} * group: "ct-groupname" */ var first = [{ value: "", label: "(All)" }]; var items = util.dataframeToD3(data.items); var opts = { options: first.concat(items), valueField: "value", labelField: "label", searchField: "label" }; var select = $(el).find("select")[0]; var selectize = $(select).selectize(opts)[0].selectize; var ctHandle = new _filter.FilterHandle(data.group); var lastKnownKeys = void 0; selectize.on("change", function () { if (selectize.items.length === 0) { lastKnownKeys = null; ctHandle.clear(); } else { var keys = {}; selectize.items.forEach(function (group) { data.map[group].forEach(function (key) { keys[key] = true; }); }); var keyArray = Object.keys(keys); keyArray.sort(); lastKnownKeys = keyArray; ctHandle.set(keyArray); } }); return { suspend: function suspend() { ctHandle.clear(); }, resume: function resume() { if (lastKnownKeys) ctHandle.set(lastKnownKeys); } }; } }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./filter":2,"./input":6,"./util":11}],9:[function(require,module,exports){ (function (global){ "use strict"; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _input = require("./input"); var input = _interopRequireWildcard(_input); var _filter = require("./filter"); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } var $ = global.jQuery; var strftime = global.strftime; input.register({ className: "crosstalk-input-slider", factory: function factory(el, data) { /* * map: {"groupA": ["keyA", "keyB", ...], ...} * group: "ct-groupname" */ var ctHandle = new _filter.FilterHandle(data.group); var opts = {}; var $el = $(el).find("input"); var dataType = $el.data("data-type"); var timeFormat = $el.data("time-format"); var round = $el.data("round"); var timeFormatter = void 0; // Set up formatting functions if (dataType === "date") { timeFormatter = strftime.utc(); opts.prettify = function (num) { return timeFormatter(timeFormat, new Date(num)); }; } else if (dataType === "datetime") { var timezone = $el.data("timezone"); if (timezone) timeFormatter = strftime.timezone(timezone);else timeFormatter = strftime; opts.prettify = function (num) { return timeFormatter(timeFormat, new Date(num)); }; } else if (dataType === "number") { if (typeof round !== "undefined") opts.prettify = function (num) { var factor = Math.pow(10, round); return Math.round(num * factor) / factor; }; } $el.ionRangeSlider(opts); function getValue() { var result = $el.data("ionRangeSlider").result; // Function for converting numeric value from slider to appropriate type. var convert = void 0; var dataType = $el.data("data-type"); if (dataType === "date") { convert = function convert(val) { return formatDateUTC(new Date(+val)); }; } else if (dataType === "datetime") { convert = function convert(val) { // Convert ms to s return +val / 1000; }; } else { convert = function convert(val) { return +val; }; } if ($el.data("ionRangeSlider").options.type === "double") { return [convert(result.from), convert(result.to)]; } else { return convert(result.from); } } var lastKnownKeys = null; $el.on("change.crosstalkSliderInput", function (event) { if (!$el.data("updating") && !$el.data("animating")) { var _getValue = getValue(), _getValue2 = _slicedToArray(_getValue, 2), from = _getValue2[0], to = _getValue2[1]; var keys = []; for (var i = 0; i < data.values.length; i++) { var val = data.values[i]; if (val >= from && val <= to) { keys.push(data.keys[i]); } } keys.sort(); ctHandle.set(keys); lastKnownKeys = keys; } }); // let $el = $(el); // $el.on("change", "input[type="checkbox"]", function() { // let checked = $el.find("input[type="checkbox"]:checked"); // if (checked.length === 0) { // ctHandle.clear(); // } else { // let keys = {}; // checked.each(function() { // data.map[this.value].forEach(function(key) { // keys[key] = true; // }); // }); // let keyArray = Object.keys(keys); // keyArray.sort(); // ctHandle.set(keyArray); // } // }); return { suspend: function suspend() { ctHandle.clear(); }, resume: function resume() { if (lastKnownKeys) ctHandle.set(lastKnownKeys); } }; } }); // Convert a number to a string with leading zeros function padZeros(n, digits) { var str = n.toString(); while (str.length < digits) { str = "0" + str; }return str; } // Given a Date object, return a string in yyyy-mm-dd format, using the // UTC date. This may be a day off from the date in the local time zone. function formatDateUTC(date) { if (date instanceof Date) { return date.getUTCFullYear() + "-" + padZeros(date.getUTCMonth() + 1, 2) + "-" + padZeros(date.getUTCDate(), 2); } else { return null; } } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./filter":2,"./input":6}],10:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectionHandle = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require("./events"); var _events2 = _interopRequireDefault(_events); var _group = require("./group"); var _group2 = _interopRequireDefault(_group); var _util = require("./util"); var util = _interopRequireWildcard(_util); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Use this class to read and write (and listen for changes to) the selection * for a Crosstalk group. This is intended to be used for linked brushing. * * If two (or more) `SelectionHandle` instances in the same webpage share the * same group name, they will share the same state. Setting the selection using * one `SelectionHandle` instance will result in the `value` property instantly * changing across the others, and `"change"` event listeners on all instances * (including the one that initiated the sending) will fire. * * @param {string} [group] - The name of the Crosstalk group, or if none, * null or undefined (or any other falsy value). This can be changed later * via the [SelectionHandle#setGroup](#setGroup) method. * @param {Object} [extraInfo] - An object whose properties will be copied to * the event object whenever an event is emitted. */ var SelectionHandle = exports.SelectionHandle = function () { function SelectionHandle() { var group = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var extraInfo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; _classCallCheck(this, SelectionHandle); this._eventRelay = new _events2.default(); this._emitter = new util.SubscriptionTracker(this._eventRelay); // Name of the group we're currently tracking, if any. Can change over time. this._group = null; // The Var we're currently tracking, if any. Can change over time. this._var = null; // The event handler subscription we currently have on var.on("change"). this._varOnChangeSub = null; this._extraInfo = util.extend({ sender: this }, extraInfo); this.setGroup(group); } /** * Changes the Crosstalk group membership of this SelectionHandle. The group * being switched away from (if any) will not have its selection value * modified as a result of calling `setGroup`, even if this handle was the * most recent handle to set the selection of the group. * * The group being switched to (if any) will also not have its selection value * modified as a result of calling `setGroup`. If you want to set the * selection value of the new group, call `set` explicitly. * * @param {string} group - The name of the Crosstalk group, or null (or * undefined) to clear the group. */ _createClass(SelectionHandle, [{ key: "setGroup", value: function setGroup(group) { var _this = this; // If group is unchanged, do nothing if (this._group === group) return; // Treat null, undefined, and other falsy values the same if (!this._group && !group) return; if (this._var) { this._var.off("change", this._varOnChangeSub); this._var = null; this._varOnChangeSub = null; } this._group = group; if (group) { this._var = (0, _group2.default)(group).var("selection"); var sub = this._var.on("change", function (e) { _this._eventRelay.trigger("change", e, _this); }); this._varOnChangeSub = sub; } } /** * Retrieves the current selection for the group represented by this * `SelectionHandle`. * * - If no selection is active, then this value will be falsy. * - If a selection is active, but no data points are selected, then this * value will be an empty array. * - If a selection is active, and data points are selected, then the keys * of the selected data points will be present in the array. */ }, { key: "_mergeExtraInfo", /** * Combines the given `extraInfo` (if any) with the handle's default * `_extraInfo` (if any). * @private */ value: function _mergeExtraInfo(extraInfo) { // Important incidental effect: shallow clone is returned return util.extend({}, this._extraInfo ? this._extraInfo : null, extraInfo ? extraInfo : null); } /** * Overwrites the current selection for the group, and raises the `"change"` * event among all of the group's '`SelectionHandle` instances (including * this one). * * @fires SelectionHandle#change * @param {string[]} selectedKeys - Falsy, empty array, or array of keys (see * {@link SelectionHandle#value}). * @param {Object} [extraInfo] - Extra properties to be included on the event * object that's passed to listeners (in addition to any options that were * passed into the `SelectionHandle` constructor). */ }, { key: "set", value: function set(selectedKeys, extraInfo) { if (this._var) this._var.set(selectedKeys, this._mergeExtraInfo(extraInfo)); } /** * Overwrites the current selection for the group, and raises the `"change"` * event among all of the group's '`SelectionHandle` instances (including * this one). * * @fires SelectionHandle#change * @param {Object} [extraInfo] - Extra properties to be included on the event * object that's passed to listeners (in addition to any that were passed * into the `SelectionHandle` constructor). */ }, { key: "clear", value: function clear(extraInfo) { if (this._var) this.set(void 0, this._mergeExtraInfo(extraInfo)); } /** * Subscribes to events on this `SelectionHandle`. * * @param {string} eventType - Indicates the type of events to listen to. * Currently, only `"change"` is supported. * @param {SelectionHandle~listener} listener - The callback function that * will be invoked when the event occurs. * @return {string} - A token to pass to {@link SelectionHandle#off} to cancel * this subscription. */ }, { key: "on", value: function on(eventType, listener) { return this._emitter.on(eventType, listener); } /** * Cancels event subscriptions created by {@link SelectionHandle#on}. * * @param {string} eventType - The type of event to unsubscribe. * @param {string|SelectionHandle~listener} listener - Either the callback * function previously passed into {@link SelectionHandle#on}, or the * string that was returned from {@link SelectionHandle#on}. */ }, { key: "off", value: function off(eventType, listener) { return this._emitter.off(eventType, listener); } /** * Shuts down the `SelectionHandle` object. * * Removes all event listeners that were added through this handle. */ }, { key: "close", value: function close() { this._emitter.removeAllListeners(); this.setGroup(null); } }, { key: "value", get: function get() { return this._var ? this._var.get() : null; } }]); return SelectionHandle; }(); /** * @callback SelectionHandle~listener * @param {Object} event - An object containing details of the event. For * `"change"` events, this includes the properties `value` (the new * value of the selection, or `undefined` if no selection is active), * `oldValue` (the previous value of the selection), and `sender` (the * `SelectionHandle` instance that made the change). */ /** * @event SelectionHandle#change * @type {object} * @property {object} value - The new value of the selection, or `undefined` * if no selection is active. * @property {object} oldValue - The previous value of the selection. * @property {SelectionHandle} sender - The `SelectionHandle` instance that * changed the value. */ },{"./events":1,"./group":4,"./util":11}],11:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.extend = extend; exports.checkSorted = checkSorted; exports.diffSortedLists = diffSortedLists; exports.dataframeToD3 = dataframeToD3; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function extend(target) { for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { sources[_key - 1] = arguments[_key]; } for (var i = 0; i < sources.length; i++) { var src = sources[i]; if (typeof src === "undefined" || src === null) continue; for (var key in src) { if (src.hasOwnProperty(key)) { target[key] = src[key]; } } } return target; } function checkSorted(list) { for (var i = 1; i < list.length; i++) { if (list[i] <= list[i - 1]) { throw new Error("List is not sorted or contains duplicate"); } } } function diffSortedLists(a, b) { var i_a = 0; var i_b = 0; if (!a) a = []; if (!b) b = []; var a_only = []; var b_only = []; checkSorted(a); checkSorted(b); while (i_a < a.length && i_b < b.length) { if (a[i_a] === b[i_b]) { i_a++; i_b++; } else if (a[i_a] < b[i_b]) { a_only.push(a[i_a++]); } else { b_only.push(b[i_b++]); } } if (i_a < a.length) a_only = a_only.concat(a.slice(i_a)); if (i_b < b.length) b_only = b_only.concat(b.slice(i_b)); return { removed: a_only, added: b_only }; } // Convert from wide: { colA: [1,2,3], colB: [4,5,6], ... } // to long: [ {colA: 1, colB: 4}, {colA: 2, colB: 5}, ... ] function dataframeToD3(df) { var names = []; var length = void 0; for (var name in df) { if (df.hasOwnProperty(name)) names.push(name); if (_typeof(df[name]) !== "object" || typeof df[name].length === "undefined") { throw new Error("All fields must be arrays"); } else if (typeof length !== "undefined" && length !== df[name].length) { throw new Error("All fields must be arrays of the same length"); } length = df[name].length; } var results = []; var item = void 0; for (var row = 0; row < length; row++) { item = {}; for (var col = 0; col < names.length; col++) { item[names[col]] = df[names[col]][row]; } results.push(item); } return results; } /** * Keeps track of all event listener additions/removals and lets all active * listeners be removed with a single operation. * * @private */ var SubscriptionTracker = exports.SubscriptionTracker = function () { function SubscriptionTracker(emitter) { _classCallCheck(this, SubscriptionTracker); this._emitter = emitter; this._subs = {}; } _createClass(SubscriptionTracker, [{ key: "on", value: function on(eventType, listener) { var sub = this._emitter.on(eventType, listener); this._subs[sub] = eventType; return sub; } }, { key: "off", value: function off(eventType, listener) { var sub = this._emitter.off(eventType, listener); if (sub) { delete this._subs[sub]; } return sub; } }, { key: "removeAllListeners", value: function removeAllListeners() { var _this = this; var current_subs = this._subs; this._subs = {}; Object.keys(current_subs).forEach(function (sub) { _this._emitter.off(current_subs[sub], sub); }); } }]); return SubscriptionTracker; }(); },{}],12:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require("./events"); var _events2 = _interopRequireDefault(_events); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Var = function () { function Var(group, name, /*optional*/value) { _classCallCheck(this, Var); this._group = group; this._name = name; this._value = value; this._events = new _events2.default(); } _createClass(Var, [{ key: "get", value: function get() { return this._value; } }, { key: "set", value: function set(value, /*optional*/event) { if (this._value === value) { // Do nothing; the value hasn't changed return; } var oldValue = this._value; this._value = value; // Alert JavaScript listeners that the value has changed var evt = {}; if (event && (typeof event === "undefined" ? "undefined" : _typeof(event)) === "object") { for (var k in event) { if (event.hasOwnProperty(k)) evt[k] = event[k]; } } evt.oldValue = oldValue; evt.value = value; this._events.trigger("change", evt, this); // TODO: Make this extensible, to let arbitrary back-ends know that // something has changed if (global.Shiny && global.Shiny.onInputChange) { global.Shiny.onInputChange(".clientValue-" + (this._group.name !== null ? this._group.name + "-" : "") + this._name, typeof value === "undefined" ? null : value); } } }, { key: "on", value: function on(eventType, listener) { return this._events.on(eventType, listener); } }, { key: "off", value: function off(eventType, listener) { return this._events.off(eventType, listener); } }]); return Var; }(); exports.default = Var; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./events":1}]},{},[5]) //# sourceMappingURL=crosstalk.js.map ================================================ FILE: vignettes/tutorials/libs/datatables-binding/datatables.js ================================================ (function() { // some helper functions: using a global object DTWidget so that it can be used // in JS() code, e.g. datatable(options = list(foo = JS('code'))); unlike R's // dynamic scoping, when 'code' is eval()'ed, JavaScript does not know objects // from the "parent frame", e.g. JS('DTWidget') will not work unless it was made // a global object var DTWidget = {}; // 123456666.7890 -> 123,456,666.7890 var markInterval = function(d, digits, interval, mark, decMark, precision) { x = precision ? d.toPrecision(digits) : d.toFixed(digits); if (!/^-?[\d.]+$/.test(x)) return x; var xv = x.split('.'); if (xv.length > 2) return x; // should have at most one decimal point xv[0] = xv[0].replace(new RegExp('\\B(?=(\\d{' + interval + '})+(?!\\d))', 'g'), mark); return xv.join(decMark); }; DTWidget.formatCurrency = function(data, currency, digits, interval, mark, decMark, before) { var d = parseFloat(data); if (isNaN(d)) return ''; var res = markInterval(d, digits, interval, mark, decMark); res = before ? (/^-/.test(res) ? '-' + currency + res.replace(/^-/, '') : currency + res) : res + currency; return res; }; DTWidget.formatString = function(data, prefix, suffix) { var d = data; if (d === null) return ''; return prefix + d + suffix; }; DTWidget.formatPercentage = function(data, digits, interval, mark, decMark) { var d = parseFloat(data); if (isNaN(d)) return ''; return markInterval(d * 100, digits, interval, mark, decMark) + '%'; }; DTWidget.formatRound = function(data, digits, interval, mark, decMark) { var d = parseFloat(data); if (isNaN(d)) return ''; return markInterval(d, digits, interval, mark, decMark); }; DTWidget.formatSignif = function(data, digits, interval, mark, decMark) { var d = parseFloat(data); if (isNaN(d)) return ''; return markInterval(d, digits, interval, mark, decMark, true); }; DTWidget.formatDate = function(data, method, params) { var d = data; if (d === null) return ''; // (new Date('2015-10-28')).toDateString() may return 2015-10-27 because the // actual time created could be like 'Tue Oct 27 2015 19:00:00 GMT-0500 (CDT)', // i.e. the date-only string is treated as UTC time instead of local time if ((method === 'toDateString' || method === 'toLocaleDateString') && /^\d{4,}\D\d{2}\D\d{2}$/.test(d)) { d = d.split(/\D/); d = new Date(d[0], d[1] - 1, d[2]); } else { d = new Date(d); } return d[method].apply(d, params); }; window.DTWidget = DTWidget; var transposeArray2D = function(a) { return a.length === 0 ? a : HTMLWidgets.transposeArray2D(a); }; var crosstalkPluginsInstalled = false; function maybeInstallCrosstalkPlugins() { if (crosstalkPluginsInstalled) return; crosstalkPluginsInstalled = true; $.fn.dataTable.ext.afnFiltering.push( function(oSettings, aData, iDataIndex) { var ctfilter = oSettings.nTable.ctfilter; if (ctfilter && !ctfilter[iDataIndex]) return false; var ctselect = oSettings.nTable.ctselect; if (ctselect && !ctselect[iDataIndex]) return false; return true; } ); } HTMLWidgets.widget({ name: "datatables", type: "output", renderOnNullValue: true, initialize: function(el, width, height) { $(el).html(' '); return { data: null, ctfilterHandle: new crosstalk.FilterHandle(), ctfilterSubscription: null, ctselectHandle: new crosstalk.SelectionHandle(), ctselectSubscription: null }; }, renderValue: function(el, data, instance) { if (el.offsetWidth === 0 || el.offsetHeight === 0) { instance.data = data; return; } instance.data = null; var $el = $(el); $el.empty(); if (data === null) { $el.append(' '); // clear previous Shiny inputs (if any) for (var i in instance.clearInputs) instance.clearInputs[i](); instance.clearInputs = {}; return; } var crosstalkOptions = data.crosstalkOptions; if (!crosstalkOptions) crosstalkOptions = { 'key': null, 'group': null }; if (crosstalkOptions.group) { maybeInstallCrosstalkPlugins(); instance.ctfilterHandle.setGroup(crosstalkOptions.group); instance.ctselectHandle.setGroup(crosstalkOptions.group); } // If we are in a flexdashboard scroll layout then we: // (a) Always want to use pagination (otherwise we'll have // a "double scroll bar" effect on the phone); and // (b) Never want to fill the container (we want the pagination // level to determine the size of the container) if (window.FlexDashboard && !window.FlexDashboard.isFillPage()) { data.options.bPaginate = true; data.fillContainer = false; } // if we are in the viewer then we always want to fillContainer and // and autoHideNavigation (unless the user has explicitly set these) if (window.HTMLWidgets.viewerMode) { if (!data.hasOwnProperty("fillContainer")) data.fillContainer = true; if (!data.hasOwnProperty("autoHideNavigation")) data.autoHideNavigation = true; } // propagate fillContainer to instance (so we have it in resize) instance.fillContainer = data.fillContainer; var cells = data.data; if (cells instanceof Array) cells = transposeArray2D(cells); $el.append(data.container); var $table = $el.find('table'); if (data.class) $table.addClass(data.class); if (data.caption) $table.prepend(data.caption); if (!data.selection) data.selection = { mode: 'none', selected: null, target: 'row' }; if (HTMLWidgets.shinyMode && data.selection.mode !== 'none' && data.selection.target === 'row+column') { if ($table.children('tfoot').length === 0) { $table.append($('')); $table.find('thead tr').clone().appendTo($table.find('tfoot')); } } // column filters var filterRow; switch (data.filter) { case 'top': $table.children('thead').append(data.filterHTML); filterRow = $table.find('thead tr:last td'); break; case 'bottom': if ($table.children('tfoot').length === 0) { $table.append($('')); } $table.children('tfoot').prepend(data.filterHTML); filterRow = $table.find('tfoot tr:first td'); break; } var options = { searchDelay: 1000 }; if (cells !== null) $.extend(options, { data: cells }); // options for fillContainer var bootstrapActive = typeof($.fn.popover) != 'undefined'; if (instance.fillContainer) { // force scrollX/scrollY and turn off autoWidth options.scrollX = true; options.scrollY = "100px"; // can be any value, we'll adjust below // if we aren't paginating then move around the info/filter controls // to save space at the bottom and rephrase the info callback if (data.options.bPaginate === false) { // we know how to do this cleanly for bootstrap, not so much // for other themes/layouts if (bootstrapActive) { options.dom = "<'row'<'col-sm-4'i><'col-sm-8'f>>" + "<'row'<'col-sm-12'tr>>"; } options.fnInfoCallback = function(oSettings, iStart, iEnd, iMax, iTotal, sPre) { return Number(iTotal).toLocaleString() + " records"; }; } } // auto hide navigation if requested if (data.autoHideNavigation === true) { if (bootstrapActive && data.options.bPaginate !== false) { // strip all nav if length >= cells if ((cells instanceof Array) && data.options.iDisplayLength >= cells.length) options.dom = "<'row'<'col-sm-12'tr>>"; // alternatively lean things out for flexdashboard mobile portrait else if (window.FlexDashboard && window.FlexDashboard.isMobilePhone()) options.dom = "<'row'<'col-sm-12'f>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-12'p>>"; } } $.extend(true, options, data.options || {}); var searchCols = options.searchCols; if (searchCols) { searchCols = searchCols.map(function(x) { return x === null ? '' : x.search; }); // FIXME: this means I don't respect the escapeRegex setting delete options.searchCols; } // server-side processing? var server = options.serverSide === true; // use the dataSrc function to pre-process JSON data returned from R var DT_rows_all = [], DT_rows_current = []; if (server && HTMLWidgets.shinyMode && typeof options.ajax === 'object' && /^session\/[\da-z]+\/dataobj/.test(options.ajax.url) && !options.ajax.dataSrc) { options.ajax.dataSrc = function(json) { DT_rows_all = $.makeArray(json.DT_rows_all); DT_rows_current = $.makeArray(json.DT_rows_current); var data = json.data; if (!colReorderEnabled()) return data; var table = $table.DataTable(), order = table.colReorder.order(), flag = true, i, j, row; for (i = 0; i < order.length; ++i) if (order[i] !== i) flag = false; if (flag) return data; for (i = 0; i < data.length; ++i) { row = data[i].slice(); for (j = 0; j < order.length; ++j) data[i][j] = row[order[j]]; } return data; }; } var thiz = this; if (instance.fillContainer) $table.on('init.dt', function(e) { thiz.fillAvailableHeight(el, $(el).innerHeight()); }); // If the page contains serveral datatables and one of which enables colReorder, // the table.colReorder.order() function will exist but throws error when called. // So it seems like the only way to know if colReorder is enabled or not is to // check the options. var colReorderEnabled = function() { return "colReorder" in options; }; var table = $table.DataTable(options); $el.data('datatable', table); // Unregister previous Crosstalk event subscriptions, if they exist if (instance.ctfilterSubscription) { instance.ctfilterHandle.off("change", instance.ctfilterSubscription); instance.ctfilterSubscription = null; } if (instance.ctselectSubscription) { instance.ctselectHandle.off("change", instance.ctselectSubscription); instance.ctselectSubscription = null; } if (!crosstalkOptions.group) { $table[0].ctfilter = null; $table[0].ctselect = null; } else { var key = crosstalkOptions.key; function keysToMatches(keys) { if (!keys) { return null; } else { var selectedKeys = {}; for (var i = 0; i < keys.length; i++) { selectedKeys[keys[i]] = true; } var matches = {}; for (var j = 0; j < key.length; j++) { if (selectedKeys[key[j]]) matches[j] = true; } return matches; } } function applyCrosstalkFilter(e) { $table[0].ctfilter = keysToMatches(e.value); table.draw(); } instance.ctfilterSubscription = instance.ctfilterHandle.on("change", applyCrosstalkFilter); applyCrosstalkFilter({value: instance.ctfilterHandle.filteredKeys}); function applyCrosstalkSelection(e) { if (e.sender !== instance.ctselectHandle) { table .rows('.' + selClass, {search: 'applied'}) .nodes() .to$() .removeClass(selClass); if (selectedRows) changeInput('rows_selected', selectedRows(), void 0, true); } if (e.sender !== instance.ctselectHandle && e.value && e.value.length) { var matches = keysToMatches(e.value); // persistent selection with plotly (& leaflet) var ctOpts = crosstalk.var("plotlyCrosstalkOpts").get() || {}; if (ctOpts.persistent === true) { var matches = $.extend(matches, $table[0].ctselect); } $table[0].ctselect = matches; table.draw(); } else { if ($table[0].ctselect) { $table[0].ctselect = null; table.draw(); } } } instance.ctselectSubscription = instance.ctselectHandle.on("change", applyCrosstalkSelection); // TODO: This next line doesn't seem to work when renderDataTable is used applyCrosstalkSelection({value: instance.ctselectHandle.value}); } var inArray = function(val, array) { return $.inArray(val, $.makeArray(array)) > -1; }; // encode + to %2B when searching in the table on server side, because // shiny::parseQueryString() treats + as spaces, and DataTables does not // encode + to %2B (or % to %25) when sending the request var encode_plus = function(x) { return server ? x.replace(/%/g, '%25').replace(/\+/g, '%2B') : x; }; // search the i-th column var searchColumn = function(i, value) { var regex = false, ci = true; if (options.search) { regex = options.search.regex, ci = options.search.caseInsensitive !== false; } return table.column(i).search(encode_plus(value), regex, !regex, ci); }; if (data.filter !== 'none') { filterRow.each(function(i, td) { var $td = $(td), type = $td.data('type'), filter; var $input = $td.children('div').first().children('input'); $input.prop('disabled', !table.settings()[0].aoColumns[i].bSearchable || type === 'disabled'); $input.on('input blur', function() { $input.next('span').toggle(Boolean($input.val())); }); // Bootstrap sets pointer-events to none and we won't be able to click // the clear button $input.next('span').css('pointer-events', 'auto').hide().click(function() { $(this).hide().prev('input').val('').trigger('input').focus(); }); var searchCol; // search string for this column if (searchCols && searchCols[i]) { searchCol = searchCols[i]; $input.val(searchCol).trigger('input'); } var $x = $td.children('div').last(); // remove the overflow: hidden attribute of the scrollHead // (otherwise the scrolling table body obscures the filters) // The workaround and the discussion from // https://github.com/rstudio/DT/issues/554#issuecomment-518007347 // Otherwise the filter selection will not be anchored to the values // when the columns number is many and scrollX is enabled. var scrollHead = $(el).find('.dataTables_scrollHead,.dataTables_scrollFoot'); var cssOverflowHead = scrollHead.css('overflow'); var scrollBody = $(el).find('.dataTables_scrollBody'); var cssOverflowBody = scrollBody.css('overflow'); var scrollTable = $(el).find('.dataTables_scroll'); var cssOverflowTable = scrollTable.css('overflow'); if (cssOverflowHead === 'hidden') { $x.on('show hide', function(e) { if (e.type === 'show') { scrollHead.css('overflow', 'visible'); scrollBody.css('overflow', 'visible'); scrollTable.css('overflow-x', 'scroll'); } else { scrollHead.css('overflow', cssOverflowHead); scrollBody.css('overflow', cssOverflowBody); scrollTable.css('overflow-x', cssOverflowTable); } }); $x.css('z-index', 25); } if (inArray(type, ['factor', 'logical'])) { $input.on({ click: function() { $input.parent().hide(); $x.show().trigger('show'); filter[0].selectize.focus(); }, input: function() { if ($input.val() === '') filter[0].selectize.setValue([]); } }); var $input2 = $x.children('select'); filter = $input2.selectize({ options: $input2.data('options').map(function(v, i) { return ({text: v, value: v}); }), plugins: ['remove_button'], hideSelected: true, onChange: function(value) { if (value === null) value = []; // compatibility with jQuery 3.0 $input.val(value.length ? JSON.stringify(value) : ''); if (value.length) $input.trigger('input'); $input.attr('title', $input.val()); if (server) { table.column(i).search(value.length ? encode_plus(JSON.stringify(value)) : '').draw(); return; } // turn off filter if nothing selected $td.data('filter', value.length > 0); table.draw(); // redraw table, and filters will be applied } }); if (searchCol) filter[0].selectize.setValue(JSON.parse(searchCol)); filter[0].selectize.on('blur', function() { $x.hide().trigger('hide'); $input.parent().show(); $input.trigger('blur'); }); filter.next('div').css('margin-bottom', 'auto'); } else if (type === 'character') { var fun = function() { searchColumn(i, $input.val()).draw(); }; if (server) { fun = $.fn.dataTable.util.throttle(fun, options.searchDelay); } $input.on('input', fun); } else if (inArray(type, ['number', 'integer', 'date', 'time'])) { var $x0 = $x; $x = $x0.children('div').first(); $x0.css({ 'background-color': '#fff', 'border': '1px #ddd solid', 'border-radius': '4px', 'padding': '20px 20px 10px 20px' }); var $spans = $x0.children('span').css({ 'margin-top': '10px', 'white-space': 'nowrap' }); var $span1 = $spans.first(), $span2 = $spans.last(); var r1 = +$x.data('min'), r2 = +$x.data('max'); // when the numbers are too small or have many decimal places, the // slider may have numeric precision problems (#150) var scale = Math.pow(10, Math.max(0, +$x.data('scale') || 0)); r1 = Math.round(r1 * scale); r2 = Math.round(r2 * scale); var scaleBack = function(x, scale) { if (scale === 1) return x; var d = Math.round(Math.log(scale) / Math.log(10)); // to avoid problems like 3.423/100 -> 0.034230000000000003 return (x / scale).toFixed(d); }; $input.on({ focus: function() { $x0.show().trigger('show'); // first, make sure the slider div leaves at least 20px between // the two (slider value) span's $x0.width(Math.max(160, $span1.outerWidth() + $span2.outerWidth() + 20)); // then, if the input is really wide, make the slider the same // width as the input if ($x0.outerWidth() < $input.outerWidth()) { $x0.outerWidth($input.outerWidth()); } // make sure the slider div does not reach beyond the right margin if ($(window).width() < $x0.offset().left + $x0.width()) { $x0.offset({ 'left': $input.offset().left + $input.outerWidth() - $x0.outerWidth() }); } }, blur: function() { $x0.hide().trigger('hide'); }, input: function() { if ($input.val() === '') filter.val([r1, r2]); }, change: function() { var v = $input.val().replace(/\s/g, ''); if (v === '') return; v = v.split('...'); if (v.length !== 2) { $input.parent().addClass('has-error'); return; } if (v[0] === '') v[0] = r1; if (v[1] === '') v[1] = r2; $input.parent().removeClass('has-error'); // treat date as UTC time at midnight var strTime = function(x) { var s = type === 'date' ? 'T00:00:00Z' : ''; var t = new Date(x + s).getTime(); // add 10 minutes to date since it does not hurt the date, and // it helps avoid the tricky floating point arithmetic problems, // e.g. sometimes the date may be a few milliseconds earlier // than the midnight due to precision problems in noUiSlider return type === 'date' ? t + 3600000 : t; }; if (inArray(type, ['date', 'time'])) { v[0] = strTime(v[0]); v[1] = strTime(v[1]); } if (v[0] != r1) v[0] *= scale; if (v[1] != r2) v[1] *= scale; filter.val(v); } }); var formatDate = function(d, isoFmt) { d = scaleBack(d, scale); if (type === 'number') return d; if (type === 'integer') return parseInt(d); var x = new Date(+d); var fmt = ('filterDateFmt' in data) ? data.filterDateFmt[i] : undefined; if (fmt !== undefined && isoFmt === false) return x[fmt.method].apply(x, fmt.params); if (type === 'date') { var pad0 = function(x) { return ('0' + x).substr(-2, 2); }; return x.getUTCFullYear() + '-' + pad0(1 + x.getUTCMonth()) + '-' + pad0(x.getUTCDate()); } else { return x.toISOString(); } }; var opts = type === 'date' ? { step: 60 * 60 * 1000 } : type === 'integer' ? { step: 1 } : {}; filter = $x.noUiSlider($.extend({ start: [r1, r2], range: {min: r1, max: r2}, connect: true }, opts)); if (scale > 1) (function() { var t1 = r1, t2 = r2; var val = filter.val(); while (val[0] > r1 || val[1] < r2) { if (val[0] > r1) { t1 -= val[0] - r1; } if (val[1] < r2) { t2 += r2 - val[1]; } filter = $x.noUiSlider($.extend({ start: [t1, t2], range: {min: t1, max: t2}, connect: true }, opts), true); val = filter.val(); } r1 = t1; r2 = t2; })(); var updateSliderText = function(v1, v2) { $span1.text(formatDate(v1, false)); $span2.text(formatDate(v2, false)); }; updateSliderText(r1, r2); var updateSlider = function(e) { var val = filter.val(); // turn off filter if in full range $td.data('filter', val[0] > r1 || val[1] < r2); var v1 = formatDate(val[0]), v2 = formatDate(val[1]), ival; if ($td.data('filter')) { ival = v1 + ' ... ' + v2; $input.attr('title', ival).val(ival).trigger('input'); } else { $input.attr('title', '').val(''); } updateSliderText(val[0], val[1]); if (e.type === 'slide') return; // no searching when sliding only if (server) { table.column(i).search($td.data('filter') ? ival : '').draw(); return; } table.draw(); }; filter.on({ set: updateSlider, slide: updateSlider }); } // server-side processing will be handled by R (or whatever server // language you use); the following code is only needed for client-side // processing if (server) { // if a search string has been pre-set, search now if (searchCol) searchColumn(i, searchCol).draw(); return; } var customFilter = function(settings, data, dataIndex) { // there is no way to attach a search function to a specific table, // and we need to make sure a global search function is not applied to // all tables (i.e. a range filter in a previous table should not be // applied to the current table); we use the settings object to // determine if we want to perform searching on the current table, // since settings.sTableId will be different to different tables if (table.settings()[0] !== settings) return true; // no filter on this column or no need to filter this column if (typeof filter === 'undefined' || !$td.data('filter')) return true; var r = filter.val(), v, r0, r1; var i_data = function(i) { if (!colReorderEnabled()) return i; var order = table.colReorder.order(), k; for (k = 0; k < order.length; ++k) if (order[k] === i) return k; return i; // in theory it will never be here... } v = data[i_data(i)]; if (type === 'number' || type === 'integer') { v = parseFloat(v); // how to handle NaN? currently exclude these rows if (isNaN(v)) return(false); r0 = parseFloat(scaleBack(r[0], scale)) r1 = parseFloat(scaleBack(r[1], scale)); if (v >= r0 && v <= r1) return true; } else if (type === 'date' || type === 'time') { v = new Date(v); r0 = new Date(r[0] / scale); r1 = new Date(r[1] / scale); if (v >= r0 && v <= r1) return true; } else if (type === 'factor') { if (r.length === 0 || inArray(v, r)) return true; } else if (type === 'logical') { if (r.length === 0) return true; if (inArray(v === '' ? 'na' : v, r)) return true; } return false; }; $.fn.dataTable.ext.search.push(customFilter); // search for the preset search strings if it is non-empty if (searchCol) { if (inArray(type, ['factor', 'logical'])) { filter[0].selectize.setValue(JSON.parse(searchCol)); } else if (type === 'character') { $input.trigger('input'); } else if (inArray(type, ['number', 'integer', 'date', 'time'])) { $input.trigger('change'); } } }); } // highlight search keywords var highlight = function() { var body = $(table.table().body()); // removing the old highlighting first body.unhighlight(); // don't highlight the "not found" row, so we get the rows using the api if (table.rows({ filter: 'applied' }).data().length === 0) return; // highlight global search keywords body.highlight($.trim(table.search()).split(/\s+/)); // then highlight keywords from individual column filters if (filterRow) filterRow.each(function(i, td) { var $td = $(td), type = $td.data('type'); if (type !== 'character') return; var $input = $td.children('div').first().children('input'); var column = table.column(i).nodes().to$(), val = $.trim($input.val()); if (type !== 'character' || val === '') return; column.highlight(val.split(/\s+/)); }); }; if (options.searchHighlight) { table .on('draw.dt.dth column-visibility.dt.dth column-reorder.dt.dth', highlight) .on('destroy', function() { // remove event handler table.off('draw.dt.dth column-visibility.dt.dth column-reorder.dt.dth'); }); // Set the option for escaping regex characters in our search string. This will be used // for all future matching. jQuery.fn.highlight.options.escapeRegex = (!options.search || !options.search.regex); // initial highlight for state saved conditions and initial states highlight(); } // run the callback function on the table instance if (typeof data.callback === 'function') data.callback(table); // double click to edit the cell, row, column, or all cells if (data.editable) table.on('dblclick.dt', 'tbody td', function(e) { // only bring up the editor when the cell itself is dbclicked, and ignore // other dbclick events bubbled up (e.g. from the ) if (e.target !== this) return; var target = [], immediate = false; switch (data.editable.target) { case 'cell': target = [this]; immediate = true; // edit will take effect immediately break; case 'row': target = table.cells(table.cell(this).index().row, '*').nodes(); break; case 'column': target = table.cells('*', table.cell(this).index().column).nodes(); break; case 'all': target = table.cells().nodes(); break; default: throw 'The editable parameter must be "cell", "row", "column", or "all"'; } var disableCols = data.editable.disable ? data.editable.disable.columns : null; for (var i = 0; i < target.length; i++) { (function(cell, current) { var $cell = $(cell), html = $cell.html(); var _cell = table.cell(cell), value = _cell.data(); var $input = $(''), changed = false; if (!immediate) { $cell.data('input', $input).data('html', html); $input.attr('title', 'Hit Ctrl+Enter to finish editing, or Esc to cancel'); } $input.val(value); if (inArray(_cell.index().column, disableCols)) { $input.attr('readonly', '').css('filter', 'invert(25%)'); } $cell.empty().append($input); if (cell === current) $input.focus(); $input.css('width', '100%'); if (immediate) $input.on('change', function() { changed = true; var valueNew = $input.val(); if (valueNew != value) { _cell.data(valueNew); if (HTMLWidgets.shinyMode) { changeInput('cell_edit', [cellInfo(cell)], 'DT.cellInfo', null, {priority: "event"}); } // for server-side processing, users have to call replaceData() to update the table if (!server) table.draw(false); } else { $cell.html(html); } $input.remove(); }).on('blur', function() { if (!changed) $input.trigger('change'); }).on('keyup', function(e) { // hit Escape to cancel editing if (e.keyCode === 27) $input.trigger('blur'); }); // bulk edit (row, column, or all) if (!immediate) $input.on('keyup', function(e) { var removeInput = function($cell, restore) { $cell.data('input').remove(); if (restore) $cell.html($cell.data('html')); } if (e.keyCode === 27) { for (var i = 0; i < target.length; i++) { removeInput($(target[i]), true); } } else if (e.keyCode === 13 && e.ctrlKey) { // Ctrl + Enter var cell, $cell, _cell, cellData = []; for (var i = 0; i < target.length; i++) { cell = target[i]; $cell = $(cell); _cell = table.cell(cell); _cell.data($cell.data('input').val()); HTMLWidgets.shinyMode && cellData.push(cellInfo(cell)); removeInput($cell, false); } if (HTMLWidgets.shinyMode) { changeInput('cell_edit', cellData, 'DT.cellInfo', null, {priority: "event"}); } if (!server) table.draw(false); } }); })(target[i], this); } }); // interaction with shiny if (!HTMLWidgets.shinyMode && !crosstalkOptions.group) return; var methods = {}; var shinyData = {}; methods.updateCaption = function(caption) { if (!caption) return; $table.children('caption').replaceWith(caption); } // register clear functions to remove input values when the table is removed instance.clearInputs = {}; var changeInput = function(id, value, type, noCrosstalk, opts) { var event = id; id = el.id + '_' + id; if (type) id = id + ':' + type; // do not update if the new value is the same as old value if (event !== 'cell_edit' && shinyData.hasOwnProperty(id) && shinyData[id] === JSON.stringify(value)) return; shinyData[id] = JSON.stringify(value); if (HTMLWidgets.shinyMode && Shiny.setInputValue) { Shiny.setInputValue(id, value, opts); if (!instance.clearInputs[id]) instance.clearInputs[id] = function() { Shiny.setInputValue(id, null); } } // HACK if (event === "rows_selected" && !noCrosstalk) { if (crosstalkOptions.group) { var keys = crosstalkOptions.key; var selectedKeys = null; if (value) { selectedKeys = []; for (var i = 0; i < value.length; i++) { // The value array's contents use 1-based row numbers, so we must // convert to 0-based before indexing into the keys array. selectedKeys.push(keys[value[i] - 1]); } } instance.ctselectHandle.set(selectedKeys); } } }; var addOne = function(x) { return x.map(function(i) { return 1 + i; }); }; var unique = function(x) { var ux = []; $.each(x, function(i, el){ if ($.inArray(el, ux) === -1) ux.push(el); }); return ux; } // change the row index of a cell var tweakCellIndex = function(cell) { var info = cell.index(); // some cell may not be valid. e.g, #759 // when using the RowGroup extension, datatables will // generate the row label and the cells are not part of // the data thus contain no row/col info if (info === undefined) return {row: null, col: null}; if (server) { info.row = DT_rows_current[info.row]; } else { info.row += 1; } return {row: info.row, col: info.column}; } // a flag to indicates if select extension is initialized or not var flagSelectExt = table.settings()[0]._select !== undefined; // the Select extension should only be used in the client mode and // when the selection.mode is set to none if (data.selection.mode === 'none' && !server && flagSelectExt) { var updateRowsSelected = function() { var rows = table.rows({selected: true}); var selected = []; $.each(rows.indexes().toArray(), function(i, v) { selected.push(v + 1); }); changeInput('rows_selected', selected); } var updateColsSelected = function() { var columns = table.columns({selected: true}); changeInput('columns_selected', columns.indexes().toArray()); } var updateCellsSelected = function() { var cells = table.cells({selected: true}); var selected = []; cells.every(function() { var row = this.index().row; var col = this.index().column; selected = selected.concat([[row + 1, col]]); }); changeInput('cells_selected', transposeArray2D(selected), 'shiny.matrix'); } table.on('select deselect', function(e, dt, type, indexes) { updateRowsSelected(); updateColsSelected(); updateCellsSelected(); }) } var selMode = data.selection.mode, selTarget = data.selection.target; if (inArray(selMode, ['single', 'multiple'])) { var selClass = inArray(data.style, ['bootstrap', 'bootstrap4']) ? 'active' : 'selected'; var selected = data.selection.selected, selected1, selected2; // selected1: row indices; selected2: column indices if (selected === null) { selected1 = selected2 = []; } else if (selTarget === 'row') { selected1 = $.makeArray(selected); } else if (selTarget === 'column') { selected2 = $.makeArray(selected); } else if (selTarget === 'row+column') { selected1 = $.makeArray(selected.rows); selected2 = $.makeArray(selected.cols); } // After users reorder the rows or filter the table, we cannot use the table index // directly. Instead, we need this function to find out the rows between the two clicks. // If user filter the table again between the start click and the end click, the behavior // would be undefined, but it should not be a problem. var shiftSelRowsIndex = function(start, end) { var indexes = server ? DT_rows_all : table.rows({ search: 'applied' }).indexes().toArray(); start = indexes.indexOf(start); end = indexes.indexOf(end); // if start is larger than end, we need to swap if (start > end) { var tmp = end; end = start; start = tmp; } return indexes.slice(start, end + 1); } var serverRowIndex = function(clientRowIndex) { return server ? DT_rows_current[clientRowIndex] : clientRowIndex + 1; } // row, column, or cell selection var lastClickedRow; if (inArray(selTarget, ['row', 'row+column'])) { var selectedRows = function() { var rows = table.rows('.' + selClass); var idx = rows.indexes().toArray(); if (!server) return addOne(idx); idx = idx.map(function(i) { return DT_rows_current[i]; }); selected1 = selMode === 'multiple' ? unique(selected1.concat(idx)) : idx; return selected1; } table.on('mousedown.dt', 'tbody tr', function(e) { var $this = $(this), thisRow = table.row(this); if (selMode === 'multiple') { if (e.shiftKey && lastClickedRow !== undefined) { // select or de-select depends on the last clicked row's status var flagSel = !$this.hasClass(selClass); var crtClickedRow = serverRowIndex(thisRow.index()); if (server) { var rowsIndex = shiftSelRowsIndex(lastClickedRow, crtClickedRow); // update current page's selClass rowsIndex.map(function(i) { var rowIndex = DT_rows_current.indexOf(i); if (rowIndex >= 0) { var row = table.row(rowIndex).nodes().to$(); var flagRowSel = !row.hasClass(selClass); if (flagSel === flagRowSel) row.toggleClass(selClass); } }); // update selected1 if (flagSel) { selected1 = unique(selected1.concat(rowsIndex)); } else { selected1 = selected1.filter(function(index) { return !inArray(index, rowsIndex); }); } } else { // js starts from 0 shiftSelRowsIndex(lastClickedRow - 1, crtClickedRow - 1).map(function(value) { var row = table.row(value).nodes().to$(); var flagRowSel = !row.hasClass(selClass); if (flagSel === flagRowSel) row.toggleClass(selClass); }); } e.preventDefault(); } else { $this.toggleClass(selClass); } } else { if ($this.hasClass(selClass)) { $this.removeClass(selClass); } else { table.$('tr.' + selClass).removeClass(selClass); $this.addClass(selClass); } } if (server && !$this.hasClass(selClass)) { var id = DT_rows_current[thisRow.index()]; // remove id from selected1 since its class .selected has been removed if (inArray(id, selected1)) selected1.splice($.inArray(id, selected1), 1); } changeInput('rows_selected', selectedRows()); changeInput('row_last_clicked', serverRowIndex(thisRow.index())); lastClickedRow = serverRowIndex(thisRow.index()); }); changeInput('rows_selected', selected1); var selectRows = function() { table.$('tr.' + selClass).removeClass(selClass); if (selected1.length === 0) return; if (server) { table.rows({page: 'current'}).every(function() { if (inArray(DT_rows_current[this.index()], selected1)) { $(this.node()).addClass(selClass); } }); } else { var selected0 = selected1.map(function(i) { return i - 1; }); $(table.rows(selected0).nodes()).addClass(selClass); } } selectRows(); // in case users have specified pre-selected rows // restore selected rows after the table is redrawn (e.g. sort/search/page); // client-side tables will preserve the selections automatically; for // server-side tables, we have to *real* row indices are in `selected1` if (server) table.on('draw.dt', selectRows); methods.selectRows = function(selected) { selected1 = $.makeArray(selected); selectRows(); changeInput('rows_selected', selected1); } } if (inArray(selTarget, ['column', 'row+column'])) { if (selTarget === 'row+column') { $(table.columns().footer()).css('cursor', 'pointer'); } var callback = function() { var colIdx = selTarget === 'column' ? table.cell(this).index().column : $.inArray(this, table.columns().footer()), thisCol = $(table.column(colIdx).nodes()); if (colIdx === -1) return; if (thisCol.hasClass(selClass)) { thisCol.removeClass(selClass); selected2.splice($.inArray(colIdx, selected2), 1); } else { if (selMode === 'single') $(table.cells().nodes()).removeClass(selClass); thisCol.addClass(selClass); selected2 = selMode === 'single' ? [colIdx] : unique(selected2.concat([colIdx])); } changeInput('columns_selected', selected2); } if (selTarget === 'column') { $(table.table().body()).on('click.dt', 'td', callback); } else { $(table.table().footer()).on('click.dt', 'tr th', callback); } changeInput('columns_selected', selected2); var selectCols = function() { table.columns().nodes().flatten().to$().removeClass(selClass); if (selected2.length > 0) table.columns(selected2).nodes().flatten().to$().addClass(selClass); } selectCols(); // in case users have specified pre-selected columns if (server) table.on('draw.dt', selectCols); methods.selectColumns = function(selected) { selected2 = $.makeArray(selected); selectCols(); changeInput('columns_selected', selected2); } } if (selTarget === 'cell') { var selected3; if (selected === null) { selected3 = []; } else { selected3 = selected; } var findIndex = function(ij) { for (var i = 0; i < selected3.length; i++) { if (ij[0] === selected3[i][0] && ij[1] === selected3[i][1]) return i; } return -1; } table.on('click.dt', 'tbody td', function() { var $this = $(this), info = tweakCellIndex(table.cell(this)); if ($this.hasClass(selClass)) { $this.removeClass(selClass); selected3.splice(findIndex([info.row, info.col]), 1); } else { if (selMode === 'single') $(table.cells().nodes()).removeClass(selClass); $this.addClass(selClass); selected3 = selMode === 'single' ? [[info.row, info.col]] : unique(selected3.concat([[info.row, info.col]])); } changeInput('cells_selected', transposeArray2D(selected3), 'shiny.matrix'); }); changeInput('cells_selected', transposeArray2D(selected3), 'shiny.matrix'); var selectCells = function() { table.$('td.' + selClass).removeClass(selClass); if (selected3.length === 0) return; if (server) { table.cells({page: 'current'}).every(function() { var info = tweakCellIndex(this); if (findIndex([info.row, info.col], selected3) > -1) $(this.node()).addClass(selClass); }); } else { selected3.map(function(ij) { $(table.cell(ij[0] - 1, ij[1]).node()).addClass(selClass); }); } }; selectCells(); // in case users have specified pre-selected columns if (server) table.on('draw.dt', selectCells); methods.selectCells = function(selected) { selected3 = selected ? selected : []; selectCells(); changeInput('cells_selected', transposeArray2D(selected3), 'shiny.matrix'); } } } // expose some table info to Shiny var updateTableInfo = function(e, settings) { // TODO: is anyone interested in the page info? // changeInput('page_info', table.page.info()); var updateRowInfo = function(id, modifier) { var idx; if (server) { idx = modifier.page === 'current' ? DT_rows_current : DT_rows_all; } else { var rows = table.rows($.extend({ search: 'applied', page: 'all' }, modifier)); idx = addOne(rows.indexes().toArray()); } changeInput('rows' + '_' + id, idx); }; updateRowInfo('current', {page: 'current'}); updateRowInfo('all', {}); } table.on('draw.dt', updateTableInfo); updateTableInfo(); // state info table.on('draw.dt column-visibility.dt', function() { changeInput('state', table.state()); }); changeInput('state', table.state()); // search info var updateSearchInfo = function() { changeInput('search', table.search()); if (filterRow) changeInput('search_columns', filterRow.toArray().map(function(td) { return $(td).find('input').first().val(); })); } table.on('draw.dt', updateSearchInfo); updateSearchInfo(); var cellInfo = function(thiz) { var info = tweakCellIndex(table.cell(thiz)); info.value = table.cell(thiz).data(); return info; } // the current cell clicked on table.on('click.dt', 'tbody td', function() { changeInput('cell_clicked', cellInfo(this)); }) changeInput('cell_clicked', {}); // do not trigger table selection when clicking on links unless they have classes table.on('click.dt', 'tbody td a', function(e) { if (this.className === '') e.stopPropagation(); }); methods.addRow = function(data, rowname) { var data0 = table.row(0).data(), n = data0.length, d = n - data.length; if (d === 1) { data = rowname.concat(data) } else if (d !== 0) { console.log(data); console.log(data0); throw 'New data must be of the same length as current data (' + n + ')'; }; table.row.add(data).draw(); } methods.updateSearch = function(keywords) { if (keywords.global !== null) $(table.table().container()).find('input[type=search]').first() .val(keywords.global).trigger('input'); var columns = keywords.columns; if (!filterRow || columns === null) return; filterRow.toArray().map(function(td, i) { var v = typeof columns === 'string' ? columns : columns[i]; if (typeof v === 'undefined') { console.log('The search keyword for column ' + i + ' is undefined') return; } $(td).find('input').first().val(v); searchColumn(i, v); }); table.draw(); } methods.hideCols = function(hide, reset) { if (reset) table.columns().visible(true, false); table.columns(hide).visible(false); } methods.showCols = function(show, reset) { if (reset) table.columns().visible(false, false); table.columns(show).visible(true); } methods.colReorder = function(order, origOrder) { table.colReorder.order(order, origOrder); } methods.selectPage = function(page) { if (table.page.info().pages < page || page < 1) { throw 'Selected page is out of range'; }; table.page(page - 1).draw(false); } methods.reloadData = function(resetPaging, clearSelection) { // empty selections first if necessary if (methods.selectRows && inArray('row', clearSelection)) methods.selectRows([]); if (methods.selectColumns && inArray('column', clearSelection)) methods.selectColumns([]); if (methods.selectCells && inArray('cell', clearSelection)) methods.selectCells([]); table.ajax.reload(null, resetPaging); } table.shinyMethods = methods; }, resize: function(el, width, height, instance) { if (instance.data) this.renderValue(el, instance.data, instance); // dynamically adjust height if fillContainer = TRUE if (instance.fillContainer) this.fillAvailableHeight(el, height); this.adjustWidth(el); }, // dynamically set the scroll body to fill available height // (used with fillContainer = TRUE) fillAvailableHeight: function(el, availableHeight) { // see how much of the table is occupied by header/footer elements // and use that to compute a target scroll body height var dtWrapper = $(el).find('div.dataTables_wrapper'); var dtScrollBody = $(el).find($('div.dataTables_scrollBody')); var framingHeight = dtWrapper.innerHeight() - dtScrollBody.innerHeight(); var scrollBodyHeight = availableHeight - framingHeight; // set the height dtScrollBody.height(scrollBodyHeight + 'px'); }, // adjust the width of columns; remove the hard-coded widths on table and the // scroll header when scrollX/Y are enabled adjustWidth: function(el) { var $el = $(el), table = $el.data('datatable'); if (table) table.columns.adjust(); $el.find('.dataTables_scrollHeadInner').css('width', '') .children('table').css('margin-left', ''); } }); if (!HTMLWidgets.shinyMode) return; Shiny.addCustomMessageHandler('datatable-calls', function(data) { var id = data.id; var el = document.getElementById(id); var table = el ? $(el).data('datatable') : null; if (!table) { console.log("Couldn't find table with id " + id); return; } var methods = table.shinyMethods, call = data.call; if (methods[call.method]) { methods[call.method].apply(table, call.args); } else { console.log("Unknown method " + call.method); } }); })(); ================================================ FILE: vignettes/tutorials/libs/datatables-css/datatables-crosstalk.css ================================================ .dt-crosstalk-fade { opacity: 0.2; } html body div.DTS div.dataTables_scrollBody { background: none; } /* Fix https://github.com/rstudio/DT/issues/563 If the `table.display` is set to "block" (e.g., pkgdown), the browser will display datatable objects strangely. The search panel and the page buttons will still be in full-width but the table body will be "compact" and shorter. In therory, having this attributes will affect `dom="t"` with `display: block` users. But in reality, there should be no one. We may remove the below lines in the future if the upstream agree to have this there. See https://github.com/DataTables/DataTablesSrc/issues/160 */ table.dataTable { display: table; } ================================================ FILE: vignettes/tutorials/libs/dt-core/css/jquery.dataTables.extra.css ================================================ /* Selected rows/cells */ table.dataTable tr.selected td, table.dataTable td.selected { background-color: #b0bed9 !important; } /* In case of scrollX/Y or FixedHeader */ .dataTables_scrollBody .dataTables_sizing { visibility: hidden; } /* The datatables' theme CSS file doesn't define the color but with white background. It leads to an issue that when the HTML's body color is set to 'white', the user can't see the text since the background is white. One case happens in the RStudio's IDE when inline viewing the DT table inside an Rmd file, if the IDE theme is set to "Cobalt". See https://github.com/rstudio/DT/issues/447 for more info This fixes should have little side-effects because all the other elements of the default theme use the #333 font color. TODO: The upstream may use relative colors for both the table background and the color. It means the table can display well without this patch then. At that time, we need to remove the below CSS attributes. */ div.datatables { color: #333; } ================================================ FILE: vignettes/tutorials/libs/header-attrs/header-attrs.js ================================================ // Pandoc 2.9 adds attributes on both header and div. We remove the former (to // be compatible with the behavior of Pandoc < 2.8). document.addEventListener('DOMContentLoaded', function(e) { var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); var i, h, a; for (i = 0; i < hs.length; i++) { h = hs[i]; if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 a = h.attributes; while (a.length > 0) h.removeAttribute(a[0].name); } }); ================================================ FILE: vignettes/tutorials/libs/htmlwidgets/htmlwidgets.js ================================================ (function() { // If window.HTMLWidgets is already defined, then use it; otherwise create a // new object. This allows preceding code to set options that affect the // initialization process (though none currently exist). window.HTMLWidgets = window.HTMLWidgets || {}; // See if we're running in a viewer pane. If not, we're in a web browser. var viewerMode = window.HTMLWidgets.viewerMode = /\bviewer_pane=1\b/.test(window.location); // See if we're running in Shiny mode. If not, it's a static document. // Note that static widgets can appear in both Shiny and static modes, but // obviously, Shiny widgets can only appear in Shiny apps/documents. var shinyMode = window.HTMLWidgets.shinyMode = typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings; // We can't count on jQuery being available, so we implement our own // version if necessary. function querySelectorAll(scope, selector) { if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) { return scope.find(selector); } if (scope.querySelectorAll) { return scope.querySelectorAll(selector); } } function asArray(value) { if (value === null) return []; if ($.isArray(value)) return value; return [value]; } // Implement jQuery's extend function extend(target /*, ... */) { if (arguments.length == 1) { return target; } for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var prop in source) { if (source.hasOwnProperty(prop)) { target[prop] = source[prop]; } } } return target; } // IE8 doesn't support Array.forEach. function forEach(values, callback, thisArg) { if (values.forEach) { values.forEach(callback, thisArg); } else { for (var i = 0; i < values.length; i++) { callback.call(thisArg, values[i], i, values); } } } // Replaces the specified method with the return value of funcSource. // // Note that funcSource should not BE the new method, it should be a function // that RETURNS the new method. funcSource receives a single argument that is // the overridden method, it can be called from the new method. The overridden // method can be called like a regular function, it has the target permanently // bound to it so "this" will work correctly. function overrideMethod(target, methodName, funcSource) { var superFunc = target[methodName] || function() {}; var superFuncBound = function() { return superFunc.apply(target, arguments); }; target[methodName] = funcSource(superFuncBound); } // Add a method to delegator that, when invoked, calls // delegatee.methodName. If there is no such method on // the delegatee, but there was one on delegator before // delegateMethod was called, then the original version // is invoked instead. // For example: // // var a = { // method1: function() { console.log('a1'); } // method2: function() { console.log('a2'); } // }; // var b = { // method1: function() { console.log('b1'); } // }; // delegateMethod(a, b, "method1"); // delegateMethod(a, b, "method2"); // a.method1(); // a.method2(); // // The output would be "b1", "a2". function delegateMethod(delegator, delegatee, methodName) { var inherited = delegator[methodName]; delegator[methodName] = function() { var target = delegatee; var method = delegatee[methodName]; // The method doesn't exist on the delegatee. Instead, // call the method on the delegator, if it exists. if (!method) { target = delegator; method = inherited; } if (method) { return method.apply(target, arguments); } }; } // Implement a vague facsimilie of jQuery's data method function elementData(el, name, value) { if (arguments.length == 2) { return el["htmlwidget_data_" + name]; } else if (arguments.length == 3) { el["htmlwidget_data_" + name] = value; return el; } else { throw new Error("Wrong number of arguments for elementData: " + arguments.length); } } // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); } function hasClass(el, className) { var re = new RegExp("\\b" + escapeRegExp(className) + "\\b"); return re.test(el.className); } // elements - array (or array-like object) of HTML elements // className - class name to test for // include - if true, only return elements with given className; // if false, only return elements *without* given className function filterByClass(elements, className, include) { var results = []; for (var i = 0; i < elements.length; i++) { if (hasClass(elements[i], className) == include) results.push(elements[i]); } return results; } function on(obj, eventName, func) { if (obj.addEventListener) { obj.addEventListener(eventName, func, false); } else if (obj.attachEvent) { obj.attachEvent(eventName, func); } } function off(obj, eventName, func) { if (obj.removeEventListener) obj.removeEventListener(eventName, func, false); else if (obj.detachEvent) { obj.detachEvent(eventName, func); } } // Translate array of values to top/right/bottom/left, as usual with // the "padding" CSS property // https://developer.mozilla.org/en-US/docs/Web/CSS/padding function unpackPadding(value) { if (typeof(value) === "number") value = [value]; if (value.length === 1) { return {top: value[0], right: value[0], bottom: value[0], left: value[0]}; } if (value.length === 2) { return {top: value[0], right: value[1], bottom: value[0], left: value[1]}; } if (value.length === 3) { return {top: value[0], right: value[1], bottom: value[2], left: value[1]}; } if (value.length === 4) { return {top: value[0], right: value[1], bottom: value[2], left: value[3]}; } } // Convert an unpacked padding object to a CSS value function paddingToCss(paddingObj) { return paddingObj.top + "px " + paddingObj.right + "px " + paddingObj.bottom + "px " + paddingObj.left + "px"; } // Makes a number suitable for CSS function px(x) { if (typeof(x) === "number") return x + "px"; else return x; } // Retrieves runtime widget sizing information for an element. // The return value is either null, or an object with fill, padding, // defaultWidth, defaultHeight fields. function sizingPolicy(el) { var sizingEl = document.querySelector("script[data-for='" + el.id + "'][type='application/htmlwidget-sizing']"); if (!sizingEl) return null; var sp = JSON.parse(sizingEl.textContent || sizingEl.text || "{}"); if (viewerMode) { return sp.viewer; } else { return sp.browser; } } // @param tasks Array of strings (or falsy value, in which case no-op). // Each element must be a valid JavaScript expression that yields a // function. Or, can be an array of objects with "code" and "data" // properties; in this case, the "code" property should be a string // of JS that's an expr that yields a function, and "data" should be // an object that will be added as an additional argument when that // function is called. // @param target The object that will be "this" for each function // execution. // @param args Array of arguments to be passed to the functions. (The // same arguments will be passed to all functions.) function evalAndRun(tasks, target, args) { if (tasks) { forEach(tasks, function(task) { var theseArgs = args; if (typeof(task) === "object") { theseArgs = theseArgs.concat([task.data]); task = task.code; } var taskFunc = tryEval(task); if (typeof(taskFunc) !== "function") { throw new Error("Task must be a function! Source:\n" + task); } taskFunc.apply(target, theseArgs); }); } } // Attempt eval() both with and without enclosing in parentheses. // Note that enclosing coerces a function declaration into // an expression that eval() can parse // (otherwise, a SyntaxError is thrown) function tryEval(code) { var result = null; try { result = eval(code); } catch(error) { if (!error instanceof SyntaxError) { throw error; } try { result = eval("(" + code + ")"); } catch(e) { if (e instanceof SyntaxError) { throw error; } else { throw e; } } } return result; } function initSizing(el) { var sizing = sizingPolicy(el); if (!sizing) return; var cel = document.getElementById("htmlwidget_container"); if (!cel) return; if (typeof(sizing.padding) !== "undefined") { document.body.style.margin = "0"; document.body.style.padding = paddingToCss(unpackPadding(sizing.padding)); } if (sizing.fill) { document.body.style.overflow = "hidden"; document.body.style.width = "100%"; document.body.style.height = "100%"; document.documentElement.style.width = "100%"; document.documentElement.style.height = "100%"; if (cel) { cel.style.position = "absolute"; var pad = unpackPadding(sizing.padding); cel.style.top = pad.top + "px"; cel.style.right = pad.right + "px"; cel.style.bottom = pad.bottom + "px"; cel.style.left = pad.left + "px"; el.style.width = "100%"; el.style.height = "100%"; } return { getWidth: function() { return cel.offsetWidth; }, getHeight: function() { return cel.offsetHeight; } }; } else { el.style.width = px(sizing.width); el.style.height = px(sizing.height); return { getWidth: function() { return el.offsetWidth; }, getHeight: function() { return el.offsetHeight; } }; } } // Default implementations for methods var defaults = { find: function(scope) { return querySelectorAll(scope, "." + this.name); }, renderError: function(el, err) { var $el = $(el); this.clearError(el); // Add all these error classes, as Shiny does var errClass = "shiny-output-error"; if (err.type !== null) { // use the classes of the error condition as CSS class names errClass = errClass + " " + $.map(asArray(err.type), function(type) { return errClass + "-" + type; }).join(" "); } errClass = errClass + " htmlwidgets-error"; // Is el inline or block? If inline or inline-block, just display:none it // and add an inline error. var display = $el.css("display"); $el.data("restore-display-mode", display); if (display === "inline" || display === "inline-block") { $el.hide(); if (err.message !== "") { var errorSpan = $("").addClass(errClass); errorSpan.text(err.message); $el.after(errorSpan); } } else if (display === "block") { // If block, add an error just after the el, set visibility:none on the // el, and position the error to be on top of the el. // Mark it with a unique ID and CSS class so we can remove it later. $el.css("visibility", "hidden"); if (err.message !== "") { var errorDiv = $("
    ").addClass(errClass).css("position", "absolute") .css("top", el.offsetTop) .css("left", el.offsetLeft) // setting width can push out the page size, forcing otherwise // unnecessary scrollbars to appear and making it impossible for // the element to shrink; so use max-width instead .css("maxWidth", el.offsetWidth) .css("height", el.offsetHeight); errorDiv.text(err.message); $el.after(errorDiv); // Really dumb way to keep the size/position of the error in sync with // the parent element as the window is resized or whatever. var intId = setInterval(function() { if (!errorDiv[0].parentElement) { clearInterval(intId); return; } errorDiv .css("top", el.offsetTop) .css("left", el.offsetLeft) .css("maxWidth", el.offsetWidth) .css("height", el.offsetHeight); }, 500); } } }, clearError: function(el) { var $el = $(el); var display = $el.data("restore-display-mode"); $el.data("restore-display-mode", null); if (display === "inline" || display === "inline-block") { if (display) $el.css("display", display); $(el.nextSibling).filter(".htmlwidgets-error").remove(); } else if (display === "block"){ $el.css("visibility", "inherit"); $(el.nextSibling).filter(".htmlwidgets-error").remove(); } }, sizing: {} }; // Called by widget bindings to register a new type of widget. The definition // object can contain the following properties: // - name (required) - A string indicating the binding name, which will be // used by default as the CSS classname to look for. // - initialize (optional) - A function(el) that will be called once per // widget element; if a value is returned, it will be passed as the third // value to renderValue. // - renderValue (required) - A function(el, data, initValue) that will be // called with data. Static contexts will cause this to be called once per // element; Shiny apps will cause this to be called multiple times per // element, as the data changes. window.HTMLWidgets.widget = function(definition) { if (!definition.name) { throw new Error("Widget must have a name"); } if (!definition.type) { throw new Error("Widget must have a type"); } // Currently we only support output widgets if (definition.type !== "output") { throw new Error("Unrecognized widget type '" + definition.type + "'"); } // TODO: Verify that .name is a valid CSS classname // Support new-style instance-bound definitions. Old-style class-bound // definitions have one widget "object" per widget per type/class of // widget; the renderValue and resize methods on such widget objects // take el and instance arguments, because the widget object can't // store them. New-style instance-bound definitions have one widget // object per widget instance; the definition that's passed in doesn't // provide renderValue or resize methods at all, just the single method // factory(el, width, height) // which returns an object that has renderValue(x) and resize(w, h). // This enables a far more natural programming style for the widget // author, who can store per-instance state using either OO-style // instance fields or functional-style closure variables (I guess this // is in contrast to what can only be called C-style pseudo-OO which is // what we required before). if (definition.factory) { definition = createLegacyDefinitionAdapter(definition); } if (!definition.renderValue) { throw new Error("Widget must have a renderValue function"); } // For static rendering (non-Shiny), use a simple widget registration // scheme. We also use this scheme for Shiny apps/documents that also // contain static widgets. window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || []; // Merge defaults into the definition; don't mutate the original definition. var staticBinding = extend({}, defaults, definition); overrideMethod(staticBinding, "find", function(superfunc) { return function(scope) { var results = superfunc(scope); // Filter out Shiny outputs, we only want the static kind return filterByClass(results, "html-widget-output", false); }; }); window.HTMLWidgets.widgets.push(staticBinding); if (shinyMode) { // Shiny is running. Register the definition with an output binding. // The definition itself will not be the output binding, instead // we will make an output binding object that delegates to the // definition. This is because we foolishly used the same method // name (renderValue) for htmlwidgets definition and Shiny bindings // but they actually have quite different semantics (the Shiny // bindings receive data that includes lots of metadata that it // strips off before calling htmlwidgets renderValue). We can't // just ignore the difference because in some widgets it's helpful // to call this.renderValue() from inside of resize(), and if // we're not delegating, then that call will go to the Shiny // version instead of the htmlwidgets version. // Merge defaults with definition, without mutating either. var bindingDef = extend({}, defaults, definition); // This object will be our actual Shiny binding. var shinyBinding = new Shiny.OutputBinding(); // With a few exceptions, we'll want to simply use the bindingDef's // version of methods if they are available, otherwise fall back to // Shiny's defaults. NOTE: If Shiny's output bindings gain additional // methods in the future, and we want them to be overrideable by // HTMLWidget binding definitions, then we'll need to add them to this // list. delegateMethod(shinyBinding, bindingDef, "getId"); delegateMethod(shinyBinding, bindingDef, "onValueChange"); delegateMethod(shinyBinding, bindingDef, "onValueError"); delegateMethod(shinyBinding, bindingDef, "renderError"); delegateMethod(shinyBinding, bindingDef, "clearError"); delegateMethod(shinyBinding, bindingDef, "showProgress"); // The find, renderValue, and resize are handled differently, because we // want to actually decorate the behavior of the bindingDef methods. shinyBinding.find = function(scope) { var results = bindingDef.find(scope); // Only return elements that are Shiny outputs, not static ones var dynamicResults = results.filter(".html-widget-output"); // It's possible that whatever caused Shiny to think there might be // new dynamic outputs, also caused there to be new static outputs. // Since there might be lots of different htmlwidgets bindings, we // schedule execution for later--no need to staticRender multiple // times. if (results.length !== dynamicResults.length) scheduleStaticRender(); return dynamicResults; }; // Wrap renderValue to handle initialization, which unfortunately isn't // supported natively by Shiny at the time of this writing. shinyBinding.renderValue = function(el, data) { Shiny.renderDependencies(data.deps); // Resolve strings marked as javascript literals to objects if (!(data.evals instanceof Array)) data.evals = [data.evals]; for (var i = 0; data.evals && i < data.evals.length; i++) { window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]); } if (!bindingDef.renderOnNullValue) { if (data.x === null) { el.style.visibility = "hidden"; return; } else { el.style.visibility = "inherit"; } } if (!elementData(el, "initialized")) { initSizing(el); elementData(el, "initialized", true); if (bindingDef.initialize) { var result = bindingDef.initialize(el, el.offsetWidth, el.offsetHeight); elementData(el, "init_result", result); } } bindingDef.renderValue(el, data.x, elementData(el, "init_result")); evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]); }; // Only override resize if bindingDef implements it if (bindingDef.resize) { shinyBinding.resize = function(el, width, height) { // Shiny can call resize before initialize/renderValue have been // called, which doesn't make sense for widgets. if (elementData(el, "initialized")) { bindingDef.resize(el, width, height, elementData(el, "init_result")); } }; } Shiny.outputBindings.register(shinyBinding, bindingDef.name); } }; var scheduleStaticRenderTimerId = null; function scheduleStaticRender() { if (!scheduleStaticRenderTimerId) { scheduleStaticRenderTimerId = setTimeout(function() { scheduleStaticRenderTimerId = null; window.HTMLWidgets.staticRender(); }, 1); } } // Render static widgets after the document finishes loading // Statically render all elements that are of this widget's class window.HTMLWidgets.staticRender = function() { var bindings = window.HTMLWidgets.widgets || []; forEach(bindings, function(binding) { var matches = binding.find(document.documentElement); forEach(matches, function(el) { var sizeObj = initSizing(el, binding); if (hasClass(el, "html-widget-static-bound")) return; el.className = el.className + " html-widget-static-bound"; var initResult; if (binding.initialize) { initResult = binding.initialize(el, sizeObj ? sizeObj.getWidth() : el.offsetWidth, sizeObj ? sizeObj.getHeight() : el.offsetHeight ); elementData(el, "init_result", initResult); } if (binding.resize) { var lastSize = { w: sizeObj ? sizeObj.getWidth() : el.offsetWidth, h: sizeObj ? sizeObj.getHeight() : el.offsetHeight }; var resizeHandler = function(e) { var size = { w: sizeObj ? sizeObj.getWidth() : el.offsetWidth, h: sizeObj ? sizeObj.getHeight() : el.offsetHeight }; if (size.w === 0 && size.h === 0) return; if (size.w === lastSize.w && size.h === lastSize.h) return; lastSize = size; binding.resize(el, size.w, size.h, initResult); }; on(window, "resize", resizeHandler); // This is needed for cases where we're running in a Shiny // app, but the widget itself is not a Shiny output, but // rather a simple static widget. One example of this is // an rmarkdown document that has runtime:shiny and widget // that isn't in a render function. Shiny only knows to // call resize handlers for Shiny outputs, not for static // widgets, so we do it ourselves. if (window.jQuery) { window.jQuery(document).on( "shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets", resizeHandler ); window.jQuery(document).on( "hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets", resizeHandler ); } // This is needed for the specific case of ioslides, which // flips slides between display:none and display:block. // Ideally we would not have to have ioslide-specific code // here, but rather have ioslides raise a generic event, // but the rmarkdown package just went to CRAN so the // window to getting that fixed may be long. if (window.addEventListener) { // It's OK to limit this to window.addEventListener // browsers because ioslides itself only supports // such browsers. on(document, "slideenter", resizeHandler); on(document, "slideleave", resizeHandler); } } var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']"); if (scriptData) { var data = JSON.parse(scriptData.textContent || scriptData.text); // Resolve strings marked as javascript literals to objects if (!(data.evals instanceof Array)) data.evals = [data.evals]; for (var k = 0; data.evals && k < data.evals.length; k++) { window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]); } binding.renderValue(el, data.x, initResult); evalAndRun(data.jsHooks.render, initResult, [el, data.x]); } }); }); invokePostRenderHandlers(); } function has_jQuery3() { if (!window.jQuery) { return false; } var $version = window.jQuery.fn.jquery; var $major_version = parseInt($version.split(".")[0]); return $major_version >= 3; } /* / Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's / on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now / really means $(setTimeout(fn)). / https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous / / Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny / one tick later than it did before, which means staticRender() is / called renderValue() earlier than (advanced) widget authors might be expecting. / https://github.com/rstudio/shiny/issues/2630 / / For a concrete example, leaflet has some methods (e.g., updateBounds) / which reference Shiny methods registered in initShiny (e.g., setInputValue). / Since leaflet is privy to this life-cycle, it knows to use setTimeout() to / delay execution of those methods (until Shiny methods are ready) / https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268 / / Ideally widget authors wouldn't need to use this setTimeout() hack that / leaflet uses to call Shiny methods on a staticRender(). In the long run, / the logic initShiny should be broken up so that method registration happens / right away, but binding happens later. */ function maybeStaticRenderLater() { if (shinyMode && has_jQuery3()) { window.jQuery(window.HTMLWidgets.staticRender); } else { window.HTMLWidgets.staticRender(); } } if (document.addEventListener) { document.addEventListener("DOMContentLoaded", function() { document.removeEventListener("DOMContentLoaded", arguments.callee, false); maybeStaticRenderLater(); }, false); } else if (document.attachEvent) { document.attachEvent("onreadystatechange", function() { if (document.readyState === "complete") { document.detachEvent("onreadystatechange", arguments.callee); maybeStaticRenderLater(); } }); } window.HTMLWidgets.getAttachmentUrl = function(depname, key) { // If no key, default to the first item if (typeof(key) === "undefined") key = 1; var link = document.getElementById(depname + "-" + key + "-attachment"); if (!link) { throw new Error("Attachment " + depname + "/" + key + " not found in document"); } return link.getAttribute("href"); }; window.HTMLWidgets.dataframeToD3 = function(df) { var names = []; var length; for (var name in df) { if (df.hasOwnProperty(name)) names.push(name); if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") { throw new Error("All fields must be arrays"); } else if (typeof(length) !== "undefined" && length !== df[name].length) { throw new Error("All fields must be arrays of the same length"); } length = df[name].length; } var results = []; var item; for (var row = 0; row < length; row++) { item = {}; for (var col = 0; col < names.length; col++) { item[names[col]] = df[names[col]][row]; } results.push(item); } return results; }; window.HTMLWidgets.transposeArray2D = function(array) { if (array.length === 0) return array; var newArray = array[0].map(function(col, i) { return array.map(function(row) { return row[i] }) }); return newArray; }; // Split value at splitChar, but allow splitChar to be escaped // using escapeChar. Any other characters escaped by escapeChar // will be included as usual (including escapeChar itself). function splitWithEscape(value, splitChar, escapeChar) { var results = []; var escapeMode = false; var currentResult = ""; for (var pos = 0; pos < value.length; pos++) { if (!escapeMode) { if (value[pos] === splitChar) { results.push(currentResult); currentResult = ""; } else if (value[pos] === escapeChar) { escapeMode = true; } else { currentResult += value[pos]; } } else { currentResult += value[pos]; escapeMode = false; } } if (currentResult !== "") { results.push(currentResult); } return results; } // Function authored by Yihui/JJ Allaire window.HTMLWidgets.evaluateStringMember = function(o, member) { var parts = splitWithEscape(member, '.', '\\'); for (var i = 0, l = parts.length; i < l; i++) { var part = parts[i]; // part may be a character or 'numeric' member name if (o !== null && typeof o === "object" && part in o) { if (i == (l - 1)) { // if we are at the end of the line then evalulate if (typeof o[part] === "string") o[part] = tryEval(o[part]); } else { // otherwise continue to next embedded object o = o[part]; } } } }; // Retrieve the HTMLWidget instance (i.e. the return value of an // HTMLWidget binding's initialize() or factory() function) // associated with an element, or null if none. window.HTMLWidgets.getInstance = function(el) { return elementData(el, "init_result"); }; // Finds the first element in the scope that matches the selector, // and returns the HTMLWidget instance (i.e. the return value of // an HTMLWidget binding's initialize() or factory() function) // associated with that element, if any. If no element matches the // selector, or the first matching element has no HTMLWidget // instance associated with it, then null is returned. // // The scope argument is optional, and defaults to window.document. window.HTMLWidgets.find = function(scope, selector) { if (arguments.length == 1) { selector = scope; scope = document; } var el = scope.querySelector(selector); if (el === null) { return null; } else { return window.HTMLWidgets.getInstance(el); } }; // Finds all elements in the scope that match the selector, and // returns the HTMLWidget instances (i.e. the return values of // an HTMLWidget binding's initialize() or factory() function) // associated with the elements, in an array. If elements that // match the selector don't have an associated HTMLWidget // instance, the returned array will contain nulls. // // The scope argument is optional, and defaults to window.document. window.HTMLWidgets.findAll = function(scope, selector) { if (arguments.length == 1) { selector = scope; scope = document; } var nodes = scope.querySelectorAll(selector); var results = []; for (var i = 0; i < nodes.length; i++) { results.push(window.HTMLWidgets.getInstance(nodes[i])); } return results; }; var postRenderHandlers = []; function invokePostRenderHandlers() { while (postRenderHandlers.length) { var handler = postRenderHandlers.shift(); if (handler) { handler(); } } } // Register the given callback function to be invoked after the // next time static widgets are rendered. window.HTMLWidgets.addPostRenderHandler = function(callback) { postRenderHandlers.push(callback); }; // Takes a new-style instance-bound definition, and returns an // old-style class-bound definition. This saves us from having // to rewrite all the logic in this file to accomodate both // types of definitions. function createLegacyDefinitionAdapter(defn) { var result = { name: defn.name, type: defn.type, initialize: function(el, width, height) { return defn.factory(el, width, height); }, renderValue: function(el, x, instance) { return instance.renderValue(x); }, resize: function(el, width, height, instance) { return instance.resize(width, height); } }; if (defn.find) result.find = defn.find; if (defn.renderError) result.renderError = defn.renderError; if (defn.clearError) result.clearError = defn.clearError; return result; } })(); ================================================ FILE: vignettes/tutorials/libs/leaflet/leaflet.css ================================================ /* required styles */ .leaflet-pane, .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-tile-container, .leaflet-pane > svg, .leaflet-pane > canvas, .leaflet-zoom-box, .leaflet-image-layer, .leaflet-layer { position: absolute; left: 0; top: 0; } .leaflet-container { overflow: hidden; } .leaflet-tile, .leaflet-marker-icon, .leaflet-marker-shadow { -webkit-user-select: none; -moz-user-select: none; user-select: none; -webkit-user-drag: none; } /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ .leaflet-safari .leaflet-tile { image-rendering: -webkit-optimize-contrast; } /* hack that prevents hw layers "stretching" when loading new tiles */ .leaflet-safari .leaflet-tile-container { width: 1600px; height: 1600px; -webkit-transform-origin: 0 0; } .leaflet-marker-icon, .leaflet-marker-shadow { display: block; } /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ .leaflet-container .leaflet-overlay-pane svg, .leaflet-container .leaflet-marker-pane img, .leaflet-container .leaflet-shadow-pane img, .leaflet-container .leaflet-tile-pane img, .leaflet-container img.leaflet-image-layer { max-width: none !important; max-height: none !important; } .leaflet-container.leaflet-touch-zoom { -ms-touch-action: pan-x pan-y; touch-action: pan-x pan-y; } .leaflet-container.leaflet-touch-drag { -ms-touch-action: pinch-zoom; /* Fallback for FF which doesn't support pinch-zoom */ touch-action: none; touch-action: pinch-zoom; } .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { -ms-touch-action: none; touch-action: none; } .leaflet-container { -webkit-tap-highlight-color: transparent; } .leaflet-container a { -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); } .leaflet-tile { filter: inherit; visibility: hidden; } .leaflet-tile-loaded { visibility: inherit; } .leaflet-zoom-box { width: 0; height: 0; -moz-box-sizing: border-box; box-sizing: border-box; z-index: 800; } /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ .leaflet-overlay-pane svg { -moz-user-select: none; } .leaflet-pane { z-index: 400; } .leaflet-tile-pane { z-index: 200; } .leaflet-overlay-pane { z-index: 400; } .leaflet-shadow-pane { z-index: 500; } .leaflet-marker-pane { z-index: 600; } .leaflet-tooltip-pane { z-index: 650; } .leaflet-popup-pane { z-index: 700; } .leaflet-map-pane canvas { z-index: 100; } .leaflet-map-pane svg { z-index: 200; } .leaflet-vml-shape { width: 1px; height: 1px; } .lvml { behavior: url(#default#VML); display: inline-block; position: absolute; } /* control positioning */ .leaflet-control { position: relative; z-index: 800; pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ pointer-events: auto; } .leaflet-top, .leaflet-bottom { position: absolute; z-index: 1000; pointer-events: none; } .leaflet-top { top: 0; } .leaflet-right { right: 0; } .leaflet-bottom { bottom: 0; } .leaflet-left { left: 0; } .leaflet-control { float: left; clear: both; } .leaflet-right .leaflet-control { float: right; } .leaflet-top .leaflet-control { margin-top: 10px; } .leaflet-bottom .leaflet-control { margin-bottom: 10px; } .leaflet-left .leaflet-control { margin-left: 10px; } .leaflet-right .leaflet-control { margin-right: 10px; } /* zoom and fade animations */ .leaflet-fade-anim .leaflet-tile { will-change: opacity; } .leaflet-fade-anim .leaflet-popup { opacity: 0; -webkit-transition: opacity 0.2s linear; -moz-transition: opacity 0.2s linear; -o-transition: opacity 0.2s linear; transition: opacity 0.2s linear; } .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { opacity: 1; } .leaflet-zoom-animated { -webkit-transform-origin: 0 0; -ms-transform-origin: 0 0; transform-origin: 0 0; } .leaflet-zoom-anim .leaflet-zoom-animated { will-change: transform; } .leaflet-zoom-anim .leaflet-zoom-animated { -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); transition: transform 0.25s cubic-bezier(0,0,0.25,1); } .leaflet-zoom-anim .leaflet-tile, .leaflet-pan-anim .leaflet-tile { -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; } .leaflet-zoom-anim .leaflet-zoom-hide { visibility: hidden; } /* cursors */ .leaflet-interactive { cursor: pointer; } .leaflet-grab { cursor: -webkit-grab; cursor: -moz-grab; } .leaflet-crosshair, .leaflet-crosshair .leaflet-interactive { cursor: crosshair; } .leaflet-popup-pane, .leaflet-control { cursor: auto; } .leaflet-dragging .leaflet-grab, .leaflet-dragging .leaflet-grab .leaflet-interactive, .leaflet-dragging .leaflet-marker-draggable { cursor: move; cursor: -webkit-grabbing; cursor: -moz-grabbing; } /* marker & overlays interactivity */ .leaflet-marker-icon, .leaflet-marker-shadow, .leaflet-image-layer, .leaflet-pane > svg path, .leaflet-tile-container { pointer-events: none; } .leaflet-marker-icon.leaflet-interactive, .leaflet-image-layer.leaflet-interactive, .leaflet-pane > svg path.leaflet-interactive { pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ pointer-events: auto; } /* visual tweaks */ .leaflet-container { background: #ddd; outline: 0; } .leaflet-container a { color: #0078A8; } .leaflet-container a.leaflet-active { outline: 2px solid orange; } .leaflet-zoom-box { border: 2px dotted #38f; background: rgba(255,255,255,0.5); } /* general typography */ .leaflet-container { font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; } /* general toolbar styles */ .leaflet-bar { box-shadow: 0 1px 5px rgba(0,0,0,0.65); border-radius: 4px; } .leaflet-bar a, .leaflet-bar a:hover { background-color: #fff; border-bottom: 1px solid #ccc; width: 26px; height: 26px; line-height: 26px; display: block; text-align: center; text-decoration: none; color: black; } .leaflet-bar a, .leaflet-control-layers-toggle { background-position: 50% 50%; background-repeat: no-repeat; display: block; } .leaflet-bar a:hover { background-color: #f4f4f4; } .leaflet-bar a:first-child { border-top-left-radius: 4px; border-top-right-radius: 4px; } .leaflet-bar a:last-child { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-bottom: none; } .leaflet-bar a.leaflet-disabled { cursor: default; background-color: #f4f4f4; color: #bbb; } .leaflet-touch .leaflet-bar a { width: 30px; height: 30px; line-height: 30px; } .leaflet-touch .leaflet-bar a:first-child { border-top-left-radius: 2px; border-top-right-radius: 2px; } .leaflet-touch .leaflet-bar a:last-child { border-bottom-left-radius: 2px; border-bottom-right-radius: 2px; } /* zoom control */ .leaflet-control-zoom-in, .leaflet-control-zoom-out { font: bold 18px 'Lucida Console', Monaco, monospace; text-indent: 1px; } .leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { font-size: 22px; } /* layers control */ .leaflet-control-layers { box-shadow: 0 1px 5px rgba(0,0,0,0.4); background: #fff; border-radius: 5px; } .leaflet-control-layers-toggle { background-image: url(images/layers.png); width: 36px; height: 36px; } .leaflet-retina .leaflet-control-layers-toggle { background-image: url(images/layers-2x.png); background-size: 26px 26px; } .leaflet-touch .leaflet-control-layers-toggle { width: 44px; height: 44px; } .leaflet-control-layers .leaflet-control-layers-list, .leaflet-control-layers-expanded .leaflet-control-layers-toggle { display: none; } .leaflet-control-layers-expanded .leaflet-control-layers-list { display: block; position: relative; } .leaflet-control-layers-expanded { padding: 6px 10px 6px 6px; color: #333; background: #fff; } .leaflet-control-layers-scrollbar { overflow-y: scroll; overflow-x: hidden; padding-right: 5px; } .leaflet-control-layers-selector { margin-top: 2px; position: relative; top: 1px; } .leaflet-control-layers label { display: block; } .leaflet-control-layers-separator { height: 0; border-top: 1px solid #ddd; margin: 5px -10px 5px -6px; } /* Default icon URLs */ .leaflet-default-icon-path { background-image: url(images/marker-icon.png); } /* attribution and scale controls */ .leaflet-container .leaflet-control-attribution { background: #fff; background: rgba(255, 255, 255, 0.7); margin: 0; } .leaflet-control-attribution, .leaflet-control-scale-line { padding: 0 5px; color: #333; } .leaflet-control-attribution a { text-decoration: none; } .leaflet-control-attribution a:hover { text-decoration: underline; } .leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale { font-size: 11px; } .leaflet-left .leaflet-control-scale { margin-left: 5px; } .leaflet-bottom .leaflet-control-scale { margin-bottom: 5px; } .leaflet-control-scale-line { border: 2px solid #777; border-top: none; line-height: 1.1; padding: 2px 5px 1px; font-size: 11px; white-space: nowrap; overflow: hidden; -moz-box-sizing: border-box; box-sizing: border-box; background: #fff; background: rgba(255, 255, 255, 0.5); } .leaflet-control-scale-line:not(:first-child) { border-top: 2px solid #777; border-bottom: none; margin-top: -2px; } .leaflet-control-scale-line:not(:first-child):not(:last-child) { border-bottom: 2px solid #777; } .leaflet-touch .leaflet-control-attribution, .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { box-shadow: none; } .leaflet-touch .leaflet-control-layers, .leaflet-touch .leaflet-bar { border: 2px solid rgba(0,0,0,0.2); background-clip: padding-box; } /* popup */ .leaflet-popup { position: absolute; text-align: center; margin-bottom: 20px; } .leaflet-popup-content-wrapper { padding: 1px; text-align: left; border-radius: 12px; } .leaflet-popup-content { margin: 13px 19px; line-height: 1.4; } .leaflet-popup-content p { margin: 18px 0; } .leaflet-popup-tip-container { width: 40px; height: 20px; position: absolute; left: 50%; margin-left: -20px; overflow: hidden; pointer-events: none; } .leaflet-popup-tip { width: 17px; height: 17px; padding: 1px; margin: -10px auto 0; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg); } .leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white; color: #333; box-shadow: 0 3px 14px rgba(0,0,0,0.4); } .leaflet-container a.leaflet-popup-close-button { position: absolute; top: 0; right: 0; padding: 4px 4px 0 0; border: none; text-align: center; width: 18px; height: 14px; font: 16px/14px Tahoma, Verdana, sans-serif; color: #c3c3c3; text-decoration: none; font-weight: bold; background: transparent; } .leaflet-container a.leaflet-popup-close-button:hover { color: #999; } .leaflet-popup-scrolled { overflow: auto; border-bottom: 1px solid #ddd; border-top: 1px solid #ddd; } .leaflet-oldie .leaflet-popup-content-wrapper { zoom: 1; } .leaflet-oldie .leaflet-popup-tip { width: 24px; margin: 0 auto; -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); } .leaflet-oldie .leaflet-popup-tip-container { margin-top: -1px; } .leaflet-oldie .leaflet-control-zoom, .leaflet-oldie .leaflet-control-layers, .leaflet-oldie .leaflet-popup-content-wrapper, .leaflet-oldie .leaflet-popup-tip { border: 1px solid #999; } /* div icon */ .leaflet-div-icon { background: #fff; border: 1px solid #666; } /* Tooltip */ /* Base styles for the element that has a tooltip */ .leaflet-tooltip { position: absolute; padding: 6px; background-color: #fff; border: 1px solid #fff; border-radius: 3px; color: #222; white-space: nowrap; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; pointer-events: none; box-shadow: 0 1px 3px rgba(0,0,0,0.4); } .leaflet-tooltip.leaflet-clickable { cursor: pointer; pointer-events: auto; } .leaflet-tooltip-top:before, .leaflet-tooltip-bottom:before, .leaflet-tooltip-left:before, .leaflet-tooltip-right:before { position: absolute; pointer-events: none; border: 6px solid transparent; background: transparent; content: ""; } /* Directions */ .leaflet-tooltip-bottom { margin-top: 6px; } .leaflet-tooltip-top { margin-top: -6px; } .leaflet-tooltip-bottom:before, .leaflet-tooltip-top:before { left: 50%; margin-left: -6px; } .leaflet-tooltip-top:before { bottom: 0; margin-bottom: -12px; border-top-color: #fff; } .leaflet-tooltip-bottom:before { top: 0; margin-top: -12px; margin-left: -6px; border-bottom-color: #fff; } .leaflet-tooltip-left { margin-left: -6px; } .leaflet-tooltip-right { margin-left: 6px; } .leaflet-tooltip-left:before, .leaflet-tooltip-right:before { top: 50%; margin-top: -6px; } .leaflet-tooltip-left:before { right: 0; margin-right: -12px; border-left-color: #fff; } .leaflet-tooltip-right:before { left: 0; margin-left: -12px; border-right-color: #fff; } ================================================ FILE: vignettes/tutorials/libs/leaflet/leaflet.js ================================================ /* @preserve * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e=0}function I(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function B(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(Qi,n,!1):"touchmove"===i?t.removeEventListener(te,n,!1):"touchend"===i&&(t.removeEventListener(ie,n,!1),t.removeEventListener(ee,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(ne.indexOf(t.target.tagName)<0))return;$(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(Qi,o,!1),se||(document.documentElement.addEventListener(Qi,R,!0),document.documentElement.addEventListener(te,D,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,N,!0),se=!0)}function R(t){oe[t.pointerId]=t,re++}function D(t){oe[t.pointerId]&&(oe[t.pointerId]=t)}function N(t){delete oe[t.pointerId],re--}function j(t,i){t.touches=[];for(var e in oe)t.touches.push(oe[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(te,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ie,n,!1),t.addEventListener(ee,n,!1)}function F(t,i,e){function n(t){var i;if(Ui){if(!Pi||"mouse"===t.pointerType)return;i=re}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Ui){if(!Pi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[ue+ae+e]=n,t[ue+he+e]=o,t[ue+"dblclick"+e]=i,t.addEventListener(ae,n,!1),t.addEventListener(he,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[ue+ae+i],n=t[ue+he+i],o=t[ue+"dblclick"+i];return t.removeEventListener(ae,e,!1),t.removeEventListener(he,n,!1),Pi||t.removeEventListener("dblclick",o,!1),this}function V(t,i,e,n){if("object"==typeof i)for(var o in i)G(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s100&&n<500||t.target._simulatedClick&&!t._simulated?Q(t):(pi=e,i(t))}function rt(t){return"string"==typeof t?document.getElementById(t):t}function at(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function ht(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ut(t){var i=t.parentNode;i&&i.removeChild(t)}function lt(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ct(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function _t(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function dt(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=gt(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function pt(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;nh&&(s=r,h=a);h>e&&(i[s]=1,Et(t,i,e,n,s),Et(t,i,e,s,o))}function kt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;ni&&(e.push(t[n]),o=n);return oi.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function Ot(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Rt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function Dt(t){return!ei(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Nt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Dt(t)}function jt(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=li(this.x),this.y=li(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.xi.y&&n.y=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lati.lng&&n.lng1,Yi=!!document.createElement("canvas").getContext,Xi=!(!document.createElementNS||!E("svg").createSVGRect),Ji=!Xi&&function(){try{var t=document.createElement("div");t.innerHTML='';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),$i=(Object.freeze||Object)({ie:wi,ielt9:Li,edge:Pi,webkit:bi,android:Ti,android23:zi,androidStock:Ci,opera:Zi,chrome:Si,gecko:Ei,safari:ki,phantom:Ai,opera12:Ii,win:Bi,ie3d:Oi,webkit3d:Ri,gecko3d:Di,any3d:Ni,mobile:ji,mobileWebkit:Wi,mobileWebkit3d:Hi,msPointer:Fi,pointer:Ui,touch:Vi,mobileOpera:qi,mobileGecko:Gi,retina:Ki,canvas:Yi,svg:Xi,vml:Ji}),Qi=Fi?"MSPointerDown":"pointerdown",te=Fi?"MSPointerMove":"pointermove",ie=Fi?"MSPointerUp":"pointerup",ee=Fi?"MSPointerCancel":"pointercancel",ne=["INPUT","SELECT","OPTION"],oe={},se=!1,re=0,ae=Fi?"MSPointerDown":Ui?"pointerdown":"touchstart",he=Fi?"MSPointerUp":Ui?"pointerup":"touchend",ue="_leaflet_",le="_leaflet_events",ce=Bi&&Si?2*window.devicePixelRatio:Ei?window.devicePixelRatio:1,_e={},de=(Object.freeze||Object)({on:V,off:q,stopPropagation:Y,disableScrollPropagation:X,disableClickPropagation:J,preventDefault:$,stop:Q,getMousePosition:tt,getWheelDelta:it,fakeStop:et,skipped:nt,isExternalTarget:ot,addListener:V,removeListener:q}),pe=xt(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),me=xt(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),fe="webkitTransition"===me||"OTransition"===me?me+"End":"transitionend";if("onselectstart"in document)mi=function(){V(window,"selectstart",$)},fi=function(){q(window,"selectstart",$)};else{var ge=xt(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);mi=function(){if(ge){var t=document.documentElement.style;gi=t[ge],t[ge]="none"}},fi=function(){ge&&(document.documentElement.style[ge]=gi,gi=void 0)}}var ve,ye,xe=(Object.freeze||Object)({TRANSFORM:pe,TRANSITION:me,TRANSITION_END:fe,get:rt,getStyle:at,create:ht,remove:ut,empty:lt,toFront:ct,toBack:_t,hasClass:dt,addClass:pt,removeClass:mt,setClass:ft,getClass:gt,setOpacity:vt,testProp:xt,setTransform:wt,setPosition:Lt,getPosition:Pt,disableTextSelection:mi,enableTextSelection:fi,disableImageDrag:bt,enableImageDrag:Tt,preventOutline:zt,restoreOutline:Mt}),we=ui.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pt(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;ithis.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),ut(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)ut(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=ht("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=Ni?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return tt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=rt(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");V(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ni,pt(t,"leaflet-container"+(Vi?" leaflet-touch":"")+(Ki?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(ki?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=at(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Lt(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(pt(t.markerPane,"leaflet-zoom-hide"),pt(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){Lt(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Lt(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?q:V;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Ni&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!ot(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!ot(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!nt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||zt(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&$(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=Ni?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){mt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=ht("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=pe,e=this._proxy.style[i];wt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();wt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){ut(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pt(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&mt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Pe=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pt(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(ut(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),be=function(t){return new Pe(t)};Le.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=ht("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=ht("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)ut(this._controlCorners[t]);ut(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Te=Pe.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),V(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&ni.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ze=Pe.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ht("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ht("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),J(s),V(s,"click",Q),V(s,"click",o,this),V(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mt(this._zoomInButton,i),mt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&pt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&pt(this._zoomInButton,i)}});Le.mergeOptions({zoomControl:!0}),Le.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ze,this.addControl(this.zoomControl))});var Me=Pe.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=ht("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ht("div",i,e)),t.imperial&&(this._iScale=ht("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ce=Pe.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=ht("div","leaflet-control-attribution"),J(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});Le.mergeOptions({attributionControl:!0}),Le.addInitHook(function(){this.options.attributionControl&&(new Ce).addTo(this)});Pe.Layers=Te,Pe.Zoom=ze,Pe.Scale=Me,Pe.Attribution=Ce,be.layers=function(t,i,e){return new Te(t,i,e)},be.zoom=function(t){return new ze(t)},be.scale=function(t){return new Me(t)},be.attribution=function(t){return new Ce(t)};var Ze=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ze.addTo=function(t,i){return t.addHandler(i,this),this};var Se,Ee={Events:hi},ke=Vi?"touchstart mousedown":"mousedown",Ae={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Ie={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Be=ui.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(V(this._dragStartTarget,ke,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Be._dragging===this&&this.finishDrag(),q(this._dragStartTarget,ke,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!dt(this._element,"leaflet-zoom-anim")&&!(Be._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Be._dragging=this,this._preventOutline&&zt(this._element),bt(),mi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new x(i.clientX,i.clientY),V(document,Ie[t.type],this._onMove,this),V(document,Ae[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY).subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},je=(Object.freeze||Object)({LonLat:De,Mercator:Ne,SphericalMercator:di}),We=i({},_i,{code:"EPSG:3395",projection:Ne,transformation:function(){var t=.5/(Math.PI*Ne.R);return S(t,.5,-t,.5)}()}),He=i({},_i,{code:"EPSG:4326",projection:De,transformation:S(1/180,1,-1/180,.5)}),Fe=i({},ci,{projection:De,transformation:S(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});ci.Earth=_i,ci.EPSG3395=We,ci.EPSG3857=vi,ci.EPSG900913=yi,ci.EPSG4326=He,ci.Simple=Fe;var Ue=ui.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});Le.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?ei(t)?t:[t]:[]).length;ithis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return Dt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=Dt(t),n=0,o=t.length;n=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){tn.prototype._setLatLngs.call(this,t),Dt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Dt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;ot.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||tn.prototype._containsPoint.call(this,t,!0)}}),nn=qe.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=ei(t)?t:t.features;if(o){for(i=0,e=o.length;i0?o:[i.src]}else{ei(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;ao?(i.height=o+"px",pt(t,"leaflet-popup-scrolled")):mt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Lt(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(at(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(Pt(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Q(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Le.mergeOptions({closePopupOnClick:!0}),Le.include({openPopup:function(t,i,e){return t instanceof un||(t=new un(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Ue.include({bindPopup:function(t,i){return t instanceof un?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new un(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Q(t),i instanceof Je?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var ln=hn.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){hn.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=hn.prototype.getEvents.call(this);return Vi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ht("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.xthis.options.maxZoom||en&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1this.options.maxZoom||void 0!==this.options.minZoom&&o1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_e.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(Ci||i.el.setAttribute("src",ni),ut(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pt(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&vt(t,this.options.opacity),Ti&&!zi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),Lt(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){if(this._map){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(vt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(pt(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),dn=_n.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Ki&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),Ti||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return V(n,"load",e(this._tileOnLoad,this,i,n)),V(n,"error",e(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Ki?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=ni,ut(i),delete this._tiles[t]))}}),pn=dn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Ki?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,dn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===He?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=L.TileLayer.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});dn.WMS=pn,Yt.wms=function(t,i){return new pn(t,i)};var mn=Ue.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&pt(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=Pt(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);Ni?wt(this._container,a,e):Lt(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),fn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){mn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");V(t,"mousemove",o(this._onMouseMove,32,this),this),V(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),V(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){delete this._ctx,ut(this._container),q(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},mn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Ki?2:1;Lt(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Ki&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){mn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,n=i.prev;e?e.prev=n:this._drawLast=n,n?n.next=e:this._drawFirst=e,delete t._order,delete this._layers[L.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var i,e=t.options.dashArray.split(","),n=[];for(i=0;i')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),vn={_initContainer:function(){this._container=ht("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(mn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=gn("shape");pt(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=gn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ut(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=gn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=ei(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=gn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ct(t._container)},_bringToBack:function(t){_t(t._container)}},yn=Ji?gn:E,xn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=yn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=yn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ut(this._container),q(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){mn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Lt(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=yn("path");t.options.className&&pt(i,t.options.className),t.options.interactive&&pt(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ut(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ct(t._path)},_bringToBack:function(t){_t(t._path)}});Ji&&xn.include(vn),Le.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this.options.preferCanvas&&Xt()||Jt()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=xn&&Jt({pane:t})||fn&&Xt({pane:t}),this._paneRenderers[t]=i),i}});var wn=en.extend({initialize:function(t,i){en.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});xn.create=yn,xn.pointsToPath=k,nn.geometryToLayer=Wt,nn.coordsToLatLng=Ht,nn.coordsToLatLngs=Ft,nn.latLngToCoords=Ut,nn.latLngsToCoords=Vt,nn.getFeature=qt,nn.asFeature=Gt,Le.mergeOptions({boxZoom:!0});var Ln=Ze.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){V(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){q(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ut(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),mi(),bt(),this._startPoint=this._map.mouseEventToContainerPoint(t),V(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ht("div","leaflet-zoom-box",this._container),pt(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();Lt(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ut(this._box),mt(this._container,"leaflet-crosshair")),fi(),Tt(),q(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});Le.addInitHook("addHandler","boxZoom",Ln),Le.mergeOptions({doubleClickZoom:!0});var Pn=Ze.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});Le.addInitHook("addHandler","doubleClickZoom",Pn),Le.mergeOptions({dragging:!0,inertia:!zi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var bn=Ze.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Be(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pt(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mt(this._map._container,"leaflet-grab"),mt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.xi.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});Le.addInitHook("addHandler","scrollWheelZoom",zn),Le.mergeOptions({tap:!0,tapTolerance:15});var Mn=Ze.extend({addHooks:function(){V(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){q(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if($(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&pt(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),V(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),q(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&mt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});Vi&&!Ui&&Le.addInitHook("addHandler","tap",Mn),Le.mergeOptions({touchZoom:Vi&&!zi,bounceAtZoomLimits:!0});var Cn=Ze.extend({addHooks:function(){pt(this._map._container,"leaflet-touch-zoom"),V(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){mt(this._map._container,"leaflet-touch-zoom"),q(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),V(document,"touchmove",this._onTouchMove,this),V(document,"touchend",this._onTouchEnd,this),$(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoomi.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),$(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),q(document,"touchmove",this._onTouchMove),q(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});Le.addInitHook("addHandler","touchZoom",Cn),Le.BoxZoom=Ln,Le.DoubleClickZoom=Pn,Le.Drag=bn,Le.Keyboard=Tn,Le.ScrollWheelZoom=zn,Le.Tap=Mn,Le.TouchZoom=Cn;var Zn=window.L;window.L=t,Object.freeze=$t,t.version="1.3.1+HEAD.ba6f97f",t.noConflict=function(){return window.L=Zn,this},t.Control=Pe,t.control=be,t.Browser=$i,t.Evented=ui,t.Mixin=Ee,t.Util=ai,t.Class=v,t.Handler=Ze,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=de,t.DomUtil=xe,t.PosAnimation=we,t.Draggable=Be,t.LineUtil=Oe,t.PolyUtil=Re,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=Z,t.transformation=S,t.Projection=je,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=ci,t.GeoJSON=nn,t.geoJSON=Kt,t.geoJson=sn,t.Layer=Ue,t.LayerGroup=Ve,t.layerGroup=function(t,i){return new Ve(t,i)},t.FeatureGroup=qe,t.featureGroup=function(t){return new qe(t)},t.ImageOverlay=rn,t.imageOverlay=function(t,i,e){return new rn(t,i,e)},t.VideoOverlay=an,t.videoOverlay=function(t,i,e){return new an(t,i,e)},t.DivOverlay=hn,t.Popup=un,t.popup=function(t,i){return new un(t,i)},t.Tooltip=ln,t.tooltip=function(t,i){return new ln(t,i)},t.Icon=Ge,t.icon=function(t){return new Ge(t)},t.DivIcon=cn,t.divIcon=function(t){return new cn(t)},t.Marker=Xe,t.marker=function(t,i){return new Xe(t,i)},t.TileLayer=dn,t.tileLayer=Yt,t.GridLayer=_n,t.gridLayer=function(t){return new _n(t)},t.SVG=xn,t.svg=Jt,t.Renderer=mn,t.Canvas=fn,t.canvas=Xt,t.Path=Je,t.CircleMarker=$e,t.circleMarker=function(t,i){return new $e(t,i)},t.Circle=Qe,t.circle=function(t,i,e){return new Qe(t,i,e)},t.Polyline=tn,t.polyline=function(t,i){return new tn(t,i)},t.Polygon=en,t.polygon=function(t,i){return new en(t,i)},t.Rectangle=wn,t.rectangle=function(t,i){return new wn(t,i)},t.Map=Le,t.map=function(t,i){return new Le(t,i)}}); ================================================ FILE: vignettes/tutorials/libs/leaflet-binding/leaflet.js ================================================ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i this.effectiveLength) throw new Error("Row argument was out of bounds: " + row + " > " + this.effectiveLength); var colIndex = -1; if (typeof col === "undefined") { var rowData = {}; this.colnames.forEach(function (name, i) { rowData[name] = _this3.columns[i][row % _this3.columns[i].length]; }); return rowData; } else if (typeof col === "string") { colIndex = this._colIndex(col); } else if (typeof col === "number") { colIndex = col; } if (colIndex < 0 || colIndex > this.columns.length) { if (missingOK) return void 0;else throw new Error("Unknown column index: " + col); } return this.columns[colIndex][row % this.columns[colIndex].length]; } }, { key: "nrow", value: function nrow() { return this.effectiveLength; } }]); return DataFrame; }(); exports.default = DataFrame; },{"./util":17}],5:[function(require,module,exports){ "use strict"; var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // In RMarkdown's self-contained mode, we don't have a way to carry around the // images that Leaflet needs but doesn't load into the page. Instead, we'll set // data URIs for the default marker, and let any others be loaded via CDN. if (typeof _leaflet2.default.Icon.Default.imagePath === "undefined") { // if in a local file, support http switch (window.location.protocol) { case "http:": // don't force http site to be done with https _leaflet2.default.Icon.Default.imagePath = "http://cdn.leafletjs.com/leaflet/v1.3.1/images/"; break; default: // file // https // otherwise use https as it works on files and https _leaflet2.default.Icon.Default.imagePath = "https://unpkg.com/leaflet@1.3.1/dist/images/"; break; } // don't know how to make this dataURI work since // will be appended to Defaul.imagePath above /* if (L.Browser.retina) { L.Icon.Default.prototype.options.iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAABSCAYAAAAWy4frAAAPiElEQVR42t1bCVCU5xkmbabtZJJOO+l0mhgT0yQe0WXZgz2570NB8I6J6UzaTBoORRFEruVGDhWUPRAQRFFREDnVxCtEBRb24DBNE3Waaatpkmluo4m+fd9v999olGVBDu3OPLj+//s+7/W93/f9//6/EwA4/T9g3AlFOUeeUGR2uMqzOyJk2R2x0qyOAmnmkS3SrCPrZJlHlsqzjypcs49OX1Jf//P7KhD885A0u10my2ovQscvybI6wEF8ivI7pFntAV6qkw9PWSBK1bEnZRltm2WZ7R8h4FbI0VG33GPgXXgCAra+A4EIn8KT4JH/FigoiJ/IIz6TZbVVKLLan5u0QESqlkckWW3p0sy2bxDAgZwO13TDytoB+NPe9+zild2DEFGuB7/NpzDodriF55o0o7XIRXXoNxMaiCSj9VU09C8EENxyj0C4thterh2EV+veuwOr6s7Dy3ssoO93k3llzxBE6PTgkXcMOF7EJ9KMtqjR9JFDQnNV9b+QqlqqEECQZ7TBgu1nYdXuIXgVneSwYtcgRFb1Q1iFGULLzRCsM90GOrZghxkiKvthec0grLpFlxCu6cKh1w6cHUSbctPhx8YlEElu4+NSVfNpBBACtpyGlbsGmBOElRhMBDofgk4GobOjQXC5CRZiUC/VDtn4qLrBJZ3A2cNg+nE4P31PgSDBbImq5UNJejMQFqi7cCicZ3iZBTAAQVoTBI4DKKCVGBDHH6nrBRlWxWr7sljVIhlTIDLVoRkS1eH/SNIPgzyzFRZV9NnG++LqQcyoGQLQgfFEIFYpcueAzc6SSiMOtTYgH9CXr+WpTbxRBeKlqn9UktZkRoACZ5PlO81YgfMM4RX9EKAxTSjCdvTjELPYW17dD8rsdiBfEBclSY2POxQIHnlIknroEAJk6U2wpMLISF/aNQShWAV/tWlSEIK2VqBNsr200gRyGmLokyS18cTdFtA7AnFNbcxAACGMrQtDLAjqBT+1cVJBNsk2+bBQ1wOcX5K0xs12A8GyzXRNafgeAYFb3mEkrBI4I/mWGUeNQI1lyp2PoO9j4aDKcH4Ebe0E8g3xgyylcc6wgbimNjSSoFtWK1sTqLRh2BM+SOgIfDGLJL8IG3ZZjUX/ViyvGYLFOwdZn/ljYI7yzsee4TjcsV/IR3FqQ+tdAxEnNSjFyQeBEK7pgRVodEnVIPhsNzqEYK0ZluFsRnq3YjH22KJyA6z4yTmSpZ5zlH8RTvWkt1CrB85PYUqjzx2BuG6sPyfeeAA8sjtwphhiCFSbwXub0S7ISPiOAZvO4h048xSfBM+cDpDieCZOggSz6JHdBv5FJ3CN6LPJR1QMgO9204h2aALgdDxzjlp4kw8YaHKyBSJJPigWb6wHQiRmbxkKL0QDXkhgD94YxGKsGskTQkvfxVnlIHBcBNfkegziwB3HAnHDuGynRXcp/utXZhrRHiWM5CPLjbdwHVDYAhFt3J8rTtoPbpktSDrE4INZ8iw12kUYEpPs4kozeOW0A3EQIovbYcfxITj798vwxbfX4Or1H8B46ROo7fwbvKY9bpNzy2hmiSOOyMrBEe2RT5x/7tjHxCFK2l/4YyBJ+95HQABmibKzEJvRs9RgF4FqE5MleGS3AumLN+6D4lYjfIeOD/e5eROg7sz7oEg7wHRk6Y3Yi/2MJwT7bCS75BvJBuGsSvqID1ggaHyeaAMeQERgyajBg3BG8SgxDAsvJFxUOcBkg7d0Ml3XjfuhCyvg6Ofix1+Al6qB6fpueotxsckFh5A92+QbydHw4vymGJxEG+rWiRL3goJWcSwvwbPECO5bDcMiRGNmchS4a1I9kP62DhOM9tPad4npEhaUdTPOsPJ+u7bJN85PpaqJ6YoT6xKcRIl1pQjwxIukxXhyIY57N1Swh7DyASbrm38MSHdRUStc+/4GjOUTV32acbhlNjNO6pWR7FPTk6xX3lGmK0ys0zrhn0Zhwh7wK3ibnVyg6we3LQa7WFQxyGSpiqRbe/o8jPXTe+EK4xDjECHOxdYRYc8++UhyfgXHma5w/Z5mJ+H63T3ChN3Y6O/guMcxj8NGicLDgYyQ3CKcnsUbMBuoa7j48ZgD+erqdczqbsYTpulj3LSu2POBfCQ58pn0EH1OwoTafwvX1+JV2VmIxEwHlJlBsdkwLHy2mZjcgjI9kJ4Ynbh6/Xu4l09YfhPjCsSJg7hpIbbng/92M5Mjn0kPcdlJGF/7JQJCSrsgAseeHzoqL+4bFnSe5EJKzgHpeaTsg3v9rCrtYFz+hScZdzAGYs8HX84H9Jn0KAYnQfyuIQT4Y5mo0akiMhQeDh44tEguXGcE0iP845MvxxzEjRs3QZ5Ux3hCtnUxbqq6PR/8cRdAcuSz1YfzGEhNm2BdDfjkvw0LcTYKokCK+oaFAolIjiDFBYl02/oujDmQC1c+ZxzC+BoIp2t35HXHPrDnA/lIcuQz6SKOOAnWVqsRbHscjidDNf0gRWF7CNX2M1l3VTOQbmpd55gDqT01xDhkmBTiJMhGsB+isdrPbGe6wrU15RjIzkQEyHB3GqYbYCAiSeHwCMBmI7mAYiwt6grX7QT9h5dHHcQ/P/sKlEm7GYd37lHGGaLut2tbirD5iT6TriCuKsVJsLrCwyWuih2Yj/unMC2VFlfsgr5hodxsZHIEZVoTkP787APw7TXHZy/ac/25rJ3pSpP24tRrZnyeW012bbtZbS9AefKZ+b6mMtjJS6V6GP/zOR3wK+pkQn7bzHbJCCRDsqFlBpz+djHCV7a2wMUr/x0xiM++ugprq45bnFhbhdNoF+MKLOt32C75SvqIb7xUO3/Fdr/8uMqDLmsqwU3VipH2QzA2k3hTr11ICnqZHMn7F+HCFIfZQQ5JfDVUvW1mzv708/V316FV/wF4Je9hsgSv3GOMYz71Jg6bkezS0CN5N1WLhSOussW2jResrnzNZXUFm5PnW0nl2CciVLQHebHBJh9U0g1S3GYQD4eQjH2QWH0C0utw15DXAEIybD0nxoUsYPMZmz4N59HYE+K0SzyC2Mo3bIHw4zTT+Kt33ESAX/FZCMWovUtMIMzvHRFKJA9G+VAGvJ7IPsKGC3HdDYI4qnwzhJQZmQ5l2AODcMSWb6mJ6fgWn+H4bsxbWzX9tmt2l9Xl7fzYcpwJGhl5MI5XESoL8kaGKB9XWww8xOoYIXBrD3hvOgnK9BbEYdypHsctSBcGYLbJ+FMvbupz2AanJ01uAPLVJab88B03H1xidKH8WB0TCCq1KNEM4YgRDm7FRlys+m8L6G6gJLmPkpuqxhJU0st8JF8FMeV+dwTipFL9zDlGewmB1wYdzJh/qRlccntHDcqevBCv6NBZ3xIz+CGP5xYTKIoMIMZzo+UTIAK3WRKgULUB+egcrTs/7A06XpQ20Tlai+O4mm0DKLuSAgPwkWgqIcOkkC+BOBRdVlcC+ciL0kUNG4jodd3vnKM13yHAK/8UBG6nTBrBOUc/pfDBRZJ88cg9DuQbL1rzxdw3yx61exPbOUazi4Rd8VqYMhBIwyunF5yz9VMCUV6vxQ+ECJcH8s05SlMy4t145xi1jAkjfIu7GIESxzYPSacC1Gfkg3fhGbD6ddMlVvuCQz/0oHAfKclSmiAAK0JN75zdC/Oy9JMKanKyTxBvOGAJJEbd4fAvVrxo9UukxMfZwbu4hwWiKDLCXCSfTNAUTba9Cs5x1SD4OBwIm4qjNQOkKE1uBH+aQkssVZmbqZ8UCLAvyS5BnLDf2hvaE6P+MZQfpYngsuBd2A1+W7EqBUZ4MUM/KXAvMjGbHvm23gCXaI1yTD9Po7KezWBJB8EXp0ACD0s+J6NnQkGzJGdPlFDHBdI+5t/Z+dGaQC4bHpvOgg+uznJcIGereiYUykIjs+WW22mrBi9WLbqnJx9wlugkIlHifvBGcgLNKLPQ4ESA+pCzI4jfwy2Ajff8CAduWzy4rLjnnWEGqFdmpfdMCKgaZEOZc5qrxg3nWM28cXmohhetPcqqsn4veG02MczDmWVmWs+4wjmr18YvWFfLBVI3bk8HubxZ5spVRZHTyQzJsSovoPHxhAKrQdyKrFNcED/wo8pnjuvzWrgHayJyIY5bz2ITw1ycJp9P7R4X8LDCHK/L2l0sEH60tmrcHzzjRet4tM9hVck+xQzKNxnGLRDqO+KUZZ7gqnHdZY1mxoQ8QUfjlYwI1taCBy5YBKrKcynd9wTqNwufEfhrqq17Ko16wh4FpPFK45ZtKDNOgnshZjDfAH9M7r4nyPONjEua/hZXjav8NzTTJvThTF6UppJtF+JqwA2NE15U6eFZdGgsmJvRyziUeBXIX7PT2huazRP+lKkgavszeM18jW0oVcfBrYCqYoRnN3aPGlw1iMM17ai1Gtqvnd/Q/H5SnvvF7f12ljkcz0psUmWBpSoz0LnRgKpBugq6L8CuxSkQde6kPcAsWqN7Ao1+yzaUacdAsckI0jwDPJPU5TBmbOxi/UW64pQOrjc+5/1V/dtJfRIbrw0KWFVWV+Hw6GNDZE6aHp7e0OUQ5qTrmY48rw/4sRWW3ojSpk36I+Wzo7Y/7hyl+ZJtXVI7WJ+45hrgacz29A32QTISrCDpiJLbuWp8Oiuh8jGYiof8eTHqDEtVKkCGmZVZqzI9scsuSIZkZXTfKnYHt8NNmLK3FaQxpb9GJz5jVcHMclWhrD+VeHfQsJLkWqohTGrlqnFZ9LrukSl97YIXpU5kVcHMSvDKTppnhNmY8WkJXXcFnSMZSY6e3cO1ruKxU/7+CGUSnbnCti4bWjHbOAvlGOApdPrJ9beDjtE5khFsaOaq8dHzMaW/vC/e6KGMWm4flYMku4cNnVmpPej8udtA1aBzrll47RGjs/aG+vX75tUkyihl1lKVZnDFrIuy+2AaOv9EvAX0nY7ROZeEJq4aF+g3zPvqHStejOYvlvGuA1FmNxtCM1P18AcMgjALv9MxYWaX9WcBktWuuu9eFqPM4mbvAzbEEg5h9tHpLIOtP+g7HeMnNHLVeG/JkvF7YWxc33jDqqy0ZhoEKovzM1P0DPSdjtFvG5ZVXLP0vn19z3KrVTvIHF3fYHHeCvruHN/AbdNN3PO69+17iLgzjrRux8El/SwIMg0M9P3HG9HqsPv+hUrrJXEvczj+AAbRx+AcX88F0v1AvBnKAnlTG8Rln5/6LuLHW5/zorT+D0wg1qq8y5xfu88CSyCnH5h3dW/ZGXve8uOMZRWP0no8cIFY7+YfswURrT36QL09ffsMppHYegW/P7CBWHvlMOGBe5/9jtdjY7R8wkTb+R9meZA6n2oJWAAAAABJRU5ErkJggg=="; } else { L.Icon.Default.prototype.options.iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAGmklEQVRYw7VXeUyTZxjvNnfELFuyIzOabermMZEeQC/OclkO49CpOHXOLJl/CAURuYbQi3KLgEhbrhZ1aDwmaoGqKII6odATmH/scDFbdC7LvFqOCc+e95s2VG50X/LLm/f4/Z7neY/ne18aANCmAr5E/xZf1uDOkTcGcWR6hl9247tT5U7Y6SNvWsKT63P58qbfeLJG8M5qcgTknrvvrdDbsT7Ml+tv82X6vVxJE33aRmgSyYtcWVMqX97Yv2JvW39UhRE2HuyBL+t+gK1116ly06EeWFNlAmHxlQE0OMiV6mQCScusKRlhS3QLeVJdl1+23h5dY4FNB3thrbYboqptEFlphTC1hSpJnbRvxP4NWgsE5Jyz86QNNi/5qSUTGuFk1gu54tN9wuK2wc3o+Wc13RCmsoBwEqzGcZsxsvCSy/9wJKf7UWf1mEY8JWfewc67UUoDbDjQC+FqK4QqLVMGGR9d2wurKzqBk3nqIT/9zLxRRjgZ9bqQgub+DdoeCC03Q8j+0QhFhBHR/eP3U/zCln7Uu+hihJ1+bBNffLIvmkyP0gpBZWYXhKussK6mBz5HT6M1Nqpcp+mBCPXosYQfrekGvrjewd59/GvKCE7TbK/04/ZV5QZYVWmDwH1mF3xa2Q3ra3DBC5vBT1oP7PTj4C0+CcL8c7C2CtejqhuCnuIQHaKHzvcRfZpnylFfXsYJx3pNLwhKzRAwAhEqG0SpusBHfAKkxw3w4627MPhoCH798z7s0ZnBJ/MEJbZSbXPhER2ih7p2ok/zSj2cEJDd4CAe+5WYnBCgR2uruyEw6zRoW6/DWJ/OeAP8pd/BGtzOZKpG8oke0SX6GMmRk6GFlyAc59K32OTEinILRJRchah8HQwND8N435Z9Z0FY1EqtxUg+0SO6RJ/mmXz4VuS+DpxXC3gXmZwIL7dBSH4zKE50wESf8qwVgrP1EIlTO5JP9Igu0aexdh28F1lmAEGJGfh7jE6ElyM5Rw/FDcYJjWhbeiBYoYNIpc2FT/SILivp0F1ipDWk4BIEo2VuodEJUifhbiltnNBIXPUFCMpthtAyqws/BPlEF/VbaIxErdxPphsU7rcCp8DohC+GvBIPJS/tW2jtvTmmAeuNO8BNOYQeG8G/2OzCJ3q+soYB5i6NhMaKr17FSal7GIHheuV3uSCY8qYVuEm1cOzqdWr7ku/R0BDoTT+DT+ohCM6/CCvKLKO4RI+dXPeAuaMqksaKrZ7L3FE5FIFbkIceeOZ2OcHO6wIhTkNo0ffgjRGxEqogXHYUPHfWAC/lADpwGcLRY3aeK4/oRGCKYcZXPVoeX/kelVYY8dUGf8V5EBRbgJXT5QIPhP9ePJi428JKOiEYhYXFBqou2Guh+p/mEB1/RfMw6rY7cxcjTrneI1FrDyuzUSRm9miwEJx8E/gUmqlyvHGkneiwErR21F3tNOK5Tf0yXaT+O7DgCvALTUBXdM4YhC/IawPU+2PduqMvuaR6eoxSwUk75ggqsYJ7VicsnwGIkZBSXKOUww73WGXyqP+J2/b9c+gi1YAg/xpwck3gJuucNrh5JvDPvQr0WFXf0piyt8f8/WI0hV4pRxxkQZdJDfDJNOAmM0Ag8jyT6hz0WGXWuP94Yh2jcfjmXAGvHCMslRimDHYuHuDsy2QtHuIavznhbYURq5R57KpzBBRZKPJi8eQg48h4j8SDdowifdIrEVdU+gbO6QNvRRt4ZBthUaZhUnjlYObNagV3keoeru3rU7rcuceqU1mJBxy+BWZYlNEBH+0eH4vRiB+OYybU2hnblYlTvkHinM4m54YnxSyaZYSF6R3jwgP7udKLGIX6r/lbNa9N6y5MFynjWDtrHd75ZvTYAPO/6RgF0k76mQla3FGq7dO+cH8sKn0Vo7nDllwAhqwLPkxrHwWmHJOo+AKJ4rab5OgrM7rVu8eWb2Pu0Dh4eDgXoOfvp7Y7QeqknRmvcTBEyq9m/HQQSCSz6LHq3z0yzsNySRfMS253wl2KyRDbcZPcfJKjZmSEOjcxyi+Y8dUOtsIEH6R2wNykdqrkYJ0RV92H0W58pkfQk7cKevsLK10Py8SdMGfXNXATY+pPbyJR/ET6n9nIfztNtZYRV9XniQu9IA2vOVgy4ir7GCLVmmd+zjkH0eAF9Po6K61pmCXHxU5rHMYd1ftc3owjwRSVRzLjKvqZEty6cRUD7jGqiOdu5HG6MdHjNcNYGqfDm5YRzLBBCCDl/2bk8a8gdbqcfwECu62Fg/HrggAAAABJRU5ErkJggg=="; } */ } },{"./global/leaflet":10}],6:[function(require,module,exports){ "use strict"; var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // add texxtsize, textOnly, and style _leaflet2.default.Tooltip.prototype.options.textsize = "10px"; _leaflet2.default.Tooltip.prototype.options.textOnly = false; _leaflet2.default.Tooltip.prototype.options.style = null; // copy original layout to not completely stomp it. var initLayoutOriginal = _leaflet2.default.Tooltip.prototype._initLayout; _leaflet2.default.Tooltip.prototype._initLayout = function () { initLayoutOriginal.call(this); this._container.style.fontSize = this.options.textsize; if (this.options.textOnly) { _leaflet2.default.DomUtil.addClass(this._container, "leaflet-tooltip-text-only"); } if (this.options.style) { for (var property in this.options.style) { this._container.style[property] = this.options.style[property]; } } }; },{"./global/leaflet":10}],7:[function(require,module,exports){ "use strict"; var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var protocolRegex = /^\/\//; var upgrade_protocol = function upgrade_protocol(urlTemplate) { if (protocolRegex.test(urlTemplate)) { if (window.location.protocol === "file:") { // if in a local file, support http // http should auto upgrade if necessary urlTemplate = "http:" + urlTemplate; } } return urlTemplate; }; var originalLTileLayerInitialize = _leaflet2.default.TileLayer.prototype.initialize; _leaflet2.default.TileLayer.prototype.initialize = function (urlTemplate, options) { urlTemplate = upgrade_protocol(urlTemplate); originalLTileLayerInitialize.call(this, urlTemplate, options); }; var originalLTileLayerWMSInitialize = _leaflet2.default.TileLayer.WMS.prototype.initialize; _leaflet2.default.TileLayer.WMS.prototype.initialize = function (urlTemplate, options) { urlTemplate = upgrade_protocol(urlTemplate); originalLTileLayerWMSInitialize.call(this, urlTemplate, options); }; },{"./global/leaflet":10}],8:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = global.HTMLWidgets; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],9:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = global.jQuery; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],10:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = global.L; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],11:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = global.L.Proj; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],12:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = global.Shiny; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],13:[function(require,module,exports){ "use strict"; var _jquery = require("./global/jquery"); var _jquery2 = _interopRequireDefault(_jquery); var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); var _shiny = require("./global/shiny"); var _shiny2 = _interopRequireDefault(_shiny); var _htmlwidgets = require("./global/htmlwidgets"); var _htmlwidgets2 = _interopRequireDefault(_htmlwidgets); var _util = require("./util"); var _crs_utils = require("./crs_utils"); var _controlStore = require("./control-store"); var _controlStore2 = _interopRequireDefault(_controlStore); var _layerManager = require("./layer-manager"); var _layerManager2 = _interopRequireDefault(_layerManager); var _methods = require("./methods"); var _methods2 = _interopRequireDefault(_methods); require("./fixup-default-icon"); require("./fixup-default-tooltip"); require("./fixup-url-protocol"); var _dataframe = require("./dataframe"); var _dataframe2 = _interopRequireDefault(_dataframe); var _clusterLayerStore = require("./cluster-layer-store"); var _clusterLayerStore2 = _interopRequireDefault(_clusterLayerStore); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } window.LeafletWidget = {}; window.LeafletWidget.utils = {}; var methods = window.LeafletWidget.methods = _jquery2.default.extend({}, _methods2.default); window.LeafletWidget.DataFrame = _dataframe2.default; window.LeafletWidget.ClusterLayerStore = _clusterLayerStore2.default; window.LeafletWidget.utils.getCRS = _crs_utils.getCRS; // Send updated bounds back to app. Takes a leaflet event object as input. function updateBounds(map) { var id = map.getContainer().id; var bounds = map.getBounds(); _shiny2.default.onInputChange(id + "_bounds", { north: bounds.getNorthEast().lat, east: bounds.getNorthEast().lng, south: bounds.getSouthWest().lat, west: bounds.getSouthWest().lng }); _shiny2.default.onInputChange(id + "_center", { lng: map.getCenter().lng, lat: map.getCenter().lat }); _shiny2.default.onInputChange(id + "_zoom", map.getZoom()); } function preventUnintendedZoomOnScroll(map) { // Prevent unwanted scroll capturing. Similar in purpose to // https://github.com/CliffCloud/Leaflet.Sleep but with a // different set of heuristics. // The basic idea is that when a mousewheel/DOMMouseScroll // event is seen, we disable scroll wheel zooming until the // user moves their mouse cursor or clicks on the map. This // is slightly trickier than just listening for mousemove, // because mousemove is fired when the page is scrolled, // even if the user did not physically move the mouse. We // handle this by examining the mousemove event's screenX // and screenY properties; if they change, we know it's a // "true" move. // lastScreen can never be null, but its x and y can. var lastScreen = { x: null, y: null }; (0, _jquery2.default)(document).on("mousewheel DOMMouseScroll", "*", function (e) { // Disable zooming (until the mouse moves or click) map.scrollWheelZoom.disable(); // Any mousemove events at this screen position will be ignored. lastScreen = { x: e.originalEvent.screenX, y: e.originalEvent.screenY }; }); (0, _jquery2.default)(document).on("mousemove", "*", function (e) { // Did the mouse really move? if (lastScreen.x !== null && e.screenX !== lastScreen.x || e.screenY !== lastScreen.y) { // It really moved. Enable zooming. map.scrollWheelZoom.enable(); lastScreen = { x: null, y: null }; } }); (0, _jquery2.default)(document).on("mousedown", ".leaflet", function (e) { // Clicking always enables zooming. map.scrollWheelZoom.enable(); lastScreen = { x: null, y: null }; }); } _htmlwidgets2.default.widget({ name: "leaflet", type: "output", factory: function factory(el, width, height) { var map = null; return { // we need to store our map in our returned object. getMap: function getMap() { return map; }, renderValue: function renderValue(data) { // Create an appropriate CRS Object if specified if (data && data.options && data.options.crs) { data.options.crs = (0, _crs_utils.getCRS)(data.options.crs); } // As per https://github.com/rstudio/leaflet/pull/294#discussion_r79584810 if (map) { map.remove(); map = function () { return; }(); // undefine map } if (data.options.mapFactory && typeof data.options.mapFactory === "function") { map = data.options.mapFactory(el, data.options); } else { map = _leaflet2.default.map(el, data.options); } preventUnintendedZoomOnScroll(map); // Store some state in the map object map.leafletr = { // Has the map ever rendered successfully? hasRendered: false, // Data to be rendered when resize is called with area != 0 pendingRenderData: null }; // Check if the map is rendered statically (no output binding) if (_htmlwidgets2.default.shinyMode && /\bshiny-bound-output\b/.test(el.className)) { map.id = el.id; // Store the map on the element so we can find it later by ID (0, _jquery2.default)(el).data("leaflet-map", map); // When the map is clicked, send the coordinates back to the app map.on("click", function (e) { _shiny2.default.onInputChange(map.id + "_click", { lat: e.latlng.lat, lng: e.latlng.lng, ".nonce": Math.random() // Force reactivity if lat/lng hasn't changed }); }); var groupTimerId = null; map.on("moveend", function (e) { updateBounds(e.target); }).on("layeradd layerremove", function (e) { // If the layer that's coming or going is a group we created, tell // the server. if (map.layerManager.getGroupNameFromLayerGroup(e.layer)) { // But to avoid chattiness, coalesce events if (groupTimerId) { clearTimeout(groupTimerId); groupTimerId = null; } groupTimerId = setTimeout(function () { groupTimerId = null; _shiny2.default.onInputChange(map.id + "_groups", map.layerManager.getVisibleGroups()); }, 100); } }); } this.doRenderValue(data, map); }, doRenderValue: function doRenderValue(data, map) { // Leaflet does not behave well when you set up a bunch of layers when // the map is not visible (width/height == 0). Popups get misaligned // relative to their owning markers, and the fitBounds calculations // are off. Therefore we wait until the map is actually showing to // render the value (we rely on the resize() callback being invoked // at the appropriate time). // // There may be an issue with leafletProxy() calls being made while // the map is not being viewed--not sure what the right solution is // there. if (el.offsetWidth === 0 || el.offsetHeight === 0) { map.leafletr.pendingRenderData = data; return; } map.leafletr.pendingRenderData = null; // Merge data options into defaults var options = _jquery2.default.extend({ zoomToLimits: "always" }, data.options); if (!map.layerManager) { map.controls = new _controlStore2.default(map); map.layerManager = new _layerManager2.default(map); } else { map.controls.clear(); map.layerManager.clear(); } var explicitView = false; if (data.setView) { explicitView = true; map.setView.apply(map, data.setView); } if (data.fitBounds) { explicitView = true; methods.fitBounds.apply(map, data.fitBounds); } if (data.flyTo) { if (!explicitView && !map.leafletr.hasRendered) { // must be done to give a initial starting point map.fitWorld(); } explicitView = true; map.flyTo.apply(map, data.flyTo); } if (data.flyToBounds) { if (!explicitView && !map.leafletr.hasRendered) { // must be done to give a initial starting point map.fitWorld(); } explicitView = true; methods.flyToBounds.apply(map, data.flyToBounds); } if (data.options.center) { explicitView = true; } // Returns true if the zoomToLimits option says that the map should be // zoomed to map elements. function needsZoom() { return options.zoomToLimits === "always" || options.zoomToLimits === "first" && !map.leafletr.hasRendered; } if (!explicitView && needsZoom() && !map.getZoom()) { if (data.limits && !_jquery2.default.isEmptyObject(data.limits)) { // Use the natural limits of what's being drawn on the map // If the size of the bounding box is 0, leaflet gets all weird var pad = 0.006; if (data.limits.lat[0] === data.limits.lat[1]) { data.limits.lat[0] = data.limits.lat[0] - pad; data.limits.lat[1] = data.limits.lat[1] + pad; } if (data.limits.lng[0] === data.limits.lng[1]) { data.limits.lng[0] = data.limits.lng[0] - pad; data.limits.lng[1] = data.limits.lng[1] + pad; } map.fitBounds([[data.limits.lat[0], data.limits.lng[0]], [data.limits.lat[1], data.limits.lng[1]]]); } else { map.fitWorld(); } } for (var i = 0; data.calls && i < data.calls.length; i++) { var call = data.calls[i]; if (methods[call.method]) methods[call.method].apply(map, call.args);else (0, _util.log)("Unknown method " + call.method); } map.leafletr.hasRendered = true; if (_htmlwidgets2.default.shinyMode) { setTimeout(function () { updateBounds(map); }, 1); } }, resize: function resize(width, height) { if (map) { map.invalidateSize(); if (map.leafletr.pendingRenderData) { this.doRenderValue(map.leafletr.pendingRenderData, map); } } } }; } }); if (_htmlwidgets2.default.shinyMode) { _shiny2.default.addCustomMessageHandler("leaflet-calls", function (data) { var id = data.id; var el = document.getElementById(id); var map = el ? (0, _jquery2.default)(el).data("leaflet-map") : null; if (!map) { (0, _util.log)("Couldn't find map with id " + id); return; } for (var i = 0; i < data.calls.length; i++) { var call = data.calls[i]; if (call.dependencies) { _shiny2.default.renderDependencies(call.dependencies); } if (methods[call.method]) methods[call.method].apply(map, call.args);else (0, _util.log)("Unknown method " + call.method); } }); } },{"./cluster-layer-store":1,"./control-store":2,"./crs_utils":3,"./dataframe":4,"./fixup-default-icon":5,"./fixup-default-tooltip":6,"./fixup-url-protocol":7,"./global/htmlwidgets":8,"./global/jquery":9,"./global/leaflet":10,"./global/shiny":12,"./layer-manager":14,"./methods":15,"./util":17}],14:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _jquery = require("./global/jquery"); var _jquery2 = _interopRequireDefault(_jquery); var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); var _util = require("./util"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var LayerManager = function () { function LayerManager(map) { _classCallCheck(this, LayerManager); this._map = map; // BEGIN layer indices // {: {: layer}} this._byGroup = {}; // {: {: layer}} this._byCategory = {}; // {: layer} this._byLayerId = {}; // {: { // "group": , // "layerId": , // "category": , // "container": // } // } this._byStamp = {}; // {: {: [, , ...], ...}} this._byCrosstalkGroup = {}; // END layer indices // {: L.layerGroup} this._categoryContainers = {}; // {: L.layerGroup} this._groupContainers = {}; } _createClass(LayerManager, [{ key: "addLayer", value: function addLayer(layer, category, layerId, group, ctGroup, ctKey) { var _this = this; // Was a group provided? var hasId = typeof layerId === "string"; var grouped = typeof group === "string"; var stamp = _leaflet2.default.Util.stamp(layer) + ""; // This will be the default layer group to add the layer to. // We may overwrite this let before using it (i.e. if a group is assigned). // This one liner creates the _categoryContainers[category] entry if it // doesn't already exist. var container = this._categoryContainers[category] = this._categoryContainers[category] || _leaflet2.default.layerGroup().addTo(this._map); var oldLayer = null; if (hasId) { // First, remove any layer with the same category and layerId var prefixedLayerId = this._layerIdKey(category, layerId); oldLayer = this._byLayerId[prefixedLayerId]; if (oldLayer) { this._removeLayer(oldLayer); } // Update layerId index this._byLayerId[prefixedLayerId] = layer; } // Update group index if (grouped) { this._byGroup[group] = this._byGroup[group] || {}; this._byGroup[group][stamp] = layer; // Since a group is assigned, don't add the layer to the category's layer // group; instead, use the group's layer group. // This one liner creates the _groupContainers[group] entry if it doesn't // already exist. container = this.getLayerGroup(group, true); } // Update category index this._byCategory[category] = this._byCategory[category] || {}; this._byCategory[category][stamp] = layer; // Update stamp index var layerInfo = this._byStamp[stamp] = { layer: layer, group: group, ctGroup: ctGroup, ctKey: ctKey, layerId: layerId, category: category, container: container, hidden: false }; // Update crosstalk group index if (ctGroup) { if (layer.setStyle) { // Need to save this info so we know what to set opacity to later layer.options.origOpacity = typeof layer.options.opacity !== "undefined" ? layer.options.opacity : 0.5; layer.options.origFillOpacity = typeof layer.options.fillOpacity !== "undefined" ? layer.options.fillOpacity : 0.2; } var ctg = this._byCrosstalkGroup[ctGroup]; if (!ctg) { ctg = this._byCrosstalkGroup[ctGroup] = {}; var crosstalk = global.crosstalk; var handleFilter = function handleFilter(e) { if (!e.value) { var groupKeys = Object.keys(ctg); for (var i = 0; i < groupKeys.length; i++) { var key = groupKeys[i]; var _layerInfo = _this._byStamp[ctg[key]]; _this._setVisibility(_layerInfo, true); } } else { var selectedKeys = {}; for (var _i = 0; _i < e.value.length; _i++) { selectedKeys[e.value[_i]] = true; } var _groupKeys = Object.keys(ctg); for (var _i2 = 0; _i2 < _groupKeys.length; _i2++) { var _key = _groupKeys[_i2]; var _layerInfo2 = _this._byStamp[ctg[_key]]; _this._setVisibility(_layerInfo2, selectedKeys[_groupKeys[_i2]]); } } }; var filterHandle = new crosstalk.FilterHandle(ctGroup); filterHandle.on("change", handleFilter); var handleSelection = function handleSelection(e) { if (!e.value || !e.value.length) { var groupKeys = Object.keys(ctg); for (var i = 0; i < groupKeys.length; i++) { var key = groupKeys[i]; var _layerInfo3 = _this._byStamp[ctg[key]]; _this._setOpacity(_layerInfo3, 1.0); } } else { var selectedKeys = {}; for (var _i3 = 0; _i3 < e.value.length; _i3++) { selectedKeys[e.value[_i3]] = true; } var _groupKeys2 = Object.keys(ctg); for (var _i4 = 0; _i4 < _groupKeys2.length; _i4++) { var _key2 = _groupKeys2[_i4]; var _layerInfo4 = _this._byStamp[ctg[_key2]]; _this._setOpacity(_layerInfo4, selectedKeys[_groupKeys2[_i4]] ? 1.0 : 0.2); } } }; var selHandle = new crosstalk.SelectionHandle(ctGroup); selHandle.on("change", handleSelection); setTimeout(function () { handleFilter({ value: filterHandle.filteredKeys }); handleSelection({ value: selHandle.value }); }, 100); } if (!ctg[ctKey]) ctg[ctKey] = []; ctg[ctKey].push(stamp); } // Add to container if (!layerInfo.hidden) container.addLayer(layer); return oldLayer; } }, { key: "brush", value: function brush(bounds, extraInfo) { var _this2 = this; /* eslint-disable no-console */ // For each Crosstalk group... Object.keys(this._byCrosstalkGroup).forEach(function (ctGroupName) { var ctg = _this2._byCrosstalkGroup[ctGroupName]; var selection = []; // ...iterate over each Crosstalk key (each of which may have multiple // layers)... Object.keys(ctg).forEach(function (ctKey) { // ...and for each layer... ctg[ctKey].forEach(function (stamp) { var layerInfo = _this2._byStamp[stamp]; // ...if it's something with a point... if (layerInfo.layer.getLatLng) { // ... and it's inside the selection bounds... // TODO: Use pixel containment, not lat/lng containment if (bounds.contains(layerInfo.layer.getLatLng())) { // ...add the key to the selection. selection.push(ctKey); } } }); }); new global.crosstalk.SelectionHandle(ctGroupName).set(selection, extraInfo); }); } }, { key: "unbrush", value: function unbrush(extraInfo) { Object.keys(this._byCrosstalkGroup).forEach(function (ctGroupName) { new global.crosstalk.SelectionHandle(ctGroupName).clear(extraInfo); }); } }, { key: "_setVisibility", value: function _setVisibility(layerInfo, visible) { if (layerInfo.hidden ^ visible) { return; } else if (visible) { layerInfo.container.addLayer(layerInfo.layer); layerInfo.hidden = false; } else { layerInfo.container.removeLayer(layerInfo.layer); layerInfo.hidden = true; } } }, { key: "_setOpacity", value: function _setOpacity(layerInfo, opacity) { if (layerInfo.layer.setOpacity) { layerInfo.layer.setOpacity(opacity); } else if (layerInfo.layer.setStyle) { layerInfo.layer.setStyle({ opacity: opacity * layerInfo.layer.options.origOpacity, fillOpacity: opacity * layerInfo.layer.options.origFillOpacity }); } } }, { key: "getLayer", value: function getLayer(category, layerId) { return this._byLayerId[this._layerIdKey(category, layerId)]; } }, { key: "removeLayer", value: function removeLayer(category, layerIds) { var _this3 = this; // Find layer info _jquery2.default.each((0, _util.asArray)(layerIds), function (i, layerId) { var layer = _this3._byLayerId[_this3._layerIdKey(category, layerId)]; if (layer) { _this3._removeLayer(layer); } }); } }, { key: "clearLayers", value: function clearLayers(category) { var _this4 = this; // Find all layers in _byCategory[category] var catTable = this._byCategory[category]; if (!catTable) { return false; } // Remove all layers. Make copy of keys to avoid mutating the collection // behind the iterator you're accessing. var stamps = []; _jquery2.default.each(catTable, function (k, v) { stamps.push(k); }); _jquery2.default.each(stamps, function (i, stamp) { _this4._removeLayer(stamp); }); } }, { key: "getLayerGroup", value: function getLayerGroup(group, ensureExists) { var g = this._groupContainers[group]; if (ensureExists && !g) { this._byGroup[group] = this._byGroup[group] || {}; g = this._groupContainers[group] = _leaflet2.default.featureGroup(); g.groupname = group; g.addTo(this._map); } return g; } }, { key: "getGroupNameFromLayerGroup", value: function getGroupNameFromLayerGroup(layerGroup) { return layerGroup.groupname; } }, { key: "getVisibleGroups", value: function getVisibleGroups() { var _this5 = this; var result = []; _jquery2.default.each(this._groupContainers, function (k, v) { if (_this5._map.hasLayer(v)) { result.push(k); } }); return result; } }, { key: "getAllGroupNames", value: function getAllGroupNames() { var result = []; _jquery2.default.each(this._groupContainers, function (k, v) { result.push(k); }); return result; } }, { key: "clearGroup", value: function clearGroup(group) { var _this6 = this; // Find all layers in _byGroup[group] var groupTable = this._byGroup[group]; if (!groupTable) { return false; } // Remove all layers. Make copy of keys to avoid mutating the collection // behind the iterator you're accessing. var stamps = []; _jquery2.default.each(groupTable, function (k, v) { stamps.push(k); }); _jquery2.default.each(stamps, function (i, stamp) { _this6._removeLayer(stamp); }); } }, { key: "clear", value: function clear() { function clearLayerGroup(key, layerGroup) { layerGroup.clearLayers(); } // Clear all indices and layerGroups this._byGroup = {}; this._byCategory = {}; this._byLayerId = {}; this._byStamp = {}; this._byCrosstalkGroup = {}; _jquery2.default.each(this._categoryContainers, clearLayerGroup); this._categoryContainers = {}; _jquery2.default.each(this._groupContainers, clearLayerGroup); this._groupContainers = {}; } }, { key: "_removeLayer", value: function _removeLayer(layer) { var stamp = void 0; if (typeof layer === "string") { stamp = layer; } else { stamp = _leaflet2.default.Util.stamp(layer); } var layerInfo = this._byStamp[stamp]; if (!layerInfo) { return false; } layerInfo.container.removeLayer(stamp); if (typeof layerInfo.group === "string") { delete this._byGroup[layerInfo.group][stamp]; } if (typeof layerInfo.layerId === "string") { delete this._byLayerId[this._layerIdKey(layerInfo.category, layerInfo.layerId)]; } delete this._byCategory[layerInfo.category][stamp]; delete this._byStamp[stamp]; if (layerInfo.ctGroup) { var ctGroup = this._byCrosstalkGroup[layerInfo.ctGroup]; var layersForKey = ctGroup[layerInfo.ctKey]; var idx = layersForKey ? layersForKey.indexOf(stamp) : -1; if (idx >= 0) { if (layersForKey.length === 1) { delete ctGroup[layerInfo.ctKey]; } else { layersForKey.splice(idx, 1); } } } } }, { key: "_layerIdKey", value: function _layerIdKey(category, layerId) { return category + "\n" + layerId; } }]); return LayerManager; }(); exports.default = LayerManager; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./global/jquery":9,"./global/leaflet":10,"./util":17}],15:[function(require,module,exports){ (function (global){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _jquery = require("./global/jquery"); var _jquery2 = _interopRequireDefault(_jquery); var _leaflet = require("./global/leaflet"); var _leaflet2 = _interopRequireDefault(_leaflet); var _shiny = require("./global/shiny"); var _shiny2 = _interopRequireDefault(_shiny); var _htmlwidgets = require("./global/htmlwidgets"); var _htmlwidgets2 = _interopRequireDefault(_htmlwidgets); var _util = require("./util"); var _crs_utils = require("./crs_utils"); var _dataframe = require("./dataframe"); var _dataframe2 = _interopRequireDefault(_dataframe); var _clusterLayerStore = require("./cluster-layer-store"); var _clusterLayerStore2 = _interopRequireDefault(_clusterLayerStore); var _mipmapper = require("./mipmapper"); var _mipmapper2 = _interopRequireDefault(_mipmapper); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var methods = {}; exports.default = methods; function mouseHandler(mapId, layerId, group, eventName, extraInfo) { return function (e) { if (!_htmlwidgets2.default.shinyMode) return; var latLng = e.target.getLatLng ? e.target.getLatLng() : e.latlng; if (latLng) { // retrieve only lat, lon values to remove prototype // and extra parameters added by 3rd party modules // these objects are for json serialization, not javascript var latLngVal = _leaflet2.default.latLng(latLng); // make sure it has consistent shape latLng = { lat: latLngVal.lat, lng: latLngVal.lng }; } var eventInfo = _jquery2.default.extend({ id: layerId, ".nonce": Math.random() // force reactivity }, group !== null ? { group: group } : null, latLng, extraInfo); _shiny2.default.onInputChange(mapId + "_" + eventName, eventInfo); }; } methods.mouseHandler = mouseHandler; methods.clearGroup = function (group) { var _this = this; _jquery2.default.each((0, _util.asArray)(group), function (i, v) { _this.layerManager.clearGroup(v); }); }; methods.setView = function (center, zoom, options) { this.setView(center, zoom, options); }; methods.fitBounds = function (lat1, lng1, lat2, lng2, options) { this.fitBounds([[lat1, lng1], [lat2, lng2]], options); }; methods.flyTo = function (center, zoom, options) { this.flyTo(center, zoom, options); }; methods.flyToBounds = function (lat1, lng1, lat2, lng2, options) { this.flyToBounds([[lat1, lng1], [lat2, lng2]], options); }; methods.setMaxBounds = function (lat1, lng1, lat2, lng2) { this.setMaxBounds([[lat1, lng1], [lat2, lng2]]); }; methods.addPopups = function (lat, lng, popup, layerId, group, options) { var _this2 = this; var df = new _dataframe2.default().col("lat", lat).col("lng", lng).col("popup", popup).col("layerId", layerId).col("group", group).cbind(options); var _loop = function _loop(i) { if (_jquery2.default.isNumeric(df.get(i, "lat")) && _jquery2.default.isNumeric(df.get(i, "lng"))) { (function () { var popup = _leaflet2.default.popup(df.get(i)).setLatLng([df.get(i, "lat"), df.get(i, "lng")]).setContent(df.get(i, "popup")); var thisId = df.get(i, "layerId"); var thisGroup = df.get(i, "group"); this.layerManager.addLayer(popup, "popup", thisId, thisGroup); }).call(_this2); } }; for (var i = 0; i < df.nrow(); i++) { _loop(i); } }; methods.removePopup = function (layerId) { this.layerManager.removeLayer("popup", layerId); }; methods.clearPopups = function () { this.layerManager.clearLayers("popup"); }; methods.addTiles = function (urlTemplate, layerId, group, options) { this.layerManager.addLayer(_leaflet2.default.tileLayer(urlTemplate, options), "tile", layerId, group); }; methods.removeTiles = function (layerId) { this.layerManager.removeLayer("tile", layerId); }; methods.clearTiles = function () { this.layerManager.clearLayers("tile"); }; methods.addWMSTiles = function (baseUrl, layerId, group, options) { if (options && options.crs) { options.crs = (0, _crs_utils.getCRS)(options.crs); } this.layerManager.addLayer(_leaflet2.default.tileLayer.wms(baseUrl, options), "tile", layerId, group); }; // Given: // {data: ["a", "b", "c"], index: [0, 1, 0, 2]} // returns: // ["a", "b", "a", "c"] function unpackStrings(iconset) { if (!iconset) { return iconset; } if (typeof iconset.index === "undefined") { return iconset; } iconset.data = (0, _util.asArray)(iconset.data); iconset.index = (0, _util.asArray)(iconset.index); return _jquery2.default.map(iconset.index, function (e, i) { return iconset.data[e]; }); } function addMarkers(map, df, group, clusterOptions, clusterId, markerFunc) { (function () { var _this3 = this; var clusterGroup = this.layerManager.getLayer("cluster", clusterId), cluster = clusterOptions !== null; if (cluster && !clusterGroup) { clusterGroup = _leaflet2.default.markerClusterGroup.layerSupport(clusterOptions); if (clusterOptions.freezeAtZoom) { var freezeAtZoom = clusterOptions.freezeAtZoom; delete clusterOptions.freezeAtZoom; clusterGroup.freezeAtZoom(freezeAtZoom); } clusterGroup.clusterLayerStore = new _clusterLayerStore2.default(clusterGroup); } var extraInfo = cluster ? { clusterId: clusterId } : {}; var _loop2 = function _loop2(i) { if (_jquery2.default.isNumeric(df.get(i, "lat")) && _jquery2.default.isNumeric(df.get(i, "lng"))) { (function () { var marker = markerFunc(df, i); var thisId = df.get(i, "layerId"); var thisGroup = cluster ? null : df.get(i, "group"); if (cluster) { clusterGroup.clusterLayerStore.add(marker, thisId); } else { this.layerManager.addLayer(marker, "marker", thisId, thisGroup, df.get(i, "ctGroup", true), df.get(i, "ctKey", true)); } var popup = df.get(i, "popup"); var popupOptions = df.get(i, "popupOptions"); if (popup !== null) { if (popupOptions !== null) { marker.bindPopup(popup, popupOptions); } else { marker.bindPopup(popup); } } var label = df.get(i, "label"); var labelOptions = df.get(i, "labelOptions"); if (label !== null) { if (labelOptions !== null) { if (labelOptions.permanent) { marker.bindTooltip(label, labelOptions).openTooltip(); } else { marker.bindTooltip(label, labelOptions); } } else { marker.bindTooltip(label); } } marker.on("click", mouseHandler(this.id, thisId, thisGroup, "marker_click", extraInfo), this); marker.on("mouseover", mouseHandler(this.id, thisId, thisGroup, "marker_mouseover", extraInfo), this); marker.on("mouseout", mouseHandler(this.id, thisId, thisGroup, "marker_mouseout", extraInfo), this); marker.on("dragend", mouseHandler(this.id, thisId, thisGroup, "marker_dragend", extraInfo), this); }).call(_this3); } }; for (var i = 0; i < df.nrow(); i++) { _loop2(i); } if (cluster) { this.layerManager.addLayer(clusterGroup, "cluster", clusterId, group); } }).call(map); } methods.addGenericMarkers = addMarkers; methods.addMarkers = function (lat, lng, icon, layerId, group, options, popup, popupOptions, clusterOptions, clusterId, label, labelOptions, crosstalkOptions) { var icondf = void 0; var getIcon = void 0; if (icon) { // Unpack icons icon.iconUrl = unpackStrings(icon.iconUrl); icon.iconRetinaUrl = unpackStrings(icon.iconRetinaUrl); icon.shadowUrl = unpackStrings(icon.shadowUrl); icon.shadowRetinaUrl = unpackStrings(icon.shadowRetinaUrl); // This cbinds the icon URLs and any other icon options; they're all // present on the icon object. icondf = new _dataframe2.default().cbind(icon); // Constructs an icon from a specified row of the icon dataframe. getIcon = function getIcon(i) { var opts = icondf.get(i); if (!opts.iconUrl) { return new _leaflet2.default.Icon.Default(); } // Composite options (like points or sizes) are passed from R with each // individual component as its own option. We need to combine them now // into their composite form. if (opts.iconWidth) { opts.iconSize = [opts.iconWidth, opts.iconHeight]; } if (opts.shadowWidth) { opts.shadowSize = [opts.shadowWidth, opts.shadowHeight]; } if (opts.iconAnchorX) { opts.iconAnchor = [opts.iconAnchorX, opts.iconAnchorY]; } if (opts.shadowAnchorX) { opts.shadowAnchor = [opts.shadowAnchorX, opts.shadowAnchorY]; } if (opts.popupAnchorX) { opts.popupAnchor = [opts.popupAnchorX, opts.popupAnchorY]; } return new _leaflet2.default.Icon(opts); }; } if (!(_jquery2.default.isEmptyObject(lat) || _jquery2.default.isEmptyObject(lng)) || _jquery2.default.isNumeric(lat) && _jquery2.default.isNumeric(lng)) { var df = new _dataframe2.default().col("lat", lat).col("lng", lng).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(options).cbind(crosstalkOptions || {}); if (icon) icondf.effectiveLength = df.nrow(); addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) { var options = df.get(i); if (icon) options.icon = getIcon(i); return _leaflet2.default.marker([df.get(i, "lat"), df.get(i, "lng")], options); }); } }; methods.addAwesomeMarkers = function (lat, lng, icon, layerId, group, options, popup, popupOptions, clusterOptions, clusterId, label, labelOptions, crosstalkOptions) { var icondf = void 0; var getIcon = void 0; if (icon) { // This cbinds the icon URLs and any other icon options; they're all // present on the icon object. icondf = new _dataframe2.default().cbind(icon); // Constructs an icon from a specified row of the icon dataframe. getIcon = function getIcon(i) { var opts = icondf.get(i); if (!opts) { return new _leaflet2.default.AwesomeMarkers.icon(); } if (opts.squareMarker) { opts.className = "awesome-marker awesome-marker-square"; } return new _leaflet2.default.AwesomeMarkers.icon(opts); }; } if (!(_jquery2.default.isEmptyObject(lat) || _jquery2.default.isEmptyObject(lng)) || _jquery2.default.isNumeric(lat) && _jquery2.default.isNumeric(lng)) { var df = new _dataframe2.default().col("lat", lat).col("lng", lng).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(options).cbind(crosstalkOptions || {}); if (icon) icondf.effectiveLength = df.nrow(); addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) { var options = df.get(i); if (icon) options.icon = getIcon(i); return _leaflet2.default.marker([df.get(i, "lat"), df.get(i, "lng")], options); }); } }; function addLayers(map, category, df, layerFunc) { var _loop3 = function _loop3(i) { (function () { var layer = layerFunc(df, i); if (!_jquery2.default.isEmptyObject(layer)) { var thisId = df.get(i, "layerId"); var thisGroup = df.get(i, "group"); this.layerManager.addLayer(layer, category, thisId, thisGroup, df.get(i, "ctGroup", true), df.get(i, "ctKey", true)); if (layer.bindPopup) { var popup = df.get(i, "popup"); var popupOptions = df.get(i, "popupOptions"); if (popup !== null) { if (popupOptions !== null) { layer.bindPopup(popup, popupOptions); } else { layer.bindPopup(popup); } } } if (layer.bindTooltip) { var label = df.get(i, "label"); var labelOptions = df.get(i, "labelOptions"); if (label !== null) { if (labelOptions !== null) { layer.bindTooltip(label, labelOptions); } else { layer.bindTooltip(label); } } } layer.on("click", mouseHandler(this.id, thisId, thisGroup, category + "_click"), this); layer.on("mouseover", mouseHandler(this.id, thisId, thisGroup, category + "_mouseover"), this); layer.on("mouseout", mouseHandler(this.id, thisId, thisGroup, category + "_mouseout"), this); var highlightStyle = df.get(i, "highlightOptions"); if (!_jquery2.default.isEmptyObject(highlightStyle)) { var defaultStyle = {}; _jquery2.default.each(highlightStyle, function (k, v) { if (k != "bringToFront" && k != "sendToBack") { if (df.get(i, k)) { defaultStyle[k] = df.get(i, k); } } }); layer.on("mouseover", function (e) { this.setStyle(highlightStyle); if (highlightStyle.bringToFront) { this.bringToFront(); } }); layer.on("mouseout", function (e) { this.setStyle(defaultStyle); if (highlightStyle.sendToBack) { this.bringToBack(); } }); } } }).call(map); }; for (var i = 0; i < df.nrow(); i++) { _loop3(i); } } methods.addGenericLayers = addLayers; methods.addCircles = function (lat, lng, radius, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions, crosstalkOptions) { if (!(_jquery2.default.isEmptyObject(lat) || _jquery2.default.isEmptyObject(lng)) || _jquery2.default.isNumeric(lat) && _jquery2.default.isNumeric(lng)) { var df = new _dataframe2.default().col("lat", lat).col("lng", lng).col("radius", radius).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options).cbind(crosstalkOptions || {}); addLayers(this, "shape", df, function (df, i) { if (_jquery2.default.isNumeric(df.get(i, "lat")) && _jquery2.default.isNumeric(df.get(i, "lng")) && _jquery2.default.isNumeric(df.get(i, "radius"))) { return _leaflet2.default.circle([df.get(i, "lat"), df.get(i, "lng")], df.get(i, "radius"), df.get(i)); } else { return null; } }); } }; methods.addCircleMarkers = function (lat, lng, radius, layerId, group, options, clusterOptions, clusterId, popup, popupOptions, label, labelOptions, crosstalkOptions) { if (!(_jquery2.default.isEmptyObject(lat) || _jquery2.default.isEmptyObject(lng)) || _jquery2.default.isNumeric(lat) && _jquery2.default.isNumeric(lng)) { var df = new _dataframe2.default().col("lat", lat).col("lng", lng).col("radius", radius).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(crosstalkOptions || {}).cbind(options); addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) { return _leaflet2.default.circleMarker([df.get(i, "lat"), df.get(i, "lng")], df.get(i)); }); } }; /* * @param lat Array of arrays of latitude coordinates for polylines * @param lng Array of arrays of longitude coordinates for polylines */ methods.addPolylines = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) { if (polygons.length > 0) { var df = new _dataframe2.default().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options); addLayers(this, "shape", df, function (df, i) { var shapes = df.get(i, "shapes"); shapes = shapes.map(function (shape) { return _htmlwidgets2.default.dataframeToD3(shape[0]); }); if (shapes.length > 1) { return _leaflet2.default.polyline(shapes, df.get(i)); } else { return _leaflet2.default.polyline(shapes[0], df.get(i)); } }); } }; methods.removeMarker = function (layerId) { this.layerManager.removeLayer("marker", layerId); }; methods.clearMarkers = function () { this.layerManager.clearLayers("marker"); }; methods.removeMarkerCluster = function (layerId) { this.layerManager.removeLayer("cluster", layerId); }; methods.removeMarkerFromCluster = function (layerId, clusterId) { var cluster = this.layerManager.getLayer("cluster", clusterId); if (!cluster) return; cluster.clusterLayerStore.remove(layerId); }; methods.clearMarkerClusters = function () { this.layerManager.clearLayers("cluster"); }; methods.removeShape = function (layerId) { this.layerManager.removeLayer("shape", layerId); }; methods.clearShapes = function () { this.layerManager.clearLayers("shape"); }; methods.addRectangles = function (lat1, lng1, lat2, lng2, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) { var df = new _dataframe2.default().col("lat1", lat1).col("lng1", lng1).col("lat2", lat2).col("lng2", lng2).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options); addLayers(this, "shape", df, function (df, i) { if (_jquery2.default.isNumeric(df.get(i, "lat1")) && _jquery2.default.isNumeric(df.get(i, "lng1")) && _jquery2.default.isNumeric(df.get(i, "lat2")) && _jquery2.default.isNumeric(df.get(i, "lng2"))) { return _leaflet2.default.rectangle([[df.get(i, "lat1"), df.get(i, "lng1")], [df.get(i, "lat2"), df.get(i, "lng2")]], df.get(i)); } else { return null; } }); }; /* * @param lat Array of arrays of latitude coordinates for polygons * @param lng Array of arrays of longitude coordinates for polygons */ methods.addPolygons = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) { if (polygons.length > 0) { var df = new _dataframe2.default().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options); addLayers(this, "shape", df, function (df, i) { // This code used to use L.multiPolygon, but that caused // double-click on a multipolygon to fail to zoom in on the // map. Surprisingly, putting all the rings in a single // polygon seems to still work; complicated multipolygons // are still rendered correctly. var shapes = df.get(i, "shapes").map(function (polygon) { return polygon.map(_htmlwidgets2.default.dataframeToD3); }).reduce(function (acc, val) { return acc.concat(val); }, []); return _leaflet2.default.polygon(shapes, df.get(i)); }); } }; methods.addGeoJSON = function (data, layerId, group, style) { // This time, self is actually needed because the callbacks below need // to access both the inner and outer senses of "this" var self = this; if (typeof data === "string") { data = JSON.parse(data); } var globalStyle = _jquery2.default.extend({}, style, data.style || {}); var gjlayer = _leaflet2.default.geoJson(data, { style: function style(feature) { if (feature.style || feature.properties.style) { return _jquery2.default.extend({}, globalStyle, feature.style, feature.properties.style); } else { return globalStyle; } }, onEachFeature: function onEachFeature(feature, layer) { var extraInfo = { featureId: feature.id, properties: feature.properties }; var popup = feature.properties ? feature.properties.popup : null; if (typeof popup !== "undefined" && popup !== null) layer.bindPopup(popup); layer.on("click", mouseHandler(self.id, layerId, group, "geojson_click", extraInfo), this); layer.on("mouseover", mouseHandler(self.id, layerId, group, "geojson_mouseover", extraInfo), this); layer.on("mouseout", mouseHandler(self.id, layerId, group, "geojson_mouseout", extraInfo), this); } }); this.layerManager.addLayer(gjlayer, "geojson", layerId, group); }; methods.removeGeoJSON = function (layerId) { this.layerManager.removeLayer("geojson", layerId); }; methods.clearGeoJSON = function () { this.layerManager.clearLayers("geojson"); }; methods.addTopoJSON = function (data, layerId, group, style) { // This time, self is actually needed because the callbacks below need // to access both the inner and outer senses of "this" var self = this; if (typeof data === "string") { data = JSON.parse(data); } var globalStyle = _jquery2.default.extend({}, style, data.style || {}); var gjlayer = _leaflet2.default.geoJson(null, { style: function style(feature) { if (feature.style || feature.properties.style) { return _jquery2.default.extend({}, globalStyle, feature.style, feature.properties.style); } else { return globalStyle; } }, onEachFeature: function onEachFeature(feature, layer) { var extraInfo = { featureId: feature.id, properties: feature.properties }; var popup = feature.properties.popup; if (typeof popup !== "undefined" && popup !== null) layer.bindPopup(popup); layer.on("click", mouseHandler(self.id, layerId, group, "topojson_click", extraInfo), this); layer.on("mouseover", mouseHandler(self.id, layerId, group, "topojson_mouseover", extraInfo), this); layer.on("mouseout", mouseHandler(self.id, layerId, group, "topojson_mouseout", extraInfo), this); } }); global.omnivore.topojson.parse(data, null, gjlayer); this.layerManager.addLayer(gjlayer, "topojson", layerId, group); }; methods.removeTopoJSON = function (layerId) { this.layerManager.removeLayer("topojson", layerId); }; methods.clearTopoJSON = function () { this.layerManager.clearLayers("topojson"); }; methods.addControl = function (html, position, layerId, classes) { function onAdd(map) { var div = _leaflet2.default.DomUtil.create("div", classes); if (typeof layerId !== "undefined" && layerId !== null) { div.setAttribute("id", layerId); } this._div = div; // It's possible for window.Shiny to be true but Shiny.initializeInputs to // not be, when a static leaflet widget is included as part of the shiny // UI directly (not through leafletOutput or uiOutput). In this case we // don't do the normal Shiny stuff as that will all happen when Shiny // itself loads and binds the entire doc. if (window.Shiny && _shiny2.default.initializeInputs) { _shiny2.default.renderHtml(html, this._div); _shiny2.default.initializeInputs(this._div); _shiny2.default.bindAll(this._div); } else { this._div.innerHTML = html; } return this._div; } function onRemove(map) { if (window.Shiny && _shiny2.default.unbindAll) { _shiny2.default.unbindAll(this._div); } } var Control = _leaflet2.default.Control.extend({ options: { position: position }, onAdd: onAdd, onRemove: onRemove }); this.controls.add(new Control(), layerId, html); }; methods.addCustomControl = function (control, layerId) { this.controls.add(control, layerId); }; methods.removeControl = function (layerId) { this.controls.remove(layerId); }; methods.getControl = function (layerId) { this.controls.get(layerId); }; methods.clearControls = function () { this.controls.clear(); }; methods.addLegend = function (options) { var legend = _leaflet2.default.control({ position: options.position }); var gradSpan = void 0; legend.onAdd = function (map) { var div = _leaflet2.default.DomUtil.create("div", options.className), colors = options.colors, labels = options.labels, legendHTML = ""; if (options.type === "numeric") { // # Formatting constants. var singleBinHeight = 20; // The distance between tick marks, in px var vMargin = 8; // If 1st tick mark starts at top of gradient, how // many extra px are needed for the top half of the // 1st label? (ditto for last tick mark/label) var tickWidth = 4; // How wide should tick marks be, in px? var labelPadding = 6; // How much distance to reserve for tick mark? // (Must be >= tickWidth) // # Derived formatting parameters. // What's the height of a single bin, in percentage (of gradient height)? // It might not just be 1/(n-1), if the gradient extends past the tick // marks (which can be the case for pretty cut points). var singleBinPct = (options.extra.p_n - options.extra.p_1) / (labels.length - 1); // Each bin is `singleBinHeight` high. How tall is the gradient? var totalHeight = 1 / singleBinPct * singleBinHeight + 1; // How far should the first tick be shifted down, relative to the top // of the gradient? var tickOffset = singleBinHeight / singleBinPct * options.extra.p_1; gradSpan = (0, _jquery2.default)("").css({ "background": "linear-gradient(" + colors + ")", "opacity": options.opacity, "height": totalHeight + "px", "width": "18px", "display": "block", "margin-top": vMargin + "px" }); var leftDiv = (0, _jquery2.default)("
    ").css("float", "left"), rightDiv = (0, _jquery2.default)("
    ").css("float", "left"); leftDiv.append(gradSpan); (0, _jquery2.default)(div).append(leftDiv).append(rightDiv).append((0, _jquery2.default)("
    ")); // Have to attach the div to the body at this early point, so that the // svg text getComputedTextLength() actually works, below. document.body.appendChild(div); var ns = "http://www.w3.org/2000/svg"; var svg = document.createElementNS(ns, "svg"); rightDiv.append(svg); var g = document.createElementNS(ns, "g"); (0, _jquery2.default)(g).attr("transform", "translate(0, " + vMargin + ")"); svg.appendChild(g); // max label width needed to set width of svg, and right-justify text var maxLblWidth = 0; // Create tick marks and labels _jquery2.default.each(labels, function (i, label) { var y = tickOffset + i * singleBinHeight + 0.5; var thisLabel = document.createElementNS(ns, "text"); (0, _jquery2.default)(thisLabel).text(labels[i]).attr("y", y).attr("dx", labelPadding).attr("dy", "0.5ex"); g.appendChild(thisLabel); maxLblWidth = Math.max(maxLblWidth, thisLabel.getComputedTextLength()); var thisTick = document.createElementNS(ns, "line"); (0, _jquery2.default)(thisTick).attr("x1", 0).attr("x2", tickWidth).attr("y1", y).attr("y2", y).attr("stroke-width", 1); g.appendChild(thisTick); }); // Now that we know the max label width, we can right-justify (0, _jquery2.default)(svg).find("text").attr("dx", labelPadding + maxLblWidth).attr("text-anchor", "end"); // Final size for (0, _jquery2.default)(svg).css({ width: maxLblWidth + labelPadding + "px", height: totalHeight + vMargin * 2 + "px" }); if (options.na_color && _jquery2.default.inArray(options.na_label, labels) < 0) { (0, _jquery2.default)(div).append("
    " + options.na_label + "
    "); } } else { if (options.na_color && _jquery2.default.inArray(options.na_label, labels) < 0) { colors.push(options.na_color); labels.push(options.na_label); } for (var i = 0; i < colors.length; i++) { legendHTML += " " + labels[i] + "
    "; } div.innerHTML = legendHTML; } if (options.title) (0, _jquery2.default)(div).prepend("
    " + options.title + "
    "); return div; }; if (options.group) { // Auto generate a layerID if not provided if (!options.layerId) { options.layerId = _leaflet2.default.Util.stamp(legend); } var map = this; map.on("overlayadd", function (e) { if (e.name === options.group) { map.controls.add(legend, options.layerId); } }); map.on("overlayremove", function (e) { if (e.name === options.group) { map.controls.remove(options.layerId); } }); map.on("groupadd", function (e) { if (e.name === options.group) { map.controls.add(legend, options.layerId); } }); map.on("groupremove", function (e) { if (e.name === options.group) { map.controls.remove(options.layerId); } }); } this.controls.add(legend, options.layerId); }; methods.addLayersControl = function (baseGroups, overlayGroups, options) { var _this4 = this; // Only allow one layers control at a time methods.removeLayersControl.call(this); var firstLayer = true; var base = {}; _jquery2.default.each((0, _util.asArray)(baseGroups), function (i, g) { var layer = _this4.layerManager.getLayerGroup(g, true); if (layer) { base[g] = layer; // Check if >1 base layers are visible; if so, hide all but the first one if (_this4.hasLayer(layer)) { if (firstLayer) { firstLayer = false; } else { _this4.removeLayer(layer); } } } }); var overlay = {}; _jquery2.default.each((0, _util.asArray)(overlayGroups), function (i, g) { var layer = _this4.layerManager.getLayerGroup(g, true); if (layer) { overlay[g] = layer; } }); this.currentLayersControl = _leaflet2.default.control.layers(base, overlay, options); this.addControl(this.currentLayersControl); }; methods.removeLayersControl = function () { if (this.currentLayersControl) { this.removeControl(this.currentLayersControl); this.currentLayersControl = null; } }; methods.addScaleBar = function (options) { // Only allow one scale bar at a time methods.removeScaleBar.call(this); var scaleBar = _leaflet2.default.control.scale(options).addTo(this); this.currentScaleBar = scaleBar; }; methods.removeScaleBar = function () { if (this.currentScaleBar) { this.currentScaleBar.remove(); this.currentScaleBar = null; } }; methods.hideGroup = function (group) { var _this5 = this; _jquery2.default.each((0, _util.asArray)(group), function (i, g) { var layer = _this5.layerManager.getLayerGroup(g, true); if (layer) { _this5.removeLayer(layer); } }); }; methods.showGroup = function (group) { var _this6 = this; _jquery2.default.each((0, _util.asArray)(group), function (i, g) { var layer = _this6.layerManager.getLayerGroup(g, true); if (layer) { _this6.addLayer(layer); } }); }; function setupShowHideGroupsOnZoom(map) { if (map.leafletr._hasInitializedShowHideGroups) { return; } map.leafletr._hasInitializedShowHideGroups = true; function setVisibility(layer, visible, group) { if (visible !== map.hasLayer(layer)) { if (visible) { map.addLayer(layer); map.fire("groupadd", { "name": group, "layer": layer }); } else { map.removeLayer(layer); map.fire("groupremove", { "name": group, "layer": layer }); } } } function showHideGroupsOnZoom() { if (!map.layerManager) return; var zoom = map.getZoom(); map.layerManager.getAllGroupNames().forEach(function (group) { var layer = map.layerManager.getLayerGroup(group, false); if (layer && typeof layer.zoomLevels !== "undefined") { setVisibility(layer, layer.zoomLevels === true || layer.zoomLevels.indexOf(zoom) >= 0, group); } }); } map.showHideGroupsOnZoom = showHideGroupsOnZoom; map.on("zoomend", showHideGroupsOnZoom); } methods.setGroupOptions = function (group, options) { var _this7 = this; _jquery2.default.each((0, _util.asArray)(group), function (i, g) { var layer = _this7.layerManager.getLayerGroup(g, true); // This slightly tortured check is because 0 is a valid value for zoomLevels if (typeof options.zoomLevels !== "undefined" && options.zoomLevels !== null) { layer.zoomLevels = (0, _util.asArray)(options.zoomLevels); } }); setupShowHideGroupsOnZoom(this); this.showHideGroupsOnZoom(); }; methods.addRasterImage = function (uri, bounds, opacity, attribution, layerId, group) { // uri is a data URI containing an image. We want to paint this image as a // layer at (top-left) bounds[0] to (bottom-right) bounds[1]. // We can't simply use ImageOverlay, as it uses bilinear scaling which looks // awful as you zoom in (and sometimes shifts positions or disappears). // Instead, we'll use a TileLayer.Canvas to draw pieces of the image. // First, some helper functions. // degree2tile converts latitude, longitude, and zoom to x and y tile // numbers. The tile numbers returned can be non-integral, as there's no // reason to expect that the lat/lng inputs are exactly on the border of two // tiles. // // We'll use this to convert the bounds we got from the server, into coords // in tile-space at a given zoom level. Note that once we do the conversion, // we don't to do any more trigonometry to convert between pixel coordinates // and tile coordinates; the source image pixel coords, destination canvas // pixel coords, and tile coords all can be scaled linearly. function degree2tile(lat, lng, zoom) { // See http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames var latRad = lat * Math.PI / 180; var n = Math.pow(2, zoom); var x = (lng + 180) / 360 * n; var y = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * n; return { x: x, y: y }; } // Given a range [from,to) and either one or two numbers, returns true if // there is any overlap between [x,x1) and the range--or if x1 is omitted, // then returns true if x is within [from,to). function overlap(from, to, x, /* optional */x1) { if (arguments.length == 3) x1 = x; return x < to && x1 >= from; } function getCanvasSmoothingProperty(ctx) { var candidates = ["imageSmoothingEnabled", "mozImageSmoothingEnabled", "webkitImageSmoothingEnabled", "msImageSmoothingEnabled"]; for (var i = 0; i < candidates.length; i++) { if (typeof ctx[candidates[i]] !== "undefined") { return candidates[i]; } } return null; } // Our general strategy is to: // 1. Load the data URI in an Image() object, so we can get its pixel // dimensions and the underlying image data. (We could have done this // by not encoding as PNG at all but just send an array of RGBA values // from the server, but that would inflate the JSON too much.) // 2. Create a hidden canvas that we use just to extract the image data // from the Image (using Context2D.getImageData()). // 3. Create a TileLayer.Canvas and add it to the map. // We want to synchronously create and attach the TileLayer.Canvas (so an // immediate call to clearRasters() will be respected, for example), but // Image loads its data asynchronously. Fortunately we can resolve this // by putting TileLayer.Canvas into async mode, which will let us create // and attach the layer but have it wait until the image is loaded before // it actually draws anything. // These are the variables that we will populate once the image is loaded. var imgData = null; // 1d row-major array, four [0-255] integers per pixel var imgDataMipMapper = null; var w = null; // image width in pixels var h = null; // image height in pixels // We'll use this array to store callbacks that need to be invoked once // imgData, w, and h have been resolved. var imgDataCallbacks = []; // Consumers of imgData, w, and h can call this to be notified when data // is available. function getImageData(callback) { if (imgData != null) { // Must not invoke the callback immediately; it's too confusing and // fragile to have a function invoke the callback *either* immediately // or in the future. Better to be consistent here. setTimeout(function () { callback(imgData, w, h, imgDataMipMapper); }, 0); } else { imgDataCallbacks.push(callback); } } var img = new Image(); img.onload = function () { // Save size w = img.width; h = img.height; // Create a dummy canvas to extract the image data var imgDataCanvas = document.createElement("canvas"); imgDataCanvas.width = w; imgDataCanvas.height = h; imgDataCanvas.style.display = "none"; document.body.appendChild(imgDataCanvas); var imgDataCtx = imgDataCanvas.getContext("2d"); imgDataCtx.drawImage(img, 0, 0); // Save the image data. imgData = imgDataCtx.getImageData(0, 0, w, h).data; imgDataMipMapper = new _mipmapper2.default(img); // Done with the canvas, remove it from the page so it can be gc'd. document.body.removeChild(imgDataCanvas); // Alert any getImageData callers who are waiting. for (var i = 0; i < imgDataCallbacks.length; i++) { imgDataCallbacks[i](imgData, w, h, imgDataMipMapper); } imgDataCallbacks = []; }; img.src = uri; var canvasTiles = _leaflet2.default.gridLayer({ opacity: opacity, attribution: attribution, detectRetina: true, async: true }); // NOTE: The done() function MUST NOT be invoked until after the current // tick; done() looks in Leaflet's tile cache for the current tile, and // since it's still being constructed, it won't be found. canvasTiles.createTile = function (tilePoint, done) { var zoom = tilePoint.z; var canvas = _leaflet2.default.DomUtil.create("canvas"); var error = void 0; // setup tile width and height according to the options var size = this.getTileSize(); canvas.width = size.x; canvas.height = size.y; getImageData(function (imgData, w, h, mipmapper) { try { // The Context2D we'll being drawing onto. It's always 256x256. var ctx = canvas.getContext("2d"); // Convert our image data's top-left and bottom-right locations into // x/y tile coordinates. This is essentially doing a spherical mercator // projection, then multiplying by 2^zoom. var topLeft = degree2tile(bounds[0][0], bounds[0][1], zoom); var bottomRight = degree2tile(bounds[1][0], bounds[1][1], zoom); // The size of the image in x/y tile coordinates. var extent = { x: bottomRight.x - topLeft.x, y: bottomRight.y - topLeft.y }; // Short circuit if tile is totally disjoint from image. if (!overlap(tilePoint.x, tilePoint.x + 1, topLeft.x, bottomRight.x)) return; if (!overlap(tilePoint.y, tilePoint.y + 1, topLeft.y, bottomRight.y)) return; // The linear resolution of the tile we're drawing is always 256px per tile unit. // If the linear resolution (in either direction) of the image is less than 256px // per tile unit, then use nearest neighbor; otherwise, use the canvas's built-in // scaling. var imgRes = { x: w / extent.x, y: h / extent.y }; // We can do the actual drawing in one of three ways: // - Call drawImage(). This is easy and fast, and results in smooth // interpolation (bilinear?). This is what we want when we are // reducing the image from its native size. // - Call drawImage() with imageSmoothingEnabled=false. This is easy // and fast and gives us nearest-neighbor interpolation, which is what // we want when enlarging the image. However, it's unsupported on many // browsers (including QtWebkit). // - Do a manual nearest-neighbor interpolation. This is what we'll fall // back to when enlarging, and imageSmoothingEnabled isn't supported. // In theory it's slower, but still pretty fast on my machine, and the // results look the same AFAICT. // Is imageSmoothingEnabled supported? If so, we can let canvas do // nearest-neighbor interpolation for us. var smoothingProperty = getCanvasSmoothingProperty(ctx); if (smoothingProperty || imgRes.x >= 256 && imgRes.y >= 256) { // Use built-in scaling // Turn off anti-aliasing if necessary if (smoothingProperty) { ctx[smoothingProperty] = imgRes.x >= 256 && imgRes.y >= 256; } // Don't necessarily draw with the full-size image; if we're // downscaling, use the mipmapper to get a pre-downscaled image // (see comments on Mipmapper class for why this matters). mipmapper.getBySize(extent.x * 256, extent.y * 256, function (mip) { // It's possible that the image will go off the edge of the canvas-- // that's OK, the canvas should clip appropriately. ctx.drawImage(mip, // Convert abs tile coords to rel tile coords, then *256 to convert // to rel pixel coords (topLeft.x - tilePoint.x) * 256, (topLeft.y - tilePoint.y) * 256, // Always draw the whole thing and let canvas clip; so we can just // convert from size in tile coords straight to pixels extent.x * 256, extent.y * 256); }); } else { // Use manual nearest-neighbor interpolation // Calculate the source image pixel coordinates that correspond with // the top-left and bottom-right of this tile. (If the source image // only partially overlaps the tile, we use max/min to limit the // sourceStart/End to only reflect the overlapping portion.) var sourceStart = { x: Math.max(0, Math.floor((tilePoint.x - topLeft.x) * imgRes.x)), y: Math.max(0, Math.floor((tilePoint.y - topLeft.y) * imgRes.y)) }; var sourceEnd = { x: Math.min(w, Math.ceil((tilePoint.x + 1 - topLeft.x) * imgRes.x)), y: Math.min(h, Math.ceil((tilePoint.y + 1 - topLeft.y) * imgRes.y)) }; // The size, in dest pixels, that each source pixel should occupy. // This might be greater or less than 1 (e.g. if x and y resolution // are very different). var pixelSize = { x: 256 / imgRes.x, y: 256 / imgRes.y }; // For each pixel in the source image that overlaps the tile... for (var row = sourceStart.y; row < sourceEnd.y; row++) { for (var col = sourceStart.x; col < sourceEnd.x; col++) { // ...extract the pixel data... var i = (row * w + col) * 4; var r = imgData[i]; var g = imgData[i + 1]; var b = imgData[i + 2]; var a = imgData[i + 3]; ctx.fillStyle = "rgba(" + [r, g, b, a / 255].join(",") + ")"; // ...calculate the corresponding pixel coord in the dest image // where it should be drawn... var pixelPos = { x: (col / imgRes.x + topLeft.x - tilePoint.x) * 256, y: (row / imgRes.y + topLeft.y - tilePoint.y) * 256 }; // ...and draw a rectangle there. ctx.fillRect(Math.round(pixelPos.x), Math.round(pixelPos.y), // Looks crazy, but this is necessary to prevent rounding from // causing overlap between this rect and its neighbors. The // minuend is the location of the next pixel, while the // subtrahend is the position of the current pixel (to turn an // absolute coordinate to a width/height). Yes, I had to look // up minuend and subtrahend. Math.round(pixelPos.x + pixelSize.x) - Math.round(pixelPos.x), Math.round(pixelPos.y + pixelSize.y) - Math.round(pixelPos.y)); } } } } catch (e) { error = e; } finally { done(error, canvas); } }); return canvas; }; this.layerManager.addLayer(canvasTiles, "image", layerId, group); }; methods.removeImage = function (layerId) { this.layerManager.removeLayer("image", layerId); }; methods.clearImages = function () { this.layerManager.clearLayers("image"); }; methods.addMeasure = function (options) { // if a measureControl already exists, then remove it and // replace with a new one methods.removeMeasure.call(this); this.measureControl = _leaflet2.default.control.measure(options); this.addControl(this.measureControl); }; methods.removeMeasure = function () { if (this.measureControl) { this.removeControl(this.measureControl); this.measureControl = null; } }; methods.addSelect = function (ctGroup) { var _this8 = this; methods.removeSelect.call(this); this._selectButton = _leaflet2.default.easyButton({ states: [{ stateName: "select-inactive", icon: "ion-qr-scanner", title: "Make a selection", onClick: function onClick(btn, map) { btn.state("select-active"); _this8._locationFilter = new _leaflet2.default.LocationFilter2(); if (ctGroup) { var selectionHandle = new global.crosstalk.SelectionHandle(ctGroup); selectionHandle.on("change", function (e) { if (e.sender !== selectionHandle) { if (_this8._locationFilter) { _this8._locationFilter.disable(); btn.state("select-inactive"); } } }); var handler = function handler(e) { _this8.layerManager.brush(_this8._locationFilter.getBounds(), { sender: selectionHandle }); }; _this8._locationFilter.on("enabled", handler); _this8._locationFilter.on("change", handler); _this8._locationFilter.on("disabled", function () { selectionHandle.close(); _this8._locationFilter = null; }); } _this8._locationFilter.addTo(map); } }, { stateName: "select-active", icon: "ion-close-round", title: "Dismiss selection", onClick: function onClick(btn, map) { btn.state("select-inactive"); _this8._locationFilter.disable(); // If explicitly dismissed, clear the crosstalk selections _this8.layerManager.unbrush(); } }] }); this._selectButton.addTo(this); }; methods.removeSelect = function () { if (this._locationFilter) { this._locationFilter.disable(); } if (this._selectButton) { this.removeControl(this._selectButton); this._selectButton = null; } }; methods.createMapPane = function (name, zIndex) { this.createPane(name); this.getPane(name).style.zIndex = zIndex; }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./cluster-layer-store":1,"./crs_utils":3,"./dataframe":4,"./global/htmlwidgets":8,"./global/jquery":9,"./global/leaflet":10,"./global/shiny":12,"./mipmapper":16,"./util":17}],16:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // This class simulates a mipmap, which shrinks images by powers of two. This // stepwise reduction results in "pixel-perfect downscaling" (where every // pixel of the original image has some contribution to the downscaled image) // as opposed to a single-step downscaling which will discard a lot of data // (and with sparse images at small scales can give very surprising results). var Mipmapper = function () { function Mipmapper(img) { _classCallCheck(this, Mipmapper); this._layers = [img]; } // The various functions on this class take a callback function BUT MAY OR MAY // NOT actually behave asynchronously. _createClass(Mipmapper, [{ key: "getBySize", value: function getBySize(desiredWidth, desiredHeight, callback) { var _this = this; var i = 0; var lastImg = this._layers[0]; var testNext = function testNext() { _this.getByIndex(i, function (img) { // If current image is invalid (i.e. too small to be rendered) or // it's smaller than what we wanted, return the last known good image. if (!img || img.width < desiredWidth || img.height < desiredHeight) { callback(lastImg); return; } else { lastImg = img; i++; testNext(); return; } }); }; testNext(); } }, { key: "getByIndex", value: function getByIndex(i, callback) { var _this2 = this; if (this._layers[i]) { callback(this._layers[i]); return; } this.getByIndex(i - 1, function (prevImg) { if (!prevImg) { // prevImg could not be calculated (too small, possibly) callback(null); return; } if (prevImg.width < 2 || prevImg.height < 2) { // Can't reduce this image any further callback(null); return; } // If reduce ever becomes truly asynchronous, we should stuff a promise or // something into this._layers[i] before calling this.reduce(), to prevent // redundant reduce operations from happening. _this2.reduce(prevImg, function (reducedImg) { _this2._layers[i] = reducedImg; callback(reducedImg); return; }); }); } }, { key: "reduce", value: function reduce(img, callback) { var imgDataCanvas = document.createElement("canvas"); imgDataCanvas.width = Math.ceil(img.width / 2); imgDataCanvas.height = Math.ceil(img.height / 2); imgDataCanvas.style.display = "none"; document.body.appendChild(imgDataCanvas); try { var imgDataCtx = imgDataCanvas.getContext("2d"); imgDataCtx.drawImage(img, 0, 0, img.width / 2, img.height / 2); callback(imgDataCanvas); } finally { document.body.removeChild(imgDataCanvas); } } }]); return Mipmapper; }(); exports.default = Mipmapper; },{}],17:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.log = log; exports.recycle = recycle; exports.asArray = asArray; function log(message) { /* eslint-disable no-console */ if (console && console.log) console.log(message); /* eslint-enable no-console */ } function recycle(values, length, inPlace) { if (length === 0 && !inPlace) return []; if (!(values instanceof Array)) { if (inPlace) { throw new Error("Can't do in-place recycling of a non-Array value"); } values = [values]; } if (typeof length === "undefined") length = values.length; var dest = inPlace ? values : []; var origLength = values.length; while (dest.length < length) { dest.push(values[dest.length % origLength]); } if (dest.length > length) { dest.splice(length, dest.length - length); } return dest; } function asArray(value) { if (value instanceof Array) return value;else return [value]; } },{}]},{},[13]); ================================================ FILE: vignettes/tutorials/libs/leafletfix/leafletfix.css ================================================ /* Work around CSS properties introduced on img by bootstrap */ img.leaflet-tile { padding: 0; margin: 0; border-radius: 0; border: none; } .info { padding: 6px 8px; font: 14px/16px Arial, Helvetica, sans-serif; background: white; background: rgba(255,255,255,0.8); box-shadow: 0 0 15px rgba(0,0,0,0.2); border-radius: 5px; } .legend { line-height: 18px; color: #555; } .legend svg text { fill: #555; } .legend svg line { stroke: #555; } .legend i { width: 18px; height: 18px; margin-right: 4px; opacity: 0.7; display: inline-block; vertical-align: top; /*For IE 7*/ zoom: 1; *display: inline; } ================================================ FILE: vignettes/tutorials/libs/remark-css/default-fonts.css ================================================ @import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz); @import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic); @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700); body { font-family: 'Droid Serif', 'Palatino Linotype', 'Book Antiqua', Palatino, 'Microsoft YaHei', 'Songti SC', serif; } h1, h2, h3 { font-family: 'Yanone Kaffeesatz'; font-weight: normal; } .remark-code, .remark-inline-code { font-family: 'Source Code Pro', 'Lucida Console', Monaco, monospace; } ================================================ FILE: vignettes/tutorials/libs/remark-css/default.css ================================================ a, a > code { color: rgb(249, 38, 114); text-decoration: none; } .footnote { position: absolute; bottom: 3em; padding-right: 4em; font-size: 90%; } .remark-code-line-highlighted { background-color: #ffff88; } .inverse { background-color: #272822; color: #d6d6d6; text-shadow: 0 0 20px #333; } .inverse h1, .inverse h2, .inverse h3 { color: #f3f3f3; } /* Two-column layout */ .left-column { color: #777; width: 20%; height: 92%; float: left; } .left-column h2:last-of-type, .left-column h3:last-child { color: #000; } .right-column { width: 75%; float: right; padding-top: 1em; } .pull-left { float: left; width: 47%; } .pull-right { float: right; width: 47%; } .pull-right + * { clear: both; } img, video, iframe { max-width: 100%; } blockquote { border-left: solid 5px lightgray; padding-left: 1em; } .remark-slide table { margin: auto; border-top: 1px solid #666; border-bottom: 1px solid #666; } .remark-slide table thead th { border-bottom: 1px solid #ddd; } th, td { padding: 5px; } .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { background: #eee } @page { margin: 0; } @media print { .remark-slide-scaler { width: 100% !important; height: 100% !important; transform: scale(1) !important; top: 0 !important; left: 0 !important; } } ================================================ FILE: vignettes/tutorials/libs/rstudio_leaflet/rstudio_leaflet.css ================================================ .leaflet-tooltip.leaflet-tooltip-text-only, .leaflet-tooltip.leaflet-tooltip-text-only:before, .leaflet-tooltip.leaflet-tooltip-text-only:after { background: none; border: none; box-shadow: none; } .leaflet-tooltip.leaflet-tooltip-text-only.leaflet-tooltip-left { margin-left: 5px; } .leaflet-tooltip.leaflet-tooltip-text-only.leaflet-tooltip-right { margin-left: -5px; } .leaflet-tooltip:after { border-right: 6px solid transparent; /* right: -16px; */ } .leaflet-popup-pane .leaflet-popup-tip-container { /* when the tooltip container is clicked, it is closed */ pointer-events: all; /* tooltips should display the "hand" icon, just like .leaflet-interactive*/ cursor: pointer; } /* have the widget be displayed in the right 'layer' */ .leaflet-map-pane { z-index: auto; } ================================================ FILE: vignettes/tutorials/rsconnect/documents/visual-introduction.Rmd/rpubs.com/rpubs/Document.dcf ================================================ name: Document title: username: account: rpubs server: rpubs.com hostUrl: rpubs.com appId: https://api.rpubs.com/api/v1/document/568397/6e2cfea2d8974518a125d297212bc2c9 bundleId: https://api.rpubs.com/api/v1/document/568397/6e2cfea2d8974518a125d297212bc2c9 url: http://rpubs.com/publish/claim/568397/c7fe036631f54a90a7ccfbf2b5346502 when: 1579889939.35125 ================================================ FILE: vignettes/tutorials/rsconnect/documents/visual-introduction.Rmd/rpubs.com/rpubs/Publish Document.dcf ================================================ name: Publish Document title: username: account: rpubs server: rpubs.com hostUrl: rpubs.com appId: https://api.rpubs.com/api/v1/document/524972/4358130cc0594724b5a1e67641b2d30e bundleId: https://api.rpubs.com/api/v1/document/524972/4358130cc0594724b5a1e67641b2d30e url: http://rpubs.com/dash2/524972 <<<<<<< HEAD when: 1567609881.87213 ======= when: 1567609742.60495 >>>>>>> 1076cd437de7a4afd95e6b51a393a9141be60f5f ================================================ FILE: vignettes/tutorials/visual-intro-styles.css ================================================ .reveal section img { background: none; border: none; box-shadow: none; } .smallfont section p{ font-size: 0.4em; } ================================================ FILE: vignettes/tutorials/visual-introduction.Rmd ================================================ --- title: "santoku - a visual introduction" author: "David Hugh-Jones" date: "`r Sys.Date()`" output: revealjs::revealjs_presentation: md_extensions: +raw_html transition: none css: visual-intro-styles.css theme: serif reveal_options: slideNumber: true hash: true navigationMode: linear --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = FALSE, fig.height = 4, fig.width = 7) library(santoku) requireNamespace("png", quietly = TRUE) requireNamespace("here", quietly = TRUE) requireNamespace("scales", quietly = TRUE) requireNamespace("plotrix", quietly = TRUE) fish <- png::readPNG(here::here("man", "figures", "fish.png")) fish <- fish[150:800, , ] fish_alpha <- fish[, , 4] fish_red <- fish[, , 1] fish_raster <- as.raster(fish) fish_x <- ncol(fish) fish_y <- nrow(fish) suppressWarnings({ # image is "upside down", so max and min are correct here fish_bottom <- apply(fish_alpha, 2, function (x) max(which(x > 0.8))) fish_top <- apply(fish_alpha, 2, function (x) min(which(x > 0.8))) }) fish_bottom[is.infinite(fish_bottom)] <- NA fish_top[is.infinite(fish_top)] <- NA fish_top <- fish_y - fish_top fish_bottom <- fish_y - fish_bottom pts <- cbind( sample(nrow(fish), 1000, replace = TRUE), round(rbeta(1000, 1, .5) * ncol(fish)) ) pts <- pts[fish_alpha[pts] > 0.9 & fish_red[pts] > 0.7,] # matrix subsetting; points within the fish! pts <- as.data.frame(pts) names(pts) <- c("y", "x") pts <- pts[, c("x", "y")] pts$y <- fish_y - pts$y pts <- rbind(pts, data.frame(x = 500, y = seq(200, 350, 10))) pts <- rbind(pts, data.frame(x = 70, y = 250)) x <- pts$x # done later, for show palette(c("coral4", "darkgreen", "darkred", "darkgoldenrod3", "bisque4", "darkorange3", "darkkhaki", "salmon4")) NA_col <- "grey" line_col <- "black" plot_the_fish <- function (cols = "peachpuff4") { par(mar = c(2, 0, 0, 0)) par(bg = "transparent") plot(c(1, fish_x), c(1, fish_y), type = 'n', axes = FALSE, xlab = "", ylab = "") rasterImage(fish_raster, 1, 1, fish_x, fish_y) cols <- scales::alpha(cols, 0.75) points(pts$x, pts$y, col = cols, pch = 19, cex = 0.5) axis(1, seq(0, fish_x, 100), cex.axis = 0.7) } demo_chop <- function (breaks, labels = lbl_intervals(), extend = NULL, ..., close_end = TRUE, left = TRUE, lines = TRUE, swoosh = FALSE, lty = 1, cex_labels = 0.7, skip_lines = numeric(0)) { chopped <- chop(pts$x, breaks, labels = labels, extend = extend, left = left, close_end = close_end, ...) if (is.numeric(breaks)) breaks <- brk_default(breaks) breaks <- breaks(pts$x, extend = extend, left = left, close_end = close_end) breaks_r <- round(c(breaks[is.finite(breaks)])) breaks_r <- breaks_r[breaks_r > 0 & breaks_r <= fish_x] chopped_cols <- palette()[chopped] chopped_cols[is.na(chopped)] <- NA_col plot_the_fish(cols = chopped_cols) if (is.function(labels)) labels <- labels(breaks) label_breaks <- c(breaks) label_breaks[label_breaks == -Inf] <- 0 label_breaks[label_breaks == Inf] <- fish_x midpoints <- (label_breaks[-1] + label_breaks[-length(label_breaks)])/2 y <- if (any(diff(midpoints) < 150)) 20 else 50 plotrix::boxed.labels(midpoints, rep_len(c(50, y), length(midpoints)), labels, cex = cex_labels, bg = "white", border = FALSE, xpad = 1.1, ypad = 1.5) breaks_r <- setdiff(breaks_r, skip_lines) if (lines) { segments( x0 = breaks_r, y0 = fish_bottom[breaks_r], y1 = fish_top[breaks_r], lwd = 1, lty = 1, col = if (swoosh) "black" else line_col ) segments( x0 = breaks_r, y0 = 65, y1 = 800, lwd = 1, lty = 2, col = if (swoosh) "black" else line_col ) if (swoosh) { left <- attr(breaks, "left") left <- left[is.finite(c(breaks)) & breaks > 0 & breaks <= fish_x] cols <- palette()[seq_len(nlevels(chopped) )] if (! is.null(extend) && ! extend) cols <- c(NA_col, cols, NA_col) # there are n colours, one per interval and `length(left)` lines. # hopefully n = length(left) - 1 # left-closed lines get the colour of their right interval line_col <- seq_along(left) line_col[!left] <- line_col[!left] - 1 # assuming the first line is left-closed and the last line is right-closed col <- cols[line_col] for (smooth in 1:5) { alpha <- 0.5 - 0.1 * smooth segments( x0 = breaks_r + 4 * smooth * ifelse(left, 1, -1), y0 = fish_bottom[breaks_r], y1 = fish_top[breaks_r], lwd = 4, col = scales::alpha(col, alpha) ) } } } } ``` ## Santoku A Japanese kitchen knife. ![chopping skills](https://media.giphy.com/media/ywSHsUF8TSdXHvDydU/giphy.gif) ## {santoku} An R package for cutting data. ![santoku logo](../../man/figures/logo.png) ## Some data ```{r, echo = TRUE} head(pts) x <- pts$x ``` ## Some data ```{r, echo = TRUE} plot_the_fish() ``` ## `chop()` `chop()` is a replacement for base R's `cut()` function. ## `chop()` ```{r, echo = TRUE, eval = FALSE} chop(x, c(300, 600, 900)) ``` ```{r} demo_chop(c(300, 600, 900)) ``` ## `extend = FALSE` ```{r, echo = TRUE, eval = FALSE} chop(x, c(300, 600, 900), extend = FALSE) ``` ```{r} demo_chop(c(300, 600, 900), extend = FALSE) legend("bottomright", "NA", col = NA_col, pch = 19, bty = "n", cex = 2) ``` ## Chopping by a single value ```{r, echo = TRUE, eval = FALSE} chop(x, c(300, 500, 500, 800)) ``` ```{r} demo_chop(c(300, 500, 500, 800), skip_lines = 500) #arrows(x0 = 500, y0 =600, y1 = fish_top[500] + 40, lwd = 2, length = 0.1) segments(x0 = c(495, 505), y0 = 65, y1 = 800, lty = 2, col = line_col) segments(x0 = c(495, 505), y0 = fish_bottom[500], y1 = fish_top[500], lty = 1, col = line_col) ``` ## `chop_width()` Chops fixed-width intervals ```{r, echo = TRUE, eval = FALSE} chop_width(x, width = 200) ``` ```{r} demo_chop(brk_width(width = 200)) ``` ## `chop_evenly()` Chops `intervals` equal-width intervals ```{r, echo = TRUE, eval = FALSE} chop_evenly(x, intervals = 5) ``` ```{r} demo_chop(brk_evenly(intervals = 5)) ``` ## `chop_proportions()` Chops intervals by proportions of the data range ```{r, echo = TRUE, eval = FALSE} chop_proportions(x, proportions = c(0.2, 0.8)) ``` ```{r} demo_chop(brk_proportions(proportions = c(0.2, 0.8))) ``` ## `chop_equally()` Chops intervals with an equal *number of elements* ```{r, echo = TRUE, eval = FALSE} chop_equally(x, groups = 5) ``` ```{r} demo_chop(brk_quantiles(1:4/5)) ``` ## `chop_n()` Chops intervals with a *fixed* number of elements ```{r, echo = TRUE, eval = FALSE} chop_n(x, 50) ``` ```{r} # close_end avoids a singleton interval at the end demo_chop(brk_n(50), close_end = TRUE) ``` ## `chop_quantiles()` ```{r, echo = TRUE, eval = FALSE} chop_quantiles(x, c(0.2, 0.8)) ``` ```{r} demo_chop(brk_quantiles(c(0.2, 0.8))) ``` ## Summary Chop by: / Size means: | number of elements | width ------------------------|--------------------|---------------- Fixed size | `chop_n()` | `chop_width()` Fixed no. of groups | `chop_equally()` | `chop_evenly()` Specific sizes | `chop_quantiles()` | `chop_proportions()` ## `chop_mean_sd()` ```{r, echo = TRUE, eval = FALSE} chop_mean_sd(x) ``` ```{r} demo_chop(brk_mean_sd()) ``` ## Quick tables {.smallfont} ```{r, echo = TRUE} tab(x, c(300, 600, 900)) tab_mean_sd(x) ``` ## Changing labels You need one more labels than breaks: ```{r, echo = TRUE, eval = FALSE} chop(x, c(300, 600, 900), labels = LETTERS[1:4]) ``` ```{r} demo_chop(c(300, 600, 900), labels = LETTERS[1:4], cex_labels = 1) ``` ## Changing labels Not sure how many intervals you will have? Use a `lbl_*` function. ```{r, echo = TRUE, eval = FALSE} chop_width(x, 200, labels = lbl_seq()) ``` ```{r} demo_chop(brk_width(200), labels = lbl_seq(), cex_labels = 1) ``` ## Changing labels Not sure how many intervals you will have? Use a `lbl_*` function. ```{r, echo = TRUE, eval = FALSE} chop_width(x, 200, labels = lbl_seq("(i)")) ``` ```{r} demo_chop(brk_width(200), labels = lbl_seq("(i)"), cex_labels = 1) ``` ## Changing labels Not sure how many intervals you will have? Use a `lbl_*` function. ```{r, echo = TRUE, eval = FALSE} chop_width(x, 200, labels = lbl_dash()) ``` ```{r} demo_chop(brk_width(200), labels = lbl_dash(), cex_labels = 0.8) ``` ## Left-closed and right-closed Breaks are closed on the left by default. ```{r, echo = TRUE, eval = FALSE} chop(x, c(200, 500, 800)) ``` ```{r} demo_chop(c(200, 500, 800), left = TRUE, swoosh = TRUE) ``` ## Left-closed and right-closed For right-closed breaks use `left = FALSE`: ```{r, echo = TRUE, eval = FALSE} chop(x, c(200, 500, 800), left = FALSE) ``` ```{r} demo_chop(c(200, 500, 800), left = FALSE, swoosh = TRUE) ``` ## Errors ![chopping fail](https://media.giphy.com/media/2zom27Xqp7PVe/giphy.gif) ## Errors
    Sometimes it's impossible to create the breaks you want. ```{r, echo = TRUE} chop_quantiles(c(-Inf, Inf), c(0.25, 0.75)) ``` When the problem comes from the data (`x`), santoku will try to carry on (e.g. by returning a single interval). When the problem comes from other parameters, e.g. `breaks` or `extend`, santoku will give an error. ```{r, echo = TRUE, error = TRUE} chop_quantiles(1:5, c(0.25, NA)) ```
    ## Happy chopping! https://hughjonesd.github.io/santoku ```{r, echo = TRUE, eval = FALSE} devtools::install_github("hughjonesd/santoku") ``` ![Chopping](https://media.giphy.com/media/ceQ6oQNVCVbgY/giphy.gif) ================================================ FILE: vignettes/website-articles/performance.Rmd ================================================ --- title: "Performance" author: "David Hugh-Jones" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Performance} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", cache = FALSE ) requireNamespace("bench", quietly = TRUE) requireNamespace("Hmisc", quietly = TRUE) library(santoku) library(ggplot2) theme_set(theme_light()) ``` ## Speed The core of santoku is written in C++. It is reasonably fast: ```{r basic} packageVersion("santoku") set.seed(27101975) mb <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e5), -2:2), base::cut(rnorm(1e5), -2:2), Hmisc::cut2(rnorm(1e5), -2:2) ) mb ``` ```{r, fig.width = 7, fig.height = 6} autoplot(mb, type = "violin") ``` ## Many breaks ```{r many-breaks} many_breaks <- seq(-2, 2, 0.001) mb_breaks <- bench::mark(min_iterations = 100, check = FALSE, santoku::chop(rnorm(1e4), many_breaks), base::cut(rnorm(1e4), many_breaks), Hmisc::cut2(rnorm(1e4), many_breaks) ) mb_breaks ``` ```{r, fig.width = 7, fig.height = 6} autoplot(mb_breaks, type = "violin") ``` ## Various chops ```{r various-chops} x <- c(rnorm(9e4), sample(-2:2, 1e4, replace = TRUE)) mb_various <- bench::mark(min_iterations = 100, check = FALSE, chop(x, -2:2), chop_equally(x, groups = 20), chop_n(x, n = 2e4), chop_quantiles(x, c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_evenly(x, intervals = 20), chop_width(x, width = 0.25), chop_proportions(x, proportions = c(0.05, 0.25, 0.5, 0.75, 0.95)), chop_mean_sd(x, sds = 1:4), chop_fn(x, scales::breaks_extended(10)), chop_pretty(x, n = 10), chop_spikes(x, -2:2, prop = 0.01), dissect(x, -2:2, prop = 0.01) ) mb_various ``` ```{r, fig.width = 7, fig.height = 6} autoplot(mb_various, type = "violin") ``` ================================================ FILE: vignettes/website-articles/performance_cache/html/__packages ================================================ base ================================================ FILE: vignettes/whats-new-in-0-9-0.Rmd ================================================ --- title: "What's new in santoku 0.9.0" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{What's new in santoku 0.9.0} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) set.seed(42) ``` Santoku 0.9.0 has a few changes. ## You can use break names for labels On the command line, sometimes you'd like to quickly add labels to your breaks. Now, you can do this simply by adding names to the `breaks` vector: ```{r} library(santoku) chop(1:5, c(1,3,5)) chop(1:5, c(Low = 1, High = 3, 5)) ``` Break names override the `labels` argument, but you can still use this for unnamed breaks: ```{r} ages <- sample(12:80, 20) tab(ages, c("Under 16" = 0, 16, 25, 35, 45, 55, "65 and over" = 65), labels = lbl_discrete() ) ``` Names can also be used for labels in `chop_quantiles()` and `chop_proportions()`: ```{r} x <- rnorm(10) chopped <- chop_quantiles(x, c("Lower tail" = 0, 0.025, "Upper tail" = 0.975) ) data.frame(x, chopped) ``` This feature is experimental for now. ## `close_end` works differently The `close_end` parameter is used to right-close the last break. This used to be applied before breaks were extended to cover items beyond the explicitly given breaks. We think this was confusing for users. So now, `close_end` is applied only after the breaks have been extended - i.e. to the very last break. In 0.8.0: ```r chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3] [2, 3] (3, 4] #> Levels: [1, 2) [2, 3] (3, 4] ``` Notice how the central break `[2, 3]` is right-closed. (The extended break `[3, 4]` is right-closed too, because extended breaks are always closed at the "outer" end.) In 0.9.0: ```r chop(1:4, 2:3, close_end = TRUE) #> [1] [1, 2) [2, 3) [3, 4] [3, 4] #> Levels: [1, 2) [2, 3) [3, 4] ``` Now, `close_end` is applied to the final, extended break `[3, 4]`, not to the explicit break `[2, 3)`. ## `close_end` is `TRUE` by default We think that for exploratory work, users typically want to include all the data between the lowest and highest break, inclusive. So, `close_end` is now `TRUE` by default. In 0.8.0: ```r chop(1:3, 2:3) #> [1] [1, 2) [2, 3) {3} #> Levels: [1, 2) [2, 3) {3} ``` In 0.9.0: ```r chop(1:3, 2:3) #> [1] [1, 2) [2, 3] [2, 3] #> Levels: [1, 2) [2, 3] ``` ## New `raw` parameter for `chop()` `lbl_*` functions have a `raw` parameter to use the raw interval endpoints in labels, rather than e.g. percentiles or standard deviations. We've moved this into the main `chop()` function. This makes it easier to use: ```{r} chop_mean_sd(x) chop_mean_sd(x, raw = TRUE) ``` The `raw` parameter to `lbl_*` functions is deprecated. ## Other changes The NEWS file lists other changes, including a new `chop_fn()` function which creates breaks using any arbitrary function. ## Feedback We expect this to be the last release before 1.0, when we'll stabilize the interface and move santoku from "experimental" to "stable". So, if you have problems or suggestions regarding any of these changes, please [file an issue](https://github.com/hughjonesd/santoku/issues).