diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ab2dd4ce..06e643bdd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ parameters: defaults: &defaults resource_class: large docker: - - image: bepsays/ci-hugoreleaser:1.22300.20200 + - image: bepsays/ci-hugoreleaser:1.22400.20000 environment: &buildenv GOMODCACHE: /root/project/gomodcache version: 2 @@ -58,7 +58,7 @@ jobs: environment: <<: [*buildenv] docker: - - image: bepsays/ci-hugoreleaser-linux-arm64:1.22300.20200 + - image: bepsays/ci-hugoreleaser-linux-arm64:1.22400.20000 steps: - *restore-cache - &attach-workspace diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 95a4ad0b6..c4f3c34c3 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -46,3 +46,4 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: HUGO_BUILD_TAGS=extended,withdeploy \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b513863c5..c49c12371 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,17 +6,17 @@ name: Test env: GOPROXY: https://proxy.golang.org GO111MODULE: on - SASS_VERSION: 1.63.2 - DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af - DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0 - DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64 + SASS_VERSION: 1.80.3 + DART_SASS_SHA_LINUX: 7c933edbad0a7d389192c5b79393485c088bd2c4398e32f5754c32af006a9ffd + DART_SASS_SHA_MACOS: 79e060b0e131c3bb3c16926bafc371dc33feab122bfa8c01aa337a072097967b + DART_SASS_SHA_WINDOWS: 0bc4708b37cd1bac4740e83ac5e3176e66b774f77fd5dd364da5b5cfc9bfb469 permissions: contents: read jobs: test: strategy: matrix: - go-version: [1.22.x, 1.23.x] + go-version: [1.23.x, 1.24.x] os: [ubuntu-latest, windows-latest] # macos disabled for now because of disk space issues. runs-on: ${{ matrix.os }} steps: @@ -112,17 +112,17 @@ jobs: sass --version; mage -v check; env: - HUGO_BUILD_TAGS: extended + HUGO_BUILD_TAGS: extended,withdeploy - if: matrix.os == 'windows-latest' # See issue #11052. We limit the build to regular test (no -race flag) on Windows for now. name: Test run: | mage -v test; env: - HUGO_BUILD_TAGS: extended + HUGO_BUILD_TAGS: extended,withdeploy - name: Build tags run: | - go install -tags extended,nodeploy + go install -tags extended - if: matrix.os == 'ubuntu-latest' name: Build for dragonfly run: | diff --git a/.gitignore b/.gitignore index b170fe204..ddad69611 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.test -imports.* \ No newline at end of file +imports.* +dist/ +public/ +.DS_Store \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b5666963..ddd3efcf2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ ->**Note:** We would apprecitate if you hold on with any big refactorings (like renaming deprecated Go packages), mainly because of potential for extra merge work for future coming in in the near future. +>**Note:** We would appreciate if you hold on with any big refactoring (like renaming deprecated Go packages), mainly because of potential for extra merge work for future coming in in the near future. # Contributing to Hugo @@ -93,6 +93,7 @@ Most title/subjects should have a lower-cased prefix with a colon and one whites * If this commit touches many packages without a common functional topic, prefix with `all:` (e.g. `all: Reformat Go code`) * If this is a documentation update, prefix with `docs:`. * If nothing of the above applies, just leave the prefix out. +* Note that the above excludes nouns seen in other repositories, e.g. "chore:". Also, if your commit references one or more GitHub issues, always end your commit message body with *See #1234* or *Fixes #1234*. Replace *1234* with the GitHub issue ID. The last example will close the issue when the commit is merged into *master*. diff --git a/Dockerfile b/Dockerfile index 65c4dbfb9..a0e34353f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,8 @@ # Twitter: https://twitter.com/gohugoio # Website: https://gohugo.io/ -ARG GO_VERSION="1.23.2" -ARG ALPINE_VERSION="3.20" +ARG GO_VERSION="1.24" +ARG ALPINE_VERSION="3.22" ARG DART_SASS_VERSION="1.79.3" FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx @@ -19,10 +19,10 @@ RUN apk add clang lld COPY --from=xx / / ARG TARGETPLATFORM -RUN xx-apk add musl-dev gcc g++ +RUN xx-apk add musl-dev gcc g++ -# Optionally set HUGO_BUILD_TAGS to "none" or "nodeploy" when building like so: -# docker build --build-arg HUGO_BUILD_TAGS=nodeploy . +# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so: +# docker build --build-arg HUGO_BUILD_TAGS=withdeploy . # # We build the extended version by default. ARG HUGO_BUILD_TAGS="extended" @@ -72,7 +72,7 @@ RUN mkdir -p /var/hugo/bin /cache && \ adduser -Sg hugo -u 1000 -h /var/hugo hugo && \ chown -R hugo: /var/hugo /cache && \ # For the Hugo's Git integration to work. - runuser -u hugo -- git config --global --add safe.directory /project && \ + runuser -u hugo -- git config --global --add safe.directory /project && \ # See https://github.com/gohugoio/hugo/issues/9810 runuser -u hugo -- git config --global core.quotepath false diff --git a/README.md b/README.md index b9ad14d01..9befa9c9d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ A fast and flexible static site generator built with love by [bep], [spf13], and [![Tests on Linux, MacOS and Windows](https://github.com/gohugoio/hugo/workflows/Test/badge.svg)](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest) [![Go Report Card](https://goreportcard.com/badge/github.com/gohugoio/hugo)](https://goreportcard.com/report/github.com/gohugoio/hugo) -[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | Mastodon | X +[Website] | [Installation] | [Documentation] | [Support] | [Contributing] | Mastodon ## Overview @@ -65,11 +65,30 @@ See the [features] section of the documentation for a comprehensive summary of H

 

- Linode + Linode     - Route Planning & Route Optimization Software + The complete IDE crafted for professional Go developers. +     + PinMe.

+## Editions + +Hugo is available in three editions: standard, extended, and extended/deploy. While the standard edition provides core functionality, the extended and extended/deploy editions offer advanced features. + +Feature|extended edition|extended/deploy edition +:--|:-:|:-: +Encode to the WebP format when [processing images]. You can decode WebP images with any edition.|:heavy_check_mark:|:heavy_check_mark: +[Transpile Sass to CSS] using the embedded LibSass transpiler. You can use the [Dart Sass] transpiler with any edition.|:heavy_check_mark:|:heavy_check_mark: +Deploy your site directly to a Google Cloud Storage bucket, an AWS S3 bucket, or an Azure Storage container. See [details].|:x:|:heavy_check_mark: + +[dart sass]: https://gohugo.io/functions/css/sass/#dart-sass +[processing images]: https://gohugo.io/content-management/image-processing/ +[transpile sass to css]: https://gohugo.io/functions/css/sass/ +[details]: https://gohugo.io/hosting-and-deployment/hugo-deploy/ + +Unless your specific deployment needs require the extended/deploy edition, we recommend the extended edition. + ## Installation Install Hugo from a [prebuilt binary], package manager, or package repository. Please see the installation instructions for your operating system: @@ -81,15 +100,11 @@ Install Hugo from a [prebuilt binary], package manager, or package repository. P ## Build from source -Hugo is available in two editions: standard and extended. With the extended edition you can: - -- Encode to the WebP format when processing images. You can decode WebP images with either edition. -- Transpile Sass to CSS using the embedded LibSass transpiler. The extended edition is not required to use the Dart Sass transpiler. - Prerequisites to build Hugo from source: -- Standard edition: Go 1.20 or later -- Extended edition: Go 1.20 or later, and GCC +- Standard edition: Go 1.23.0 or later +- Extended edition: Go 1.23.0 or later, and GCC +- Extended/deploy edition: Go 1.23.0 or later, and GCC Build the standard edition: @@ -102,6 +117,13 @@ Build the extended edition: ```text CGO_ENABLED=1 go install -tags extended github.com/gohugoio/hugo@latest ``` + +Build the extended/deploy edition: + +```text +CGO_ENABLED=1 go install -tags extended,withdeploy github.com/gohugoio/hugo@latest +``` + ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=gohugoio/hugo&type=Timeline)](https://star-history.com/#gohugoio/hugo&Timeline) @@ -148,153 +170,113 @@ Hugo stands on the shoulders of great open source libraries. Run `hugo env --log See current dependencies ```text -cloud.google.com/go/compute/metadata="v0.2.3" -cloud.google.com/go/iam="v1.1.5" -cloud.google.com/go/storage="v1.35.1" -cloud.google.com/go="v0.110.10" -github.com/Azure/azure-sdk-for-go/sdk/azcore="v1.9.0" -github.com/Azure/azure-sdk-for-go/sdk/azidentity="v1.4.0" -github.com/Azure/azure-sdk-for-go/sdk/internal="v1.5.0" -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob="v1.2.0" -github.com/Azure/go-autorest/autorest/to="v0.4.0" -github.com/AzureAD/microsoft-authentication-library-for-go="v1.2.0" github.com/BurntSushi/locker="v0.0.0-20171006230638-a6e239ea1c69" -github.com/alecthomas/chroma/v2="v2.14.0" +github.com/PuerkitoBio/goquery="v1.10.1" +github.com/alecthomas/chroma/v2="v2.15.0" +github.com/andybalholm/cascadia="v1.3.3" github.com/armon/go-radix="v1.0.1-0.20221118154546-54df44f2176c" -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream="v1.5.4" -github.com/aws/aws-sdk-go-v2/config="v1.26.1" -github.com/aws/aws-sdk-go-v2/credentials="v1.16.12" -github.com/aws/aws-sdk-go-v2/feature/ec2/imds="v1.14.10" -github.com/aws/aws-sdk-go-v2/feature/s3/manager="v1.15.7" -github.com/aws/aws-sdk-go-v2/internal/configsources="v1.3.5" -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2="v2.6.5" -github.com/aws/aws-sdk-go-v2/internal/ini="v1.7.2" -github.com/aws/aws-sdk-go-v2/internal/v4a="v1.2.9" -github.com/aws/aws-sdk-go-v2/service/cloudfront="v1.35.4" -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding="v1.10.4" -github.com/aws/aws-sdk-go-v2/service/internal/checksum="v1.2.9" -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url="v1.10.9" -github.com/aws/aws-sdk-go-v2/service/internal/s3shared="v1.16.9" -github.com/aws/aws-sdk-go-v2/service/s3="v1.47.5" -github.com/aws/aws-sdk-go-v2/service/sso="v1.18.5" -github.com/aws/aws-sdk-go-v2/service/ssooidc="v1.21.5" -github.com/aws/aws-sdk-go-v2/service/sts="v1.26.5" -github.com/aws/aws-sdk-go-v2="v1.26.1" -github.com/aws/aws-sdk-go="v1.50.7" -github.com/aws/smithy-go="v1.20.2" github.com/bep/clocks="v0.5.0" github.com/bep/debounce="v1.2.0" -github.com/bep/gitmap="v1.1.2" +github.com/bep/gitmap="v1.6.0" github.com/bep/goat="v0.5.0" -github.com/bep/godartsass/v2="v2.0.0" -github.com/bep/godartsass="v1.2.0" -github.com/bep/golibsass="v1.1.1" +github.com/bep/godartsass/v2="v2.3.2" +github.com/bep/golibsass="v1.2.0" github.com/bep/gowebp="v0.3.0" -github.com/bep/lazycache="v0.4.0" +github.com/bep/imagemeta="v0.8.4" +github.com/bep/lazycache="v0.7.0" github.com/bep/logg="v0.4.0" github.com/bep/mclib="v1.20400.20402" github.com/bep/overlayfs="v0.9.2" -github.com/bep/simplecobra="v0.4.0" +github.com/bep/simplecobra="v0.5.0" github.com/bep/tmc="v0.5.1" +github.com/cespare/xxhash/v2="v2.3.0" github.com/clbanning/mxj/v2="v2.7.0" -github.com/cli/safeexec="v1.0.1" -github.com/cpuguy83/go-md2man/v2="v2.0.3" +github.com/cpuguy83/go-md2man/v2="v2.0.4" github.com/disintegration/gift="v1.2.1" -github.com/dlclark/regexp2="v1.11.0" -github.com/dustin/go-humanize="v1.0.1" -github.com/evanw/esbuild="v0.21.4" -github.com/fatih/color="v1.16.0" +github.com/dlclark/regexp2="v1.11.5" +github.com/dop251/goja="v0.0.0-20250125213203-5ef83b82af17" +github.com/evanw/esbuild="v0.24.2" +github.com/fatih/color="v1.18.0" github.com/frankban/quicktest="v1.14.6" -github.com/fsnotify/fsnotify="v1.7.0" -github.com/getkin/kin-openapi="v0.123.0" +github.com/fsnotify/fsnotify="v1.8.0" +github.com/getkin/kin-openapi="v0.129.0" github.com/ghodss/yaml="v1.0.0" -github.com/go-openapi/jsonpointer="v0.20.2" -github.com/go-openapi/swag="v0.22.8" -github.com/gobuffalo/flect="v1.0.2" +github.com/go-openapi/jsonpointer="v0.21.0" +github.com/go-openapi/swag="v0.23.0" +github.com/go-sourcemap/sourcemap="v2.1.4+incompatible" +github.com/gobuffalo/flect="v1.0.3" github.com/gobwas/glob="v0.2.3" github.com/gohugoio/go-i18n/v2="v2.1.3-0.20230805085216-e63c13218d0e" +github.com/gohugoio/hashstructure="v0.5.0" github.com/gohugoio/httpcache="v0.7.0" github.com/gohugoio/hugo-goldmark-extensions/extras="v0.2.0" -github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.2.0" +github.com/gohugoio/hugo-goldmark-extensions/passthrough="v0.3.0" github.com/gohugoio/locales="v0.14.0" github.com/gohugoio/localescompressed="v1.0.1" -github.com/golang-jwt/jwt/v5="v5.1.0" -github.com/golang/groupcache="v0.0.0-20210331224755-41bb18bfe9da" -github.com/golang/protobuf="v1.5.3" +github.com/golang/freetype="v0.0.0-20170609003504-e2365dfdc4a0" github.com/google/go-cmp="v0.6.0" -github.com/google/s2a-go="v0.1.7" -github.com/google/uuid="v1.4.0" -github.com/google/wire="v0.5.0" -github.com/googleapis/enterprise-certificate-proxy="v0.3.2" -github.com/googleapis/gax-go/v2="v2.12.0" -github.com/gorilla/websocket="v1.5.1" -github.com/hairyhenderson/go-codeowners="v0.4.0" +github.com/google/pprof="v0.0.0-20250208200701-d0013a598941" +github.com/gorilla/websocket="v1.5.3" +github.com/hairyhenderson/go-codeowners="v0.7.0" github.com/hashicorp/golang-lru/v2="v2.0.7" -github.com/invopop/yaml="v0.2.0" github.com/jdkato/prose="v1.2.1" -github.com/jmespath/go-jmespath="v0.4.0" github.com/josharian/intern="v1.0.0" github.com/kr/pretty="v0.3.1" github.com/kr/text="v0.2.0" -github.com/kylelemons/godebug="v1.1.0" -github.com/kyokomi/emoji/v2="v2.2.12" +github.com/kyokomi/emoji/v2="v2.2.13" +github.com/lucasb-eyer/go-colorful="v1.2.0" github.com/mailru/easyjson="v0.7.7" github.com/makeworld-the-better-one/dither/v2="v2.4.0" github.com/marekm4/color-extractor="v1.2.1" github.com/mattn/go-colorable="v0.1.13" github.com/mattn/go-isatty="v0.0.20" github.com/mattn/go-runewidth="v0.0.9" -github.com/mitchellh/hashstructure="v1.1.0" +github.com/mazznoer/csscolorparser="v0.1.5" github.com/mitchellh/mapstructure="v1.5.1-0.20231216201459-8508981c8b6c" github.com/mohae/deepcopy="v0.0.0-20170929034955-c48cc78d4826" github.com/muesli/smartcrop="v0.3.0" github.com/niklasfasching/go-org="v1.7.0" +github.com/oasdiff/yaml3="v0.0.0-20241210130736-a94c01f36349" +github.com/oasdiff/yaml="v0.0.0-20241210131133-6b86fb107d80" github.com/olekukonko/tablewriter="v0.0.5" github.com/pbnjay/memory="v0.0.0-20210728143218-7b4eea64cf58" -github.com/pelletier/go-toml/v2="v2.2.2" +github.com/pelletier/go-toml/v2="v2.2.3" github.com/perimeterx/marshmallow="v1.1.5" -github.com/pkg/browser="v0.0.0-20210911075715-681adbf594b8" +github.com/pkg/browser="v0.0.0-20240102092130-5ac0b6a4141c" github.com/pkg/errors="v0.9.1" -github.com/rogpeppe/go-internal="v1.12.0" +github.com/rivo/uniseg="v0.4.7" +github.com/rogpeppe/go-internal="v1.13.1" github.com/russross/blackfriday/v2="v2.1.0" -github.com/rwcarlsen/goexif="v0.0.0-20190401172101-9e8deecbddbd" -github.com/sass/dart-sass/compiler="1.77.5" -github.com/sass/dart-sass/implementation="1.77.5" -github.com/sass/dart-sass/protocol="2.7.1" -github.com/sass/libsass="3.6.5" +github.com/sass/libsass="3.6.6" github.com/spf13/afero="v1.11.0" -github.com/spf13/cast="v1.6.0" -github.com/spf13/cobra="v1.8.0" +github.com/spf13/cast="v1.7.1" +github.com/spf13/cobra="v1.8.1" github.com/spf13/fsync="v0.10.1" -github.com/spf13/pflag="v1.0.5" -github.com/tdewolff/minify/v2="v2.20.20" -github.com/tdewolff/parse/v2="v2.7.13" +github.com/spf13/pflag="v1.0.6" +github.com/tdewolff/minify/v2="v2.20.37" +github.com/tdewolff/parse/v2="v2.7.15" +github.com/tetratelabs/wazero="v1.8.2" github.com/webmproject/libwebp="v1.3.2" -github.com/yuin/goldmark-emoji="v1.0.3" -github.com/yuin/goldmark="v1.7.4" -go.opencensus.io="v0.24.0" +github.com/yuin/goldmark-emoji="v1.0.4" +github.com/yuin/goldmark="v1.7.8" go.uber.org/automaxprocs="v1.5.3" -gocloud.dev="v0.36.0" -golang.org/x/crypto="v0.23.0" -golang.org/x/exp="v0.0.0-20221031165847-c99f073a8326" -golang.org/x/image="v0.16.0" -golang.org/x/mod="v0.17.0" -golang.org/x/net="v0.25.0" -golang.org/x/oauth2="v0.15.0" -golang.org/x/sync="v0.7.0" -golang.org/x/sys="v0.20.0" -golang.org/x/text="v0.15.0" -golang.org/x/time="v0.5.0" -golang.org/x/tools="v0.20.0" -golang.org/x/xerrors="v0.0.0-20231012003039-104605ab7028" -google.golang.org/api="v0.152.0" -google.golang.org/genproto/googleapis/api="v0.0.0-20231120223509-83a465c0220f" -google.golang.org/genproto/googleapis/rpc="v0.0.0-20231120223509-83a465c0220f" -google.golang.org/genproto="v0.0.0-20231120223509-83a465c0220f" -google.golang.org/grpc="v1.59.0" -google.golang.org/protobuf="v1.33.0" +golang.org/x/crypto="v0.33.0" +golang.org/x/exp="v0.0.0-20250210185358-939b2ce775ac" +golang.org/x/image="v0.24.0" +golang.org/x/mod="v0.23.0" +golang.org/x/net="v0.35.0" +golang.org/x/sync="v0.11.0" +golang.org/x/sys="v0.30.0" +golang.org/x/text="v0.22.0" +golang.org/x/tools="v0.30.0" +golang.org/x/xerrors="v0.0.0-20240903120638-7835f813f4da" +gonum.org/v1/plot="v0.15.0" +google.golang.org/protobuf="v1.36.5" gopkg.in/yaml.v2="v2.4.0" gopkg.in/yaml.v3="v3.0.1" +oss.terrastruct.com/d2="v0.6.9" +oss.terrastruct.com/util-go="v0.0.0-20241005222610-44c011a04896" +rsc.io/qr="v0.2.0" software.sslmate.com/src/go-pkcs12="v0.2.0" ``` diff --git a/bench.sh b/bench.sh deleted file mode 100755 index c6a20a7e3..000000000 --- a/bench.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# allow user to override go executable by running as GOEXE=xxx make ... -GOEXE="${GOEXE-go}" - -# Convenience script to -# - For a given branch -# - Run benchmark tests for a given package -# - Do the same for master -# - then compare the two runs with benchcmp - -benchFilter=".*" - -if (( $# < 2 )); - then - echo "USAGE: ./bench.sh (and (regexp, optional))" - exit 1 -fi - - - -if [ $# -eq 3 ]; then - benchFilter=$3 -fi - - -BRANCH=$1 -PACKAGE=$2 - -git checkout $BRANCH -"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-$BRANCH.txt - -git checkout master -"${GOEXE}" test -test.run=NONE -bench="$benchFilter" -test.benchmem=true ./$PACKAGE > /tmp/bench-$PACKAGE-master.txt - - -benchcmp /tmp/bench-$PACKAGE-master.txt /tmp/bench-$PACKAGE-$BRANCH.txt diff --git a/benchSite.sh b/benchSite.sh deleted file mode 100755 index aae21231c..000000000 --- a/benchSite.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# allow user to override go executable by running as GOEXE=xxx make ... -GOEXE="${GOEXE-go}" - -# Send in a regexp matching the benchmarks you want to run, i.e. './benchSite.sh "YAML"'. -# Note the quotes, which will be needed for more complex expressions. -# The above will run all variations, but only for front matter YAML. - -echo "Running with BenchmarkSiteBuilding/${1}" - -"${GOEXE}" test -run="NONE" -bench="BenchmarkSiteBuilding/${1}" -test.benchmem=true ./hugolib -memprofile mem.prof -count 3 -cpuprofile cpu.prof diff --git a/benchbep.sh b/benchbep.sh deleted file mode 100755 index efd616c88..000000000 --- a/benchbep.sh +++ /dev/null @@ -1 +0,0 @@ -gobench -package=./hugolib -bench="BenchmarkSiteNew/Deep_content_tree" \ No newline at end of file diff --git a/bepdock.sh b/bepdock.sh deleted file mode 100755 index a7ac0c639..000000000 --- a/bepdock.sh +++ /dev/null @@ -1 +0,0 @@ -docker run --rm --mount type=bind,source="$(pwd)",target=/hugo -w /hugo -i -t bepsays/ci-goreleaser:1.11-2 /bin/bash \ No newline at end of file diff --git a/cache/dynacache/dynacache.go b/cache/dynacache/dynacache.go index 5007e27ba..25d0f9b29 100644 --- a/cache/dynacache/dynacache.go +++ b/cache/dynacache/dynacache.go @@ -176,11 +176,12 @@ func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManag } // ClearOnRebuild prepares the cache for a new rebuild taking the given changeset into account. -func (c *Cache) ClearOnRebuild(changeset ...identity.Identity) { +// predicate is optional and will clear any entry for which it returns true. +func (c *Cache) ClearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{ NumWorkers: len(c.partitions), Handle: func(ctx context.Context, partition PartitionManager) error { - partition.clearOnRebuild(changeset...) + partition.clearOnRebuild(predicate, changeset...) return nil }, }) @@ -430,12 +431,25 @@ func (p *Partition[K, V]) doGetOrCreateWitTimeout(key K, duration time.Duration, errch := make(chan error, 1) go func() { - v, _, err := p.c.GetOrCreate(key, create) - if err != nil { - errch <- err - return - } - resultch <- v + var ( + v V + err error + ) + defer func() { + if r := recover(); r != nil { + if rerr, ok := r.(error); ok { + err = rerr + } else { + err = fmt.Errorf("panic: %v", r) + } + } + if err != nil { + errch <- err + } else { + resultch <- v + } + }() + v, _, err = p.c.GetOrCreate(key, create) }() select { @@ -466,7 +480,12 @@ func (p *Partition[K, V]) clearMatching(predicate func(k, v any) bool) { }) } -func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) { +func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) { + if predicate == nil { + predicate = func(k, v any) bool { + return false + } + } opts := p.getOptions() if opts.ClearWhen == ClearNever { return @@ -512,7 +531,7 @@ func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) { // Second pass needs to be done in a separate loop to catch any // elements marked as stale in the other partitions. p.c.DeleteFunc(func(key K, v V) bool { - if shouldDelete(key, v) { + if predicate(key, v) || shouldDelete(key, v) { p.trace.Log( logg.StringFunc( func() string { @@ -588,7 +607,7 @@ type PartitionManager interface { adjustMaxSize(addend int) int getMaxSize() int getOptions() OptionsPartition - clearOnRebuild(changeset ...identity.Identity) + clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) clearMatching(predicate func(k, v any) bool) clearStale() } diff --git a/cache/dynacache/dynacache_test.go b/cache/dynacache/dynacache_test.go index a58a8d94b..78b2fc82e 100644 --- a/cache/dynacache/dynacache_test.go +++ b/cache/dynacache/dynacache_test.go @@ -14,8 +14,11 @@ package dynacache import ( + "errors" + "fmt" "path/filepath" "testing" + "time" qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/common/loggers" @@ -144,13 +147,13 @@ func TestClear(t *testing.T) { c.Assert(cache.Keys(predicateAll), qt.HasLen, 4) - cache.ClearOnRebuild() + cache.ClearOnRebuild(nil) // Stale items are always cleared. c.Assert(cache.Keys(predicateAll), qt.HasLen, 2) cache = newTestCache(t) - cache.ClearOnRebuild(identity.StringIdentity("changed")) + cache.ClearOnRebuild(nil, identity.StringIdentity("changed")) c.Assert(cache.Keys(nil), qt.HasLen, 1) @@ -165,6 +168,58 @@ func TestClear(t *testing.T) { cache.adjustCurrentMaxSize() } +func TestPanicInCreate(t *testing.T) { + t.Parallel() + c := qt.New(t) + cache := newTestCache(t) + + p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", OptionsPartition{Weight: 30, ClearWhen: ClearOnRebuild}) + + willPanic := func(i int) func() { + return func() { + p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + panic(errors.New(key)) + }) + } + } + + // GetOrCreateWitTimeout needs to recover from panics in the create func. + willErr := func(i int) error { + _, err := p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{}, errors.New(key) + }) + return err + } + + for i := range 3 { + for range 3 { + c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("panic-%d", i)) + c.Assert(willErr(i), qt.ErrorMatches, fmt.Sprintf("error-%d", i)) + } + } + + // Test the same keys again without the panic. + for i := range 3 { + for range 3 { + v, err := p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil + }) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("panic-%d", i)) + + v, err = p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil + }) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("error-%d", i)) + } + } +} + func TestAdjustCurrentMaxSize(t *testing.T) { t.Parallel() c := qt.New(t) diff --git a/cache/filecache/filecache_pruner_test.go b/cache/filecache/filecache_pruner_test.go index f0cecfe9f..b49ba7645 100644 --- a/cache/filecache/filecache_pruner_test.go +++ b/cache/filecache/filecache_pruner_test.go @@ -59,7 +59,7 @@ dir = ":resourceDir/_gen" caches, err := filecache.NewCaches(p) c.Assert(err, qt.IsNil) cache := caches[name] - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) cache.GetOrCreateBytes(id, func() ([]byte, error) { return []byte("abc"), nil @@ -74,7 +74,7 @@ dir = ":resourceDir/_gen" c.Assert(err, qt.IsNil) c.Assert(count, qt.Equals, 5, msg) - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) v := cache.GetString(id) if i < 5 { @@ -97,7 +97,7 @@ dir = ":resourceDir/_gen" c.Assert(count, qt.Equals, 4) // Now only the i5 should be left. - for i := 0; i < 10; i++ { + for i := range 10 { id := fmt.Sprintf("i%d", i) v := cache.GetString(id) if i != 5 { diff --git a/cache/filecache/filecache_test.go b/cache/filecache/filecache_test.go index 59fb09276..a30aaa50b 100644 --- a/cache/filecache/filecache_test.go +++ b/cache/filecache/filecache_test.go @@ -105,7 +105,7 @@ dir = ":cacheDir/c" } for _, ca := range []*filecache.Cache{caches.ImageCache(), caches.AssetsCache(), caches.GetJSONCache(), caches.GetCSVCache()} { - for i := 0; i < 2; i++ { + for range 2 { info, r, err := ca.GetOrCreate("a", rf("abc")) c.Assert(err, qt.IsNil) c.Assert(r, qt.Not(qt.IsNil)) @@ -193,11 +193,11 @@ dir = "/cache/c" var wg sync.WaitGroup - for i := 0; i < 50; i++ { + for i := range 50 { wg.Add(1) go func(i int) { defer wg.Done() - for j := 0; j < 20; j++ { + for range 20 { ca := caches.Get(cacheName) c.Assert(ca, qt.Not(qt.IsNil)) filename, data := filenameData(i) diff --git a/cache/httpcache/httpcache.go b/cache/httpcache/httpcache.go index 98f7fedd4..bd6d4bf7d 100644 --- a/cache/httpcache/httpcache.go +++ b/cache/httpcache/httpcache.go @@ -42,7 +42,7 @@ var DefaultConfig = Config{ // Config holds the configuration for the HTTP cache. type Config struct { - // Configures the HTTP cache behaviour (RFC 9111). + // Configures the HTTP cache behavior (RFC 9111). // When this is not enabled for a resource, Hugo will go straight to the file cache. Cache Cache @@ -52,7 +52,7 @@ type Config struct { } type Cache struct { - // Enable HTTP cache behaviour (RFC 9111) for these rsources. + // Enable HTTP cache behavior (RFC 9111) for these resources. For GlobMatcher } @@ -122,6 +122,10 @@ type GlobMatcher struct { Includes []string } +func (gm GlobMatcher) IsZero() bool { + return len(gm.Includes) == 0 && len(gm.Excludes) == 0 +} + type ConfigCompiled struct { For predicate.P[string] PollConfigs []PollConfigCompiled @@ -155,6 +159,9 @@ func (p PollConfigCompiled) IsZero() bool { } func (gm *GlobMatcher) CompilePredicate() (func(string) bool, error) { + if gm.IsZero() { + panic("no includes or excludes") + } var p predicate.P[string] for _, include := range gm.Includes { g, err := glob.Compile(include, '/') @@ -181,7 +188,7 @@ func (gm *GlobMatcher) CompilePredicate() (func(string) bool, error) { return p, nil } -func DecodeConfig(bcfg config.BaseConfig, m map[string]any) (Config, error) { +func DecodeConfig(_ config.BaseConfig, m map[string]any) (Config, error) { if len(m) == 0 { return DefaultConfig, nil } @@ -203,5 +210,20 @@ func DecodeConfig(bcfg config.BaseConfig, m map[string]any) (Config, error) { return c, err } + if c.Cache.For.IsZero() { + c.Cache.For = DefaultConfig.Cache.For + } + + for pci := range c.Polls { + if c.Polls[pci].For.IsZero() { + c.Polls[pci].For = DefaultConfig.Cache.For + c.Polls[pci].Disable = true + } + } + + if len(c.Polls) == 0 { + c.Polls = DefaultConfig.Polls + } + return c, nil } diff --git a/cache/httpcache/httpcache_integration_test.go b/cache/httpcache/httpcache_integration_test.go index d3337c023..4d6a5f718 100644 --- a/cache/httpcache/httpcache_integration_test.go +++ b/cache/httpcache/httpcache_integration_test.go @@ -22,6 +22,8 @@ import ( ) func TestConfigCustom(t *testing.T) { + t.Parallel() + files := ` -- hugo.toml -- [httpcache] @@ -51,6 +53,8 @@ includes = ["**gohugo.io**"] } func TestConfigDefault(t *testing.T) { + t.Parallel() + files := ` -- hugo.toml -- ` @@ -62,3 +66,30 @@ func TestConfigDefault(t *testing.T) { b.Assert(compiled.For("https://gohugo.io/foo.jpg"), qt.IsFalse) b.Assert(compiled.PollConfigFor("https://gohugo.io/foo.jpg").Config.Disable, qt.IsTrue) } + +func TestConfigPollsOnly(t *testing.T) { + t.Parallel() + files := ` +-- hugo.toml -- +[httpcache] +[[httpcache.polls]] +low = "5s" +high = "32s" +[httpcache.polls.for] +includes = ["**gohugo.io**"] + + +` + + b := hugolib.Test(t, files) + + compiled := b.H.Configs.Base.C.HTTPCache + + b.Assert(compiled.For("https://gohugo.io/posts.json"), qt.IsFalse) + b.Assert(compiled.For("https://gohugo.io/foo.jpg"), qt.IsFalse) + + pc := compiled.PollConfigFor("https://gohugo.io/foo.jpg") + b.Assert(pc.Config.Low, qt.Equals, 5*time.Second) + b.Assert(pc.Config.High, qt.Equals, 32*time.Second) + b.Assert(compiled.PollConfigFor("https://example.com/foo.jpg").IsZero(), qt.IsTrue) +} diff --git a/cache/httpcache/httpcache_test.go b/cache/httpcache/httpcache_test.go index e3659f97b..60c07d056 100644 --- a/cache/httpcache/httpcache_test.go +++ b/cache/httpcache/httpcache_test.go @@ -17,6 +17,7 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" ) func TestGlobMatcher(t *testing.T) { @@ -40,3 +41,33 @@ func TestGlobMatcher(t *testing.T) { c.Assert(p("foo/bar/foo.css"), qt.IsFalse) c.Assert(p("foo/bar/foo.xml"), qt.IsTrue) } + +func TestDefaultConfig(t *testing.T) { + c := qt.New(t) + + _, err := DefaultConfig.Compile() + c.Assert(err, qt.IsNil) +} + +func TestDecodeConfigInjectsDefaultAndCompiles(t *testing.T) { + c := qt.New(t) + + cfg, err := DecodeConfig(config.BaseConfig{}, map[string]interface{}{}) + c.Assert(err, qt.IsNil) + c.Assert(cfg, qt.DeepEquals, DefaultConfig) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) + + cfg, err = DecodeConfig(config.BaseConfig{}, map[string]any{ + "cache": map[string]any{ + "polls": []map[string]any{ + {"disable": true}, + }, + }, + }) + c.Assert(err, qt.IsNil) + + _, err = cfg.Compile() + c.Assert(err, qt.IsNil) +} diff --git a/codegen/methods.go b/codegen/methods.go index 299063bb5..08ac97b00 100644 --- a/codegen/methods.go +++ b/codegen/methods.go @@ -26,6 +26,7 @@ import ( "path/filepath" "reflect" "regexp" + "slices" "sort" "strings" "sync" @@ -102,7 +103,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T } for _, t := range include { - for i := 0; i < t.NumMethod(); i++ { + for i := range t.NumMethod() { m := t.Method(i) if excludes[m.Name] || seen[m.Name] { @@ -122,7 +123,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T method := Method{Owner: t, OwnerName: ownerName, Name: m.Name} - for i := 0; i < numIn; i++ { + for i := range numIn { in := m.Type.In(i) name, pkg := nameAndPackage(in) @@ -137,7 +138,7 @@ func (c *Inspector) MethodsFromTypes(include []reflect.Type, exclude []reflect.T numOut := m.Type.NumOut() if numOut > 0 { - for i := 0; i < numOut; i++ { + for i := range numOut { out := m.Type.Out(i) name, pkg := nameAndPackage(out) @@ -304,7 +305,7 @@ func (m Method) inOutStr() string { } args := make([]string, len(m.In)) - for i := 0; i < len(args); i++ { + for i := range args { args[i] = fmt.Sprintf("arg%d", i) } return "(" + strings.Join(args, ", ") + ")" @@ -316,7 +317,7 @@ func (m Method) inStr() string { } args := make([]string, len(m.In)) - for i := 0; i < len(args); i++ { + for i := range args { args[i] = fmt.Sprintf("arg%d %s", i, m.In[i]) } return "(" + strings.Join(args, ", ") + ")" @@ -339,7 +340,7 @@ func (m Method) outStrNamed() string { } outs := make([]string, len(m.Out)) - for i := 0; i < len(outs); i++ { + for i := range outs { outs[i] = fmt.Sprintf("o%d %s", i, m.Out[i]) } @@ -435,7 +436,7 @@ func (m Methods) ToMarshalJSON(receiver, pkgPath string, excludes ...string) (st // Exclude self for i, pkgImp := range pkgImports { if pkgImp == pkgPath { - pkgImports = append(pkgImports[:i], pkgImports[i+1:]...) + pkgImports = slices.Delete(pkgImports, i, i+1) } } } diff --git a/commands/commandeer.go b/commands/commandeer.go index ad2adf3a2..bf9655637 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -39,7 +39,6 @@ import ( "github.com/gohugoio/hugo/common/hstrings" "github.com/gohugoio/hugo/common/htime" - "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/types" @@ -102,9 +101,10 @@ type configKey struct { // This is the root command. type rootCommand struct { - Printf func(format string, v ...interface{}) - Println func(a ...interface{}) - Out io.Writer + Printf func(format string, v ...any) + Println func(a ...any) + StdOut io.Writer + StdErr io.Writer logger loggers.Logger @@ -141,8 +141,6 @@ type rootCommand struct { logLevel string - verbose bool - debug bool quiet bool devMode bool // Hidden flag. @@ -359,7 +357,7 @@ func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotEx } func (r *rootCommand) newDepsConfig(conf *commonConfig) deps.DepsCfg { - return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild} + return deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, StdOut: r.logger.StdOut(), StdErr: r.logger.StdErr(), LogLevel: r.logger.Level(), ChangesFromBuild: r.changesFromBuild} } func (r *rootCommand) Name() string { @@ -424,21 +422,23 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args } func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { - r.Out = os.Stdout + r.StdOut = os.Stdout + r.StdErr = os.Stderr if r.quiet { - r.Out = io.Discard + r.StdOut = io.Discard + r.StdErr = io.Discard } // Used by mkcert (server). - log.SetOutput(r.Out) + log.SetOutput(r.StdOut) - r.Printf = func(format string, v ...interface{}) { + r.Printf = func(format string, v ...any) { if !r.quiet { - fmt.Fprintf(r.Out, format, v...) + fmt.Fprintf(r.StdOut, format, v...) } } - r.Println = func(a ...interface{}) { + r.Println = func(a ...any) { if !r.quiet { - fmt.Fprintln(r.Out, a...) + fmt.Fprintln(r.StdOut, a...) } } _, running := runner.Command.(*serverCommand) @@ -447,6 +447,8 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { if err != nil { return err } + // Set up the global logger early to allow info deprecations during config load. + loggers.SetGlobalLogger(r.logger) r.changesFromBuild = make(chan []identity.Identity, 10) @@ -482,32 +484,21 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { default: return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel) } - } else { - if r.verbose { - hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0") - hugo.Deprecate("--verbose", "use --logLevel info", "v0.114.0") - level = logg.LevelInfo - } - - if r.debug { - hugo.Deprecate("--debug", "use --logLevel debug", "v0.114.0") - level = logg.LevelDebug - } } } optsLogger := loggers.Options{ DistinctLevel: logg.LevelWarn, Level: level, - Stdout: r.Out, - Stderr: r.Out, + StdOut: r.StdOut, + StdErr: r.StdErr, StoreErrors: running, } return loggers.New(optsLogger), nil } -func (r *rootCommand) Reset() { +func (r *rootCommand) resetLogs() { r.logger.Reset() loggers.Log().Reset() } @@ -549,6 +540,7 @@ Complete documentation is available at https://gohugo.io/.` cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory") _ = cmd.MarkFlagDirname("themesDir") cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern") + cmd.PersistentFlags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file") _ = cmd.RegisterFlagCompletionFunc("ignoreVendorPaths", cobra.NoFileCompletions) cmd.PersistentFlags().String("clock", "", "set the clock used by Hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00") _ = cmd.RegisterFlagCompletionFunc("clock", cobra.NoFileCompletions) @@ -560,8 +552,6 @@ Complete documentation is available at https://gohugo.io/.` cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode") cmd.PersistentFlags().BoolVarP(&r.renderToMemory, "renderToMemory", "M", false, "render to memory (mostly useful when running the server)") - cmd.PersistentFlags().BoolVarP(&r.verbose, "verbose", "v", false, "verbose output") - cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output") cmd.PersistentFlags().BoolVarP(&r.devMode, "devMode", "", false, "only used for internal testing, flag hidden.") cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)") _ = cmd.RegisterFlagCompletionFunc("logLevel", cobra.FixedCompletions([]string{"debug", "info", "warn", "error"}, cobra.ShellCompDirectiveNoFileComp)) @@ -606,7 +596,6 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) { cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.") cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files") cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files") - cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file") cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations") cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.") cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.") diff --git a/commands/config.go b/commands/config.go index b250fc329..7d166b9b8 100644 --- a/commands/config.go +++ b/commands/config.go @@ -43,8 +43,9 @@ func newConfigCommand() *configCommand { type configCommand struct { r *rootCommand - format string - lang string + format string + lang string + printZero bool commands []simplecobra.Commander } @@ -78,7 +79,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg dec.SetIndent("", " ") dec.SetEscapeHTML(false) - if err := dec.Encode(parser.ReplacingJSONMarshaller{Value: config, KeysToLower: true, OmitEmpty: true}); err != nil { + if err := dec.Encode(parser.ReplacingJSONMarshaller{Value: config, KeysToLower: true, OmitEmpty: !c.printZero}); err != nil { return err } @@ -89,7 +90,7 @@ func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg os.Stdout.Write(buf.Bytes()) default: // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. - var m map[string]interface{} + var m map[string]any if err := json.Unmarshal(buf.Bytes(), &m); err != nil { return err } @@ -115,6 +116,7 @@ func (c *configCommand) Init(cd *simplecobra.Commandeer) error { cmd.Flags().StringVar(&c.format, "format", "toml", "preferred file format (toml, yaml or json)") _ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp)) cmd.Flags().StringVar(&c.lang, "lang", "", "the language to display config for. Defaults to the first language defined.") + cmd.Flags().BoolVar(&c.printZero, "printZero", false, `include config options with zero values (e.g. false, 0, "") in the output`) _ = cmd.RegisterFlagCompletionFunc("lang", cobra.NoFileCompletions) applyLocalFlagsBuildConfig(cmd, c.r) diff --git a/commands/deploy.go b/commands/deploy.go index f0bc670ca..3e9d3df20 100644 --- a/commands/deploy.go +++ b/commands/deploy.go @@ -11,21 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy - -// Copyright 2024 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//go:build withdeploy package commands @@ -33,7 +19,6 @@ import ( "context" "github.com/gohugoio/hugo/deploy" - "github.com/gohugoio/hugo/deploy/deployconfig" "github.com/bep/simplecobra" "github.com/spf13/cobra" @@ -60,17 +45,7 @@ documentation. return deployer.Deploy(ctx) }, withc: func(cmd *cobra.Command, r *rootCommand) { - cmd.ValidArgsFunction = cobra.NoFileCompletions - cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one") - _ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions) - cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target") - cmd.Flags().Bool("dryRun", false, "dry run") - cmd.Flags().Bool("force", false, "force upload of all files") - cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target") - cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable") - _ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions) - cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10") - _ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions) + applyDeployFlags(cmd, r) }, } } diff --git a/commands/deploy_flags.go b/commands/deploy_flags.go new file mode 100644 index 000000000..d4326547a --- /dev/null +++ b/commands/deploy_flags.go @@ -0,0 +1,33 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "github.com/gohugoio/hugo/deploy/deployconfig" + "github.com/spf13/cobra" +) + +func applyDeployFlags(cmd *cobra.Command, r *rootCommand) { + cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.Flags().String("target", "", "target deployment from deployments section in config file; defaults to the first one") + _ = cmd.RegisterFlagCompletionFunc("target", cobra.NoFileCompletions) + cmd.Flags().Bool("confirm", false, "ask for confirmation before making changes to the target") + cmd.Flags().Bool("dryRun", false, "dry run") + cmd.Flags().Bool("force", false, "force upload of all files") + cmd.Flags().Bool("invalidateCDN", deployconfig.DefaultConfig.InvalidateCDN, "invalidate the CDN cache listed in the deployment target") + cmd.Flags().Int("maxDeletes", deployconfig.DefaultConfig.MaxDeletes, "maximum # of files to delete, or -1 to disable") + _ = cmd.RegisterFlagCompletionFunc("maxDeletes", cobra.NoFileCompletions) + cmd.Flags().Int("workers", deployconfig.DefaultConfig.Workers, "number of workers to transfer files. defaults to 10") + _ = cmd.RegisterFlagCompletionFunc("workers", cobra.NoFileCompletions) +} diff --git a/commands/deploy_off.go b/commands/deploy_off.go index 8a481bd96..8f5eaa2de 100644 --- a/commands/deploy_off.go +++ b/commands/deploy_off.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build nodeploy -// +build nodeploy +//go:build !withdeploy // Copyright 2024 The Hugo Authors. All rights reserved. // @@ -31,6 +30,7 @@ package commands import ( "context" + "errors" "github.com/bep/simplecobra" "github.com/spf13/cobra" @@ -40,9 +40,10 @@ func newDeployCommand() simplecobra.Commander { return &simpleCommand{ name: "deploy", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { - return nil + return errors.New("deploy not supported in this version of Hugo; install a release with 'withdeploy' in the archive filename or build yourself with the 'withdeploy' build tag. Also see https://github.com/gohugoio/hugo/pull/12995") }, withc: func(cmd *cobra.Command, r *rootCommand) { + applyDeployFlags(cmd, r) cmd.Hidden = true }, } diff --git a/commands/gen.go b/commands/gen.go index 83b4d637c..1c5361840 100644 --- a/commands/gen.go +++ b/commands/gen.go @@ -21,6 +21,7 @@ import ( "os" "path" "path/filepath" + "slices" "strings" "github.com/alecthomas/chroma/v2" @@ -49,6 +50,7 @@ func newGenCommand() *genCommand { highlightStyle string lineNumbersInlineStyle string lineNumbersTableStyle string + omitEmpty bool ) newChromaStyles := func() simplecobra.Commander { @@ -60,6 +62,10 @@ func newGenCommand() *genCommand { See https://xyproto.github.io/splash/docs/all.html for a preview of the available styles`, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { + style = strings.ToLower(style) + if !slices.Contains(styles.Names(), style) { + return fmt.Errorf("invalid style: %s", style) + } builder := styles.Get(style).Builder() if highlightStyle != "" { builder.Add(chroma.LineHighlight, highlightStyle) @@ -74,8 +80,17 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl if err != nil { return err } - formatter := html.New(html.WithAllClasses(true)) - formatter.WriteCSS(os.Stdout, style) + + var formatter *html.Formatter + if omitEmpty { + formatter = html.New(html.WithClasses(true)) + } else { + formatter = html.New(html.WithAllClasses(true)) + } + + w := os.Stdout + fmt.Fprintf(w, "/* Generated using: hugo %s */\n\n", strings.Join(os.Args[1:], " ")) + formatter.WriteCSS(w, style) return nil }, withc: func(cmd *cobra.Command, r *rootCommand) { @@ -88,6 +103,8 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl _ = cmd.RegisterFlagCompletionFunc("lineNumbersInlineStyle", cobra.NoFileCompletions) cmd.PersistentFlags().StringVar(&lineNumbersTableStyle, "lineNumbersTableStyle", "", `foreground and background colors for table line numbers, e.g. --lineNumbersTableStyle "#fff000 bg:#000fff"`) _ = cmd.RegisterFlagCompletionFunc("lineNumbersTableStyle", cobra.NoFileCompletions) + cmd.PersistentFlags().BoolVar(&omitEmpty, "omitEmpty", false, `omit empty CSS rules`) + _ = cmd.RegisterFlagCompletionFunc("omitEmpty", cobra.NoFileCompletions) }, } } @@ -142,7 +159,7 @@ url: %s return &simpleCommand{ name: "doc", - short: "Generate Markdown documentation for the Hugo CLI.", + short: "Generate Markdown documentation for the Hugo CLI", long: `Generate Markdown documentation for the Hugo CLI. This command is, mostly, used to create up-to-date documentation of Hugo's command-line interface for https://gohugo.io/. @@ -167,13 +184,13 @@ url: %s prepender := func(filename string) string { name := filepath.Base(filename) base := strings.TrimSuffix(name, path.Ext(name)) - url := "/commands/" + strings.ToLower(base) + "/" + url := "/docs/reference/commands/" + strings.ToLower(base) + "/" return fmt.Sprintf(gendocFrontmatterTemplate, strings.Replace(base, "_", " ", -1), base, url) } linkHandler := func(name string) string { base := strings.TrimSuffix(name, path.Ext(name)) - return "/commands/" + strings.ToLower(base) + "/" + return "/docs/reference/commands/" + strings.ToLower(base) + "/" } r.Println("Generating Hugo command-line documentation in", gendocdir, "...") doc.GenMarkdownTreeCustom(cd.CobraCommand.Root(), gendocdir, prepender, linkHandler) @@ -194,7 +211,7 @@ url: %s newDocsHelper := func() simplecobra.Commander { return &simpleCommand{ name: "docshelper", - short: "Generate some data files for the Hugo docs.", + short: "Generate some data files for the Hugo docs", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { r.Println("Generate docs data to", docsHelperTarget) @@ -215,7 +232,7 @@ url: %s } // Decode the JSON to a map[string]interface{} and then unmarshal it again to the correct format. - var m map[string]interface{} + var m map[string]any if err := json.Unmarshal(buf.Bytes(), &m); err != nil { return err } diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index 42bf68a37..3b57ac5e9 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -27,7 +27,6 @@ import ( "sync/atomic" "time" - "github.com/bep/logg" "github.com/bep/simplecobra" "github.com/fsnotify/fsnotify" "github.com/gohugoio/hugo/common/herrors" @@ -63,7 +62,7 @@ type hugoBuilder struct { // Currently only set when in "fast render mode". changeDetector *fileChangeDetector - visitedURLs *types.EvictingStringQueue + visitedURLs *types.EvictingQueue[string] fullRebuildSem *semaphore.Weighted debounce func(f func()) @@ -136,10 +135,6 @@ func (e *hugoBuilderErrState) wasErr() bool { return e.waserr } -func (c *hugoBuilder) errCount() int { - return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError) -} - // getDirList provides NewWatcher() with a list of directories to watch for changes. func (c *hugoBuilder) getDirList() ([]string, error) { h, err := c.hugo() @@ -345,7 +340,6 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa for { select { case changes := <-c.r.changesFromBuild: - c.errState.setBuildErr(nil) unlock, err := h.LockBuild() if err != nil { c.r.logger.Errorln("Failed to acquire a build lock: %s", err) @@ -358,7 +352,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa } if c.s != nil && c.s.doLiveReload { doReload := c.changeDetector == nil || len(c.changeDetector.changed()) > 0 - doReload = doReload || c.showErrorInBrowser && c.errCount() > 0 + doReload = doReload || c.showErrorInBrowser && c.errState.buildErr() != nil if doReload { livereload.ForceRefresh() } @@ -372,7 +366,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa return } c.handleEvents(watcher, staticSyncer, evs, configSet) - if c.showErrorInBrowser && c.errCount() > 0 { + if c.showErrorInBrowser && c.errState.buildErr() != nil { // Need to reload browser to show the error livereload.ForceRefresh() } @@ -419,11 +413,17 @@ func (c *hugoBuilder) build() error { } func (c *hugoBuilder) buildSites(noBuildLock bool) (err error) { - h, err := c.hugo() + defer func() { + c.errState.setBuildErr(err) + }() + + var h *hugolib.HugoSites + h, err = c.hugo() if err != nil { - return err + return } - return h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock}) + err = h.Build(hugolib.BuildCfg{NoBuildLock: noBuildLock}) + return } func (c *hugoBuilder) copyStatic() (map[string]uint64, error) { @@ -619,6 +619,9 @@ func (c *hugoBuilder) fullRebuild(changeType string) { // Set the processing on pause until the state is recovered. c.errState.setPaused(true) c.handleBuildErr(err, "Failed to reload config") + if c.s.doLiveReload { + livereload.ForceRefresh() + } } else { c.errState.setPaused(false) } @@ -660,7 +663,20 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, var n int for _, ev := range evs { keep := true - if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { + // Write and rename operations are often followed by CHMOD. + // There may be valid use cases for rebuilding the site on CHMOD, + // but that will require more complex logic than this simple conditional. + // On OS X this seems to be related to Spotlight, see: + // https://github.com/go-fsnotify/fsnotify/issues/15 + // A workaround is to put your site(s) on the Spotlight exception list, + // but that may be a little mysterious for most end users. + // So, for now, we skip reload on CHMOD. + // We do have to check for WRITE though. On slower laptops a Chmod + // could be aggregated with other important events, and we still want + // to rebuild on those + if ev.Op == fsnotify.Chmod { + keep = false + } else if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { if _, err := os.Stat(ev.Name); err != nil { keep = false } @@ -802,21 +818,6 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, continue } - // Write and rename operations are often followed by CHMOD. - // There may be valid use cases for rebuilding the site on CHMOD, - // but that will require more complex logic than this simple conditional. - // On OS X this seems to be related to Spotlight, see: - // https://github.com/go-fsnotify/fsnotify/issues/15 - // A workaround is to put your site(s) on the Spotlight exception list, - // but that may be a little mysterious for most end users. - // So, for now, we skip reload on CHMOD. - // We do have to check for WRITE though. On slower laptops a Chmod - // could be aggregated with other important events, and we still want - // to rebuild on those - if ev.Op&(fsnotify.Chmod|fsnotify.Write|fsnotify.Create) == fsnotify.Chmod { - continue - } - walkAdder := func(path string, f hugofs.FileMetaInfo) error { if f.IsDir() { c.r.logger.Println("adding created directory to watchlist", path) @@ -917,7 +918,11 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, changed := c.changeDetector.changed() if c.changeDetector != nil { - lrl.Logf("build changed %d files", len(changed)) + if len(changed) >= 10 { + lrl.Logf("build changed %d files", len(changed)) + } else { + lrl.Logf("build changed %d files: %q", len(changed), changed) + } if len(changed) == 0 { // Nothing has changed. return @@ -963,10 +968,13 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(otherChanges[0]), false) lrl.Logf("refreshing %q", pathToRefresh) livereload.RefreshPath(pathToRefresh) - } else if len(cssChanges) == 0 { + } else if len(cssChanges) == 0 || len(otherChanges) > 1 { lrl.Logf("force refresh") livereload.ForceRefresh() } + } else { + lrl.Logf("force refresh") + livereload.ForceRefresh() } if len(cssChanges) > 0 { @@ -1081,37 +1089,44 @@ func (c *hugoBuilder) printChangeDetected(typ string) { c.r.logger.Println(htime.Now().Format(layout)) } -func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error { +func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) (err error) { + defer func() { + c.errState.setBuildErr(err) + }() if err := c.errState.buildErr(); err != nil { ferrs := herrors.UnwrapFileErrorsWithErrorContext(err) for _, err := range ferrs { events = append(events, fsnotify.Event{Name: err.Position().Filename, Op: fsnotify.Write}) } } - c.errState.setBuildErr(nil) - h, err := c.hugo() + var h *hugolib.HugoSites + h, err = c.hugo() if err != nil { - return err + return } - - return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...) + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...) + return } -func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) error { - c.errState.setBuildErr(nil) - h, err := c.hugo() +func (c *hugoBuilder) rebuildSitesForChanges(ids []identity.Identity) (err error) { + defer func() { + c.errState.setBuildErr(err) + }() + + var h *hugolib.HugoSites + h, err = c.hugo() if err != nil { - return err + return } whatChanged := &hugolib.WhatChanged{} whatChanged.Add(ids...) - err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}) - c.errState.setBuildErr(err) - return err + err = h.Build(hugolib.BuildCfg{NoBuildLock: true, WhatChanged: whatChanged, RecentlyTouched: c.visitedURLs, ErrRecovery: c.errState.wasErr()}) + + return } func (c *hugoBuilder) reloadConfig() error { - c.r.Reset() + c.r.resetLogs() c.r.configVersionID.Add(1) if err := c.withConfE(func(conf *commonConfig) error { diff --git a/commands/list.go b/commands/list.go index f362e22f1..42f3408ba 100644 --- a/commands/list.go +++ b/commands/list.go @@ -57,7 +57,7 @@ func newListCommand() *listCommand { return err } - writer := csv.NewWriter(r.Out) + writer := csv.NewWriter(r.StdOut) defer writer.Flush() writer.Write([]string{ diff --git a/commands/mod.go b/commands/mod.go index dda7840cc..58155f9be 100644 --- a/commands/mod.go +++ b/commands/mod.go @@ -44,16 +44,16 @@ func newModCommands() *modCommands { npmCommand := &simpleCommand{ name: "npm", - short: "Various npm helpers.", + short: "Various npm helpers", long: `Various npm (Node package manager) helpers.`, commands: []simplecobra.Commander{ &simpleCommand{ name: "pack", - short: "Experimental: Prepares and writes a composite package.json file for your project.", + short: "Experimental: Prepares and writes a composite package.json file for your project", long: `Prepares and writes a composite package.json file for your project. On first run it creates a "package.hugo.json" in the project root if not already there. This file will be used as a template file -with the base dependency set. +with the base dependency set. This set will be merged with all "package.hugo.json" files found in the dependency tree, picking the version closest to the project. @@ -80,12 +80,12 @@ so this may/will change in future versions of Hugo. commands: []simplecobra.Commander{ &simpleCommand{ name: "init", - short: "Initialize this project as a Hugo Module.", + short: "Initialize this project as a Hugo Module", long: `Initialize this project as a Hugo Module. It will try to guess the module path, but you may help by passing it as an argument, e.g: - + hugo mod init github.com/gohugoio/testshortcodes - + Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module inside a subfolder on GitHub, as one example. `, @@ -111,7 +111,7 @@ so this may/will change in future versions of Hugo. }, &simpleCommand{ name: "verify", - short: "Verify dependencies.", + short: "Verify dependencies", long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded.`, withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions @@ -129,7 +129,7 @@ so this may/will change in future versions of Hugo. }, &simpleCommand{ name: "graph", - short: "Print a module dependency graph.", + short: "Print a module dependency graph", long: `Print a module dependency graph with information about module status (disabled, vendored). Note that for vendored modules, that is the version listed and not the one from go.mod. `, @@ -149,7 +149,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "clean", - short: "Delete the Hugo Module cache for the current project.", + short: "Delete the Hugo Module cache for the current project", long: `Delete the Hugo Module cache for the current project.`, withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions @@ -175,7 +175,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "tidy", - short: "Remove unused entries in go.mod and go.sum.", + short: "Remove unused entries in go.mod and go.sum", withc: func(cmd *cobra.Command, r *rootCommand) { cmd.ValidArgsFunction = cobra.NoFileCompletions applyLocalFlagsBuildConfig(cmd, r) @@ -190,7 +190,7 @@ Note that for vendored modules, that is the version listed and not the one from }, &simpleCommand{ name: "vendor", - short: "Vendor all module dependencies into the _vendor directory.", + short: "Vendor all module dependencies into the _vendor directory", long: `Vendor all module dependencies into the _vendor directory. If a module is vendored, that is where Hugo will look for it's dependencies. `, @@ -209,16 +209,16 @@ Note that for vendored modules, that is the version listed and not the one from &simpleCommand{ name: "get", - short: "Resolves dependencies in your current Hugo Project.", + short: "Resolves dependencies in your current Hugo project", long: ` -Resolves dependencies in your current Hugo Project. +Resolves dependencies in your current Hugo project. Some examples: Install the latest version possible for a given module: hugo mod get github.com/gohugoio/testshortcodes - + Install a specific version: hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 diff --git a/commands/new.go b/commands/new.go index 901ea02d6..81e1c65a4 100644 --- a/commands/new.go +++ b/commands/new.go @@ -76,10 +76,8 @@ Ensure you run this within the root directory of your site.`, &simpleCommand{ name: "site", use: "site [path]", - short: "Create a new site (skeleton)", - long: `Create a new site in the provided directory. -The new site will have the correct structure, but no content or theme yet. -Use ` + "`hugo new [contentPath]`" + ` to create new content.`, + short: "Create a new site", + long: `Create a new site at the specified path.`, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { if len(args) < 1 { return newUserError("path needs to be provided") @@ -124,11 +122,9 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`, &simpleCommand{ name: "theme", use: "theme [name]", - short: "Create a new theme (skeleton)", - long: `Create a new theme (skeleton) called [name] in ./themes. -New theme is a skeleton. Please add content to the touched files. Add your -name to the copyright line in the license and adjust the theme.toml file -according to your needs.`, + short: "Create a new theme", + long: `Create a new theme with the specified name in the ./themes directory. +This generates a functional theme including template examples and sample content.`, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { if len(args) < 1 { return newUserError("theme name needs to be provided") @@ -144,7 +140,7 @@ according to your needs.`, createpath := paths.AbsPathify(conf.configs.Base.WorkingDir, filepath.Join(conf.configs.Base.ThemesDir, args[0])) r.Println("Creating new theme in", createpath) - err = skeletons.CreateTheme(createpath, sourceFs) + err = skeletons.CreateTheme(createpath, sourceFs, format) if err != nil { return err } @@ -152,7 +148,14 @@ according to your needs.`, return nil }, withc: func(cmd *cobra.Command, r *rootCommand) { - cmd.ValidArgsFunction = cobra.NoFileCompletions + cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return []string{}, cobra.ShellCompDirectiveNoFileComp + } + return []string{}, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveFilterDirs + } + cmd.Flags().StringVar(&format, "format", "toml", "preferred file format (toml, yaml or json)") + _ = cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions([]string{"toml", "yaml", "json"}, cobra.ShellCompDirectiveNoFileComp)) }, }, }, diff --git a/commands/release.go b/commands/release.go index b05226d35..059f04eb8 100644 --- a/commands/release.go +++ b/commands/release.go @@ -32,7 +32,7 @@ func newReleaseCommand() simplecobra.Commander { return &simpleCommand{ name: "release", - short: "Release a new version of Hugo.", + short: "Release a new version of Hugo", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { rel, err := releaser.New(skipPush, try, step) if err != nil { diff --git a/commands/server.go b/commands/server.go index 84d4165f0..c8895b9a1 100644 --- a/commands/server.go +++ b/commands/server.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io" + "maps" "net" "net/http" _ "net/http/pprof" @@ -32,6 +33,7 @@ import ( "path" "path/filepath" "regexp" + "sort" "strconv" "strings" "sync" @@ -40,12 +42,14 @@ import ( "time" "github.com/bep/mclib" + "github.com/pkg/browser" "github.com/bep/debounce" "github.com/bep/simplecobra" "github.com/fsnotify/fsnotify" "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/urls" @@ -55,7 +59,6 @@ import ( "github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib/filesystems" "github.com/gohugoio/hugo/livereload" - "github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/transform" "github.com/gohugoio/hugo/transform/livereloadinject" "github.com/spf13/afero" @@ -82,10 +85,14 @@ const ( configChangeGoWork = "go work file" ) +const ( + hugoHeaderRedirect = "X-Hugo-Redirect" +) + func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder { - var visitedURLs *types.EvictingStringQueue + var visitedURLs *types.EvictingQueue[string] if s != nil && !s.disableFastRender { - visitedURLs = types.NewEvictingStringQueue(20) + visitedURLs = types.NewEvictingQueue[string](20) } return &hugoBuilder{ r: r, @@ -113,7 +120,7 @@ func newServerCommand() *serverCommand { commands: []simplecobra.Commander{ &simpleCommand{ name: "trust", - short: "Install the local CA in the system trust store.", + short: "Install the local CA in the system trust store", run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { action := "-install" if uninstall { @@ -189,9 +196,7 @@ func (f *fileChangeDetector) PrepareNew() { } f.prev = make(map[string]uint64) - for k, v := range f.current { - f.prev[k] = v - } + maps.Copy(f.prev, f.current) f.current = make(map[string]uint64) } @@ -209,16 +214,17 @@ func (f *fileChangeDetector) changed() []string { } } - return f.filterIrrelevant(c) + return f.filterIrrelevantAndSort(c) } -func (f *fileChangeDetector) filterIrrelevant(in []string) []string { +func (f *fileChangeDetector) filterIrrelevantAndSort(in []string) []string { var filtered []string for _, v := range in { if !f.irrelevantRe.MatchString(v) { filtered = append(filtered, v) } } + sort.Strings(filtered) return filtered } @@ -304,64 +310,65 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string w.Header().Set(header.Key, header.Value) } - if redirect := serverConfig.MatchRedirect(requestURI); !redirect.IsZero() { - // fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) - doRedirect := true - // This matches Netlify's behavior and is needed for SPA behavior. - // See https://docs.netlify.com/routing/redirects/rewrites-proxies/ - if !redirect.Force { - path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path())) - if root != "" { - path = filepath.Join(root, path) - } - var fs afero.Fs - f.c.withConf(func(conf *commonConfig) { - fs = conf.fs.PublishDirServer - }) - - fi, err := fs.Stat(path) - - if err == nil { - if fi.IsDir() { - // There will be overlapping directories, so we - // need to check for a file. - _, err = fs.Stat(filepath.Join(path, "index.html")) - doRedirect = err != nil - } else { - doRedirect = false + if canRedirect(requestURI, r) { + if redirect := serverConfig.MatchRedirect(requestURI, r.Header); !redirect.IsZero() { + doRedirect := true + // This matches Netlify's behavior and is needed for SPA behavior. + // See https://docs.netlify.com/routing/redirects/rewrites-proxies/ + if !redirect.Force { + path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path())) + if root != "" { + path = filepath.Join(root, path) } - } - } + var fs afero.Fs + f.c.withConf(func(conf *commonConfig) { + fs = conf.fs.PublishDirServer + }) + + fi, err := fs.Stat(path) - if doRedirect { - switch redirect.Status { - case 404: - w.WriteHeader(404) - file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path())) if err == nil { - defer file.Close() - io.Copy(w, file) - } else { - fmt.Fprintln(w, "

Page Not Found

") + if fi.IsDir() { + // There will be overlapping directories, so we + // need to check for a file. + _, err = fs.Stat(filepath.Join(path, "index.html")) + doRedirect = err != nil + } else { + doRedirect = false + } } - return - case 200: - if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil { - requestURI = redirect.To - r = r2 - } - default: - w.Header().Set("Content-Type", "") - http.Redirect(w, r, redirect.To, redirect.Status) - return + } + if doRedirect { + w.Header().Set(hugoHeaderRedirect, "true") + switch redirect.Status { + case 404: + w.WriteHeader(404) + file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path())) + if err == nil { + defer file.Close() + io.Copy(w, file) + } else { + fmt.Fprintln(w, "

Page Not Found

") + } + return + case 200: + if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil { + requestURI = redirect.To + r = r2 + } + default: + w.Header().Set("Content-Type", "") + http.Redirect(w, r, redirect.To, redirect.Status) + return + + } } } - } if f.c.fastRenderMode && f.c.errState.buildErr() == nil { - if strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") { + if isNavigation(requestURI, r) { if !f.c.visitedURLs.Contains(requestURI) { // If not already on stack, re-render that single page. if err := f.c.partialReRender(requestURI); err != nil { @@ -448,6 +455,7 @@ type serverCommand struct { // Flags. renderStaticToDisk bool navigateToChanged bool + openBrowser bool serverAppend bool serverInterface string tlsCertFile string @@ -539,6 +547,7 @@ of a second, you will be able to save and see your changes nearly instantly.` cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL") cmd.Flags().BoolVar(&c.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild") cmd.Flags().BoolVarP(&c.navigateToChanged, "navigateToChanged", "N", false, "navigate to changed content file on live browser reload") + cmd.Flags().BoolVarP(&c.openBrowser, "openBrowser", "O", false, "open the site in a browser after server startup") cmd.Flags().BoolVar(&c.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory") cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes") cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser") @@ -618,7 +627,7 @@ func (c *serverCommand) setServerInfoInConfig() error { panic("no server ports set") } return c.withConfE(func(conf *commonConfig) error { - for i, language := range conf.configs.Languages { + for i, language := range conf.configs.LanguagesDefaultFirst { isMultihost := conf.configs.IsMultihost var serverPort int if isMultihost { @@ -648,9 +657,8 @@ func (c *serverCommand) setServerInfoInConfig() error { } func (c *serverCommand) getErrorWithContext() any { - errCount := c.errCount() - - if errCount == 0 { + buildErr := c.errState.buildErr() + if buildErr == nil { return nil } @@ -659,7 +667,7 @@ func (c *serverCommand) getErrorWithContext() any { m["Error"] = cleanErrorLog(c.r.logger.Errors()) m["Version"] = hugo.BuildVersionString() - ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr()) + ferrors := herrors.UnwrapFileErrorsWithErrorContext(buildErr) m["Files"] = ferrors return m @@ -750,7 +758,7 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error { c.serverPorts = make([]serverPortListener, len(conf.configs.Languages)) } currentServerPort := c.serverPort - for i := 0; i < len(c.serverPorts); i++ { + for i := range c.serverPorts { l, err := net.Listen("tcp", net.JoinHostPort(c.serverInterface, strconv.Itoa(currentServerPort))) if err == nil { c.serverPorts[i] = serverPortListener{ln: l, p: currentServerPort} @@ -830,22 +838,25 @@ func (c *serverCommand) fixURL(baseURLFromConfig, baseURLFromFlag string, port i return u.String(), nil } -func (c *serverCommand) partialReRender(urls ...string) error { +func (c *serverCommand) partialReRender(urls ...string) (err error) { defer func() { c.errState.setWasErr(false) }() - c.errState.setBuildErr(nil) - visited := types.NewEvictingStringQueue(len(urls)) + visited := types.NewEvictingQueue[string](len(urls)) for _, url := range urls { visited.Add(url) } - h, err := c.hugo() + var h *hugolib.HugoSites + h, err = c.hugo() if err != nil { - return err + return } + // Note: We do not set NoBuildLock as the file lock is not acquired at this stage. - return h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyVisited: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()}) + err = h.Build(hugolib.BuildCfg{NoBuildLock: false, RecentlyTouched: visited, PartialReRender: true, ErrRecovery: c.errState.wasErr()}) + + return } func (c *serverCommand) serve() error { @@ -886,16 +897,16 @@ func (c *serverCommand) serve() error { // To allow the en user to change the error template while the server is running, we use // the freshest template we can provide. var ( - errTempl tpl.Template - templHandler tpl.TemplateHandler + errTempl *tplimpl.TemplInfo + templHandler *tplimpl.TemplateStore ) - getErrorTemplateAndHandler := func(h *hugolib.HugoSites) (tpl.Template, tpl.TemplateHandler) { + getErrorTemplateAndHandler := func(h *hugolib.HugoSites) (*tplimpl.TemplInfo, *tplimpl.TemplateStore) { if h == nil { return errTempl, templHandler } - templHandler := h.Tmpl() - errTempl, found := templHandler.Lookup("_server/error.html") - if !found { + templHandler := h.GetTemplateStore() + errTempl := templHandler.LookupByPath("/_server/error.html") + if errTempl == nil { panic("template server/error.html not found") } return errTempl, templHandler @@ -996,6 +1007,13 @@ func (c *serverCommand) serve() error { c.r.Println("Press Ctrl+C to stop") + if c.openBrowser { + // There may be more than one baseURL in multihost mode, open the first. + if err := browser.OpenURL(baseURLs[0].String()); err != nil { + c.r.logger.Warnf("Failed to open browser: %s", err) + } + } + err = func() error { for { select { @@ -1216,3 +1234,24 @@ func formatByteCount(b uint64) string { return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) } + +func canRedirect(requestURIWithoutQuery string, r *http.Request) bool { + if r.Header.Get(hugoHeaderRedirect) != "" { + return false + } + return isNavigation(requestURIWithoutQuery, r) +} + +// Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate +// Fall back to the file extension if not set. +// The main take here is that we don't want to have CSS/JS files etc. partake in this logic. +func isNavigation(requestURIWithoutQuery string, r *http.Request) bool { + return r.Header.Get("Sec-Fetch-Mode") == "navigate" || isPropablyHTMLRequest(requestURIWithoutQuery) +} + +func isPropablyHTMLRequest(requestURIWithoutQuery string) bool { + if strings.HasSuffix(requestURIWithoutQuery, "/") || strings.HasSuffix(requestURIWithoutQuery, "html") || strings.HasSuffix(requestURIWithoutQuery, "htm") { + return true + } + return !strings.Contains(requestURIWithoutQuery, ".") +} diff --git a/common/collections/append.go b/common/collections/append.go index 8f1e21ea3..db9db8bf3 100644 --- a/common/collections/append.go +++ b/common/collections/append.go @@ -117,7 +117,7 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro tos = append(tos, nil) continue } - for i := 0; i < slice.Len(); i++ { + for i := range slice.Len() { tos = append(tos, slice.Index(i).Interface()) } } @@ -128,7 +128,7 @@ func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, erro func appendToInterfaceSlice(tov reflect.Value, from ...any) ([]any, error) { var tos []any - for i := 0; i < tov.Len(); i++ { + for i := range tov.Len() { tos = append(tos, tov.Index(i).Interface()) } diff --git a/common/collections/append_test.go b/common/collections/append_test.go index f791b731a..62d9015ce 100644 --- a/common/collections/append_test.go +++ b/common/collections/append_test.go @@ -15,6 +15,7 @@ package collections import ( "html/template" + "reflect" "testing" qt "github.com/frankban/quicktest" @@ -77,6 +78,7 @@ func TestAppend(t *testing.T) { {[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}}, {[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}}, {[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}}, + {[]string{"a", "b"}, []any{}, []string{"a", "b"}}, } { result, err := Append(test.start, test.addend...) @@ -146,3 +148,66 @@ func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) { c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"}) c.Assert(slice, qt.DeepEquals, []string{"d", "b"}) } + +func TestIndirect(t *testing.T) { + t.Parallel() + c := qt.New(t) + + type testStruct struct { + Field string + } + + var ( + nilPtr *testStruct + nilIface interface{} = nil + nonNilIface interface{} = &testStruct{Field: "hello"} + ) + + tests := []struct { + name string + input any + wantKind reflect.Kind + wantNil bool + }{ + { + name: "nil pointer", + input: nilPtr, + wantKind: reflect.Ptr, + wantNil: true, + }, + { + name: "nil interface", + input: nilIface, + wantKind: reflect.Invalid, + wantNil: false, + }, + { + name: "non-nil pointer to struct", + input: &testStruct{Field: "abc"}, + wantKind: reflect.Struct, + wantNil: false, + }, + { + name: "non-nil interface holding pointer", + input: nonNilIface, + wantKind: reflect.Struct, + wantNil: false, + }, + { + name: "plain value", + input: testStruct{Field: "xyz"}, + wantKind: reflect.Struct, + wantNil: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := reflect.ValueOf(tt.input) + got, isNil := indirect(v) + + c.Assert(got.Kind(), qt.Equals, tt.wantKind) + c.Assert(isNil, qt.Equals, tt.wantNil) + }) + } +} diff --git a/common/collections/slice_test.go b/common/collections/slice_test.go index 20961aac0..4008a5e6c 100644 --- a/common/collections/slice_test.go +++ b/common/collections/slice_test.go @@ -136,3 +136,37 @@ func TestSortedStringSlice(t *testing.T) { c.Assert(s.Count("z"), qt.Equals, 0) c.Assert(s.Count("a"), qt.Equals, 1) } + +func TestStringSliceToInterfaceSlice(t *testing.T) { + t.Parallel() + c := qt.New(t) + + tests := []struct { + name string + in []string + want []any + }{ + { + name: "empty slice", + in: []string{}, + want: []any{}, + }, + { + name: "single element", + in: []string{"hello"}, + want: []any{"hello"}, + }, + { + name: "multiple elements", + in: []string{"a", "b", "c"}, + want: []any{"a", "b", "c"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := StringSliceToInterfaceSlice(tt.in) + c.Assert(got, qt.DeepEquals, tt.want) + }) + } +} diff --git a/common/collections/stack.go b/common/collections/stack.go index 96d32fe4b..ff0db2f02 100644 --- a/common/collections/stack.go +++ b/common/collections/stack.go @@ -13,6 +13,8 @@ package collections +import "slices" + import "sync" // Stack is a simple LIFO stack that is safe for concurrent use. @@ -73,7 +75,7 @@ func (s *Stack[T]) DrainMatching(predicate func(T) bool) []T { for i := len(s.items) - 1; i >= 0; i-- { if predicate(s.items[i]) { items = append(items, s.items[i]) - s.items = append(s.items[:i], s.items[i+1:]...) + s.items = slices.Delete(s.items, i, i+1) } } return items diff --git a/common/collections/stack_test.go b/common/collections/stack_test.go new file mode 100644 index 000000000..965d4dbc8 --- /dev/null +++ b/common/collections/stack_test.go @@ -0,0 +1,77 @@ +package collections + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestNewStack(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStack[int]() + + c.Assert(s, qt.IsNotNil) +} + +func TestStackBasic(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStack[int]() + + c.Assert(s.Len(), qt.Equals, 0) + + s.Push(1) + s.Push(2) + s.Push(3) + + c.Assert(s.Len(), qt.Equals, 3) + + top, ok := s.Peek() + c.Assert(ok, qt.Equals, true) + c.Assert(top, qt.Equals, 3) + + popped, ok := s.Pop() + c.Assert(ok, qt.Equals, true) + c.Assert(popped, qt.Equals, 3) + + c.Assert(s.Len(), qt.Equals, 2) + + _, _ = s.Pop() + _, _ = s.Pop() + _, ok = s.Pop() + + c.Assert(ok, qt.Equals, false) +} + +func TestStackDrain(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStack[string]() + s.Push("a") + s.Push("b") + + got := s.Drain() + + c.Assert(got, qt.DeepEquals, []string{"a", "b"}) + c.Assert(s.Len(), qt.Equals, 0) +} + +func TestStackDrainMatching(t *testing.T) { + t.Parallel() + c := qt.New(t) + + s := NewStack[int]() + s.Push(1) + s.Push(2) + s.Push(3) + s.Push(4) + + got := s.DrainMatching(func(v int) bool { return v%2 == 0 }) + + c.Assert(got, qt.DeepEquals, []int{4, 2}) + c.Assert(s.Drain(), qt.DeepEquals, []int{1, 3}) +} diff --git a/common/constants/constants.go b/common/constants/constants.go index f8f057e05..c7bbaa541 100644 --- a/common/constants/constants.go +++ b/common/constants/constants.go @@ -21,6 +21,10 @@ const ( ErrRemoteGetCSV = "error-remote-getcsv" WarnFrontMatterParamsOverrides = "warning-frontmatter-params-overrides" + WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html" + WarnGoldmarkRawHTML = "warning-goldmark-raw-html" + WarnPartialSuperfluousPrefix = "warning-partial-superfluous-prefix" + WarnHomePageIsLeafBundle = "warning-home-page-is-leaf-bundle" ) // Field/method names with special meaning. @@ -39,7 +43,7 @@ const ( ResourceTransformationFingerprint = "fingerprint" ) -// IsResourceTransformationLinkChange returns whether the given name is a resource transformation that changes the permalink based on the content. +// IsResourceTransformationPermalinkHash returns whether the given name is a resource transformation that changes the permalink based on the content. func IsResourceTransformationPermalinkHash(name string) bool { return name == ResourceTransformationFingerprint } diff --git a/common/hashing/hashing.go b/common/hashing/hashing.go index 70aa74ecd..e45356758 100644 --- a/common/hashing/hashing.go +++ b/common/hashing/hashing.go @@ -38,6 +38,19 @@ func XXHashFromReader(r io.Reader) (uint64, int64, error) { return h.Sum64(), size, nil } +// XxHashFromReaderHexEncoded calculates the xxHash for the given reader +// and returns the hash as a hex encoded string. +func XxHashFromReaderHexEncoded(r io.Reader) (string, error) { + h := getXxHashReadFrom() + defer putXxHashReadFrom(h) + _, err := io.Copy(h, r) + if err != nil { + return "", err + } + hash := h.Sum(nil) + return hex.EncodeToString(hash), nil +} + // XXHashFromString calculates the xxHash for the given string. func XXHashFromString(s string) (uint64, error) { h := xxhash.New() @@ -70,6 +83,13 @@ func HashString(vs ...any) string { return strconv.FormatUint(hash, 10) } +// HashStringHex returns a hash from the given elements as a hex encoded string. +// See HashString for more information. +func HashStringHex(vs ...any) string { + hash := HashUint64(vs...) + return strconv.FormatUint(hash, 16) +} + var hashOptsPool = sync.Pool{ New: func() any { return &hashstructure.HashOptions{ @@ -103,16 +123,24 @@ func HashUint64(vs ...any) uint64 { o = elements } - hashOpts := getHashOpts() - defer putHashOpts(hashOpts) - - hash, err := hashstructure.Hash(o, hashOpts) + hash, err := Hash(o) if err != nil { panic(err) } return hash } +// Hash returns a hash from vs. +func Hash(vs ...any) (uint64, error) { + hashOpts := getHashOpts() + defer putHashOpts(hashOpts) + var v any = vs + if len(vs) == 1 { + v = vs[0] + } + return hashstructure.Hash(v, hashOpts) +} + type keyer interface { Key() string } diff --git a/common/hashing/hashing_test.go b/common/hashing/hashing_test.go index 09286c035..105b6d8b5 100644 --- a/common/hashing/hashing_test.go +++ b/common/hashing/hashing_test.go @@ -37,12 +37,12 @@ func TestXxHashFromReaderPara(t *testing.T) { c := qt.New(t) var wg sync.WaitGroup - for i := 0; i < 10; i++ { + for i := range 10 { i := i wg.Add(1) go func() { defer wg.Done() - for j := 0; j < 100; j++ { + for j := range 100 { s := strings.Repeat("Hello ", i+j+1*42) r := strings.NewReader(s) got, size, err := XXHashFromReader(r) @@ -142,3 +142,16 @@ func BenchmarkHashString(b *testing.B) { }) } } + +func BenchmarkHashMap(b *testing.B) { + m := map[string]any{} + for i := range 1000 { + m[fmt.Sprintf("key%d", i)] = i + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + HashString(m) + } +} diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go index 1ece0cca4..acaebb4bc 100644 --- a/common/herrors/error_locator.go +++ b/common/herrors/error_locator.go @@ -152,10 +152,7 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext } if ectx.Position.LineNumber > 0 { - low := ectx.Position.LineNumber - 3 - if low < 0 { - low = 0 - } + low := max(ectx.Position.LineNumber-3, 0) if ectx.Position.LineNumber > 2 { ectx.LinesPos = 2 @@ -163,10 +160,7 @@ func locateError(r io.Reader, le FileError, matches LineMatcherFn) *ErrorContext ectx.LinesPos = ectx.Position.LineNumber - 1 } - high := ectx.Position.LineNumber + 2 - if high > len(lines) { - high = len(lines) - } + high := min(ectx.Position.LineNumber+2, len(lines)) ectx.Lines = lines[low:high] diff --git a/common/herrors/errors.go b/common/herrors/errors.go index 40833e55c..c7ee90dd0 100644 --- a/common/herrors/errors.go +++ b/common/herrors/errors.go @@ -133,6 +133,21 @@ func IsNotExist(err error) bool { return false } +// IsExist returns true if the error is a file exists error. +// Unlike os.IsExist, this also considers wrapped errors. +func IsExist(err error) bool { + if os.IsExist(err) { + return true + } + + // os.IsExist does not consider wrapped errors. + if os.IsExist(errors.Unwrap(err)) { + return true + } + + return false +} + var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`) const deferredPrefix = "__hdeferred/" diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 32a6f0081..38b198656 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -20,8 +20,6 @@ import ( "io" "path/filepath" - godartsassv1 "github.com/bep/godartsass" - "github.com/bep/godartsass/v2" "github.com/bep/golibsass/libsass/libsasserrors" "github.com/gohugoio/hugo/common/paths" @@ -153,8 +151,6 @@ func (e *fileError) causeString() string { // Avoid repeating the file info in the error message. case godartsass.SassError: return v.Message - case godartsassv1.SassError: - return v.Message case libsasserrors.Error: return v.Message default: @@ -262,8 +258,27 @@ func openFile(filename string, fs afero.Fs) (afero.File, string, error) { return f, realFilename, nil } -// Cause returns the underlying error or itself if it does not implement Unwrap. +// Cause returns the underlying error, that is, +// it unwraps errors until it finds one that does not implement +// the Unwrap method. +// For a shallow variant, see Unwrap. func Cause(err error) error { + type unwrapper interface { + Unwrap() error + } + + for err != nil { + cause, ok := err.(unwrapper) + if !ok { + break + } + err = cause.Unwrap() + } + return err +} + +// Unwrap returns the underlying error or itself if it does not implement Unwrap. +func Unwrap(err error) error { if u := errors.Unwrap(err); u != nil { return u } @@ -271,7 +286,7 @@ func Cause(err error) error { } func extractFileTypePos(err error) (string, text.Position) { - err = Cause(err) + err = Unwrap(err) var fileType string @@ -388,14 +403,7 @@ func extractPosition(e error) (pos text.Position) { case godartsass.SassError: span := v.Span start := span.Start - filename, _ := paths.UrlToFilename(span.Url) - pos.Filename = filename - pos.Offset = start.Offset - pos.ColumnNumber = start.Column - case godartsassv1.SassError: - span := v.Span - start := span.Start - filename, _ := paths.UrlToFilename(span.Url) + filename, _ := paths.UrlStringToFilename(span.Url) pos.Filename = filename pos.Offset = start.Offset pos.ColumnNumber = start.Column diff --git a/common/hexec/exec.go b/common/hexec/exec.go index 4f23d20f5..c3a6ebf57 100644 --- a/common/hexec/exec.go +++ b/common/hexec/exec.go @@ -26,7 +26,9 @@ import ( "strings" "sync" - "github.com/cli/safeexec" + "github.com/bep/logg" + "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/security" ) @@ -86,7 +88,7 @@ var WithEnviron = func(env []string) func(c *commandeer) { } // New creates a new Exec using the provided security config. -func New(cfg security.Config, workingDir string) *Exec { +func New(cfg security.Config, workingDir string, log loggers.Logger) *Exec { var baseEnviron []string for _, v := range os.Environ() { k, _ := config.SplitEnvVar(v) @@ -96,9 +98,11 @@ func New(cfg security.Config, workingDir string) *Exec { } return &Exec{ - sc: cfg, - workingDir: workingDir, - baseEnviron: baseEnviron, + sc: cfg, + workingDir: workingDir, + infol: log.InfoCommand("exec"), + baseEnviron: baseEnviron, + newNPXRunnerCache: maps.NewCache[string, func(arg ...any) (Runner, error)](), } } @@ -108,28 +112,18 @@ func IsNotFound(err error) bool { return errors.As(err, ¬FoundErr) } -// SafeCommand is a wrapper around os/exec Command which uses a LookPath -// implementation that does not search in current directory before looking in PATH. -// See https://github.com/cli/safeexec and the linked issues. -func SafeCommand(name string, arg ...string) (*exec.Cmd, error) { - bin, err := safeexec.LookPath(name) - if err != nil { - return nil, err - } - - return exec.Command(bin, arg...), nil -} - // Exec enforces a security policy for commands run via os/exec. type Exec struct { sc security.Config workingDir string + infol logg.LevelLogger // os.Environ filtered by the Exec.OsEnviron whitelist filter. baseEnviron []string - npxInit sync.Once - npxAvailable bool + newNPXRunnerCache *maps.Cache[string, func(arg ...any) (Runner, error)] + npxInit sync.Once + npxAvailable bool } func (e *Exec) New(name string, arg ...any) (Runner, error) { @@ -155,25 +149,86 @@ func (e *Exec) new(name string, fullyQualifiedName string, arg ...any) (Runner, return cm.command(arg...) } +type binaryLocation int + +func (b binaryLocation) String() string { + switch b { + case binaryLocationNodeModules: + return "node_modules/.bin" + case binaryLocationNpx: + return "npx" + case binaryLocationPath: + return "PATH" + } + return "unknown" +} + +const ( + binaryLocationNodeModules binaryLocation = iota + 1 + binaryLocationNpx + binaryLocationPath +) + // Npx will in order: // 1. Try fo find the binary in the WORKINGDIR/node_modules/.bin directory. // 2. If not found, and npx is available, run npx --no-install . // 3. Fall back to the PATH. +// If name is "tailwindcss", we will try the PATH as the second option. func (e *Exec) Npx(name string, arg ...any) (Runner, error) { - // npx is slow, so first try the common case. - nodeBinFilename := filepath.Join(e.workingDir, nodeModulesBinPath, name) - _, err := safeexec.LookPath(nodeBinFilename) - if err == nil { - return e.new(name, nodeBinFilename, arg...) + if err := e.sc.CheckAllowedExec(name); err != nil { + return nil, err } - e.checkNpx() - if e.npxAvailable { - r, err := e.npx(name, arg...) - if err == nil { - return r, nil + + newRunner, err := e.newNPXRunnerCache.GetOrCreate(name, func() (func(...any) (Runner, error), error) { + type tryFunc func() func(...any) (Runner, error) + tryFuncs := map[binaryLocation]tryFunc{ + binaryLocationNodeModules: func() func(...any) (Runner, error) { + nodeBinFilename := filepath.Join(e.workingDir, nodeModulesBinPath, name) + _, err := exec.LookPath(nodeBinFilename) + if err != nil { + return nil + } + return func(arg2 ...any) (Runner, error) { + return e.new(name, nodeBinFilename, arg2...) + } + }, + binaryLocationNpx: func() func(...any) (Runner, error) { + e.checkNpx() + if !e.npxAvailable { + return nil + } + return func(arg2 ...any) (Runner, error) { + return e.npx(name, arg2...) + } + }, + binaryLocationPath: func() func(...any) (Runner, error) { + if _, err := exec.LookPath(name); err != nil { + return nil + } + return func(arg2 ...any) (Runner, error) { + return e.New(name, arg2...) + } + }, } + + locations := []binaryLocation{binaryLocationNodeModules, binaryLocationNpx, binaryLocationPath} + if name == "tailwindcss" { + // See https://github.com/gohugoio/hugo/issues/13221#issuecomment-2574801253 + locations = []binaryLocation{binaryLocationNodeModules, binaryLocationPath, binaryLocationNpx} + } + for _, loc := range locations { + if f := tryFuncs[loc](); f != nil { + e.infol.Logf("resolve %q using %s", name, loc) + return f, nil + } + } + return nil, &NotFoundError{name: name, method: fmt.Sprintf("in %s", locations[len(locations)-1])} + }) + if err != nil { + return nil, err } - return e.New(name, arg...) + + return newRunner(arg...) } const ( @@ -278,7 +333,7 @@ func (c *commandeer) command(arg ...any) (*cmdWrapper, error) { bin = c.fullyQualifiedName } else { var err error - bin, err = safeexec.LookPath(c.name) + bin, err = exec.LookPath(c.name) if err != nil { return nil, &NotFoundError{ name: c.name, @@ -316,7 +371,7 @@ func InPath(binaryName string) bool { if strings.Contains(binaryName, "/") { panic("binary name should not contain any slash") } - _, err := safeexec.LookPath(binaryName) + _, err := exec.LookPath(binaryName) return err == nil } @@ -326,7 +381,7 @@ func LookPath(binaryName string) string { if strings.Contains(binaryName, "/") { panic("binary name should not contain any slash") } - s, err := safeexec.LookPath(binaryName) + s, err := exec.LookPath(binaryName) if err != nil { return "" } diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index 5113a3886..545371374 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -74,6 +74,16 @@ func IsTruthful(in any) bool { } } +// IsMap reports whether v is a map. +func IsMap(v any) bool { + return reflect.ValueOf(v).Kind() == reflect.Map +} + +// IsSlice reports whether v is a slice. +func IsSlice(v any) bool { + return reflect.ValueOf(v).Kind() == reflect.Slice +} + var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem() // IsTruthfulValue returns whether the given value has a meaningful truth value. @@ -124,12 +134,7 @@ type methodKey struct { name string } -type methods struct { - sync.RWMutex - cache map[methodKey]int -} - -var methodCache = &methods{cache: make(map[methodKey]int)} +var methodCache sync.Map // GetMethodByName is the same as reflect.Value.MethodByName, but it caches the // type lookup. @@ -147,22 +152,16 @@ func GetMethodByName(v reflect.Value, name string) reflect.Value { // -1 if no such method exists. func GetMethodIndexByName(tp reflect.Type, name string) int { k := methodKey{tp, name} - methodCache.RLock() - index, found := methodCache.cache[k] - methodCache.RUnlock() + v, found := methodCache.Load(k) if found { - return index + return v.(int) } - - methodCache.Lock() - defer methodCache.Unlock() - m, ok := tp.MethodByName(name) - index = m.Index + index := m.Index if !ok { index = -1 } - methodCache.cache[k] = index + methodCache.Store(k, index) if !ok { return -1 @@ -223,6 +222,27 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) { return time.Time{}, false } +// ToSliceAny converts the given value to a slice of any if possible. +func ToSliceAny(v any) ([]any, bool) { + if v == nil { + return nil, false + } + switch vv := v.(type) { + case []any: + return vv, true + default: + vvv := reflect.ValueOf(v) + if vvv.Kind() == reflect.Slice { + out := make([]any, vvv.Len()) + for i := range vvv.Len() { + out[i] = vvv.Index(i).Interface() + } + return out, true + } + } + return nil, false +} + func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value { fn := v.MethodByName(name) var args []reflect.Value diff --git a/common/hreflect/helpers_test.go b/common/hreflect/helpers_test.go index 27b774337..cbcad0f22 100644 --- a/common/hreflect/helpers_test.go +++ b/common/hreflect/helpers_test.go @@ -50,6 +50,19 @@ func TestIsContextType(t *testing.T) { c.Assert(IsContextType(reflect.TypeOf(valueCtx)), qt.IsTrue) } +func TestToSliceAny(t *testing.T) { + c := qt.New(t) + + checkOK := func(in any, expected []any) { + out, ok := ToSliceAny(in) + c.Assert(ok, qt.Equals, true) + c.Assert(out, qt.DeepEquals, expected) + } + + checkOK([]any{1, 2, 3}, []any{1, 2, 3}) + checkOK([]int{1, 2, 3}, []any{1, 2, 3}) +} + func BenchmarkIsContextType(b *testing.B) { type k string b.Run("value", func(b *testing.B) { @@ -121,3 +134,17 @@ func BenchmarkGetMethodByName(b *testing.B) { } } } + +func BenchmarkGetMethodByNamePara(b *testing.B) { + v := reflect.ValueOf(&testStruct{}) + methods := []string{"Method1", "Method2", "Method3", "Method4", "Method5"} + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + for _, method := range methods { + _ = GetMethodByName(v, method) + } + } + }) +} diff --git a/common/hstrings/strings.go b/common/hstrings/strings.go index 1232eee37..1de38678f 100644 --- a/common/hstrings/strings.go +++ b/common/hstrings/strings.go @@ -16,6 +16,7 @@ package hstrings import ( "fmt" "regexp" + "slices" "strings" "sync" @@ -50,12 +51,7 @@ func (s StringEqualFold) Eq(s2 any) bool { // EqualAny returns whether a string is equal to any of the given strings. func EqualAny(a string, b ...string) bool { - for _, s := range b { - if a == s { - return true - } - } - return false + return slices.Contains(b, a) } // regexpCache represents a cache of regexp objects protected by a mutex. @@ -103,12 +99,7 @@ func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) { // InSlice checks if a string is an element of a slice of strings // and returns a boolean value. func InSlice(arr []string, el string) bool { - for _, v := range arr { - if v == el { - return true - } - } - return false + return slices.Contains(arr, el) } // InSlicEqualFold checks if a string is an element of a slice of strings @@ -137,7 +128,7 @@ func ToString(v any) (string, bool) { return "", false } -type Tuple struct { - First string - Second string -} +type ( + Strings2 [2]string + Strings3 [3]string +) diff --git a/common/hugio/hasBytesWriter_test.go b/common/hugio/hasBytesWriter_test.go index 49487ab0b..9e689a112 100644 --- a/common/hugio/hasBytesWriter_test.go +++ b/common/hugio/hasBytesWriter_test.go @@ -46,18 +46,18 @@ func TestHasBytesWriter(t *testing.T) { return strings.Repeat("ab cfo", r.Intn(33)) } - for i := 0; i < 22; i++ { + for range 22 { h, w := neww() - fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr()) + fmt.Fprint(w, rndStr()+"abc __foobar"+rndStr()) c.Assert(h.Patterns[0].Match, qt.Equals, true) h, w = neww() - fmt.Fprintf(w, rndStr()+"abc __f") - fmt.Fprintf(w, "oo bar"+rndStr()) + fmt.Fprint(w, rndStr()+"abc __f") + fmt.Fprint(w, "oo bar"+rndStr()) c.Assert(h.Patterns[0].Match, qt.Equals, true) h, w = neww() - fmt.Fprintf(w, rndStr()+"abc __moo bar") + fmt.Fprint(w, rndStr()+"abc __moo bar") c.Assert(h.Patterns[0].Match, qt.Equals, false) } diff --git a/common/hugio/readers.go b/common/hugio/readers.go index 25e327908..c4304c84e 100644 --- a/common/hugio/readers.go +++ b/common/hugio/readers.go @@ -74,13 +74,13 @@ type StringReader interface { ReadString() string } -// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser +// NewReadSeekerNoOpCloserFromBytes uses bytes.NewReader to create a new ReadSeekerNoOpCloser // from the given bytes slice. func NewReadSeekerNoOpCloserFromBytes(content []byte) readSeekerNopCloser { return readSeekerNopCloser{bytes.NewReader(content)} } -// NewReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker. +// NewOpenReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker. // The ReadSeeker will be seeked to the beginning before returned. func NewOpenReadSeekCloser(r ReadSeekCloser) OpenReadSeekCloser { return func() (ReadSeekCloser, error) { diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index f21103940..764a86a97 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -25,14 +25,13 @@ import ( "sync" "time" - godartsassv1 "github.com/bep/godartsass" "github.com/bep/logg" - "github.com/mitchellh/mapstructure" "github.com/bep/godartsass/v2" "github.com/gohugoio/hugo/common/hcontext" "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/hugofs/files" "github.com/spf13/afero" @@ -55,6 +54,8 @@ var ( vendorInfo string ) +var _ maps.StoreProvider = (*HugoInfo)(nil) + // HugoInfo contains information about the current Hugo environment type HugoInfo struct { CommitHash string @@ -72,6 +73,8 @@ type HugoInfo struct { conf ConfigProvider deps []*Dependency + store *maps.Scratch + // Context gives access to some of the context scoped variables. Context Context } @@ -116,6 +119,10 @@ func (i HugoInfo) Deps() []*Dependency { return i.deps } +func (i HugoInfo) Store() *maps.Scratch { + return i.store +} + // Deprecated: Use hugo.IsMultihost instead. func (i HugoInfo) IsMultiHost() bool { Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0") @@ -132,9 +139,13 @@ func (i HugoInfo) IsMultilingual() bool { return i.conf.IsMultilingual() } -type contextKey string +type contextKey uint8 -var markupScope = hcontext.NewContextDispatcher[string](contextKey("markupScope")) +const ( + contextKeyMarkupScope contextKey = iota +) + +var markupScope = hcontext.NewContextDispatcher[string](contextKeyMarkupScope) type Context struct{} @@ -185,6 +196,7 @@ func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo { Environment: conf.Environment(), conf: conf, deps: deps, + store: maps.NewScratch(), GoVersion: goVersion, } } @@ -308,7 +320,7 @@ func GetDependencyListNonGo() []string { if dartSass := dartSassVersion(); dartSass.ProtocolVersion != "" { dartSassPath := "github.com/sass/dart-sass-embedded" - if IsDartSassV2() { + if IsDartSassGeV2() { dartSassPath = "github.com/sass/dart-sass" } deps = append(deps, @@ -355,22 +367,15 @@ type Dependency struct { } func dartSassVersion() godartsass.DartSassVersion { - if DartSassBinaryName == "" { + if DartSassBinaryName == "" || !IsDartSassGeV2() { return godartsass.DartSassVersion{} } - if IsDartSassV2() { - v, _ := godartsass.Version(DartSassBinaryName) - return v - } - - v, _ := godartsassv1.Version(DartSassBinaryName) - var vv godartsass.DartSassVersion - mapstructure.WeakDecode(v, &vv) - return vv + v, _ := godartsass.Version(DartSassBinaryName) + return v } // DartSassBinaryName is the name of the Dart Sass binary to use. -// TODO(beop) find a better place for this. +// TODO(bep) find a better place for this. var DartSassBinaryName string func init() { @@ -395,7 +400,10 @@ var ( dartSassBinaryNamesV2 = []string{"dart-sass", "sass"} ) -func IsDartSassV2() bool { +// TODO(bep) we eventually want to remove this, but keep it for a while to throw an informative error. +// We stopped supporting the old binary in Hugo 0.139.0. +func IsDartSassGeV2() bool { + // dart-sass-embedded was the first version of the embedded Dart Sass before it was moved into the main project. return !strings.Contains(DartSassBinaryName, "embedded") } @@ -407,22 +415,39 @@ func IsDartSassV2() bool { // 2. Their theme to work for at least the last few Hugo versions. func Deprecate(item, alternative string, version string) { level := deprecationLogLevelFromVersion(version) - DeprecateLevel(item, alternative, version, level) + deprecateLevel(item, alternative, version, level) +} + +// See Deprecate for details. +func DeprecateWithLogger(item, alternative string, version string, log logg.Logger) { + level := deprecationLogLevelFromVersion(version) + deprecateLevelWithLogger(item, alternative, version, level, log) +} + +// DeprecateLevelMin informs about a deprecation starting at the given version, but with a minimum log level. +func DeprecateLevelMin(item, alternative string, version string, minLevel logg.Level) { + level := max(deprecationLogLevelFromVersion(version), minLevel) + deprecateLevel(item, alternative, version, level) +} + +// deprecateLevel informs about a deprecation logging at the given level. +func deprecateLevel(item, alternative, version string, level logg.Level) { + deprecateLevelWithLogger(item, alternative, version, level, loggers.Log().Logger()) } // DeprecateLevel informs about a deprecation logging at the given level. -func DeprecateLevel(item, alternative, version string, level logg.Level) { +func deprecateLevelWithLogger(item, alternative, version string, level logg.Level, log logg.Logger) { var msg string if level == logg.LevelError { - msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in Hugo %s. %s", item, version, CurrentVersion.Next().ReleaseVersion(), alternative) + msg = fmt.Sprintf("%s was deprecated in Hugo %s and subsequently removed. %s", item, version, alternative) } else { msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative) } - loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf(msg) + log.WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf("%s", msg) } -// We ususally do about one minor version a month. +// We usually do about one minor version a month. // We want people to run at least the current and previous version without any warnings. // We want people who don't update Hugo that often to see the warnings and errors before we remove the feature. func deprecationLogLevelFromVersion(ver string) logg.Level { @@ -430,11 +455,11 @@ func deprecationLogLevelFromVersion(ver string) logg.Level { to := CurrentVersion minorDiff := to.Minor - from.Minor switch { - case minorDiff >= 12: - // Start failing the build after about a year. + case minorDiff >= 15: + // Start failing the build after about 15 months. return logg.LevelError - case minorDiff >= 6: - // Start printing warnings after about six months. + case minorDiff >= 3: + // Start printing warnings after about 3 months. return logg.LevelWarn default: return logg.LevelInfo diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go index 241d8c0ae..f938073da 100644 --- a/common/hugo/hugo_test.go +++ b/common/hugo/hugo_test.go @@ -57,12 +57,16 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) { c.Assert(deprecationLogLevelFromVersion("0.55.0"), qt.Equals, logg.LevelError) ver := CurrentVersion c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) - ver.Minor -= 1 - c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo) - ver.Minor -= 6 + ver.Minor -= 3 c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn) - ver.Minor -= 6 + ver.Minor -= 4 + c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn) + ver.Minor -= 13 c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError) + + // Added just to find the threshold for where we can remove deprecated items. + // Subtract 5 from the minor version of the first ERRORed version => 0.122.0. + c.Assert(deprecationLogLevelFromVersion("0.127.0"), qt.Equals, logg.LevelError) } func TestMarkupScope(t *testing.T) { diff --git a/common/hugo/vars_extended.go b/common/hugo/vars_extended.go index edbaff243..ab01e2647 100644 --- a/common/hugo/vars_extended.go +++ b/common/hugo/vars_extended.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build extended -// +build extended package hugo diff --git a/common/hugo/vars_regular.go b/common/hugo/vars_regular.go index 223df4b6c..a78aeb0b6 100644 --- a/common/hugo/vars_regular.go +++ b/common/hugo/vars_regular.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !extended -// +build !extended package hugo diff --git a/resources/resource_transformers/js/build_test.go b/common/hugo/vars_withdeploy.go similarity index 82% rename from resources/resource_transformers/js/build_test.go rename to common/hugo/vars_withdeploy.go index 30a4490ed..4e0c3efbb 100644 --- a/resources/resource_transformers/js/build_test.go +++ b/common/hugo/vars_withdeploy.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,4 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -package js +//go:build withdeploy + +package hugo + +var IsWithdeploy = true diff --git a/tpl/tplimpl/templateFuncster.go b/common/hugo/vars_withdeploy_off.go similarity index 82% rename from tpl/tplimpl/templateFuncster.go rename to common/hugo/vars_withdeploy_off.go index 96404f51b..36e9bd874 100644 --- a/tpl/tplimpl/templateFuncster.go +++ b/common/hugo/vars_withdeploy_off.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,4 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tplimpl +//go:build !withdeploy + +package hugo + +var IsWithdeploy = false diff --git a/common/hugo/version.go b/common/hugo/version.go index 6cabfdbb9..cf5988840 100644 --- a/common/hugo/version.go +++ b/common/hugo/version.go @@ -152,6 +152,9 @@ func BuildVersionString() string { if IsExtended { version += "+extended" } + if IsWithdeploy { + version += "+withdeploy" + } osArch := bi.GoOS + "/" + bi.GoArch diff --git a/common/hugo/version_current.go b/common/hugo/version_current.go index 870719dcb..ba367ceb5 100644 --- a/common/hugo/version_current.go +++ b/common/hugo/version_current.go @@ -17,7 +17,7 @@ package hugo // This should be the only one. var CurrentVersion = Version{ Major: 0, - Minor: 137, + Minor: 148, PatchLevel: 0, Suffix: "-DEV", } diff --git a/common/loggers/handlerterminal.go b/common/loggers/handlerterminal.go index 53f6e41da..c6a86d3a2 100644 --- a/common/loggers/handlerterminal.go +++ b/common/loggers/handlerterminal.go @@ -18,18 +18,19 @@ package loggers import ( "fmt" "io" + "regexp" "strings" "sync" "github.com/bep/logg" ) -// newNoColoursHandler creates a new NoColoursHandler -func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noColoursHandler { +// newNoAnsiEscapeHandler creates a new noAnsiEscapeHandler +func newNoAnsiEscapeHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noAnsiEscapeHandler { if predicate == nil { predicate = func(e *logg.Entry) bool { return true } } - return &noColoursHandler{ + return &noAnsiEscapeHandler{ noLevelPrefix: noLevelPrefix, outWriter: outWriter, errWriter: errWriter, @@ -37,15 +38,15 @@ func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, pre } } -type noColoursHandler struct { +type noAnsiEscapeHandler struct { mu sync.Mutex - outWriter io.Writer // Defaults to os.Stdout. - errWriter io.Writer // Defaults to os.Stderr. + outWriter io.Writer + errWriter io.Writer predicate func(*logg.Entry) bool noLevelPrefix bool } -func (h *noColoursHandler) HandleLog(e *logg.Entry) error { +func (h *noAnsiEscapeHandler) HandleLog(e *logg.Entry) error { if !h.predicate(e) { return nil } @@ -71,10 +72,12 @@ func (h *noColoursHandler) HandleLog(e *logg.Entry) error { prefix = prefix + ": " } + msg := stripANSI(e.Message) + if h.noLevelPrefix { - fmt.Fprintf(w, "%s%s", prefix, e.Message) + fmt.Fprintf(w, "%s%s", prefix, msg) } else { - fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, e.Message) + fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, msg) } for _, field := range e.Fields { @@ -88,3 +91,10 @@ func (h *noColoursHandler) HandleLog(e *logg.Entry) error { return nil } + +var ansiRe = regexp.MustCompile(`\x1b\[[0-9;]*m`) + +// stripANSI removes ANSI escape codes from s. +func stripANSI(s string) string { + return ansiRe.ReplaceAllString(s, "") +} diff --git a/common/loggers/handlerterminal_test.go b/common/loggers/handlerterminal_test.go new file mode 100644 index 000000000..f45ce80df --- /dev/null +++ b/common/loggers/handlerterminal_test.go @@ -0,0 +1,40 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// Some functions in this file (see comments) is based on the Go source code, +// copyright The Go Authors and governed by a BSD-style license. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package loggers + +import ( + "bytes" + "testing" + + "github.com/bep/logg" + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/terminal" +) + +func TestNoAnsiEscapeHandler(t *testing.T) { + c := qt.New(t) + + test := func(s string) { + c.Assert(stripANSI(terminal.Notice(s)), qt.Equals, s) + } + test(`error in "file.md:1:2"`) + + var buf bytes.Buffer + h := newNoAnsiEscapeHandler(&buf, &buf, false, nil) + h.HandleLog(&logg.Entry{Message: terminal.Notice(`error in "file.md:1:2"`), Level: logg.LevelInfo}) + + c.Assert(buf.String(), qt.Equals, "INFO error in \"file.md:1:2\"\n") +} diff --git a/common/loggers/logger.go b/common/loggers/logger.go index 4e2f3ab21..a013049f7 100644 --- a/common/loggers/logger.go +++ b/common/loggers/logger.go @@ -38,8 +38,8 @@ var ( // Options defines options for the logger. type Options struct { Level logg.Level - Stdout io.Writer - Stderr io.Writer + StdOut io.Writer + StdErr io.Writer DistinctLevel logg.Level StoreErrors bool HandlerPost func(e *logg.Entry) error @@ -48,21 +48,22 @@ type Options struct { // New creates a new logger with the given options. func New(opts Options) Logger { - if opts.Stdout == nil { - opts.Stdout = os.Stdout + if opts.StdOut == nil { + opts.StdOut = os.Stdout } - if opts.Stderr == nil { - opts.Stderr = os.Stdout + if opts.StdErr == nil { + opts.StdErr = os.Stderr } + if opts.Level == 0 { opts.Level = logg.LevelWarn } var logHandler logg.Handler - if terminal.PrintANSIColors(os.Stdout) { - logHandler = newDefaultHandler(opts.Stdout, opts.Stderr) + if terminal.PrintANSIColors(os.Stderr) { + logHandler = newDefaultHandler(opts.StdErr, opts.StdErr) } else { - logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil) + logHandler = newNoAnsiEscapeHandler(opts.StdErr, opts.StdErr, false, nil) } errorsw := &strings.Builder{} @@ -95,7 +96,7 @@ func New(opts Options) Logger { } if opts.StoreErrors { - h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool { + h := newNoAnsiEscapeHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool { return e.Level >= logg.LevelError }) @@ -137,7 +138,8 @@ func New(opts Options) Logger { logCounters: logCounters, errors: errorsw, reset: reset, - out: opts.Stdout, + stdOut: opts.StdOut, + stdErr: opts.StdErr, level: opts.Level, logger: logger, tracel: l.WithLevel(logg.LevelTrace), @@ -153,8 +155,6 @@ func NewDefault() Logger { opts := Options{ DistinctLevel: logg.LevelWarn, Level: logg.LevelWarn, - Stdout: os.Stdout, - Stderr: os.Stdout, } return New(opts) } @@ -163,8 +163,6 @@ func NewTrace() Logger { opts := Options{ DistinctLevel: logg.LevelWarn, Level: logg.LevelTrace, - Stdout: os.Stdout, - Stderr: os.Stdout, } return New(opts) } @@ -189,7 +187,8 @@ type Logger interface { Level() logg.Level LoggCount(logg.Level) int Logger() logg.Logger - Out() io.Writer + StdOut() io.Writer + StdErr() io.Writer Printf(format string, v ...any) Println(v ...any) PrintTimerIfDelayed(start time.Time, name string) @@ -207,7 +206,8 @@ type logAdapter struct { logCounters *logLevelCounter errors *strings.Builder reset func() - out io.Writer + stdOut io.Writer + stdErr io.Writer level logg.Level logger logg.Logger tracel logg.LevelLogger @@ -259,8 +259,12 @@ func (l *logAdapter) Logger() logg.Logger { return l.logger } -func (l *logAdapter) Out() io.Writer { - return l.out +func (l *logAdapter) StdOut() io.Writer { + return l.stdOut +} + +func (l *logAdapter) StdErr() io.Writer { + return l.stdErr } // PrintTimerIfDelayed prints a time statement to the FEEDBACK logger @@ -271,7 +275,7 @@ func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) { if milli < 500 { return } - l.Printf("%s in %v ms", name, milli) + fmt.Fprintf(l.stdErr, "%s in %v ms", name, milli) } func (l *logAdapter) Printf(format string, v ...any) { @@ -279,11 +283,11 @@ func (l *logAdapter) Printf(format string, v ...any) { if !strings.HasSuffix(format, "\n") { format += "\n" } - fmt.Fprintf(l.out, format, v...) + fmt.Fprintf(l.stdOut, format, v...) } func (l *logAdapter) Println(v ...any) { - fmt.Fprintln(l.out, v...) + fmt.Fprintln(l.stdOut, v...) } func (l *logAdapter) Reset() { diff --git a/common/loggers/logger_test.go b/common/loggers/logger_test.go index dcf94b123..bc8975b06 100644 --- a/common/loggers/logger_test.go +++ b/common/loggers/logger_test.go @@ -31,13 +31,13 @@ func TestLogDistinct(t *testing.T) { opts := loggers.Options{ DistinctLevel: logg.LevelWarn, StoreErrors: true, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) - for i := 0; i < 10; i++ { + for range 10 { l.Errorln("error 1") l.Errorln("error 2") l.Warnln("warn 1") @@ -54,8 +54,8 @@ func TestHookLast(t *testing.T) { HandlerPost: func(e *logg.Entry) error { panic(e.Message) }, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) @@ -70,8 +70,8 @@ func TestOptionStoreErrors(t *testing.T) { opts := loggers.Options{ StoreErrors: true, - Stderr: &sb, - Stdout: &sb, + StdErr: &sb, + StdOut: &sb, } l := loggers.New(opts) @@ -131,13 +131,13 @@ func TestReset(t *testing.T) { opts := loggers.Options{ StoreErrors: true, DistinctLevel: logg.LevelWarn, - Stdout: io.Discard, - Stderr: io.Discard, + StdOut: io.Discard, + StdErr: io.Discard, } l := loggers.New(opts) - for i := 0; i < 3; i++ { + for range 3 { l.Errorln("error 1") l.Errorln("error 2") l.Errorln("error 1") diff --git a/common/loggers/loggerglobal.go b/common/loggers/loggerglobal.go index c3e2970d0..b8c9a6931 100644 --- a/common/loggers/loggerglobal.go +++ b/common/loggers/loggerglobal.go @@ -21,7 +21,15 @@ import ( "github.com/bep/logg" ) -func InitGlobalLogger(level logg.Level, panicOnWarnings bool) { +// SetGlobalLogger sets the global logger. +// This is used in a few places in Hugo, e.g. deprecated functions. +func SetGlobalLogger(logger Logger) { + logMu.Lock() + defer logMu.Unlock() + log = logger +} + +func initGlobalLogger(level logg.Level, panicOnWarnings bool) { logMu.Lock() defer logMu.Unlock() var logHookLast func(e *logg.Entry) error @@ -50,5 +58,5 @@ func Log() Logger { var log Logger func init() { - InitGlobalLogger(logg.LevelWarn, false) + initGlobalLogger(logg.LevelWarn, false) } diff --git a/common/maps/cache.go b/common/maps/cache.go index 0175974b5..de1535994 100644 --- a/common/maps/cache.go +++ b/common/maps/cache.go @@ -13,11 +13,14 @@ package maps -import "sync" +import ( + "sync" +) // Cache is a simple thread safe cache backed by a map. type Cache[K comparable, T any] struct { - m map[K]T + m map[K]T + hasBeenInitialized bool sync.RWMutex } @@ -34,11 +37,16 @@ func (c *Cache[K, T]) Get(key K) (T, bool) { return zero, false } c.RLock() - v, found := c.m[key] + v, found := c.get(key) c.RUnlock() return v, found } +func (c *Cache[K, T]) get(key K) (T, bool) { + v, found := c.m[key] + return v, found +} + // GetOrCreate gets the value for the given key if it exists, or creates it if not. func (c *Cache[K, T]) GetOrCreate(key K, create func() (T, error)) (T, error) { c.RLock() @@ -61,19 +69,77 @@ func (c *Cache[K, T]) GetOrCreate(key K, create func() (T, error)) (T, error) { return v, nil } +// Contains returns whether the given key exists in the cache. +func (c *Cache[K, T]) Contains(key K) bool { + c.RLock() + _, found := c.m[key] + c.RUnlock() + return found +} + +// InitAndGet initializes the cache if not already done and returns the value for the given key. +// The init state will be reset on Reset or Drain. +func (c *Cache[K, T]) InitAndGet(key K, init func(get func(key K) (T, bool), set func(key K, value T)) error) (T, error) { + var v T + c.RLock() + if !c.hasBeenInitialized { + c.RUnlock() + if err := func() error { + c.Lock() + defer c.Unlock() + // Double check in case another goroutine has initialized it in the meantime. + if !c.hasBeenInitialized { + err := init(c.get, c.set) + if err != nil { + return err + } + c.hasBeenInitialized = true + } + return nil + }(); err != nil { + return v, err + } + // Reacquire the read lock. + c.RLock() + } + + v = c.m[key] + c.RUnlock() + + return v, nil +} + // Set sets the given key to the given value. func (c *Cache[K, T]) Set(key K, value T) { c.Lock() - c.m[key] = value + c.set(key, value) c.Unlock() } +// SetIfAbsent sets the given key to the given value if the key does not already exist in the cache. +func (c *Cache[K, T]) SetIfAbsent(key K, value T) { + c.RLock() + if _, found := c.get(key); !found { + c.RUnlock() + c.Set(key, value) + } else { + c.RUnlock() + } +} + +func (c *Cache[K, T]) set(key K, value T) { + c.m[key] = value +} + // ForEeach calls the given function for each key/value pair in the cache. -func (c *Cache[K, T]) ForEeach(f func(K, T)) { +// If the function returns false, the iteration stops. +func (c *Cache[K, T]) ForEeach(f func(K, T) bool) { c.RLock() defer c.RUnlock() for k, v := range c.m { - f(k, v) + if !f(k, v) { + return + } } } @@ -81,6 +147,7 @@ func (c *Cache[K, T]) Drain() map[K]T { c.Lock() m := c.m c.m = make(map[K]T) + c.hasBeenInitialized = false c.Unlock() return m } @@ -93,7 +160,8 @@ func (c *Cache[K, T]) Len() int { func (c *Cache[K, T]) Reset() { c.Lock() - c.m = make(map[K]T) + clear(c.m) + c.hasBeenInitialized = false c.Unlock() } diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index b4f9c5a3d..40c8ac824 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -73,10 +73,14 @@ func TestPrepareParams(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { // PrepareParams modifies input. + prepareClone := PrepareParamsClone(test.input) PrepareParams(test.input) if !reflect.DeepEqual(test.expected, test.input) { t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input) } + if !reflect.DeepEqual(test.expected, prepareClone) { + t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, prepareClone) + } }) } } diff --git a/common/maps/ordered.go b/common/maps/ordered.go new file mode 100644 index 000000000..0da9d239d --- /dev/null +++ b/common/maps/ordered.go @@ -0,0 +1,144 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package maps + +import ( + "slices" + + "github.com/gohugoio/hugo/common/hashing" +) + +// Ordered is a map that can be iterated in the order of insertion. +// Note that insertion order is not affected if a key is re-inserted into the map. +// In a nil map, all operations are no-ops. +// This is not thread safe. +type Ordered[K comparable, T any] struct { + // The keys in the order they were added. + keys []K + // The values. + values map[K]T +} + +// NewOrdered creates a new Ordered map. +func NewOrdered[K comparable, T any]() *Ordered[K, T] { + return &Ordered[K, T]{values: make(map[K]T)} +} + +// Set sets the value for the given key. +// Note that insertion order is not affected if a key is re-inserted into the map. +func (m *Ordered[K, T]) Set(key K, value T) { + if m == nil { + return + } + // Check if key already exists. + if _, found := m.values[key]; !found { + m.keys = append(m.keys, key) + } + m.values[key] = value +} + +// Get gets the value for the given key. +func (m *Ordered[K, T]) Get(key K) (T, bool) { + if m == nil { + var v T + return v, false + } + value, found := m.values[key] + return value, found +} + +// Has returns whether the given key exists in the map. +func (m *Ordered[K, T]) Has(key K) bool { + if m == nil { + return false + } + _, found := m.values[key] + return found +} + +// Delete deletes the value for the given key. +func (m *Ordered[K, T]) Delete(key K) { + if m == nil { + return + } + delete(m.values, key) + for i, k := range m.keys { + if k == key { + m.keys = slices.Delete(m.keys, i, i+1) + break + } + } +} + +// Clone creates a shallow copy of the map. +func (m *Ordered[K, T]) Clone() *Ordered[K, T] { + if m == nil { + return nil + } + clone := NewOrdered[K, T]() + for _, k := range m.keys { + clone.Set(k, m.values[k]) + } + return clone +} + +// Keys returns the keys in the order they were added. +func (m *Ordered[K, T]) Keys() []K { + if m == nil { + return nil + } + return m.keys +} + +// Values returns the values in the order they were added. +func (m *Ordered[K, T]) Values() []T { + if m == nil { + return nil + } + var values []T + for _, k := range m.keys { + values = append(values, m.values[k]) + } + return values +} + +// Len returns the number of items in the map. +func (m *Ordered[K, T]) Len() int { + if m == nil { + return 0 + } + return len(m.keys) +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// TODO(bep) replace with iter.Seq2 when we bump go Go 1.24. +func (m *Ordered[K, T]) Range(f func(key K, value T) bool) { + if m == nil { + return + } + for _, k := range m.keys { + if !f(k, m.values[k]) { + return + } + } +} + +// Hash calculates a hash from the values. +func (m *Ordered[K, T]) Hash() (uint64, error) { + if m == nil { + return 0, nil + } + return hashing.Hash(m.values) +} diff --git a/common/maps/ordered_test.go b/common/maps/ordered_test.go new file mode 100644 index 000000000..65a827810 --- /dev/null +++ b/common/maps/ordered_test.go @@ -0,0 +1,99 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package maps + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +func TestOrdered(t *testing.T) { + c := qt.New(t) + + m := NewOrdered[string, int]() + m.Set("a", 1) + m.Set("b", 2) + m.Set("c", 3) + + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 2, 3}) + + v, found := m.Get("b") + c.Assert(found, qt.Equals, true) + c.Assert(v, qt.Equals, 2) + + m.Set("b", 22) + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 22, 3}) + + m.Delete("b") + + c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "c"}) + c.Assert(m.Values(), qt.DeepEquals, []int{1, 3}) +} + +func TestOrderedHash(t *testing.T) { + c := qt.New(t) + + m := NewOrdered[string, int]() + m.Set("a", 1) + m.Set("b", 2) + m.Set("c", 3) + + h1, err := m.Hash() + c.Assert(err, qt.IsNil) + + m.Set("d", 4) + + h2, err := m.Hash() + c.Assert(err, qt.IsNil) + + c.Assert(h1, qt.Not(qt.Equals), h2) + + m = NewOrdered[string, int]() + m.Set("b", 2) + m.Set("a", 1) + m.Set("c", 3) + + h3, err := m.Hash() + c.Assert(err, qt.IsNil) + // Order does not matter. + c.Assert(h1, qt.Equals, h3) +} + +func TestOrderedNil(t *testing.T) { + c := qt.New(t) + + var m *Ordered[string, int] + + m.Set("a", 1) + c.Assert(m.Keys(), qt.IsNil) + c.Assert(m.Values(), qt.IsNil) + v, found := m.Get("a") + c.Assert(found, qt.Equals, false) + c.Assert(v, qt.Equals, 0) + m.Delete("a") + var b bool + m.Range(func(k string, v int) bool { + b = true + return true + }) + c.Assert(b, qt.Equals, false) + c.Assert(m.Len(), qt.Equals, 0) + c.Assert(m.Clone(), qt.IsNil) + h, err := m.Hash() + c.Assert(err, qt.IsNil) + c.Assert(h, qt.Equals, uint64(0)) +} diff --git a/common/maps/params.go b/common/maps/params.go index a8cbba555..819f796e4 100644 --- a/common/maps/params.go +++ b/common/maps/params.go @@ -303,7 +303,7 @@ func toMergeStrategy(v any) ParamsMergeStrategy { } // PrepareParams -// * makes all the keys in the given map lower cased and will do so +// * makes all the keys in the given map lower cased and will do so recursively. // * This will modify the map given. // * Any nested map[interface{}]interface{}, map[string]interface{},map[string]string will be converted to Params. // * Any _merge value will be converted to proper type and value. @@ -343,3 +343,42 @@ func PrepareParams(m Params) { } } } + +// PrepareParamsClone is like PrepareParams, but it does not modify the input. +func PrepareParamsClone(m Params) Params { + m2 := make(Params) + for k, v := range m { + var retyped bool + lKey := strings.ToLower(k) + if lKey == MergeStrategyKey { + v = toMergeStrategy(v) + retyped = true + } else { + switch vv := v.(type) { + case map[any]any: + var p Params = cast.ToStringMap(v) + v = PrepareParamsClone(p) + retyped = true + case map[string]any: + var p Params = v.(map[string]any) + v = PrepareParamsClone(p) + retyped = true + case map[string]string: + p := make(Params) + for k, v := range vv { + p[k] = v + } + v = p + PrepareParams(p) + retyped = true + } + } + + if retyped || k != lKey { + m2[lKey] = v + } else { + m2[k] = v + } + } + return m2 +} diff --git a/common/maps/scratch.go b/common/maps/scratch.go index e9f412540..cf5231783 100644 --- a/common/maps/scratch.go +++ b/common/maps/scratch.go @@ -22,31 +22,18 @@ import ( "github.com/gohugoio/hugo/common/math" ) -// Scratch is a writable context used for stateful operations in Page/Node rendering. +type StoreProvider interface { + // Store returns a Scratch that can be used to store temporary state. + // Store is not reset on server rebuilds. + Store() *Scratch +} + +// Scratch is a writable context used for stateful build operations type Scratch struct { values map[string]any mu sync.RWMutex } -// Scratcher provides a scratching service. -type Scratcher interface { - // Scratch returns a "scratch pad" that can be used to store state. - Scratch() *Scratch -} - -type scratcher struct { - s *Scratch -} - -func (s scratcher) Scratch() *Scratch { - return s.s -} - -// NewScratcher creates a new Scratcher. -func NewScratcher() Scratcher { - return scratcher{s: NewScratch()} -} - // Add will, for single values, add (using the + operator) the addend to the existing addend (if found). // Supports numeric values and strings. // diff --git a/common/maps/scratch_test.go b/common/maps/scratch_test.go index b515adb1d..f07169e61 100644 --- a/common/maps/scratch_test.go +++ b/common/maps/scratch_test.go @@ -140,7 +140,7 @@ func TestScratchInParallel(t *testing.T) { for i := 1; i <= 10; i++ { wg.Add(1) go func(j int) { - for k := 0; k < 10; k++ { + for k := range 10 { newVal := int64(k + j) _, err := scratch.Add(key, newVal) @@ -185,7 +185,7 @@ func TestScratchSetInMap(t *testing.T) { scratch.SetInMap("key", "zyx", "Zyx") scratch.SetInMap("key", "abc", "Abc (updated)") scratch.SetInMap("key", "def", "Def") - c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Abc (updated)", 1: "Def", 2: "Lux", 3: "Zyx"}) + c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, any([]any{"Abc (updated)", "Def", "Lux", "Zyx"})) } func TestScratchDeleteInMap(t *testing.T) { @@ -199,7 +199,7 @@ func TestScratchDeleteInMap(t *testing.T) { scratch.DeleteInMap("key", "abc") scratch.SetInMap("key", "def", "Def") scratch.DeleteInMap("key", "lmn") // Do nothing - c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, []any{0: "Def", 1: "Lux", 2: "Zyx"}) + c.Assert(scratch.GetSortedMapValues("key"), qt.DeepEquals, any([]any{"Def", "Lux", "Zyx"})) } func TestScratchGetSortedMapValues(t *testing.T) { diff --git a/common/math/math.go b/common/math/math.go index d4e2c1148..f88fbcd9c 100644 --- a/common/math/math.go +++ b/common/math/math.go @@ -26,29 +26,32 @@ func DoArithmetic(a, b any, op rune) (any, error) { var ai, bi int64 var af, bf float64 var au, bu uint64 + var isInt, isFloat, isUint bool switch av.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ai = av.Int() switch bv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + isInt = true bi = bv.Int() case reflect.Float32, reflect.Float64: + isFloat = true af = float64(ai) // may overflow - ai = 0 bf = bv.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: bu = bv.Uint() if ai >= 0 { + isUint = true au = uint64(ai) - ai = 0 } else { + isInt = true bi = int64(bu) // may overflow - bu = 0 } default: return nil, errors.New("can't apply the operator to the values") } case reflect.Float32, reflect.Float64: + isFloat = true af = av.Float() switch bv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -66,17 +69,18 @@ func DoArithmetic(a, b any, op rune) (any, error) { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: bi = bv.Int() if bi >= 0 { + isUint = true bu = uint64(bi) - bi = 0 } else { + isInt = true ai = int64(au) // may overflow - au = 0 } case reflect.Float32, reflect.Float64: + isFloat = true af = float64(au) // may overflow - au = 0 bf = bv.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + isUint = true bu = bv.Uint() default: return nil, errors.New("can't apply the operator to the values") @@ -94,38 +98,32 @@ func DoArithmetic(a, b any, op rune) (any, error) { switch op { case '+': - if ai != 0 || bi != 0 { + if isInt { return ai + bi, nil - } else if af != 0 || bf != 0 { + } else if isFloat { return af + bf, nil - } else if au != 0 || bu != 0 { - return au + bu, nil } - return 0, nil + return au + bu, nil case '-': - if ai != 0 || bi != 0 { + if isInt { return ai - bi, nil - } else if af != 0 || bf != 0 { + } else if isFloat { return af - bf, nil - } else if au != 0 || bu != 0 { - return au - bu, nil } - return 0, nil + return au - bu, nil case '*': - if ai != 0 || bi != 0 { + if isInt { return ai * bi, nil - } else if af != 0 || bf != 0 { + } else if isFloat { return af * bf, nil - } else if au != 0 || bu != 0 { - return au * bu, nil } - return 0, nil + return au * bu, nil case '/': - if bi != 0 { + if isInt && bi != 0 { return ai / bi, nil - } else if bf != 0 { + } else if isFloat && bf != 0 { return af / bf, nil - } else if bu != 0 { + } else if isUint && bu != 0 { return au / bu, nil } return nil, errors.New("can't divide the value by 0") diff --git a/common/math/math_test.go b/common/math/math_test.go index 89e391ce0..d75d30a69 100644 --- a/common/math/math_test.go +++ b/common/math/math_test.go @@ -30,10 +30,12 @@ func TestDoArithmetic(t *testing.T) { expect any }{ {3, 2, '+', int64(5)}, + {0, 0, '+', int64(0)}, {3, 2, '-', int64(1)}, {3, 2, '*', int64(6)}, {3, 2, '/', int64(1)}, {3.0, 2, '+', float64(5)}, + {0.0, 0, '+', float64(0.0)}, {3.0, 2, '-', float64(1)}, {3.0, 2, '*', float64(6)}, {3.0, 2, '/', float64(1.5)}, @@ -42,18 +44,22 @@ func TestDoArithmetic(t *testing.T) { {3, 2.0, '*', float64(6)}, {3, 2.0, '/', float64(1.5)}, {3.0, 2.0, '+', float64(5)}, + {0.0, 0.0, '+', float64(0.0)}, {3.0, 2.0, '-', float64(1)}, {3.0, 2.0, '*', float64(6)}, {3.0, 2.0, '/', float64(1.5)}, {uint(3), uint(2), '+', uint64(5)}, + {uint(0), uint(0), '+', uint64(0)}, {uint(3), uint(2), '-', uint64(1)}, {uint(3), uint(2), '*', uint64(6)}, {uint(3), uint(2), '/', uint64(1)}, {uint(3), 2, '+', uint64(5)}, + {uint(0), 0, '+', uint64(0)}, {uint(3), 2, '-', uint64(1)}, {uint(3), 2, '*', uint64(6)}, {uint(3), 2, '/', uint64(1)}, {3, uint(2), '+', uint64(5)}, + {0, uint(0), '+', uint64(0)}, {3, uint(2), '-', uint64(1)}, {3, uint(2), '*', uint64(6)}, {3, uint(2), '/', uint64(1)}, @@ -66,16 +72,15 @@ func TestDoArithmetic(t *testing.T) { {-3, uint(2), '*', int64(-6)}, {-3, uint(2), '/', int64(-1)}, {uint(3), 2.0, '+', float64(5)}, + {uint(0), 0.0, '+', float64(0)}, {uint(3), 2.0, '-', float64(1)}, {uint(3), 2.0, '*', float64(6)}, {uint(3), 2.0, '/', float64(1.5)}, {3.0, uint(2), '+', float64(5)}, + {0.0, uint(0), '+', float64(0)}, {3.0, uint(2), '-', float64(1)}, {3.0, uint(2), '*', float64(6)}, {3.0, uint(2), '/', float64(1.5)}, - {0, 0, '+', 0}, - {0, 0, '-', 0}, - {0, 0, '*', 0}, {"foo", "bar", '+', "foobar"}, {3, 0, '/', false}, {3.0, 0, '/', false}, diff --git a/common/para/para_test.go b/common/para/para_test.go index 2d9188ecf..cf24a4e37 100644 --- a/common/para/para_test.go +++ b/common/para/para_test.go @@ -42,7 +42,7 @@ func TestPara(t *testing.T) { c.Run("Order", func(c *qt.C) { n := 500 ints := make([]int, n) - for i := 0; i < n; i++ { + for i := range n { ints[i] = i } @@ -51,7 +51,7 @@ func TestPara(t *testing.T) { var result []int var mu sync.Mutex - for i := 0; i < n; i++ { + for i := range n { i := i r.Run(func() error { mu.Lock() @@ -78,7 +78,7 @@ func TestPara(t *testing.T) { var counter int64 - for i := 0; i < n; i++ { + for range n { r.Run(func() error { atomic.AddInt64(&counter, 1) time.Sleep(1 * time.Millisecond) diff --git a/common/paths/pathparser.go b/common/paths/pathparser.go index 94329fe7a..1cae710e8 100644 --- a/common/paths/pathparser.go +++ b/common/paths/pathparser.go @@ -23,6 +23,11 @@ import ( "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources/kinds" +) + +const ( + identifierBaseof = "baseof" ) // PathParser parses a path into a Path. @@ -33,6 +38,10 @@ type PathParser struct { // Reports whether the given language is disabled. IsLangDisabled func(string) bool + // IsOutputFormat reports whether the given name is a valid output format. + // The second argument is optional. + IsOutputFormat func(name, ext string) bool + // Reports whether the given ext is a content file. IsContentExt func(string) bool } @@ -83,13 +92,10 @@ func (pp *PathParser) Parse(c, s string) *Path { } func (pp *PathParser) newPath(component string) *Path { - return &Path{ - component: component, - posContainerLow: -1, - posContainerHigh: -1, - posSectionHigh: -1, - posIdentifierLanguage: -1, - } + p := &Path{} + p.reset() + p.component = component + return p } func (pp *PathParser) parse(component, s string) (*Path, error) { @@ -114,10 +120,101 @@ func (pp *PathParser) parse(component, s string) (*Path, error) { return p, nil } -func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) { - hasLang := pp.LanguageIndex != nil - hasLang = hasLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts) +func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot, numDots int, isLast bool) { + if p.posContainerHigh != -1 { + return + } + mayHaveLang := numDots > 1 && p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil + mayHaveLang = mayHaveLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts) + mayHaveOutputFormat := component == files.ComponentFolderLayouts + mayHaveKind := p.posIdentifierKind == -1 && mayHaveOutputFormat + var mayHaveLayout bool + if p.pathType == TypeShortcode { + mayHaveLayout = !isLast && component == files.ComponentFolderLayouts + } else { + mayHaveLayout = component == files.ComponentFolderLayouts + } + var found bool + var high int + if len(p.identifiersKnown) > 0 { + high = lastDot + } else { + high = len(p.s) + } + id := types.LowHigh[string]{Low: i + 1, High: high} + sid := p.s[id.Low:id.High] + + if len(p.identifiersKnown) == 0 { + // The first is always the extension. + p.identifiersKnown = append(p.identifiersKnown, id) + found = true + + // May also be the output format. + if mayHaveOutputFormat && pp.IsOutputFormat(sid, "") { + p.posIdentifierOutputFormat = 0 + } + } else { + + var langFound bool + + if mayHaveLang { + var disabled bool + _, langFound = pp.LanguageIndex[sid] + if !langFound { + disabled = pp.IsLangDisabled != nil && pp.IsLangDisabled(sid) + if disabled { + p.disabled = true + langFound = true + } + } + found = langFound + if langFound { + p.identifiersKnown = append(p.identifiersKnown, id) + p.posIdentifierLanguage = len(p.identifiersKnown) - 1 + } + } + + if !found && mayHaveOutputFormat { + // At this point we may already have resolved an output format, + // but we need to keep looking for a more specific one, e.g. amp before html. + // Use both name and extension to prevent + // false positives on the form css.html. + if pp.IsOutputFormat(sid, p.Ext()) { + found = true + p.identifiersKnown = append(p.identifiersKnown, id) + p.posIdentifierOutputFormat = len(p.identifiersKnown) - 1 + } + } + + if !found && mayHaveKind { + if kinds.GetKindMain(sid) != "" { + found = true + p.identifiersKnown = append(p.identifiersKnown, id) + p.posIdentifierKind = len(p.identifiersKnown) - 1 + } + } + + if !found && sid == identifierBaseof { + found = true + p.identifiersKnown = append(p.identifiersKnown, id) + p.posIdentifierBaseof = len(p.identifiersKnown) - 1 + } + + if !found && mayHaveLayout { + p.identifiersKnown = append(p.identifiersKnown, id) + p.posIdentifierLayout = len(p.identifiersKnown) - 1 + found = true + } + + if !found { + p.identifiersUnknown = append(p.identifiersUnknown, id) + } + + } +} + +func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) { if runtime.GOOS == "windows" { s = path.Clean(filepath.ToSlash(s)) if s == "." { @@ -140,46 +237,26 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) { p.s = s slashCount := 0 + lastDot := 0 + lastSlashIdx := strings.LastIndex(s, "/") + numDots := strings.Count(s[lastSlashIdx+1:], ".") + if strings.Contains(s, "/_shortcodes/") { + p.pathType = TypeShortcode + } for i := len(s) - 1; i >= 0; i-- { c := s[i] switch c { case '.': - if p.posContainerHigh == -1 { - var high int - if len(p.identifiers) > 0 { - high = p.identifiers[len(p.identifiers)-1].Low - 1 - } else { - high = len(p.s) - } - id := types.LowHigh[string]{Low: i + 1, High: high} - if len(p.identifiers) == 0 { - p.identifiers = append(p.identifiers, id) - } else if len(p.identifiers) == 1 { - // Check for a valid language. - s := p.s[id.Low:id.High] - - if hasLang { - var disabled bool - _, langFound := pp.LanguageIndex[s] - if !langFound { - disabled = pp.IsLangDisabled != nil && pp.IsLangDisabled(s) - if disabled { - p.disabled = true - langFound = true - } - } - if langFound { - p.posIdentifierLanguage = 1 - p.identifiers = append(p.identifiers, id) - } - } - } - } + pp.parseIdentifier(component, s, p, i, lastDot, numDots, false) + lastDot = i case '/': slashCount++ if p.posContainerHigh == -1 { + if lastDot > 0 { + pp.parseIdentifier(component, s, p, i, lastDot, numDots, true) + } p.posContainerHigh = i + 1 } else if p.posContainerLow == -1 { p.posContainerLow = i + 1 @@ -190,26 +267,52 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) { } } - if len(p.identifiers) > 0 { + if len(p.identifiersKnown) > 0 { isContentComponent := p.component == files.ComponentFolderContent || p.component == files.ComponentFolderArchetypes isContent := isContentComponent && pp.IsContentExt(p.Ext()) - id := p.identifiers[len(p.identifiers)-1] - b := p.s[p.posContainerHigh : id.Low-1] - if isContent { - switch b { - case "index": - p.bundleType = PathTypeLeaf - case "_index": - p.bundleType = PathTypeBranch - default: - p.bundleType = PathTypeContentSingle - } + id := p.identifiersKnown[len(p.identifiersKnown)-1] - if slashCount == 2 && p.IsLeafBundle() { - p.posSectionHigh = 0 + if id.Low > p.posContainerHigh { + b := p.s[p.posContainerHigh : id.Low-1] + if isContent { + switch b { + case "index": + p.pathType = TypeLeaf + case "_index": + p.pathType = TypeBranch + default: + p.pathType = TypeContentSingle + } + + if slashCount == 2 && p.IsLeafBundle() { + p.posSectionHigh = 0 + } + } else if b == files.NameContentData && files.IsContentDataExt(p.Ext()) { + p.pathType = TypeContentData } - } else if b == files.NameContentData && files.IsContentDataExt(p.Ext()) { - p.bundleType = PathTypeContentData + } + } + + if p.pathType < TypeMarkup && component == files.ComponentFolderLayouts { + if p.posIdentifierBaseof != -1 { + p.pathType = TypeBaseof + } else { + pth := p.Path() + if strings.Contains(pth, "/_shortcodes/") { + p.pathType = TypeShortcode + } else if strings.Contains(pth, "/_markup/") { + p.pathType = TypeMarkup + } else if strings.HasPrefix(pth, "/_partials/") { + p.pathType = TypePartial + } + } + } + + if p.pathType == TypeShortcode && p.posIdentifierLayout != -1 { + id := p.identifiersKnown[p.posIdentifierLayout] + if id.Low == p.posContainerHigh { + // First identifier is shortcode name. + p.posIdentifierLayout = -1 } } @@ -218,35 +321,44 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) { func ModifyPathBundleTypeResource(p *Path) { if p.IsContent() { - p.bundleType = PathTypeContentResource + p.pathType = TypeContentResource } else { - p.bundleType = PathTypeFile + p.pathType = TypeFile } } -type PathType int +//go:generate stringer -type Type + +type Type int const ( + // A generic resource, e.g. a JSON file. - PathTypeFile PathType = iota + TypeFile Type = iota // All below are content files. // A resource of a content type with front matter. - PathTypeContentResource + TypeContentResource // E.g. /blog/my-post.md - PathTypeContentSingle + TypeContentSingle // All below are bundled content files. // Leaf bundles, e.g. /blog/my-post/index.md - PathTypeLeaf + TypeLeaf // Branch bundles, e.g. /blog/_index.md - PathTypeBranch + TypeBranch // Content data file, _content.gotmpl. - PathTypeContentData + TypeContentData + + // Layout types. + TypeMarkup + TypeShortcode + TypePartial + TypeBaseof ) type Path struct { @@ -257,13 +369,18 @@ type Path struct { posContainerHigh int posSectionHigh int - component string - bundleType PathType + component string + pathType Type - identifiers []types.LowHigh[string] + identifiersKnown []types.LowHigh[string] + identifiersUnknown []types.LowHigh[string] - posIdentifierLanguage int - disabled bool + posIdentifierLanguage int + posIdentifierOutputFormat int + posIdentifierKind int + posIdentifierLayout int + posIdentifierBaseof int + disabled bool trimLeadingSlash bool @@ -293,9 +410,13 @@ func (p *Path) reset() { p.posContainerHigh = -1 p.posSectionHigh = -1 p.component = "" - p.bundleType = 0 - p.identifiers = p.identifiers[:0] + p.pathType = 0 + p.identifiersKnown = p.identifiersKnown[:0] p.posIdentifierLanguage = -1 + p.posIdentifierOutputFormat = -1 + p.posIdentifierKind = -1 + p.posIdentifierLayout = -1 + p.posIdentifierBaseof = -1 p.disabled = false p.trimLeadingSlash = false p.unnormalized = nil @@ -316,6 +437,9 @@ func (p *Path) norm(s string) string { // IdentifierBase satisfies identity.Identity. func (p *Path) IdentifierBase() string { + if p.Component() == files.ComponentFolderLayouts { + return p.Path() + } return p.Base() } @@ -332,6 +456,13 @@ func (p *Path) Container() string { return p.norm(p.s[p.posContainerLow : p.posContainerHigh-1]) } +func (p *Path) String() string { + if p == nil { + return "" + } + return p.Path() +} + // ContainerDir returns the container directory for this path. // For content bundles this will be the parent directory. func (p *Path) ContainerDir() string { @@ -352,13 +483,13 @@ func (p *Path) Section() string { // IsContent returns true if the path is a content file (e.g. mypost.md). // Note that this will also return true for content files in a bundle. func (p *Path) IsContent() bool { - return p.BundleType() >= PathTypeContentResource + return p.Type() >= TypeContentResource && p.Type() <= TypeContentData } // isContentPage returns true if the path is a content file (e.g. mypost.md), // but nof if inside a leaf bundle. func (p *Path) isContentPage() bool { - return p.BundleType() >= PathTypeContentSingle + return p.Type() >= TypeContentSingle && p.Type() <= TypeContentData } // Name returns the last element of path. @@ -372,7 +503,7 @@ func (p *Path) Name() string { // Name returns the last element of path without any extension. func (p *Path) NameNoExt() string { if i := p.identifierIndex(0); i != -1 { - return p.s[p.posContainerHigh : p.identifiers[i].Low-1] + return p.s[p.posContainerHigh : p.identifiersKnown[i].Low-1] } return p.s[p.posContainerHigh:] } @@ -384,7 +515,7 @@ func (p *Path) NameNoLang() string { return p.Name() } - return p.s[p.posContainerHigh:p.identifiers[i].Low-1] + p.s[p.identifiers[i].High:] + return p.s[p.posContainerHigh:p.identifiersKnown[i].Low-1] + p.s[p.identifiersKnown[i].High:] } // BaseNameNoIdentifier returns the logical base name for a resource without any identifier (e.g. no extension). @@ -398,10 +529,26 @@ func (p *Path) BaseNameNoIdentifier() string { // NameNoIdentifier returns the last element of path without any identifier (e.g. no extension). func (p *Path) NameNoIdentifier() string { - if len(p.identifiers) > 0 { - return p.s[p.posContainerHigh : p.identifiers[len(p.identifiers)-1].Low-1] + lowHigh := p.nameLowHigh() + return p.s[lowHigh.Low:lowHigh.High] +} + +func (p *Path) nameLowHigh() types.LowHigh[string] { + if len(p.identifiersKnown) > 0 { + lastID := p.identifiersKnown[len(p.identifiersKnown)-1] + if p.posContainerHigh == lastID.Low { + // The last identifier is the name. + return lastID + } + return types.LowHigh[string]{ + Low: p.posContainerHigh, + High: p.identifiersKnown[len(p.identifiersKnown)-1].Low - 1, + } + } + return types.LowHigh[string]{ + Low: p.posContainerHigh, + High: len(p.s), } - return p.s[p.posContainerHigh:] } // Dir returns all but the last element of path, typically the path's directory. @@ -421,6 +568,11 @@ func (p *Path) Path() (d string) { return p.norm(p.s) } +// PathNoLeadingSlash returns the full path without the leading slash. +func (p *Path) PathNoLeadingSlash() string { + return p.Path()[1:] +} + // Unnormalized returns the Path with the original case preserved. func (p *Path) Unnormalized() *Path { return p.unnormalized @@ -436,6 +588,28 @@ func (p *Path) PathNoIdentifier() string { return p.base(false, false) } +// PathBeforeLangAndOutputFormatAndExt returns the path up to the first identifier that is not a language or output format. +func (p *Path) PathBeforeLangAndOutputFormatAndExt() string { + if len(p.identifiersKnown) == 0 { + return p.norm(p.s) + } + i := p.identifierIndex(0) + + if j := p.posIdentifierOutputFormat; i == -1 || (j != -1 && j < i) { + i = j + } + if j := p.posIdentifierLanguage; i == -1 || (j != -1 && j < i) { + i = j + } + + if i == -1 { + return p.norm(p.s) + } + + id := p.identifiersKnown[i] + return p.norm(p.s[:id.Low-1]) +} + // PathRel returns the path relative to the given owner. func (p *Path) PathRel(owner *Path) string { ob := owner.Base() @@ -462,26 +636,42 @@ func (p *Path) Base() string { return p.base(!p.isContentPage(), p.IsBundle()) } +// Used in template lookups. +// For pages with Type set, we treat that as the section. +func (p *Path) BaseReTyped(typ string) (d string) { + base := p.Base() + if typ == "" || p.Section() == typ { + return base + } + d = "/" + typ + if p.posSectionHigh != -1 { + d += base[p.posSectionHigh:] + } + d = p.norm(d) + return +} + // BaseNoLeadingSlash returns the base path without the leading slash. func (p *Path) BaseNoLeadingSlash() string { return p.Base()[1:] } func (p *Path) base(preserveExt, isBundle bool) string { - if len(p.identifiers) == 0 { + if len(p.identifiersKnown) == 0 { return p.norm(p.s) } - if preserveExt && len(p.identifiers) == 1 { + if preserveExt && len(p.identifiersKnown) == 1 { // Preserve extension. return p.norm(p.s) } - id := p.identifiers[len(p.identifiers)-1] - high := id.Low - 1 + var high int if isBundle { high = p.posContainerHigh - 1 + } else { + high = p.nameLowHigh().High } if high == 0 { @@ -493,7 +683,7 @@ func (p *Path) base(preserveExt, isBundle bool) string { } // For txt files etc. we want to preserve the extension. - id = p.identifiers[0] + id := p.identifiersKnown[0] return p.norm(p.s[:high] + p.s[id.Low-1:id.High]) } @@ -502,8 +692,20 @@ func (p *Path) Ext() string { return p.identifierAsString(0) } +func (p *Path) OutputFormat() string { + return p.identifierAsString(p.posIdentifierOutputFormat) +} + +func (p *Path) Kind() string { + return p.identifierAsString(p.posIdentifierKind) +} + +func (p *Path) Layout() string { + return p.identifierAsString(p.posIdentifierLayout) +} + func (p *Path) Lang() string { - return p.identifierAsString(1) + return p.identifierAsString(p.posIdentifierLanguage) } func (p *Path) Identifier(i int) string { @@ -515,35 +717,43 @@ func (p *Path) Disabled() bool { } func (p *Path) Identifiers() []string { - ids := make([]string, len(p.identifiers)) - for i, id := range p.identifiers { + ids := make([]string, len(p.identifiersKnown)) + for i, id := range p.identifiersKnown { ids[i] = p.s[id.Low:id.High] } return ids } -func (p *Path) BundleType() PathType { - return p.bundleType +func (p *Path) IdentifiersUnknown() []string { + ids := make([]string, len(p.identifiersUnknown)) + for i, id := range p.identifiersUnknown { + ids[i] = p.s[id.Low:id.High] + } + return ids +} + +func (p *Path) Type() Type { + return p.pathType } func (p *Path) IsBundle() bool { - return p.bundleType >= PathTypeLeaf + return p.pathType >= TypeLeaf && p.pathType <= TypeContentData } func (p *Path) IsBranchBundle() bool { - return p.bundleType == PathTypeBranch + return p.pathType == TypeBranch } func (p *Path) IsLeafBundle() bool { - return p.bundleType == PathTypeLeaf + return p.pathType == TypeLeaf } func (p *Path) IsContentData() bool { - return p.bundleType == PathTypeContentData + return p.pathType == TypeContentData } -func (p Path) ForBundleType(t PathType) *Path { - p.bundleType = t +func (p Path) ForType(t Type) *Path { + p.pathType = t return &p } @@ -553,12 +763,12 @@ func (p *Path) identifierAsString(i int) string { return "" } - id := p.identifiers[i] + id := p.identifiersKnown[i] return p.s[id.Low:id.High] } func (p *Path) identifierIndex(i int) int { - if i < 0 || i >= len(p.identifiers) { + if i < 0 || i >= len(p.identifiersKnown) { return -1 } return i diff --git a/common/paths/pathparser_test.go b/common/paths/pathparser_test.go index e8fee96e1..b1734aef2 100644 --- a/common/paths/pathparser_test.go +++ b/common/paths/pathparser_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/gohugoio/hugo/hugofs/files" + "github.com/gohugoio/hugo/resources/kinds" qt "github.com/frankban/quicktest" ) @@ -26,10 +27,18 @@ var testParser = &PathParser{ LanguageIndex: map[string]int{ "no": 0, "en": 1, + "fr": 2, }, IsContentExt: func(ext string) bool { return ext == "md" }, + IsOutputFormat: func(name, ext string) bool { + switch name { + case "html", "amp", "csv", "rss": + return true + } + return false + }, } func TestParse(t *testing.T) { @@ -105,17 +114,19 @@ func TestParse(t *testing.T) { "Basic Markdown file", "/a/b/c.md", func(c *qt.C, p *Path) { + c.Assert(p.Ext(), qt.Equals, "md") + c.Assert(p.Type(), qt.Equals, TypeContentSingle) c.Assert(p.IsContent(), qt.IsTrue) c.Assert(p.IsLeafBundle(), qt.IsFalse) c.Assert(p.Name(), qt.Equals, "c.md") c.Assert(p.Base(), qt.Equals, "/a/b/c") + c.Assert(p.BaseReTyped("foo"), qt.Equals, "/foo/b/c") c.Assert(p.Section(), qt.Equals, "a") c.Assert(p.BaseNameNoIdentifier(), qt.Equals, "c") c.Assert(p.Path(), qt.Equals, "/a/b/c.md") c.Assert(p.Dir(), qt.Equals, "/a/b") c.Assert(p.Container(), qt.Equals, "b") c.Assert(p.ContainerDir(), qt.Equals, "/a/b") - c.Assert(p.Ext(), qt.Equals, "md") }, }, { @@ -130,7 +141,7 @@ func TestParse(t *testing.T) { // Reclassify it as a content resource. ModifyPathBundleTypeResource(p) - c.Assert(p.BundleType(), qt.Equals, PathTypeContentResource) + c.Assert(p.Type(), qt.Equals, TypeContentResource) c.Assert(p.IsContent(), qt.IsTrue) c.Assert(p.Name(), qt.Equals, "b.md") c.Assert(p.Base(), qt.Equals, "/a/b.md") @@ -163,8 +174,10 @@ func TestParse(t *testing.T) { c.Assert(p.NameNoIdentifier(), qt.Equals, "b.a.b") c.Assert(p.NameNoLang(), qt.Equals, "b.a.b.txt") c.Assert(p.Identifiers(), qt.DeepEquals, []string{"txt", "no"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{"b", "a", "b"}) c.Assert(p.Base(), qt.Equals, "/a/b.a.b.txt") c.Assert(p.BaseNoLeadingSlash(), qt.Equals, "a/b.a.b.txt") + c.Assert(p.Path(), qt.Equals, "/a/b.a.b.no.txt") c.Assert(p.PathNoLang(), qt.Equals, "/a/b.a.b.txt") c.Assert(p.Ext(), qt.Equals, "txt") c.Assert(p.PathNoIdentifier(), qt.Equals, "/a/b.a.b") @@ -174,7 +187,11 @@ func TestParse(t *testing.T) { "Home branch cundle", "/_index.md", func(c *qt.C, p *Path) { + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md"}) + c.Assert(p.IsBranchBundle(), qt.IsTrue) + c.Assert(p.IsBundle(), qt.IsTrue) c.Assert(p.Base(), qt.Equals, "/") + c.Assert(p.BaseReTyped("foo"), qt.Equals, "/foo") c.Assert(p.Path(), qt.Equals, "/_index.md") c.Assert(p.Container(), qt.Equals, "") c.Assert(p.ContainerDir(), qt.Equals, "/") @@ -185,12 +202,14 @@ func TestParse(t *testing.T) { "/a/index.md", func(c *qt.C, p *Path) { c.Assert(p.Base(), qt.Equals, "/a") + c.Assert(p.BaseReTyped("foo"), qt.Equals, "/foo/a") c.Assert(p.BaseNameNoIdentifier(), qt.Equals, "a") c.Assert(p.Container(), qt.Equals, "a") c.Assert(p.Container(), qt.Equals, "a") c.Assert(p.ContainerDir(), qt.Equals, "") c.Assert(p.Dir(), qt.Equals, "/a") c.Assert(p.Ext(), qt.Equals, "md") + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{"index"}) c.Assert(p.Identifiers(), qt.DeepEquals, []string{"md"}) c.Assert(p.IsBranchBundle(), qt.IsFalse) c.Assert(p.IsBundle(), qt.IsTrue) @@ -208,6 +227,7 @@ func TestParse(t *testing.T) { func(c *qt.C, p *Path) { c.Assert(p.Base(), qt.Equals, "/a/b") c.Assert(p.BaseNameNoIdentifier(), qt.Equals, "b") + c.Assert(p.BaseReTyped("foo"), qt.Equals, "/foo/b") c.Assert(p.Container(), qt.Equals, "b") c.Assert(p.ContainerDir(), qt.Equals, "/a") c.Assert(p.Dir(), qt.Equals, "/a/b") @@ -220,6 +240,7 @@ func TestParse(t *testing.T) { c.Assert(p.NameNoExt(), qt.Equals, "index.no") c.Assert(p.NameNoIdentifier(), qt.Equals, "index") c.Assert(p.NameNoLang(), qt.Equals, "index.md") + c.Assert(p.Path(), qt.Equals, "/a/b/index.no.md") c.Assert(p.PathNoLang(), qt.Equals, "/a/b/index.md") c.Assert(p.Section(), qt.Equals, "a") }, @@ -355,11 +376,225 @@ func TestParse(t *testing.T) { } for _, test := range tests { c.Run(test.name, func(c *qt.C) { + if test.name != "Home branch cundle" { + // return + } test.assert(c, testParser.Parse(files.ComponentFolderContent, test.path)) }) } } +func TestParseLayouts(t *testing.T) { + c := qt.New(t) + + tests := []struct { + name string + path string + assert func(c *qt.C, p *Path) + }{ + { + "Basic", + "/list.html", + func(c *qt.C, p *Path) { + c.Assert(p.Base(), qt.Equals, "/list.html") + c.Assert(p.OutputFormat(), qt.Equals, "html") + }, + }, + { + "Lang", + "/list.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "list"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{}) + c.Assert(p.Base(), qt.Equals, "/list.html") + c.Assert(p.Lang(), qt.Equals, "no") + }, + }, + { + "Kind", + "/section.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Kind(), qt.Equals, kinds.KindSection) + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{}) + c.Assert(p.Base(), qt.Equals, "/section.html") + c.Assert(p.Lang(), qt.Equals, "no") + }, + }, + { + "Layout", + "/list.section.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Layout(), qt.Equals, "list") + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{}) + c.Assert(p.Base(), qt.Equals, "/list.html") + c.Assert(p.Lang(), qt.Equals, "no") + }, + }, + { + "Layout multiple", + "/mylayout.list.section.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Layout(), qt.Equals, "mylayout") + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "mylayout"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{}) + c.Assert(p.Base(), qt.Equals, "/mylayout.html") + c.Assert(p.Lang(), qt.Equals, "no") + }, + }, + { + "Layout shortcode", + "/_shortcodes/myshort.list.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Layout(), qt.Equals, "list") + }, + }, + { + "Layout baseof", + "/baseof.list.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Layout(), qt.Equals, "list") + }, + }, + { + "Lang and output format", + "/list.no.amp.not.html", + func(c *qt.C, p *Path) { + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "not", "amp", "no", "list"}) + c.Assert(p.OutputFormat(), qt.Equals, "amp") + c.Assert(p.Ext(), qt.Equals, "html") + c.Assert(p.Lang(), qt.Equals, "no") + c.Assert(p.Base(), qt.Equals, "/list.html") + }, + }, + { + "Term", + "/term.html", + func(c *qt.C, p *Path) { + c.Assert(p.Base(), qt.Equals, "/term.html") + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "term"}) + c.Assert(p.PathNoIdentifier(), qt.Equals, "/term") + c.Assert(p.PathBeforeLangAndOutputFormatAndExt(), qt.Equals, "/term") + c.Assert(p.Lang(), qt.Equals, "") + c.Assert(p.Kind(), qt.Equals, "term") + c.Assert(p.OutputFormat(), qt.Equals, "html") + }, + }, + { + "Shortcode with layout", + "/_shortcodes/myshortcode.list.html", + func(c *qt.C, p *Path) { + c.Assert(p.Base(), qt.Equals, "/_shortcodes/myshortcode.html") + c.Assert(p.Type(), qt.Equals, TypeShortcode) + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list"}) + c.Assert(p.Layout(), qt.Equals, "list") + c.Assert(p.PathNoIdentifier(), qt.Equals, "/_shortcodes/myshortcode") + c.Assert(p.PathBeforeLangAndOutputFormatAndExt(), qt.Equals, "/_shortcodes/myshortcode.list") + c.Assert(p.Lang(), qt.Equals, "") + c.Assert(p.Kind(), qt.Equals, "") + c.Assert(p.OutputFormat(), qt.Equals, "html") + }, + }, + { + "Sub dir", + "/pages/home.html", + func(c *qt.C, p *Path) { + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "home"}) + c.Assert(p.Lang(), qt.Equals, "") + c.Assert(p.Kind(), qt.Equals, "home") + c.Assert(p.OutputFormat(), qt.Equals, "html") + c.Assert(p.Dir(), qt.Equals, "/pages") + }, + }, + { + "Baseof", + "/pages/baseof.list.section.fr.amp.html", + func(c *qt.C, p *Path) { + c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "amp", "fr", "section", "list", "baseof"}) + c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{}) + c.Assert(p.Kind(), qt.Equals, kinds.KindSection) + c.Assert(p.Lang(), qt.Equals, "fr") + c.Assert(p.OutputFormat(), qt.Equals, "amp") + c.Assert(p.Dir(), qt.Equals, "/pages") + c.Assert(p.NameNoIdentifier(), qt.Equals, "baseof") + c.Assert(p.Type(), qt.Equals, TypeBaseof) + c.Assert(p.IdentifierBase(), qt.Equals, "/pages/baseof.list.section.fr.amp.html") + }, + }, + { + "Markup", + "/_markup/render-link.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeMarkup) + }, + }, + { + "Markup nested", + "/foo/_markup/render-link.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeMarkup) + }, + }, + { + "Shortcode", + "/_shortcodes/myshortcode.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeShortcode) + }, + }, + { + "Shortcode nested", + "/foo/_shortcodes/myshortcode.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeShortcode) + }, + }, + { + "Shortcode nested sub", + "/foo/_shortcodes/foo/myshortcode.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeShortcode) + }, + }, + { + "Partials", + "/_partials/foo.bar", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypePartial) + }, + }, + { + "Shortcode lang in root", + "/_shortcodes/no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeShortcode) + c.Assert(p.Lang(), qt.Equals, "") + c.Assert(p.NameNoIdentifier(), qt.Equals, "no") + }, + }, + { + "Shortcode lang layout", + "/_shortcodes/myshortcode.no.html", + func(c *qt.C, p *Path) { + c.Assert(p.Type(), qt.Equals, TypeShortcode) + c.Assert(p.Lang(), qt.Equals, "no") + c.Assert(p.Layout(), qt.Equals, "") + c.Assert(p.NameNoIdentifier(), qt.Equals, "myshortcode") + }, + }, + } + + for _, test := range tests { + c.Run(test.name, func(c *qt.C) { + if test.name != "Shortcode lang layout" { + // return + } + test.assert(c, testParser.Parse(files.ComponentFolderLayouts, test.path)) + }) + } +} + func TestHasExt(t *testing.T) { c := qt.New(t) diff --git a/common/paths/paths_integration_test.go b/common/paths/paths_integration_test.go index 62d40f527..f5ea3066a 100644 --- a/common/paths/paths_integration_test.go +++ b/common/paths/paths_integration_test.go @@ -78,3 +78,26 @@ disablePathToLower = true b.AssertFileContent("public/en/mysection/mybundle/index.html", "en|Single") b.AssertFileContent("public/fr/MySection/MyBundle/index.html", "fr|Single") } + +func TestIssue13596(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +-- content/p1/index.md -- +--- +title: p1 +--- +-- content/p1/a.1.txt -- +-- content/p1/a.2.txt -- +-- layouts/all.html -- +{{ range .Resources.Match "*" }}{{ .Name }}|{{ end }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/p1/index.html", "a.1.txt|a.2.txt|") + b.AssertFileExists("public/p1/a.1.txt", true) + b.AssertFileExists("public/p1/a.2.txt", true) // fails +} diff --git a/common/paths/pathtype_string.go b/common/paths/pathtype_string.go deleted file mode 100644 index 7a99f8a03..000000000 --- a/common/paths/pathtype_string.go +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by "stringer -type=PathType"; DO NOT EDIT. - -package paths - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[PathTypeFile-0] - _ = x[PathTypeContentResource-1] - _ = x[PathTypeContentSingle-2] - _ = x[PathTypeLeaf-3] - _ = x[PathTypeBranch-4] -} - -const _PathType_name = "PathTypeFilePathTypeContentResourcePathTypeContentSinglePathTypeLeafPathTypeBranch" - -var _PathType_index = [...]uint8{0, 12, 35, 56, 68, 82} - -func (i PathType) String() string { - if i < 0 || i >= PathType(len(_PathType_index)-1) { - return "PathType(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _PathType_name[_PathType_index[i]:_PathType_index[i+1]] -} diff --git a/common/paths/type_string.go b/common/paths/type_string.go new file mode 100644 index 000000000..08fbcc835 --- /dev/null +++ b/common/paths/type_string.go @@ -0,0 +1,32 @@ +// Code generated by "stringer -type Type"; DO NOT EDIT. + +package paths + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TypeFile-0] + _ = x[TypeContentResource-1] + _ = x[TypeContentSingle-2] + _ = x[TypeLeaf-3] + _ = x[TypeBranch-4] + _ = x[TypeContentData-5] + _ = x[TypeMarkup-6] + _ = x[TypeShortcode-7] + _ = x[TypePartial-8] + _ = x[TypeBaseof-9] +} + +const _Type_name = "TypeFileTypeContentResourceTypeContentSingleTypeLeafTypeBranchTypeContentDataTypeMarkupTypeShortcodeTypePartialTypeBaseof" + +var _Type_index = [...]uint8{0, 8, 27, 44, 52, 62, 77, 87, 100, 111, 121} + +func (i Type) String() string { + if i < 0 || i >= Type(len(_Type_index)-1) { + return "Type(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Type_name[_Type_index[i]:_Type_index[i+1]] +} diff --git a/common/paths/url.go b/common/paths/url.go index 75b4b644a..1d1408b51 100644 --- a/common/paths/url.go +++ b/common/paths/url.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Hugo Authors. All rights reserved. +// Copyright 2024 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import ( "net/url" "path" "path/filepath" + "runtime" "strings" ) @@ -159,31 +160,6 @@ func Uglify(in string) string { return path.Clean(in) } -// UrlToFilename converts the URL s to a filename. -// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned. -func UrlToFilename(s string) (string, bool) { - u, err := url.ParseRequestURI(s) - if err != nil { - return filepath.FromSlash(s), false - } - - p := u.Path - - if p == "" { - p, _ = url.QueryUnescape(u.Opaque) - return filepath.FromSlash(p), true - } - - p = filepath.FromSlash(p) - - if u.Host != "" { - // C:\data\file.txt - p = strings.ToUpper(u.Host) + ":" + p - } - - return p, true -} - // URLEscape escapes unicode letters. func URLEscape(uri string) string { // escape unicode letters @@ -193,3 +169,105 @@ func URLEscape(uri string) string { } return u.String() } + +// TrimExt trims the extension from a path.. +func TrimExt(in string) string { + return strings.TrimSuffix(in, path.Ext(in)) +} + +// From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45 +func UrlFromFilename(filename string) (*url.URL, error) { + if !filepath.IsAbs(filename) { + return nil, fmt.Errorf("filepath must be absolute") + } + + // If filename has a Windows volume name, convert the volume to a host and prefix + // per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/. + if vol := filepath.VolumeName(filename); vol != "" { + if strings.HasPrefix(vol, `\\`) { + filename = filepath.ToSlash(filename[2:]) + i := strings.IndexByte(filename, '/') + + if i < 0 { + // A degenerate case. + // \\host.example.com (without a share name) + // becomes + // file://host.example.com/ + return &url.URL{ + Scheme: "file", + Host: filename, + Path: "/", + }, nil + } + + // \\host.example.com\Share\path\to\file + // becomes + // file://host.example.com/Share/path/to/file + return &url.URL{ + Scheme: "file", + Host: filename[:i], + Path: filepath.ToSlash(filename[i:]), + }, nil + } + + // C:\path\to\file + // becomes + // file:///C:/path/to/file + return &url.URL{ + Scheme: "file", + Path: "/" + filepath.ToSlash(filename), + }, nil + } + + // /path/to/file + // becomes + // file:///path/to/file + return &url.URL{ + Scheme: "file", + Path: filepath.ToSlash(filename), + }, nil +} + +// UrlStringToFilename converts the URL s to a filename. +// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned. +func UrlStringToFilename(s string) (string, bool) { + u, err := url.ParseRequestURI(s) + if err != nil { + return filepath.FromSlash(s), false + } + + p := u.Path + + if p == "" { + p, _ = url.QueryUnescape(u.Opaque) + return filepath.FromSlash(p), false + } + + if runtime.GOOS != "windows" { + return p, true + } + + if len(p) == 0 || p[0] != '/' { + return filepath.FromSlash(p), false + } + + p = filepath.FromSlash(p) + + if len(u.Host) == 1 { + // file://c/Users/... + return strings.ToUpper(u.Host) + ":" + p, true + } + + if u.Host != "" && u.Host != "localhost" { + if filepath.VolumeName(u.Host) != "" { + return "", false + } + return `\\` + u.Host + p, true + } + + if vol := filepath.VolumeName(p[1:]); vol == "" || strings.HasPrefix(vol, `\\`) { + return "", false + } + + return p[1:], true +} diff --git a/common/rungroup/rungroup.go b/common/rungroup/rungroup.go index 96ec57883..80a730ca9 100644 --- a/common/rungroup/rungroup.go +++ b/common/rungroup/rungroup.go @@ -51,7 +51,7 @@ func Run[T any](ctx context.Context, cfg Config[T]) Group[T] { // Buffered for performance. ch := make(chan T, cfg.NumWorkers) - for i := 0; i < cfg.NumWorkers; i++ { + for range cfg.NumWorkers { g.Go(func() error { for { select { diff --git a/common/tasks/tasks.go b/common/tasks/tasks.go index 1f7e061f9..3f8a754e9 100644 --- a/common/tasks/tasks.go +++ b/common/tasks/tasks.go @@ -103,10 +103,7 @@ func (r *RunEvery) Add(name string, f Func) { f.IntervalHigh = 20 * time.Second } - start := f.IntervalHigh / 3 - if start < f.IntervalLow { - start = f.IntervalLow - } + start := max(f.IntervalHigh/3, f.IntervalLow) f.interval = start f.last = time.Now() diff --git a/common/terminal/colors.go b/common/terminal/colors.go index 8aa0e1af2..fef6efce8 100644 --- a/common/terminal/colors.go +++ b/common/terminal/colors.go @@ -17,7 +17,6 @@ package terminal import ( "fmt" "os" - "runtime" "strings" isatty "github.com/mattn/go-isatty" @@ -41,10 +40,6 @@ func PrintANSIColors(f *os.File) bool { // IsTerminal return true if the file descriptor is terminal and the TERM // environment variable isn't a dumb one. func IsTerminal(f *os.File) bool { - if runtime.GOOS == "windows" { - return false - } - fd := f.Fd() return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)) } diff --git a/common/types/closer.go b/common/types/closer.go index 2844b1986..9f8875a8a 100644 --- a/common/types/closer.go +++ b/common/types/closer.go @@ -19,6 +19,13 @@ type Closer interface { Close() error } +// CloserFunc is a convenience type to create a Closer from a function. +type CloserFunc func() error + +func (f CloserFunc) Close() error { + return f() +} + type CloseAdder interface { Add(Closer) } diff --git a/common/types/convert.go b/common/types/convert.go index 0cb5035df..6b1750376 100644 --- a/common/types/convert.go +++ b/common/types/convert.go @@ -69,7 +69,7 @@ func ToStringSlicePreserveStringE(v any) ([]string, error) { switch vv.Kind() { case reflect.Slice, reflect.Array: result = make([]string, vv.Len()) - for i := 0; i < vv.Len(); i++ { + for i := range vv.Len() { s, err := cast.ToStringE(vv.Index(i).Interface()) if err != nil { return nil, err diff --git a/common/types/evictingqueue.go b/common/types/evictingqueue.go index 88add59d5..a335be3b2 100644 --- a/common/types/evictingqueue.go +++ b/common/types/evictingqueue.go @@ -15,27 +15,28 @@ package types import ( + "slices" "sync" ) -// EvictingStringQueue is a queue which automatically evicts elements from the head of +// EvictingQueue is a queue which automatically evicts elements from the head of // the queue when attempting to add new elements onto the queue and it is full. // This queue orders elements LIFO (last-in-first-out). It throws away duplicates. -// Note: This queue currently does not contain any remove (poll etc.) methods. -type EvictingStringQueue struct { +type EvictingQueue[T comparable] struct { size int - vals []string - set map[string]bool + vals []T + set map[T]bool mu sync.Mutex + zero T } -// NewEvictingStringQueue creates a new queue with the given size. -func NewEvictingStringQueue(size int) *EvictingStringQueue { - return &EvictingStringQueue{size: size, set: make(map[string]bool)} +// NewEvictingQueue creates a new queue with the given size. +func NewEvictingQueue[T comparable](size int) *EvictingQueue[T] { + return &EvictingQueue[T]{size: size, set: make(map[T]bool)} } // Add adds a new string to the tail of the queue if it's not already there. -func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue { +func (q *EvictingQueue[T]) Add(v T) *EvictingQueue[T] { q.mu.Lock() if q.set[v] { q.mu.Unlock() @@ -45,7 +46,7 @@ func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue { if len(q.set) == q.size { // Full delete(q.set, q.vals[0]) - q.vals = append(q.vals[:0], q.vals[1:]...) + q.vals = slices.Delete(q.vals, 0, 1) } q.set[v] = true q.vals = append(q.vals, v) @@ -54,7 +55,7 @@ func (q *EvictingStringQueue) Add(v string) *EvictingStringQueue { return q } -func (q *EvictingStringQueue) Len() int { +func (q *EvictingQueue[T]) Len() int { if q == nil { return 0 } @@ -64,19 +65,22 @@ func (q *EvictingStringQueue) Len() int { } // Contains returns whether the queue contains v. -func (q *EvictingStringQueue) Contains(v string) bool { +func (q *EvictingQueue[T]) Contains(v T) bool { + if q == nil { + return false + } q.mu.Lock() defer q.mu.Unlock() return q.set[v] } // Peek looks at the last element added to the queue. -func (q *EvictingStringQueue) Peek() string { +func (q *EvictingQueue[T]) Peek() T { q.mu.Lock() l := len(q.vals) if l == 0 { q.mu.Unlock() - return "" + return q.zero } elem := q.vals[l-1] q.mu.Unlock() @@ -84,9 +88,12 @@ func (q *EvictingStringQueue) Peek() string { } // PeekAll looks at all the elements in the queue, with the newest first. -func (q *EvictingStringQueue) PeekAll() []string { +func (q *EvictingQueue[T]) PeekAll() []T { + if q == nil { + return nil + } q.mu.Lock() - vals := make([]string, len(q.vals)) + vals := make([]T, len(q.vals)) copy(vals, q.vals) q.mu.Unlock() for i, j := 0, len(vals)-1; i < j; i, j = i+1, j-1 { @@ -96,9 +103,9 @@ func (q *EvictingStringQueue) PeekAll() []string { } // PeekAllSet returns PeekAll as a set. -func (q *EvictingStringQueue) PeekAllSet() map[string]bool { +func (q *EvictingQueue[T]) PeekAllSet() map[T]bool { all := q.PeekAll() - set := make(map[string]bool) + set := make(map[T]bool) for _, v := range all { set[v] = true } diff --git a/common/types/evictingqueue_test.go b/common/types/evictingqueue_test.go index 7489ba88d..b93243f3c 100644 --- a/common/types/evictingqueue_test.go +++ b/common/types/evictingqueue_test.go @@ -23,7 +23,7 @@ import ( func TestEvictingStringQueue(t *testing.T) { c := qt.New(t) - queue := NewEvictingStringQueue(3) + queue := NewEvictingQueue[string](3) c.Assert(queue.Peek(), qt.Equals, "") queue.Add("a") @@ -53,9 +53,9 @@ func TestEvictingStringQueueConcurrent(t *testing.T) { var wg sync.WaitGroup val := "someval" - queue := NewEvictingStringQueue(3) + queue := NewEvictingQueue[string](3) - for j := 0; j < 100; j++ { + for range 100 { wg.Add(1) go func() { defer wg.Done() diff --git a/common/types/types.go b/common/types/types.go index d32391a88..7e94c1eea 100644 --- a/common/types/types.go +++ b/common/types/types.go @@ -28,6 +28,16 @@ type RLocker interface { RUnlock() } +type Locker interface { + Lock() + Unlock() +} + +type RWLocker interface { + RLocker + Locker +} + // KeyValue is a interface{} tuple. type KeyValue struct { Key any @@ -59,7 +69,7 @@ func (k KeyValues) String() string { // KeyValues struct. func NewKeyValuesStrings(key string, values ...string) KeyValues { iv := make([]any, len(values)) - for i := 0; i < len(values); i++ { + for i := range values { iv[i] = values[i] } return KeyValues{Key: key, Values: iv} @@ -133,22 +143,3 @@ func NewBool(b bool) *bool { type PrintableValueProvider interface { PrintableValue() any } - -var _ PrintableValueProvider = Result[any]{} - -// Result is a generic result type. -type Result[T any] struct { - // The result value. - Value T - - // The error value. - Err error -} - -// PrintableValue returns the value or panics if there is an error. -func (r Result[T]) PrintableValue() any { - if r.Err != nil { - panic(r.Err) - } - return r.Value -} diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 35517ece2..0db0be1d8 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -82,7 +82,7 @@ func init() { } configLanguageKeys = make(map[string]bool) addKeys := func(v reflect.Value) { - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { name := strings.ToLower(v.Type().Field(i).Name) if skip[name] { continue @@ -128,6 +128,9 @@ type Config struct { // {"identifiers": ["markup"] } Markup markup_config.Config `mapstructure:"-"` + // ContentTypes are the media types that's considered content in Hugo. + ContentTypes *config.ConfigNamespace[map[string]media.ContentTypeConfig, media.ContentTypes] `mapstructure:"-"` + // The mediatypes configuration section maps the MIME type (a string) to a configuration object for that type. // {"identifiers": ["mediatypes"], "refs": ["types:media:type"] } MediaTypes *config.ConfigNamespace[map[string]media.MediaTypeConfig, media.Types] `mapstructure:"-"` @@ -143,7 +146,7 @@ type Config struct { // The cascade configuration section contains the top level front matter cascade configuration options, // a slice of page matcher and params to apply to those pages. - Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"` + Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]] `mapstructure:"-"` // The segments defines segments for the site. Used for partial/segmented builds. Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"` @@ -301,6 +304,18 @@ func (c *Config) CompileConfig(logger loggers.Logger) error { } } + defaultOutputFormat := outputFormats[0] + c.DefaultOutputFormat = strings.ToLower(c.DefaultOutputFormat) + if c.DefaultOutputFormat != "" { + f, found := outputFormats.GetByName(c.DefaultOutputFormat) + if !found { + return fmt.Errorf("unknown default output format %q", c.DefaultOutputFormat) + } + defaultOutputFormat = f + } else { + c.DefaultOutputFormat = defaultOutputFormat.Name + } + disabledLangs := make(map[string]bool) for _, lang := range c.DisableLanguages { disabledLangs[lang] = true @@ -381,32 +396,63 @@ func (c *Config) CompileConfig(logger loggers.Logger) error { // Legacy paginate values. if c.Paginate != 0 { - hugo.Deprecate("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0") + hugo.DeprecateWithLogger("site config key paginate", "Use pagination.pagerSize instead.", "v0.128.0", logger.Logger()) c.Pagination.PagerSize = c.Paginate } if c.PaginatePath != "" { - hugo.Deprecate("site config key paginatePath", "Use pagination.path instead.", "v0.128.0") + hugo.DeprecateWithLogger("site config key paginatePath", "Use pagination.path instead.", "v0.128.0", logger.Logger()) c.Pagination.Path = c.PaginatePath } + // Legacy privacy values. + if c.Privacy.Twitter.Disable { + hugo.DeprecateWithLogger("site config key privacy.twitter.disable", "Use privacy.x.disable instead.", "v0.141.0", logger.Logger()) + c.Privacy.X.Disable = c.Privacy.Twitter.Disable + } + + if c.Privacy.Twitter.EnableDNT { + hugo.DeprecateWithLogger("site config key privacy.twitter.enableDNT", "Use privacy.x.enableDNT instead.", "v0.141.0", logger.Logger()) + c.Privacy.X.EnableDNT = c.Privacy.Twitter.EnableDNT + } + + if c.Privacy.Twitter.Simple { + hugo.DeprecateWithLogger("site config key privacy.twitter.simple", "Use privacy.x.simple instead.", "v0.141.0", logger.Logger()) + c.Privacy.X.Simple = c.Privacy.Twitter.Simple + } + + // Legacy services values. + if c.Services.Twitter.DisableInlineCSS { + hugo.DeprecateWithLogger("site config key services.twitter.disableInlineCSS", "Use services.x.disableInlineCSS instead.", "v0.141.0", logger.Logger()) + c.Services.X.DisableInlineCSS = c.Services.Twitter.DisableInlineCSS + } + + // Legacy permalink tokens + vs := fmt.Sprintf("%v", c.Permalinks) + if strings.Contains(vs, ":filename") { + hugo.DeprecateWithLogger("the \":filename\" permalink token", "Use \":contentbasename\" instead.", "0.144.0", logger.Logger()) + } + if strings.Contains(vs, ":slugorfilename") { + hugo.DeprecateWithLogger("the \":slugorfilename\" permalink token", "Use \":slugorcontentbasename\" instead.", "0.144.0", logger.Logger()) + } + c.C = &ConfigCompiled{ - Timeout: timeout, - BaseURL: baseURL, - BaseURLLiveReload: baseURL, - DisabledKinds: disabledKinds, - DisabledLanguages: disabledLangs, - IgnoredLogs: ignoredLogIDs, - KindOutputFormats: kindOutputFormats, - ContentTypes: media.DefaultContentTypes.FromTypes(c.MediaTypes.Config), - CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle), - IsUglyURLSection: isUglyURL, - IgnoreFile: ignoreFile, - SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...), - MainSections: c.MainSections, - Clock: clock, - HTTPCache: httpCache, - transientErr: transientErr, + Timeout: timeout, + BaseURL: baseURL, + BaseURLLiveReload: baseURL, + DisabledKinds: disabledKinds, + DisabledLanguages: disabledLangs, + IgnoredLogs: ignoredLogIDs, + KindOutputFormats: kindOutputFormats, + DefaultOutputFormat: defaultOutputFormat, + CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle), + IsUglyURLSection: isUglyURL, + IgnoreFile: ignoreFile, + SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...), + MainSections: c.MainSections, + Clock: clock, + HTTPCache: httpCache, + transientErr: transientErr, } for _, s := range allDecoderSetups { @@ -430,22 +476,22 @@ func (c *Config) IsLangDisabled(lang string) bool { // ConfigCompiled holds values and functions that are derived from the config. type ConfigCompiled struct { - Timeout time.Duration - BaseURL urls.BaseURL - BaseURLLiveReload urls.BaseURL - ServerInterface string - KindOutputFormats map[string]output.Formats - ContentTypes media.ContentTypes - DisabledKinds map[string]bool - DisabledLanguages map[string]bool - IgnoredLogs map[string]bool - CreateTitle func(s string) string - IsUglyURLSection func(section string) bool - IgnoreFile func(filename string) bool - SegmentFilter segments.SegmentFilter - MainSections []string - Clock time.Time - HTTPCache httpcache.ConfigCompiled + Timeout time.Duration + BaseURL urls.BaseURL + BaseURLLiveReload urls.BaseURL + ServerInterface string + KindOutputFormats map[string]output.Formats + DefaultOutputFormat output.Format + DisabledKinds map[string]bool + DisabledLanguages map[string]bool + IgnoredLogs map[string]bool + CreateTitle func(s string) string + IsUglyURLSection func(section string) bool + IgnoreFile func(filename string) bool + SegmentFilter segments.SegmentFilter + MainSections []string + Clock time.Time + HTTPCache httpcache.ConfigCompiled // This is set to the last transient error found during config compilation. // With themes/modules we compute the configuration in multiple passes, and @@ -505,6 +551,13 @@ type RootConfig struct { // Set this to true to put all languages below their language ID. DefaultContentLanguageInSubdir bool + // The default output format to use for the site. + // If not set, we will use the first output format. + DefaultOutputFormat string + + // Disable generation of redirect to the default language when DefaultContentLanguageInSubdir is enabled. + DisableDefaultLanguageRedirect bool + // Disable creation of alias redirect pages. DisableAliases bool @@ -723,15 +776,16 @@ type Configs struct { } func (c *Configs) Validate(logger loggers.Logger) error { - for p := range c.Base.Cascade.Config { + c.Base.Cascade.Config.Range(func(p page.PageMatcher, cfg page.PageMatcherParamsConfig) bool { page.CheckCascadePattern(logger, p) - } + return true + }) return nil } // transientErr returns the last transient error found during config compilation. func (c *Configs) transientErr() error { - for _, l := range c.LanguageConfigSlice { + for _, l := range c.LanguageConfigMap { if l.C.transientErr != nil { return l.C.transientErr } @@ -746,31 +800,58 @@ func (c *Configs) IsZero() bool { func (c *Configs) Init() error { var languages langs.Languages - defaultContentLanguage := c.Base.DefaultContentLanguage - for k, v := range c.LanguageConfigMap { + + var langKeys []string + var hasEn bool + + const en = "en" + + for k := range c.LanguageConfigMap { + langKeys = append(langKeys, k) + if k == en { + hasEn = true + } + } + + // Sort the LanguageConfigSlice by language weight (if set) or lang. + sort.Slice(langKeys, func(i, j int) bool { + ki := langKeys[i] + kj := langKeys[j] + lki := c.LanguageConfigMap[ki] + lkj := c.LanguageConfigMap[kj] + li := lki.Languages[ki] + lj := lkj.Languages[kj] + if li.Weight != lj.Weight { + return li.Weight < lj.Weight + } + return ki < kj + }) + + // See issue #13646. + defaultConfigLanguageFallback := en + if !hasEn { + // Pick the first one. + defaultConfigLanguageFallback = langKeys[0] + } + + if c.Base.DefaultContentLanguage == "" { + c.Base.DefaultContentLanguage = defaultConfigLanguageFallback + } + + for _, k := range langKeys { + v := c.LanguageConfigMap[k] + if v.DefaultContentLanguage == "" { + v.DefaultContentLanguage = defaultConfigLanguageFallback + } c.LanguageConfigSlice = append(c.LanguageConfigSlice, v) languageConf := v.Languages[k] - language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf) + language, err := langs.NewLanguage(k, c.Base.DefaultContentLanguage, v.TimeZone, languageConf) if err != nil { return err } languages = append(languages, language) } - // Sort the sites by language weight (if set) or lang. - sort.Slice(languages, func(i, j int) bool { - li := languages[i] - lj := languages[j] - if li.Weight != lj.Weight { - return li.Weight < lj.Weight - } - return li.Lang < lj.Lang - }) - - for _, l := range languages { - c.LanguageConfigSlice = append(c.LanguageConfigSlice, c.LanguageConfigMap[l.Lang]) - } - // Filter out disabled languages. var n int for _, l := range languages { @@ -783,12 +864,12 @@ func (c *Configs) Init() error { var languagesDefaultFirst langs.Languages for _, l := range languages { - if l.Lang == defaultContentLanguage { + if l.Lang == c.Base.DefaultContentLanguage { languagesDefaultFirst = append(languagesDefaultFirst, l) } } for _, l := range languages { - if l.Lang != defaultContentLanguage { + if l.Lang != c.Base.DefaultContentLanguage { languagesDefaultFirst = append(languagesDefaultFirst, l) } } @@ -796,7 +877,24 @@ func (c *Configs) Init() error { c.Languages = languages c.LanguagesDefaultFirst = languagesDefaultFirst - c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: c.Base.C.ContentTypes.IsContentSuffix} + c.ContentPathParser = &paths.PathParser{ + LanguageIndex: languagesDefaultFirst.AsIndexSet(), + IsLangDisabled: c.Base.IsLangDisabled, + IsContentExt: c.Base.ContentTypes.Config.IsContentSuffix, + IsOutputFormat: func(name, ext string) bool { + if name == "" { + return false + } + + if of, ok := c.Base.OutputFormats.Config.GetByName(name); ok { + if ext != "" && !of.MediaType.HasSuffix(ext) { + return false + } + return true + } + return false + }, + } c.configLangs = make([]config.AllProvider, len(c.Languages)) for i, l := range c.LanguagesDefaultFirst { @@ -857,17 +955,48 @@ func (c Configs) GetByLang(lang string) config.AllProvider { return nil } +func newDefaultConfig() *Config { + return &Config{ + Taxonomies: map[string]string{"tag": "tags", "category": "categories"}, + Sitemap: config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, + RootConfig: RootConfig{ + Environment: hugo.EnvironmentProduction, + TitleCaseStyle: "AP", + PluralizeListTitles: true, + CapitalizeListTitles: true, + StaticDir: []string{"static"}, + SummaryLength: 70, + Timeout: "60s", + + CommonDirs: config.CommonDirs{ + ArcheTypeDir: "archetypes", + ContentDir: "content", + ResourceDir: "resources", + PublishDir: "public", + ThemesDir: "themes", + AssetDir: "assets", + LayoutDir: "layouts", + I18nDir: "i18n", + DataDir: "data", + }, + }, + } +} + // fromLoadConfigResult creates a new Config from res. func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) { if !res.Cfg.IsSet("languages") { // We need at least one lang := res.Cfg.GetString("defaultContentLanguage") + if lang == "" { + lang = "en" + } res.Cfg.Set("languages", maps.Params{lang: maps.Params{}}) } bcfg := res.BaseConfig cfg := res.Cfg - all := &Config{} + all := newDefaultConfig() err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil) if err != nil { @@ -877,6 +1006,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon langConfigMap := make(map[string]*Config) languagesConfig := cfg.GetStringMap("languages") + var isMultihost bool if err := all.CompileConfig(logger); err != nil { @@ -888,30 +1018,17 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon var differentRootKeys []string switch x := v.(type) { case maps.Params: - var params maps.Params - pv, found := x["params"] - if found { - params = pv.(maps.Params) - } else { - params = maps.Params{ + _, found := x["params"] + if !found { + x["params"] = maps.Params{ maps.MergeStrategyKey: maps.ParamsMergeStrategyDeep, } - x["params"] = params } for kk, vv := range x { if kk == "_merge" { continue } - if kk != maps.MergeStrategyKey && !configLanguageKeys[kk] { - // This should have been placed below params. - // We accidentally allowed it in the past, so we need to support it a little longer, - // But log a warning. - if _, found := params[kk]; !found { - hugo.Deprecate(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), "v0.112.0") - params[kk] = vv - } - } if kk == "baseurl" { // baseURL configure don the language level is a multihost setup. isMultihost = true diff --git a/config/allconfig/allconfig_integration_test.go b/config/allconfig/allconfig_integration_test.go index 38e763b70..8f6cacf84 100644 --- a/config/allconfig/allconfig_integration_test.go +++ b/config/allconfig/allconfig_integration_test.go @@ -5,8 +5,10 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/media" ) func TestDirsMount(t *testing.T) { @@ -97,7 +99,7 @@ suffixes = ["html", "xhtml"] b := hugolib.Test(t, files) conf := b.H.Configs.Base - contentTypes := conf.C.ContentTypes + contentTypes := conf.ContentTypes.Config b.Assert(contentTypes.HTML.Suffixes(), qt.DeepEquals, []string{"html", "xhtml"}) b.Assert(contentTypes.Markdown.Suffixes(), qt.DeepEquals, []string{"md", "mdown", "markdown"}) @@ -175,3 +177,205 @@ func TestMapUglyURLs(t *testing.T) { b.Assert(c.C.IsUglyURLSection("posts"), qt.IsTrue) b.Assert(c.C.IsUglyURLSection("blog"), qt.IsFalse) } + +// Issue 13199 +func TestInvalidOutputFormat(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +[outputs] +home = ['html','foo'] +-- layouts/index.html -- +x +` + + b, err := hugolib.TestE(t, files) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `failed to create config: unknown output format "foo" for kind "home"`) +} + +// Issue 13201 +func TestLanguageConfigSlice(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +[languages.en] +title = 'TITLE_EN' +weight = 2 +[languages.de] +title = 'TITLE_DE' +weight = 1 +[languages.fr] +title = 'TITLE_FR' +weight = 3 +` + + b := hugolib.Test(t, files) + b.Assert(b.H.Configs.LanguageConfigSlice[0].Title, qt.Equals, `TITLE_DE`) +} + +func TestContentTypesDefault(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.com" + + +` + + b := hugolib.Test(t, files) + + ct := b.H.Configs.Base.ContentTypes + c := ct.Config + s := ct.SourceStructure.(map[string]media.ContentTypeConfig) + + b.Assert(c.IsContentFile("foo.md"), qt.Equals, true) + b.Assert(len(s), qt.Equals, 6) +} + +func TestMergeDeep(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +theme = ["theme1", "theme2"] +_merge = "deep" +-- themes/theme1/hugo.toml -- +[sitemap] +filename = 'mysitemap.xml' +[services] +[services.googleAnalytics] +id = 'foo bar' +[taxonomies] + foo = 'bars' +-- themes/theme2/config/_default/hugo.toml -- +[taxonomies] + bar = 'baz' +-- layouts/home.html -- +GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}. + +` + + b := hugolib.Test(t, files) + + conf := b.H.Configs + base := conf.Base + + b.Assert(base.Environment, qt.Equals, hugo.EnvironmentProduction) + b.Assert(base.BaseURL, qt.Equals, "https://example.com") + b.Assert(base.Sitemap.Filename, qt.Equals, "mysitemap.xml") + b.Assert(base.Taxonomies, qt.DeepEquals, map[string]string{"bar": "baz", "foo": "bars"}) + + b.AssertFileContent("public/index.html", "GA ID: foo bar.") +} + +func TestMergeDeepBuildStats(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +title = "Theme 1" +_merge = "deep" +[module] +[module.hugoVersion] +[[module.imports]] +path = "theme1" +-- themes/theme1/hugo.toml -- +[build] +[build.buildStats] +disableIDs = true +enable = true +-- layouts/home.html -- +Home. + +` + + b := hugolib.Test(t, files, hugolib.TestOptOsFs()) + + conf := b.H.Configs + base := conf.Base + + b.Assert(base.Title, qt.Equals, "Theme 1") + b.Assert(len(base.Module.Imports), qt.Equals, 1) + b.Assert(base.Build.BuildStats.Enable, qt.Equals, true) + b.AssertFileExists("/hugo_stats.json", true) +} + +func TestMergeDeepBuildStatsTheme(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +_merge = "deep" +theme = ["theme1"] +-- themes/theme1/hugo.toml -- +title = "Theme 1" +[build] +[build.buildStats] +disableIDs = true +enable = true +-- layouts/home.html -- +Home. + +` + + b := hugolib.Test(t, files, hugolib.TestOptOsFs()) + + conf := b.H.Configs + base := conf.Base + + b.Assert(base.Title, qt.Equals, "Theme 1") + b.Assert(len(base.Module.Imports), qt.Equals, 1) + b.Assert(base.Build.BuildStats.Enable, qt.Equals, true) + b.AssertFileExists("/hugo_stats.json", true) +} + +func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +[languages] +[languages.nn] +weight = 20 +[languages.sv] +weight = 10 +[languages.sv.taxonomies] + tag = "taggar" +-- layouts/all.html -- +All. +` + + b := hugolib.Test(t, files) + + b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv") +} + +func TestDefaultConfigEnvDisableLanguagesIssue13707(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableLanguages = [] +[languages] +[languages.en] +weight = 1 +[languages.nn] +weight = 2 +[languages.sv] +weight = 3 +` + + b := hugolib.Test(t, files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) { + conf.Environ = []string{`HUGO_DISABLELANGUAGES=sv nn`} + })) + + b.Assert(len(b.H.Sites), qt.Equals, 1) +} diff --git a/config/allconfig/alldecoders.go b/config/allconfig/alldecoders.go index 60e571999..035349790 100644 --- a/config/allconfig/alldecoders.go +++ b/config/allconfig/alldecoders.go @@ -163,6 +163,15 @@ var allDecoderSetups = map[string]decodeWeight{ return err }, }, + "contenttypes": { + key: "contenttypes", + weight: 100, // This needs to be decoded after media types. + decode: func(d decodeWeight, p decodeConfig) error { + var err error + p.c.ContentTypes, err = media.DecodeContentTypes(p.p.GetStringMap(d.key), p.c.MediaTypes.Config) + return err + }, + }, "mediatypes": { key: "mediatypes", decode: func(d decodeWeight, p decodeConfig) error { @@ -240,14 +249,18 @@ var allDecoderSetups = map[string]decodeWeight{ key: "sitemap", decode: func(d decodeWeight, p decodeConfig) error { var err error - p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key)) + if p.p.IsSet(d.key) { + p.c.Sitemap, err = config.DecodeSitemap(p.c.Sitemap, p.p.GetStringMap(d.key)) + } return err }, }, "taxonomies": { key: "taxonomies", decode: func(d decodeWeight, p decodeConfig) error { - p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) + if p.p.IsSet(d.key) { + p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) + } return nil }, }, @@ -297,15 +310,17 @@ var allDecoderSetups = map[string]decodeWeight{ } // Validate defaultContentLanguage. - var found bool - for lang := range p.c.Languages { - if lang == p.c.DefaultContentLanguage { - found = true - break + if p.c.DefaultContentLanguage != "" { + var found bool + for lang := range p.c.Languages { + if lang == p.c.DefaultContentLanguage { + found = true + break + } + } + if !found { + return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage) } - } - if !found { - return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage) } return nil @@ -315,7 +330,7 @@ var allDecoderSetups = map[string]decodeWeight{ key: "cascade", decode: func(d decodeWeight, p decodeConfig) error { var err error - p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key)) + p.c.Cascade, err = page.DecodeCascadeConfig(nil, true, p.p.Get(d.key)) return err }, }, diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go index 38d2309ef..6990a3590 100644 --- a/config/allconfig/configlanguage.go +++ b/config/allconfig/configlanguage.go @@ -137,15 +137,15 @@ func (c ConfigLanguage) Watching() bool { return c.m.Base.Internal.Watch } -func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager { +func (c ConfigLanguage) NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager { if !c.Watching() { return identity.NopManager } - return identity.NewManager(name) + return identity.NewManager(name, opts...) } func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider { - return c.config.C.ContentTypes + return c.config.ContentTypes.Config } // GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use. diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 16e2019cf..4fb8bbaef 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -91,7 +91,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) { return nil, fmt.Errorf("failed to init config: %w", err) } - loggers.InitGlobalLogger(d.Logger.Level(), configs.Base.PanicOnWarning) + loggers.SetGlobalLogger(d.Logger) return configs, nil } @@ -159,63 +159,9 @@ func (l configLoader) applyConfigAliases() error { func (l configLoader) applyDefaultConfig() error { defaultSettings := maps.Params{ - "baseURL": "", - "cleanDestinationDir": false, - "watch": false, - "contentDir": "content", - "resourceDir": "resources", - "publishDir": "public", - "publishDirOrig": "public", - "themesDir": "themes", - "assetDir": "assets", - "layoutDir": "layouts", - "i18nDir": "i18n", - "dataDir": "data", - "archetypeDir": "archetypes", - "configDir": "config", - "staticDir": "static", - "buildDrafts": false, - "buildFuture": false, - "buildExpired": false, - "params": maps.Params{}, - "environment": hugo.EnvironmentProduction, - "uglyURLs": false, - "verbose": false, - "ignoreCache": false, - "canonifyURLs": false, - "relativeURLs": false, - "removePathAccents": false, - "titleCaseStyle": "AP", - "taxonomies": maps.Params{"tag": "tags", "category": "categories"}, - "permalinks": maps.Params{}, - "sitemap": maps.Params{"priority": -1, "filename": "sitemap.xml"}, - "menus": maps.Params{}, - "disableLiveReload": false, - "pluralizeListTitles": true, - "capitalizeListTitles": true, - "forceSyncStatic": false, - "footnoteAnchorPrefix": "", - "footnoteReturnLinkContents": "", - "newContentEditor": "", - "paginate": 0, // Moved into the paginator struct in Hugo v0.128.0. - "paginatePath": "", // Moved into the paginator struct in Hugo v0.128.0. - "summaryLength": 70, - "rssLimit": -1, - "sectionPagesMenu": "", - "disablePathToLower": false, - "hasCJKLanguage": false, - "enableEmoji": false, - "defaultContentLanguage": "en", - "defaultContentLanguageInSubdir": false, - "enableMissingTranslationPlaceholders": false, - "enableGitInfo": false, - "ignoreFiles": make([]string, 0), - "disableAliases": false, - "debug": false, - "disableFastRender": false, - "timeout": "30s", - "timeZone": "", - "enableInlineShortcodes": false, + // These dirs are used early/before we build the config struct. + "themesDir": "themes", + "configDir": "config", } l.cfg.SetDefaults(defaultSettings) @@ -287,40 +233,51 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error { if existing != nil { val, err := metadecoders.Default.UnmarshalStringTo(env.Value, existing) - if err != nil { + if err == nil { + val = l.envValToVal(env.Key, val) + if owner != nil { + owner[nestedKey] = val + } else { + l.cfg.Set(env.Key, val) + } continue } - - if owner != nil { - owner[nestedKey] = val - } else { - l.cfg.Set(env.Key, val) - } - } else { - if nestedKey != "" { - owner[nestedKey] = env.Value - } else { - var val any - key := strings.ReplaceAll(env.Key, delim, ".") - _, ok := allDecoderSetups[key] - if ok { - // A map. - if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]interface{}{}); err == nil { - val = v - } - } - if val == nil { - // A string. - val = l.envStringToVal(key, env.Value) - } - l.cfg.Set(key, val) - } } + + if owner != nil && nestedKey != "" { + owner[nestedKey] = env.Value + } else { + var val any + key := strings.ReplaceAll(env.Key, delim, ".") + _, ok := allDecoderSetups[key] + if ok { + // A map. + if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil { + val = v + } + } + + if val == nil { + // A string. + val = l.envStringToVal(key, env.Value) + } + l.cfg.Set(key, val) + } + } return nil } +func (l *configLoader) envValToVal(k string, v any) any { + switch v := v.(type) { + case string: + return l.envStringToVal(k, v) + default: + return v + } +} + func (l *configLoader) envStringToVal(k, v string) any { switch k { case "disablekinds", "disablelanguages": @@ -470,7 +427,7 @@ func (l *configLoader) loadModules(configs *Configs, ignoreModuleDoesNotExist bo ignoreVendor, _ = hglob.GetGlob(hglob.NormalizePath(s)) } - ex := hexec.New(conf.Security, workingDir) + ex := hexec.New(conf.Security, workingDir, l.Logger) hook := func(m *modules.ModulesConfig) error { for _, tc := range m.AllModules { diff --git a/config/commonConfig.go b/config/commonConfig.go index 9dea4a2fc..947078672 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -15,7 +15,9 @@ package config import ( "fmt" + "net/http" "regexp" + "slices" "sort" "strings" @@ -127,7 +129,7 @@ func (w BuildStats) Enabled() bool { } func (b BuildConfig) clone() BuildConfig { - b.CacheBusters = append([]CacheBuster{}, b.CacheBusters...) + b.CacheBusters = slices.Clone(b.CacheBusters) return b } @@ -226,7 +228,22 @@ type Server struct { Redirects []Redirect compiledHeaders []glob.Glob - compiledRedirects []glob.Glob + compiledRedirects []redirect +} + +type redirect struct { + from glob.Glob + fromRe *regexp.Regexp + headers map[string]glob.Glob +} + +func (r redirect) matchHeader(header http.Header) bool { + for k, v := range r.headers { + if !v.Match(header.Get(k)) { + return false + } + } + return true } func (s *Server) CompileConfig(logger loggers.Logger) error { @@ -234,10 +251,41 @@ func (s *Server) CompileConfig(logger loggers.Logger) error { return nil } for _, h := range s.Headers { - s.compiledHeaders = append(s.compiledHeaders, glob.MustCompile(h.For)) + g, err := glob.Compile(h.For) + if err != nil { + return fmt.Errorf("failed to compile Headers glob %q: %w", h.For, err) + } + s.compiledHeaders = append(s.compiledHeaders, g) } for _, r := range s.Redirects { - s.compiledRedirects = append(s.compiledRedirects, glob.MustCompile(r.From)) + if r.From == "" && r.FromRe == "" { + return fmt.Errorf("redirects must have either From or FromRe set") + } + rd := redirect{ + headers: make(map[string]glob.Glob), + } + if r.From != "" { + g, err := glob.Compile(r.From) + if err != nil { + return fmt.Errorf("failed to compile Redirect glob %q: %w", r.From, err) + } + rd.from = g + } + if r.FromRe != "" { + re, err := regexp.Compile(r.FromRe) + if err != nil { + return fmt.Errorf("failed to compile Redirect regexp %q: %w", r.FromRe, err) + } + rd.fromRe = re + } + for k, v := range r.FromHeaders { + g, err := glob.Compile(v) + if err != nil { + return fmt.Errorf("failed to compile Redirect header glob %q: %w", v, err) + } + rd.headers[k] = g + } + s.compiledRedirects = append(s.compiledRedirects, rd) } return nil @@ -266,22 +314,42 @@ func (s *Server) MatchHeaders(pattern string) []types.KeyValueStr { return matches } -func (s *Server) MatchRedirect(pattern string) Redirect { +func (s *Server) MatchRedirect(pattern string, header http.Header) Redirect { if s.compiledRedirects == nil { return Redirect{} } pattern = strings.TrimSuffix(pattern, "index.html") - for i, g := range s.compiledRedirects { + for i, r := range s.compiledRedirects { redir := s.Redirects[i] - // No redirect to self. - if redir.To == pattern { - return Redirect{} + var found bool + + if r.from != nil { + if r.from.Match(pattern) { + found = header == nil || r.matchHeader(header) + // We need to do regexp group replacements if needed. + } } - if g.Match(pattern) { + if r.fromRe != nil { + m := r.fromRe.FindStringSubmatch(pattern) + if m != nil { + if !found { + found = header == nil || r.matchHeader(header) + } + + if found { + // Replace $1, $2 etc. in To. + for i, g := range m[1:] { + redir.To = strings.ReplaceAll(redir.To, fmt.Sprintf("$%d", i+1), g) + } + } + } + } + + if found { return redir } } @@ -295,8 +363,22 @@ type Headers struct { } type Redirect struct { + // From is the Glob pattern to match. + // One of From or FromRe must be set. From string - To string + + // FromRe is the regexp to match. + // This regexp can contain group matches (e.g. $1) that can be used in the To field. + // One of From or FromRe must be set. + FromRe string + + // To is the target URL. + To string + + // Headers to match for the redirect. + // This maps the HTTP header name to a Glob pattern with values to match. + // If the map is empty, the redirect will always be triggered. + FromHeaders map[string]string // HTTP status code to use for the redirect. // A status code of 200 will trigger a URL rewrite. @@ -369,7 +451,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error { } func (r Redirect) IsZero() bool { - return r.From == "" + return r.From == "" && r.FromRe == "" } const ( @@ -383,17 +465,7 @@ func DecodeServer(cfg Provider) (Server, error) { _ = mapstructure.WeakDecode(cfg.GetStringMap("server"), s) for i, redir := range s.Redirects { - // Get it in line with the Hugo server for OK responses. - // We currently treat the 404 as a special case, they are always "ugly", so keep them as is. - if redir.Status != 404 { - redir.To = strings.TrimSuffix(redir.To, "index.html") - if !strings.HasPrefix(redir.To, "https") && !strings.HasSuffix(redir.To, "/") { - // There are some tricky infinite loop situations when dealing - // when the target does not have a trailing slash. - // This can certainly be handled better, but not time for that now. - return Server{}, fmt.Errorf("unsupported redirect to value %q in server config; currently this must be either a remote destination or a local folder, e.g. \"/blog/\" or \"/blog/index.html\"", redir.To) - } - } + redir.To = strings.TrimSuffix(redir.To, "index.html") s.Redirects[i] = redir } @@ -401,7 +473,7 @@ func DecodeServer(cfg Provider) (Server, error) { // Set up a default redirect for 404s. s.Redirects = []Redirect{ { - From: "**", + From: "/**", To: "/404.html", Status: 404, }, diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go index 425d3e970..05ba185e3 100644 --- a/config/commonConfig_test.go +++ b/config/commonConfig_test.go @@ -71,7 +71,28 @@ X-Content-Type-Options = "nosniff" [[server.redirects]] from = "/foo/**" -to = "/foo/index.html" +to = "/baz/index.html" +status = 200 + +[[server.redirects]] +from = "/loop/**" +to = "/loop/foo/" +status = 200 + +[[server.redirects]] +from = "/b/**" +fromRe = "/b/(.*)/" +to = "/baz/$1/" +status = 200 + +[[server.redirects]] +fromRe = "/c/(.*)/" +to = "/boo/$1/" +status = 200 + +[[server.redirects]] +fromRe = "/d/(.*)/" +to = "/boo/$1/" status = 200 [[server.redirects]] @@ -79,11 +100,6 @@ from = "/google/**" to = "https://google.com/" status = 301 -[[server.redirects]] -from = "/**" -to = "/default/index.html" -status = 301 - `, "toml") @@ -100,45 +116,35 @@ status = 301 {Key: "X-XSS-Protection", Value: "1; mode=block"}, }) - c.Assert(s.MatchRedirect("/foo/bar/baz"), qt.DeepEquals, Redirect{ + c.Assert(s.MatchRedirect("/foo/bar/baz", nil), qt.DeepEquals, Redirect{ From: "/foo/**", - To: "/foo/", + To: "/baz/", Status: 200, }) - c.Assert(s.MatchRedirect("/someother"), qt.DeepEquals, Redirect{ - From: "/**", - To: "/default/", - Status: 301, + c.Assert(s.MatchRedirect("/foo/bar/", nil), qt.DeepEquals, Redirect{ + From: "/foo/**", + To: "/baz/", + Status: 200, }) - c.Assert(s.MatchRedirect("/google/foo"), qt.DeepEquals, Redirect{ + c.Assert(s.MatchRedirect("/b/c/", nil), qt.DeepEquals, Redirect{ + From: "/b/**", + FromRe: "/b/(.*)/", + To: "/baz/c/", + Status: 200, + }) + + c.Assert(s.MatchRedirect("/c/d/", nil).To, qt.Equals, "/boo/d/") + c.Assert(s.MatchRedirect("/c/d/e/", nil).To, qt.Equals, "/boo/d/e/") + + c.Assert(s.MatchRedirect("/someother", nil), qt.DeepEquals, Redirect{}) + + c.Assert(s.MatchRedirect("/google/foo", nil), qt.DeepEquals, Redirect{ From: "/google/**", To: "https://google.com/", Status: 301, }) - - // No redirect loop, please. - c.Assert(s.MatchRedirect("/default/index.html"), qt.DeepEquals, Redirect{}) - c.Assert(s.MatchRedirect("/default/"), qt.DeepEquals, Redirect{}) - - for _, errorCase := range []string{ - `[[server.redirects]] -from = "/**" -to = "/file" -status = 301`, - `[[server.redirects]] -from = "/**" -to = "/foo/file.html" -status = 301`, - } { - - cfg, err := FromConfigString(errorCase, "toml") - c.Assert(err, qt.IsNil) - _, err = DecodeServer(cfg) - c.Assert(err, qt.Not(qt.IsNil)) - - } } func TestBuildConfigCacheBusters(t *testing.T) { @@ -160,7 +166,7 @@ func TestBuildConfigCacheBusters(t *testing.T) { func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) { c := qt.New(t) cfg := New() - cfg.Set("build", map[string]interface{}{ + cfg.Set("build", map[string]any{ "cacheBusters": []map[string]string{ { "source": "assets/watching/hugo_stats\\.json", diff --git a/config/configProvider.go b/config/configProvider.go index ee6691cf1..c21342dce 100644 --- a/config/configProvider.go +++ b/config/configProvider.go @@ -58,7 +58,7 @@ type AllProvider interface { BuildDrafts() bool Running() bool Watching() bool - NewIdentityManager(name string) identity.Manager + NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager FastRenderMode() bool PrintUnusedTemplates() bool EnableMissingTranslationPlaceholders() bool @@ -76,7 +76,7 @@ type AllProvider interface { } // We cannot import the media package as that would create a circular dependency. -// This interface defineds a sub set of what media.ContentTypes provides. +// This interface defines a subset of what media.ContentTypes provides. type ContentTypesProvider interface { IsContentSuffix(suffix string) bool IsContentFile(filename string) bool diff --git a/config/defaultConfigProvider.go b/config/defaultConfigProvider.go index f840c6cb0..8c1d63851 100644 --- a/config/defaultConfigProvider.go +++ b/config/defaultConfigProvider.go @@ -15,7 +15,6 @@ package config import ( "fmt" - "sort" "strings" "sync" @@ -26,42 +25,6 @@ import ( "github.com/gohugoio/hugo/common/maps" ) -var ( - - // ConfigRootKeysSet contains all of the config map root keys. - ConfigRootKeysSet = map[string]bool{ - "build": true, - "caches": true, - "cascade": true, - "frontmatter": true, - "languages": true, - "imaging": true, - "markup": true, - "mediatypes": true, - "menus": true, - "minify": true, - "module": true, - "outputformats": true, - "params": true, - "permalinks": true, - "related": true, - "sitemap": true, - "privacy": true, - "security": true, - "taxonomies": true, - } - - // ConfigRootKeys is a sorted version of ConfigRootKeysSet. - ConfigRootKeys []string -) - -func init() { - for k := range ConfigRootKeysSet { - ConfigRootKeys = append(ConfigRootKeys, k) - } - sort.Strings(ConfigRootKeys) -} - // New creates a Provider backed by an empty maps.Params. func New() Provider { return &defaultConfigProvider{ @@ -382,7 +345,7 @@ func (c *defaultConfigProvider) getNestedKeyAndMap(key string, create bool) (str c.keyCache.Store(key, parts) } current := c.root - for i := 0; i < len(parts)-1; i++ { + for i := range len(parts) - 1 { next, found := current[parts[i]] if !found { if create { diff --git a/config/defaultConfigProvider_test.go b/config/defaultConfigProvider_test.go index 65f10ec6a..cd6247e60 100644 --- a/config/defaultConfigProvider_test.go +++ b/config/defaultConfigProvider_test.go @@ -332,7 +332,7 @@ func TestDefaultConfigProvider(t *testing.T) { return nil } - for i := 0; i < 20; i++ { + for i := range 20 { i := i r.Run(func() error { const v = 42 diff --git a/config/namespace.go b/config/namespace.go index 46b5014c3..e41b56e2d 100644 --- a/config/namespace.go +++ b/config/namespace.go @@ -22,7 +22,7 @@ import ( func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) { // Calculate the hash of the input (not including any defaults applied later). // This allows us to introduce new config options without breaking the hash. - h := hashing.HashString(configSource) + h := hashing.HashStringHex(configSource) // Build the config c, ext, err := buildConfig(configSource) diff --git a/config/namespace_test.go b/config/namespace_test.go index 5eacdeac7..f443523a4 100644 --- a/config/namespace_test.go +++ b/config/namespace_test.go @@ -29,7 +29,7 @@ func TestNamespace(t *testing.T) { // ns, err := config.DecodeNamespace[map[string]DocsMediaTypeConfig](in, defaultMediaTypesConfig, buildConfig) ns, err := DecodeNamespace[[]*tstNsExt]( - map[string]interface{}{"foo": "bar"}, + map[string]any{"foo": "bar"}, func(v any) (*tstNsExt, any, error) { t := &tstNsExt{} m, err := maps.ToStringMapE(v) @@ -42,8 +42,8 @@ func TestNamespace(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(ns, qt.Not(qt.IsNil)) - c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"}) - c.Assert(ns.SourceHash, qt.Equals, "1450430416588600409") + c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]any{"foo": "bar"}) + c.Assert(ns.SourceHash, qt.Equals, "1420f6c7782f7459") c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"}) c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil)) } diff --git a/config/privacy/privacyConfig.go b/config/privacy/privacyConfig.go index 8880b1036..900f73540 100644 --- a/config/privacy/privacyConfig.go +++ b/config/privacy/privacyConfig.go @@ -30,9 +30,10 @@ type Config struct { Disqus Disqus GoogleAnalytics GoogleAnalytics Instagram Instagram - Twitter Twitter + Twitter Twitter // deprecated in favor of X in v0.141.0 Vimeo Vimeo YouTube YouTube + X X } // Disqus holds the privacy configuration settings related to the Disqus template. @@ -58,7 +59,8 @@ type Instagram struct { Simple bool } -// Twitter holds the privacy configuration settingsrelated to the Twitter shortcode. +// Twitter holds the privacy configuration settings related to the Twitter shortcode. +// Deprecated in favor of X in v0.141.0. type Twitter struct { Service `mapstructure:",squash"` @@ -70,7 +72,7 @@ type Twitter struct { Simple bool } -// Vimeo holds the privacy configuration settingsrelated to the Vimeo shortcode. +// Vimeo holds the privacy configuration settings related to the Vimeo shortcode. type Vimeo struct { Service `mapstructure:",squash"` @@ -84,7 +86,7 @@ type Vimeo struct { Simple bool } -// YouTube holds the privacy configuration settingsrelated to the YouTube shortcode. +// YouTube holds the privacy configuration settings related to the YouTube shortcode. type YouTube struct { Service `mapstructure:",squash"` @@ -94,6 +96,20 @@ type YouTube struct { PrivacyEnhanced bool } +// X holds the privacy configuration settings related to the X shortcode. +type X struct { + Service `mapstructure:",squash"` + + // When set to true, the X post and its embedded page on your site are not + // used for purposes that include personalized suggestions and personalized + // ads. + EnableDNT bool + + // If simple mode is enabled, a static and no-JS version of the X post will + // be built. + Simple bool +} + // DecodeConfig creates a privacy Config from a given Hugo configuration. func DecodeConfig(cfg config.Provider) (pc Config, err error) { if !cfg.IsSet(privacyConfigKey) { diff --git a/config/privacy/privacyConfig_test.go b/config/privacy/privacyConfig_test.go index bff627f48..1dd20215b 100644 --- a/config/privacy/privacyConfig_test.go +++ b/config/privacy/privacyConfig_test.go @@ -36,7 +36,7 @@ respectDoNotTrack = true [privacy.instagram] disable = true simple = true -[privacy.twitter] +[privacy.x] disable = true enableDNT = true simple = true @@ -59,9 +59,10 @@ simple = true got := []bool{ pc.Disqus.Disable, pc.GoogleAnalytics.Disable, pc.GoogleAnalytics.RespectDoNotTrack, pc.Instagram.Disable, - pc.Instagram.Simple, pc.Twitter.Disable, pc.Twitter.EnableDNT, - pc.Twitter.Simple, pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple, - pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable, + pc.Instagram.Simple, + pc.Vimeo.Disable, pc.Vimeo.EnableDNT, pc.Vimeo.Simple, + pc.YouTube.PrivacyEnhanced, pc.YouTube.Disable, pc.X.Disable, pc.X.EnableDNT, + pc.X.Simple, } c.Assert(got, qt.All(qt.Equals), true) diff --git a/config/security/whitelist.go b/config/security/whitelist.go index 92eb3102f..5ce369a1f 100644 --- a/config/security/whitelist.go +++ b/config/security/whitelist.go @@ -73,7 +73,7 @@ func NewWhitelist(patterns ...string) (Whitelist, error) { var patternsr []*regexp.Regexp - for i := 0; i < len(patterns); i++ { + for i := range patterns { p := strings.TrimSpace(patterns[i]) if p == "" { continue diff --git a/config/services/servicesConfig.go b/config/services/servicesConfig.go index 1b4317e92..f9d5e1a6e 100644 --- a/config/services/servicesConfig.go +++ b/config/services/servicesConfig.go @@ -31,7 +31,8 @@ type Config struct { Disqus Disqus GoogleAnalytics GoogleAnalytics Instagram Instagram - Twitter Twitter + Twitter Twitter // deprecated in favor of X in v0.141.0 + X X RSS RSS } @@ -61,6 +62,7 @@ type Instagram struct { } // Twitter holds the functional configuration settings related to the Twitter shortcodes. +// Deprecated in favor of X in v0.141.0. type Twitter struct { // The Simple variant of Twitter is decorated with a basic set of inline styles. // This means that if you want to provide your own CSS, you want @@ -68,6 +70,14 @@ type Twitter struct { DisableInlineCSS bool } +// X holds the functional configuration settings related to the X shortcodes. +type X struct { + // The Simple variant of X is decorated with a basic set of inline styles. + // This means that if you want to provide your own CSS, you want + // to disable the inline CSS provided by Hugo. + DisableInlineCSS bool +} + // RSS holds the functional configuration settings related to the RSS feeds. type RSS struct { // Limit the number of pages. @@ -91,6 +101,9 @@ func DecodeConfig(cfg config.Provider) (c Config, err error) { if c.RSS.Limit == 0 { c.RSS.Limit = cfg.GetInt(rssLimitKey) + if c.RSS.Limit == 0 { + c.RSS.Limit = -1 + } } return diff --git a/config/services/servicesConfig_test.go b/config/services/servicesConfig_test.go index 12b042a5a..952a7fe1c 100644 --- a/config/services/servicesConfig_test.go +++ b/config/services/servicesConfig_test.go @@ -36,6 +36,8 @@ id = "ga_id" disableInlineCSS = true [services.twitter] disableInlineCSS = true +[services.x] +disableInlineCSS = true ` cfg, err := config.FromConfigString(tomlConfig, "toml") c.Assert(err, qt.IsNil) diff --git a/create/content.go b/create/content.go index f7c343d42..a4661c1ba 100644 --- a/create/content.go +++ b/create/content.go @@ -82,11 +82,13 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string, force bool) error b.setArcheTypeFilenameToUse(ext) withBuildLock := func() (string, error) { - unlock, err := h.BaseFs.LockBuild() - if err != nil { - return "", fmt.Errorf("failed to acquire a build lock: %s", err) + if !h.Configs.Base.NoBuildLock { + unlock, err := h.BaseFs.LockBuild() + if err != nil { + return "", fmt.Errorf("failed to acquire a build lock: %s", err) + } + defer unlock() } - defer unlock() if b.isDir { return "", b.buildDir() @@ -289,7 +291,7 @@ func (b *contentBuilder) applyArcheType(contentFilename string, archetypeFi hugo func (b *contentBuilder) mapArcheTypeDir() error { var m archetypeMap - seen := map[hstrings.Tuple]bool{} + seen := map[hstrings.Strings2]bool{} walkFn := func(path string, fim hugofs.FileMetaInfo) error { if fim.IsDir() { @@ -299,7 +301,7 @@ func (b *contentBuilder) mapArcheTypeDir() error { pi := fim.Meta().PathInfo if pi.IsContent() { - pathLang := hstrings.Tuple{First: pi.PathNoIdentifier(), Second: fim.Meta().Lang} + pathLang := hstrings.Strings2{pi.PathBeforeLangAndOutputFormatAndExt(), fim.Meta().Lang} if seen[pathLang] { // Duplicate content file, e.g. page.md and page.html. // In the regular build, we will filter out the duplicates, but diff --git a/create/content_test.go b/create/content_test.go index 63045cbea..429edfc26 100644 --- a/create/content_test.go +++ b/create/content_test.go @@ -129,7 +129,7 @@ site RegularPages: {{ len site.RegularPages }} ` - c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil) + c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), fmt.Appendf(nil, contentFile, "index.md"), 0o755), qt.IsNil) c.Assert(afero.WriteFile(mm, filepath.Join(defaultArchetypeDir, "index.md"), []byte("default archetype index.md"), 0o755), qt.IsNil) c.Assert(initFs(mm), qt.IsNil) diff --git a/create/skeletons/skeletons.go b/create/skeletons/skeletons.go index 54b669522..a6241ef92 100644 --- a/create/skeletons/skeletons.go +++ b/create/skeletons/skeletons.go @@ -34,10 +34,60 @@ var siteFs embed.FS var themeFs embed.FS // CreateTheme creates a theme skeleton. -func CreateTheme(createpath string, sourceFs afero.Fs) error { +func CreateTheme(createpath string, sourceFs afero.Fs, format string) error { if exists, _ := helpers.Exists(createpath, sourceFs); exists { return errors.New(createpath + " already exists") } + + format = strings.ToLower(format) + + siteConfig := map[string]any{ + "baseURL": "https://example.org/", + "languageCode": "en-US", + "title": "My New Hugo Site", + "menus": map[string]any{ + "main": []any{ + map[string]any{ + "name": "Home", + "pageRef": "/", + "weight": 10, + }, + map[string]any{ + "name": "Posts", + "pageRef": "/posts", + "weight": 20, + }, + map[string]any{ + "name": "Tags", + "pageRef": "/tags", + "weight": 30, + }, + }, + }, + "module": map[string]any{ + "hugoVersion": map[string]any{ + "extended": false, + "min": "0.146.0", + }, + }, + } + + err := createSiteConfig(sourceFs, createpath, siteConfig, format) + if err != nil { + return err + } + + defaultArchetype := map[string]any{ + "title": "{{ replace .File.ContentBaseName \"-\" \" \" | title }}", + "date": "{{ .Date }}", + "draft": true, + } + + err = createDefaultArchetype(sourceFs, createpath, defaultArchetype, format) + if err != nil { + return err + } + return copyFiles(createpath, sourceFs, themeFs) } @@ -71,12 +121,24 @@ func CreateSite(createpath string, sourceFs afero.Fs, force bool, format string) } } - err := newSiteCreateConfig(sourceFs, createpath, format) + siteConfig := map[string]any{ + "baseURL": "https://example.org/", + "title": "My New Hugo Site", + "languageCode": "en-us", + } + + err := createSiteConfig(sourceFs, createpath, siteConfig, format) if err != nil { return err } - err = newSiteCreateArchetype(sourceFs, createpath, format) + defaultArchetype := map[string]any{ + "title": "{{ replace .File.ContentBaseName \"-\" \" \" | title }}", + "date": "{{ .Date }}", + "draft": true, + } + + err = createDefaultArchetype(sourceFs, createpath, defaultArchetype, format) if err != nil { return err } @@ -99,13 +161,7 @@ func copyFiles(createpath string, sourceFs afero.Fs, skeleton embed.FS) error { }) } -func newSiteCreateConfig(fs afero.Fs, createpath string, format string) (err error) { - in := map[string]string{ - "baseURL": "https://example.org/", - "title": "My New Hugo Site", - "languageCode": "en-us", - } - +func createSiteConfig(fs afero.Fs, createpath string, in map[string]any, format string) (err error) { var buf bytes.Buffer err = parser.InterfaceToConfig(in, metadecoders.FormatFromString(format), &buf) if err != nil { @@ -115,13 +171,7 @@ func newSiteCreateConfig(fs afero.Fs, createpath string, format string) (err err return helpers.WriteToDisk(filepath.Join(createpath, "hugo."+format), &buf, fs) } -func newSiteCreateArchetype(fs afero.Fs, createpath string, format string) (err error) { - in := map[string]any{ - "title": "{{ replace .File.ContentBaseName \"-\" \" \" | title }}", - "date": "{{ .Date }}", - "draft": true, - } - +func createDefaultArchetype(fs afero.Fs, createpath string, in map[string]any, format string) (err error) { var buf bytes.Buffer err = parser.InterfaceToFrontMatter(in, metadecoders.FormatFromString(format), &buf) if err != nil { diff --git a/create/skeletons/theme/LICENSE b/create/skeletons/theme/LICENSE deleted file mode 100644 index 8aa26455d..000000000 --- a/create/skeletons/theme/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) [year] [fullname] - -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. diff --git a/create/skeletons/theme/README.md b/create/skeletons/theme/README.md deleted file mode 100644 index 7cec74e07..000000000 --- a/create/skeletons/theme/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Theme Name - -## Features - -## Installation - -## Configuration diff --git a/create/skeletons/theme/archetypes/default.md b/create/skeletons/theme/archetypes/default.md deleted file mode 100644 index c6f3fcef6..000000000 --- a/create/skeletons/theme/archetypes/default.md +++ /dev/null @@ -1,5 +0,0 @@ -+++ -title = '{{ replace .File.ContentBaseName "-" " " | title }}' -date = {{ .Date }} -draft = true -+++ diff --git a/create/skeletons/theme/hugo.toml b/create/skeletons/theme/hugo.toml deleted file mode 100644 index 6c35bc476..000000000 --- a/create/skeletons/theme/hugo.toml +++ /dev/null @@ -1,23 +0,0 @@ -baseURL = 'https://example.org/' -languageCode = 'en-US' -title = 'My New Hugo Site' - -[[menus.main]] -name = 'Home' -pageRef = '/' -weight = 10 - -[[menus.main]] -name = 'Posts' -pageRef = '/posts' -weight = 20 - -[[menus.main]] -name = 'Tags' -pageRef = '/tags' -weight = 30 - -[module] - [module.hugoVersion] - extended = false - min = "0.116.0" diff --git a/create/skeletons/theme/layouts/partials/footer.html b/create/skeletons/theme/layouts/_partials/footer.html similarity index 100% rename from create/skeletons/theme/layouts/partials/footer.html rename to create/skeletons/theme/layouts/_partials/footer.html diff --git a/create/skeletons/theme/layouts/partials/head.html b/create/skeletons/theme/layouts/_partials/head.html similarity index 100% rename from create/skeletons/theme/layouts/partials/head.html rename to create/skeletons/theme/layouts/_partials/head.html diff --git a/create/skeletons/theme/layouts/partials/head/css.html b/create/skeletons/theme/layouts/_partials/head/css.html similarity index 86% rename from create/skeletons/theme/layouts/partials/head/css.html rename to create/skeletons/theme/layouts/_partials/head/css.html index 91b928ded..d76d23a16 100644 --- a/create/skeletons/theme/layouts/partials/head/css.html +++ b/create/skeletons/theme/layouts/_partials/head/css.html @@ -1,5 +1,5 @@ {{- with resources.Get "css/main.css" }} - {{- if eq hugo.Environment "development" }} + {{- if hugo.IsDevelopment }} {{- else }} {{- with . | minify | fingerprint }} diff --git a/create/skeletons/theme/layouts/_partials/head/js.html b/create/skeletons/theme/layouts/_partials/head/js.html new file mode 100644 index 000000000..16ffbedfe --- /dev/null +++ b/create/skeletons/theme/layouts/_partials/head/js.html @@ -0,0 +1,16 @@ +{{- with resources.Get "js/main.js" }} + {{- $opts := dict + "minify" (not hugo.IsDevelopment) + "sourceMap" (cond hugo.IsDevelopment "external" "") + "targetPath" "js/main.js" + }} + {{- with . | js.Build $opts }} + {{- if hugo.IsDevelopment }} + + {{- else }} + {{- with . | fingerprint }} + + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/create/skeletons/theme/layouts/partials/header.html b/create/skeletons/theme/layouts/_partials/header.html similarity index 100% rename from create/skeletons/theme/layouts/partials/header.html rename to create/skeletons/theme/layouts/_partials/header.html diff --git a/create/skeletons/theme/layouts/partials/menu.html b/create/skeletons/theme/layouts/_partials/menu.html similarity index 96% rename from create/skeletons/theme/layouts/partials/menu.html rename to create/skeletons/theme/layouts/_partials/menu.html index 718318096..14245b55d 100644 --- a/create/skeletons/theme/layouts/partials/menu.html +++ b/create/skeletons/theme/layouts/_partials/menu.html @@ -18,7 +18,7 @@ Renders a menu for the given menu ID. {{- end }} -{{- define "partials/inline/menu/walk.html" }} +{{- define "_partials/inline/menu/walk.html" }} {{- $page := .page }} {{- range .menuEntries }} {{- $attrs := dict "href" .URL }} diff --git a/create/skeletons/theme/layouts/partials/terms.html b/create/skeletons/theme/layouts/_partials/terms.html similarity index 100% rename from create/skeletons/theme/layouts/partials/terms.html rename to create/skeletons/theme/layouts/_partials/terms.html diff --git a/create/skeletons/theme/layouts/_default/baseof.html b/create/skeletons/theme/layouts/baseof.html similarity index 100% rename from create/skeletons/theme/layouts/_default/baseof.html rename to create/skeletons/theme/layouts/baseof.html diff --git a/create/skeletons/theme/layouts/_default/home.html b/create/skeletons/theme/layouts/home.html similarity index 100% rename from create/skeletons/theme/layouts/_default/home.html rename to create/skeletons/theme/layouts/home.html diff --git a/create/skeletons/theme/layouts/_default/single.html b/create/skeletons/theme/layouts/page.html similarity index 100% rename from create/skeletons/theme/layouts/_default/single.html rename to create/skeletons/theme/layouts/page.html diff --git a/create/skeletons/theme/layouts/partials/head/js.html b/create/skeletons/theme/layouts/partials/head/js.html deleted file mode 100644 index 18fe84291..000000000 --- a/create/skeletons/theme/layouts/partials/head/js.html +++ /dev/null @@ -1,12 +0,0 @@ -{{- with resources.Get "js/main.js" }} - {{- if eq hugo.Environment "development" }} - {{- with . | js.Build }} - - {{- end }} - {{- else }} - {{- $opts := dict "minify" true }} - {{- with . | js.Build $opts | fingerprint }} - - {{- end }} - {{- end }} -{{- end }} diff --git a/create/skeletons/theme/layouts/_default/list.html b/create/skeletons/theme/layouts/section.html similarity index 100% rename from create/skeletons/theme/layouts/_default/list.html rename to create/skeletons/theme/layouts/section.html diff --git a/create/skeletons/theme/layouts/taxonomy.html b/create/skeletons/theme/layouts/taxonomy.html new file mode 100644 index 000000000..c2e787519 --- /dev/null +++ b/create/skeletons/theme/layouts/taxonomy.html @@ -0,0 +1,7 @@ +{{ define "main" }} +

{{ .Title }}

+ {{ .Content }} + {{ range .Pages }} +

{{ .LinkTitle }}

+ {{ end }} +{{ end }} diff --git a/create/skeletons/theme/layouts/term.html b/create/skeletons/theme/layouts/term.html new file mode 100644 index 000000000..c2e787519 --- /dev/null +++ b/create/skeletons/theme/layouts/term.html @@ -0,0 +1,7 @@ +{{ define "main" }} +

{{ .Title }}

+ {{ .Content }} + {{ range .Pages }} +

{{ .LinkTitle }}

+ {{ end }} +{{ end }} diff --git a/create/skeletons/theme/theme.toml b/create/skeletons/theme/theme.toml deleted file mode 100644 index 3ba316463..000000000 --- a/create/skeletons/theme/theme.toml +++ /dev/null @@ -1,31 +0,0 @@ -name = 'Theme name' -license = 'MIT' -licenselink = 'https://github.com/owner/repo/LICENSE' -description = 'Theme description' - -# The home page of the theme, where the source can be found -homepage = 'https://github.com/owner/repo' - -# If you have a running demo of the theme -demosite = 'https://owner.github.io/repo' - -# Taxonomy terms -tags = ['blog', 'company'] -features = ['some', 'awesome', 'features'] - -# If the theme has multiple authors -authors = [ - {name = 'Name of author', homepage = 'Website of author'}, - {name = 'Name of author', homepage = 'Website of author'} -] - -# If the theme has a single author -[author] - name = 'Your name' - homepage = 'Your website' - -# If porting an existing theme -[original] - author = 'Name of original author' - homepage = 'Website of original author' - repo = 'https://github.com/owner/repo' diff --git a/deploy/cloudfront.go b/deploy/cloudfront.go index 4481eb52a..3202a73ea 100644 --- a/deploy/cloudfront.go +++ b/deploy/cloudfront.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy +//go:build withdeploy package deploy diff --git a/deploy/deploy.go b/deploy/deploy.go index a69e974b7..57e1f41a2 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy +//go:build withdeploy package deploy @@ -37,6 +36,7 @@ import ( "github.com/dustin/go-humanize" "github.com/gobwas/glob" "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/common/para" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/deploy/deployconfig" "github.com/gohugoio/hugo/media" @@ -488,7 +488,12 @@ func knownHiddenDirectory(name string) bool { // walkLocal walks the source directory and returns a flat list of files, // using localFile.SlashPath as the map keys. func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, include, exclude glob.Glob, mediaTypes media.Types, mappath func(string) string) (map[string]*localFile, error) { - retval := map[string]*localFile{} + retval := make(map[string]*localFile) + var mu sync.Mutex + + workers := para.New(d.cfg.Workers) + g, _ := workers.Start(context.Background()) + err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -509,45 +514,54 @@ func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, incl return nil } - // When a file system is HFS+, its filepath is in NFD form. - if runtime.GOOS == "darwin" { - path = norm.NFC.String(path) - } - - // Check include/exclude matchers. - slashpath := filepath.ToSlash(path) - if include != nil && !include.Match(slashpath) { - d.logger.Infof(" dropping %q due to include\n", slashpath) - return nil - } - if exclude != nil && exclude.Match(slashpath) { - d.logger.Infof(" dropping %q due to exclude\n", slashpath) - return nil - } - - // Find the first matching matcher (if any). - var m *deployconfig.Matcher - for _, cur := range matchers { - if cur.Matches(slashpath) { - m = cur - break + // Process each file in a worker + g.Run(func() error { + // When a file system is HFS+, its filepath is in NFD form. + if runtime.GOOS == "darwin" { + path = norm.NFC.String(path) } - } - // Apply any additional modifications to the local path, to map it to - // the remote path. - if mappath != nil { - slashpath = mappath(slashpath) - } - lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes) - if err != nil { - return err - } - retval[lf.SlashPath] = lf + + // Check include/exclude matchers. + slashpath := filepath.ToSlash(path) + if include != nil && !include.Match(slashpath) { + d.logger.Infof(" dropping %q due to include\n", slashpath) + return nil + } + if exclude != nil && exclude.Match(slashpath) { + d.logger.Infof(" dropping %q due to exclude\n", slashpath) + return nil + } + + // Find the first matching matcher (if any). + var m *deployconfig.Matcher + for _, cur := range matchers { + if cur.Matches(slashpath) { + m = cur + break + } + } + // Apply any additional modifications to the local path, to map it to + // the remote path. + if mappath != nil { + slashpath = mappath(slashpath) + } + lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes) + if err != nil { + return err + } + mu.Lock() + retval[lf.SlashPath] = lf + mu.Unlock() + return nil + }) return nil }) if err != nil { return nil, err } + if err := g.Wait(); err != nil { + return nil, err + } return retval, nil } diff --git a/deploy/deploy_azure.go b/deploy/deploy_azure.go index fc7daca3b..b1ce7358c 100644 --- a/deploy/deploy_azure.go +++ b/deploy/deploy_azure.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !solaris && !nodeploy -// +build !solaris,!nodeploy +//go:build !solaris && withdeploy package deploy diff --git a/deploy/deploy_test.go b/deploy/deploy_test.go index 17dffc25a..bdc8299a0 100644 --- a/deploy/deploy_test.go +++ b/deploy/deploy_test.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy +//go:build withdeploy package deploy @@ -624,7 +623,7 @@ func TestEndToEndSync(t *testing.T) { localFs: test.fs, bucket: test.bucket, mediaTypes: media.DefaultTypes, - cfg: deployconfig.DeployConfig{MaxDeletes: -1}, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, } // Initial deployment should sync remote with local. @@ -707,7 +706,7 @@ func TestMaxDeletes(t *testing.T) { localFs: test.fs, bucket: test.bucket, mediaTypes: media.DefaultTypes, - cfg: deployconfig.DeployConfig{MaxDeletes: -1}, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, } // Sync remote with local. @@ -837,7 +836,7 @@ func TestIncludeExclude(t *testing.T) { } deployer := &Deployer{ localFs: fsTest.fs, - cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket, target: tgt, mediaTypes: media.DefaultTypes, } @@ -894,7 +893,7 @@ func TestIncludeExcludeRemoteDelete(t *testing.T) { } deployer := &Deployer{ localFs: fsTest.fs, - cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket, mediaTypes: media.DefaultTypes, } @@ -946,7 +945,7 @@ func TestCompression(t *testing.T) { deployer := &Deployer{ localFs: test.fs, bucket: test.bucket, - cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}}, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}}, mediaTypes: media.DefaultTypes, } @@ -1001,7 +1000,7 @@ func TestMatching(t *testing.T) { deployer := &Deployer{ localFs: test.fs, bucket: test.bucket, - cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}}, + cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}}, mediaTypes: media.DefaultTypes, } @@ -1098,5 +1097,6 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) ( func newDeployer() *Deployer { return &Deployer{ logger: loggers.NewDefault(), + cfg: deployconfig.DeployConfig{Workers: 2}, } } diff --git a/deploy/deployconfig/deployConfig_test.go b/deploy/deployconfig/deployConfig_test.go index 0b92a5cb1..38d0aadd6 100644 --- a/deploy/deployconfig/deployConfig_test.go +++ b/deploy/deployconfig/deployConfig_test.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy +//go:build withdeploy package deployconfig diff --git a/deploy/google.go b/deploy/google.go index 6e492bc01..5b302e95b 100644 --- a/deploy/google.go +++ b/deploy/google.go @@ -11,8 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !nodeploy -// +build !nodeploy +//go:build withdeploy package deploy diff --git a/deps/deps.go b/deps/deps.go index 4389036cb..d0d6d95fc 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "os" "path/filepath" "sort" "strings" @@ -23,10 +24,12 @@ import ( "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/internal/js" "github.com/gohugoio/hugo/internal/warpc" "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/postpub" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/gohugoio/hugo/metrics" "github.com/gohugoio/hugo/resources" @@ -44,9 +47,6 @@ type Deps struct { ExecHelper *hexec.Exec - // The templates to use. This will usually implement the full tpl.TemplateManager. - tmplHandlers *tpl.TemplateHandlers - // The file systems to use. Fs *hugofs.Fs `json:"-"` @@ -74,7 +74,8 @@ type Deps struct { // The site building. Site page.Site - TemplateProvider ResourceProvider + TemplateStore *tplimpl.TemplateStore + // Used in tests OverloadedTemplateFuncs map[string]any @@ -83,10 +84,13 @@ type Deps struct { Metrics metrics.Provider // BuildStartListeners will be notified before a build starts. - BuildStartListeners *Listeners + BuildStartListeners *Listeners[any] // BuildEndListeners will be notified after a build finishes. - BuildEndListeners *Listeners + BuildEndListeners *Listeners[any] + + // OnChangeListeners will be notified when something changes. + OnChangeListeners *Listeners[identity.Identity] // Resources that gets closed when the build is done or the server shuts down. BuildClosers *types.Closers @@ -94,10 +98,16 @@ type Deps struct { // This is common/global for all sites. BuildState *BuildState + // Misc counters. + Counters *Counters + // Holds RPC dispatchers for Katex etc. // TODO(bep) rethink this re. a plugin setup, but this will have to do for now. WasmDispatchers *warpc.Dispatchers + // The JS batcher client. + JSBatcherClient js.BatcherClient + isClosed bool *globalErrHandler @@ -116,8 +126,8 @@ func (d Deps) Clone(s page.Site, conf config.AllProvider) (*Deps, error) { return &d, nil } -func (d *Deps) SetTempl(t *tpl.TemplateHandlers) { - d.tmplHandlers = t +func (d *Deps) GetTemplateStore() *tplimpl.TemplateStore { + return d.TemplateStore } func (d *Deps) Init() error { @@ -139,10 +149,12 @@ func (d *Deps) Init() error { logger: d.Log, } } - if d.BuildState == nil { d.BuildState = &BuildState{} } + if d.Counters == nil { + d.Counters = &Counters{} + } if d.BuildState.DeferredExecutions == nil { if d.BuildState.DeferredExecutionsGroupedByRenderingContext == nil { d.BuildState.DeferredExecutionsGroupedByRenderingContext = make(map[tpl.RenderingContext]*DeferredExecutions) @@ -154,23 +166,27 @@ func (d *Deps) Init() error { } if d.BuildStartListeners == nil { - d.BuildStartListeners = &Listeners{} + d.BuildStartListeners = &Listeners[any]{} } if d.BuildEndListeners == nil { - d.BuildEndListeners = &Listeners{} + d.BuildEndListeners = &Listeners[any]{} } if d.BuildClosers == nil { d.BuildClosers = &types.Closers{} } + if d.OnChangeListeners == nil { + d.OnChangeListeners = &Listeners[identity.Identity]{} + } + if d.Metrics == nil && d.Conf.TemplateMetrics() { d.Metrics = metrics.NewProvider(d.Conf.TemplateMetricsHints()) } if d.ExecHelper == nil { - d.ExecHelper = hexec.New(d.Conf.GetConfigSection("security").(security.Config), d.Conf.WorkingDir()) + d.ExecHelper = hexec.New(d.Conf.GetConfigSection("security").(security.Config), d.Conf.WorkingDir(), d.Log) } if d.MemCache == nil { @@ -245,22 +261,17 @@ func (d *Deps) Init() error { return nil } +// TODO(bep) rework this to get it in line with how we manage templates. func (d *Deps) Compile(prototype *Deps) error { var err error if prototype == nil { - if err = d.TemplateProvider.NewResource(d); err != nil { - return err - } + if err = d.TranslationProvider.NewResource(d); err != nil { return err } return nil } - if err = d.TemplateProvider.CloneResource(d, prototype); err != nil { - return err - } - if err = d.TranslationProvider.CloneResource(d, prototype); err != nil { return err } @@ -268,6 +279,23 @@ func (d *Deps) Compile(prototype *Deps) error { return nil } +// MkdirTemp returns a temporary directory path that will be cleaned up on exit. +func (d Deps) MkdirTemp(pattern string) (string, error) { + filename, err := os.MkdirTemp("", pattern) + if err != nil { + return "", err + } + d.BuildClosers.Add( + types.CloserFunc( + func() error { + return os.RemoveAll(filename) + }, + ), + ) + + return filename, nil +} + type globalErrHandler struct { logger loggers.Logger @@ -306,15 +334,16 @@ func (e *globalErrHandler) StopErrorCollector() { } // Listeners represents an event listener. -type Listeners struct { +type Listeners[T any] struct { sync.Mutex // A list of funcs to be notified about an event. - listeners []func() + // If the return value is true, the listener will be removed. + listeners []func(...T) bool } // Add adds a function to a Listeners instance. -func (b *Listeners) Add(f func()) { +func (b *Listeners[T]) Add(f func(...T) bool) { if b == nil { return } @@ -324,12 +353,16 @@ func (b *Listeners) Add(f func()) { } // Notify executes all listener functions. -func (b *Listeners) Notify() { +func (b *Listeners[T]) Notify(vs ...T) { b.Lock() defer b.Unlock() + temp := b.listeners[:0] for _, notify := range b.listeners { - notify() + if !notify(vs...) { + temp = append(temp, notify) + } } + b.listeners = temp } // ResourceProvider is used to create and refresh, and clone resources needed. @@ -338,14 +371,6 @@ type ResourceProvider interface { CloneResource(dst, src *Deps) error } -func (d *Deps) Tmpl() tpl.TemplateHandler { - return d.tmplHandlers.Tmpl -} - -func (d *Deps) TextTmpl() tpl.TemplateParseFinder { - return d.tmplHandlers.TxtTmpl -} - func (d *Deps) Close() error { if d.isClosed { return nil @@ -372,9 +397,11 @@ type DepsCfg struct { // The logging level to use. LogLevel logg.Level - // Where to write the logs. - // Currently we typically write everything to stdout. - LogOut io.Writer + // Logging output. + StdErr io.Writer + + // The console output. + StdOut io.Writer // The file systems to use Fs *hugofs.Fs @@ -412,6 +439,12 @@ type BuildState struct { DeferredExecutionsGroupedByRenderingContext map[tpl.RenderingContext]*DeferredExecutions } +// Misc counters. +type Counters struct { + // Counter for the math.Counter function. + MathCounter atomic.Uint64 +} + type DeferredExecutions struct { // A set of filenames in /public that // contains a post-processing prefix. diff --git a/docs/.codespellrc b/docs/.codespellrc new file mode 100644 index 000000000..564fc77c0 --- /dev/null +++ b/docs/.codespellrc @@ -0,0 +1,13 @@ +# Config file for codespell. +# https://github.com/codespell-project/codespell#using-a-config-file + +[codespell] + +# Comma separated list of dirs to be skipped. +skip = _vendor,.cspell.json,chroma.css,chroma_dark.css + +# Comma separated list of words to be ignored. Words must be lowercased. +ignore-words-list = abl,edn,te,ue,trys,januar,womens,crossreferences + +# Check file names as well. +check-filenames = true diff --git a/docs/.cspell.json b/docs/.cspell.json index 01d248e25..bf61489da 100644 --- a/docs/.cspell.json +++ b/docs/.cspell.json @@ -60,6 +60,7 @@ "redirections", "subexpression", "suppressible", + "synchronisation", "templating", "transpile", "unmarshal", @@ -68,6 +69,7 @@ "# ----------------------------------------------------------------------", "# cspell: ignore hugo terminology", "# ----------------------------------------------------------------------", + "alignx", "attrlink", "canonify", "codeowners", @@ -85,6 +87,8 @@ "stringifier", "struct", "toclevels", + "unmarshal", + "unpublishdate", "zgotmplz", "# ----------------------------------------------------------------------", "# cspell: ignore foreign language words", @@ -129,6 +133,7 @@ "Samsa", "Stucki", "Thénardier", + "WASI", "# ----------------------------------------------------------------------", "# cspell: ignore operating systems and software packages", "# ----------------------------------------------------------------------", @@ -156,17 +161,24 @@ "# cspell: ignore miscellaneous", "# ----------------------------------------------------------------------", "achristie", + "ccpa", + "cpra", "ddmaurier", "dring", + "fleqn", "inor", "jausten", "jdoe", "jsmith", + "leqno", "milli", + "monokai", + "mysanityprojectid", "rgba", "rsmith", "tdewolff", "tjones", + "vcard", "wcag", "xfeff" ] diff --git a/docs/.github/ISSUE_TEMPLATE/config.yml b/docs/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/docs/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/docs/.github/ISSUE_TEMPLATE/default.md b/docs/.github/ISSUE_TEMPLATE/default.md new file mode 100644 index 000000000..ada35b3a5 --- /dev/null +++ b/docs/.github/ISSUE_TEMPLATE/default.md @@ -0,0 +1,6 @@ +--- +name: Default +about: This is the default issue template. +labels: + - NeedsTriage +--- diff --git a/docs/.github/workflows/spellcheck.yml b/docs/.github/workflows/spellcheck.yml index 86f8f53a5..e01ab1764 100644 --- a/docs/.github/workflows/spellcheck.yml +++ b/docs/.github/workflows/spellcheck.yml @@ -20,3 +20,8 @@ jobs: incremental_files_only: true inline: warning strict: false + - uses: codespell-project/actions-codespell@v2 + with: + check_filenames: true + check_hidden: true + # by default, codespell uses configuration from the .codespellrc diff --git a/docs/.gitignore b/docs/.gitignore index f9cab2f80..5208c5c3a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,10 +1,12 @@ +.DS_Store +.hugo_build.lock /.idea /.vscode -/public /dist -node_modules +/public +/resources +hugo_stats.json +node_modules/ nohup.out -.DS_Store +package-lock.json trace.out -.hugo_build.lock -resources/_gen/images/ \ No newline at end of file diff --git a/docs/.markdownlint.yaml b/docs/.markdownlint.yaml index d9c2c5a67..dbb5b2ee8 100644 --- a/docs/.markdownlint.yaml +++ b/docs/.markdownlint.yaml @@ -22,4 +22,6 @@ MD041: false MD046: false MD049: false MD050: false +MD051: false MD053: false +MD055: false diff --git a/docs/.prettierignore b/docs/.prettierignore new file mode 100644 index 000000000..f24bbcef0 --- /dev/null +++ b/docs/.prettierignore @@ -0,0 +1,18 @@ +# Ignore all SVG icons. +**/icons.html + +# These are whitespace sensitive. +layouts/_default/_markup/render-code* +layouts/_default/_markup/render-table* +layouts/shortcodes/glossary-term.html +layouts/shortcodes/glossary.html +layouts/shortcodes/highlighting-styles.html +layouts/shortcodes/list-pages-in-section.html +layouts/shortcodes/quick-reference.html + +# No root node. +layouts/partials/layouts/head/head.html + +# Auto generated. +assets/css/components/chroma*.css +assets/jsconfig.json diff --git a/docs/.prettierrc b/docs/.prettierrc new file mode 100644 index 000000000..395ae39af --- /dev/null +++ b/docs/.prettierrc @@ -0,0 +1,24 @@ +{ + "plugins": [ + "prettier-plugin-go-template", + "@awmottaz/prettier-plugin-void-html" + ], + "overrides": [ + { + "files": ["*.html"], + "options": { + "parser": "go-template", + "goTemplateBracketSpacing": true, + "bracketSameLine": true + } + }, + { + "files": ["*.js", "*.ts"], + "options": { + "useTabs": true, + "printWidth": 120, + "singleQuote": true + } + } + ] +} diff --git a/docs/LICENSE.md b/docs/LICENSE.md index b09cd7856..d4facbf8a 100644 --- a/docs/LICENSE.md +++ b/docs/LICENSE.md @@ -1,201 +1,3 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +See [content/LICENSE.md](content/LICENSE.md) for the license of the content of this repository. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +The theme (layouts, CSS, JavaScript etc.) of this repository has no open source license. It is custom made for the Hugo sites and is not meant for reuse. diff --git a/docs/README.md b/docs/README.md index 93eaaf02e..58d0e748c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,14 +7,21 @@ A fast and flexible static site generator built with love by [bep], [spf13], and [![Netlify Status](https://api.netlify.com/api/v1/badges/e0dbbfc7-34f1-4393-a679-c16e80162705/deploy-status)](https://app.netlify.com/sites/gohugoio/deploys) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://gohugo.io/contribute/documentation/) -This is the repository for the [Hugo](https://github.com/gohugoio/hugo) documentation site. +This is the repository for the [Hugo](https://github.com/gohugoio/hugo) documentation site. Please see the [contributing] section for guidelines, examples, and process. - - [bep]: https://github.com/bep [spf13]: https://github.com/spf13 [friends]: https://github.com/gohugoio/hugo/graphs/contributors [go]: https://go.dev/ [contributing]: https://gohugo.io/contribute/documentation + +# Install + +```sh +npm i +hugo server +``` + +**Note:** We're working on removing the need to run `npm i` for local development. Stay tuned. diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css deleted file mode 100644 index 0122f9758..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_algolia.css +++ /dev/null @@ -1,11 +0,0 @@ -.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-webkit-input-placeholder{color:#aaa}.searchbox__input:-ms-input-placeholder{color:#aaa}.searchbox__input::-ms-input-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}}@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} -/*# sourceMappingURL=docsearch.min.css.map */ -a.algolia-docsearch-suggestion { - text-decoration: none !important; -} -.algolia-docsearch-suggestion--category-header { - background: #0594cb; - padding-left: .25rem !important; - color: white !important; - border-radius: 3px; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css deleted file mode 100644 index 997931ac4..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_animation.css +++ /dev/null @@ -1,21 +0,0 @@ -.animated { - animation-duration: .5s; - animation-fill-mode: forwards; - animation-timing-function: ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -.fadeIn { - animation-name: fadeIn; -} -.animated-delay-1 { - animation-delay: 0.5s; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css deleted file mode 100644 index 11fae8702..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_carousel.css +++ /dev/null @@ -1,25 +0,0 @@ -/* These styles enhance the home page carousel, located here: themes/gohugoioTheme/layouts/partials/home-page-sections/showcase.html */ -.overflow-x-scroll{ - -webkit-overflow-scrolling: touch; -} -.row { - transition: 450ms transform; - font-size: 0; -} -.tile { - transition: 450ms all; -} -.details { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(0,0,0,0.9)), to(rgba(0,0,0,0))); - background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0) 100%); - transition: 450ms opacity; -} -.tile:hover .details { - opacity: 1; -} -.row:hover .tile { - opacity: 0.3; -} -.row:hover .tile:hover { - opacity: 1; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css deleted file mode 100644 index c82e77ee7..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_code.css +++ /dev/null @@ -1,89 +0,0 @@ -.chroma .lntable pre { - padding: 0; - margin: 0; - border: 0; -} - -.chroma .lntable pre code { - padding: 0; - margin: 0; -} - -code { - padding: 0.2em; - margin: 0; - font-size: 85%; - background-color: rgba(27,31,35,0.05); - border-radius: 3px; -} - -pre code { - display: block; - padding: 1.5em 1.5em; - font-size: .875rem; - line-height: 2; - overflow-x: auto; -} - -pre { - background-color: #fff; - color: #333; - white-space: pre; - hyphens: none; - position: relative; - border-width: 1px; - border-color: #ccc; - border-style: solid; -} - -/* The Pygments highlighter comes with its own styles. */ -.highlight pre { - background-color: inherit; - color: inherit; - padding: 0.5em; - font-size: .875rem; -} - - -/*We are adding the copy button content here so we can change it with javascript. See the "Clipboard scripts"*/ -.copy:after { - content: "Copy" -} -.copied:after { - content: "Copied" -} - -@media (--breakpoint-large) { - .full-width - { - /*width: 100vw; - position: relative; - left: 50%; - right: 50%; - margin-left: -50vw; - margin-right: -50vw;*/ - /*width: 60vw;*/ - /*position: relative; - left: 50%; - right: 50%;*/ - /*margin-left: -30vw;*/ - margin-right: -30vw; - max-width: 100vw; - } -} - -.code-block .line-numbers-rows { - background: #2f3a46; - border: none; - bottom: -50px; - color: #98a4b3; - left: -178px; - padding: 50px 0; - top: -50px; - width: 138px -} - -.code-block .line-numbers-rows>span:before { - color: inherit; - padding-right: 30px -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css deleted file mode 100644 index 1d61a7725..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_color-scheme.css +++ /dev/null @@ -1,38 +0,0 @@ -.primary-color {color: var(--primary-color)} -.bg-primary-color {background-color: var(--primary-color)} -.hover-bg-primary-color:hover {background-color: var(--primary-color)} - -.primary-color-dark {color: var(--primary-color-dark)} -.bg-primary-color-dark {background-color: var(--primary-color-dark)} -.hover-bg-primary-color-dark:hover {background-color: var(--primary-color-dark)} - -.primary-color-light {color: var(--primary-color-light)} -.bg-primary-color-light {background-color: var(--primary-color-light)} -.hover-bg-primary-color-light:hover {background-color: var(--primary-color-light)} - -.accent-color {color: var(--accent-color)} -.bg-accent-color {background-color: var(--accent-color)} -.hover-bg-accent-color:hover {background-color: var(--accent-color)} - -.accent-color-light {color: var(--accent-color-light)} -.hover-accent-color-light:hover {color: var(--accent-color-light)} -.bg-accent-color-light {background-color: var(--accent-color-light)} -.hover-bg-accent-color-light:hover {background-color: var(--accent-color-light)} - -.accent-color-dark {color: var(--accent-color-dark)} -.bg-accent-color-dark {background-color: var(--accent-color-dark)} -.hover-bg-accent-color-dark:hover {background-color: var(--accent-color-dark)} - -.text-color-primary {color: var(--text-color-primary)} -.text-on-primary-color {color: var(--text-on-primary-color)} -.text-color-secondary {color: var(--text-color-secondary)} -.text-color-disabled {color: var(--text-color-disabled)} -.divider-color {color: var(--divider-color)} -.warn-color {color: var(--warn-color)} - - -.nested-links a { - color: var(--primary-color); - text-decoration: none; - -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css deleted file mode 100644 index e1e938c74..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_columns.css +++ /dev/null @@ -1,11 +0,0 @@ -.column-count-2 {column-count: 1} -.column-gap-1 {column-gap: 0} -.break-inside-avoid {break-inside: auto} - - -@media (--breakpoint-large) { - .column-count-3-l {column-count: 3} - .column-count-2-l {column-count: 2} - .column-gap-1-l {column-gap: 1} - .break-inside-avoid-l {break-inside: avoid} -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css deleted file mode 100644 index 4e092e8bf..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content-tables.css +++ /dev/null @@ -1,28 +0,0 @@ -.prose table { - width: 100%; - margin-bottom: 3em; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; - border: 1px solid var(--light-gray); - & th { - background-color: var(--primary-color); - border-bottom: 1px solid var(--primary-color); - color: white; - font-weight: 400; - - text-align: left; - padding: .375em .5em; - } - - & td, & tc { - padding: .75em .5em; - text-align: left; - border-right: 1px solid var(--light-gray); - } - -} - -.prose table tr:nth-child(even) { - background-color: var(--light-gray); -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css deleted file mode 100644 index 9306f8b2f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_content.css +++ /dev/null @@ -1,53 +0,0 @@ -.prose ul, .prose ol { - margin-bottom: 2em; -} -.prose ul li, .prose ol li { - margin-bottom: .5em; -} -.prose li:hover { - background-color: var(--light-gray) -} -.prose ::selection { - background: var(--primary-color); /* WebKit/Blink Browsers */ - color: white; -} - -.prose-glossary h3 { - margin-top: 0; - font-size: 1.125rem; -} - -.prose-glossary h3:first-of-type { - margin-top: 3em; -} - -.prose-glossary h3 ~ p { - margin: 0.5em 0 2em 0; -} - -body { - -line-height: 1.45; - -} - -p {margin-bottom: 1.3em;} - -h1, h2, h3, h4 { -margin: 1.414em 0 0.5em; - -line-height: 1.2; -} - -h1 { -margin-top: 0; -font-size: 2.441em; -} - -h2 {font-size: 1.953em;} - -h3 {font-size: 1.563em;} - -h4 {font-size: 1.25em;} - -small, .font_small {font-size: 0.8em;} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css deleted file mode 100644 index e28f67d4b..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_definition-lists.css +++ /dev/null @@ -1,9 +0,0 @@ - -dl dt { - font-weight: bold; - font-size: 1.125rem; -} -dd { - margin: .5em 0 2em 0; - padding: 0; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css deleted file mode 100644 index 0ea8e9b72..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_documentation-styles.css +++ /dev/null @@ -1,54 +0,0 @@ -.note, -.warning { - - border-left-width: 4px; - border-left-style: solid; - position: relative; - border-color: var(--primary-color); - - display: block; -} -.note #exclamation-icon, -.warning #exclamation-icon { - - fill: var(--primary-color); - position: absolute; - top: 35%; - left: -12px; - /*background-color: white;*/ -} - - .admonition-content { - display: block; - margin: 0px; - padding: .125em 1em; - /*margin-left: 1em;*/ - margin-top: 2em; - margin-bottom: 2em; - overflow-x: auto; - /*font-size: .9375em;*/ - background-color: var(--black-05); - } - - - .hide-child-menu .child-menu { - display: none; - } - .hide-child-menu:hover .child-menu, - .hide-child-menu:focus .child-menu, - .hide-child-menu:active .child-menu { - display: block; - } - - -/*documentation-copy headings exaggerate spacing and size to chunk content */ - .documentation-copy h2 { - margin-top: 3em; - &.minor { - font-size: inherit; - margin-top: inherit; - border-bottom: none; - } - } - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css deleted file mode 100644 index da9f04c81..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_fluid-type.css +++ /dev/null @@ -1,10 +0,0 @@ -.f2-fluid { - font-size: 2.25rem; -} - -@media (--breakpoint-large) { - .f2-fluid { - font-size: 1.25rem; - font-size: calc(0.875rem + 0.5 * ((100vw - 20rem) / 60)); - } -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css deleted file mode 100644 index 440b5efdd..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_font-family.css +++ /dev/null @@ -1,80 +0,0 @@ -/* From https://www.cssfontstack.com */ -code, .code, pre code, .highlight pre { - font-family: 'inconsolata',Menlo,Monaco,'Courier New',monospace; -} - -.sans-serif { - font-family: 'Muli', - avenir, - 'helvetica neue', helvetica, - ubuntu, - roboto, noto, - 'segoe ui', arial, - sans-serif; -} - - -.serif { - font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif; -} - -/* Monospaced Typefaces (for code) */ - - -.courier { - font-family: 'Courier Next', - courier, - monospace; -} - - -/* Sans-Serif Typefaces */ - -.helvetica { - font-family: 'helvetica neue', helvetica, - sans-serif; -} - -.avenir { - font-family: 'avenir next', avenir, - sans-serif; -} - - -/* Serif Typefaces */ - -.athelas { - font-family: athelas, - georgia, - serif; -} - -.georgia { - font-family: georgia, - serif; -} - -.times { - font-family: times, - serif; -} - -.bodoni { - font-family: "Bodoni MT", - serif; -} - -.calisto { - font-family: "Calisto MT", - serif; -} - -.garamond { - font-family: garamond, - serif; -} - -.baskerville { - font-family: baskerville, - serif; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css deleted file mode 100644 index 56a16be6d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_header-link.css +++ /dev/null @@ -1,15 +0,0 @@ -.header-link:after { - position: relative; - left: 0.5em; - opacity: 0; - font-size: 0.8em; - -moz-transition: opacity 0.2s ease-in-out 0.1s; - -ms-transition: opacity 0.2s ease-in-out 0.1s; -} -h2:hover .header-link, -h3:hover .header-link, -h4:hover .header-link, -h5:hover .header-link, -h6:hover .header-link { - opacity: 1; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css deleted file mode 100644 index 0b1df9610..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_hugo-internal-template-styling.css +++ /dev/null @@ -1,52 +0,0 @@ -/* pagination.html: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/template_embedded.go#L117 */ -.pagination { - margin: 3rem 0; -} - -.pagination li { - display: inline-block; - margin-right: .375rem; - font-size: .875rem; - margin-bottom: 2.5em; -} -.pagination li a { - padding: .5rem .625rem; - background-color: white; - color: #333; - border: 1px solid #ddd; - border-radius: 3px; - text-decoration: none; -} -.pagination li.disabled { - display: none; -} -.pagination li.active a:link, -.pagination li.active a:active, -.pagination li.active a:visited { - background-color: #ddd; -} - -/* Hides non-meaningful TOC items*/ -#TableOfContents ul li ul li ul li{ - display: none; - } - - -#TableOfContents ul li { - color: black; - display: block; - margin-bottom: .375em; - line-height: 1.375; -} - -#TableOfContents ul li a{ - width: 100%; - padding: .25em .375em; - margin-left: -.375em; - -} -#TableOfContents ul li a:hover { - background-color: #999; - color: white; - -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css deleted file mode 100644 index 7991450fe..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_no-js.css +++ /dev/null @@ -1,7 +0,0 @@ -.no-js .needs-js { - opacity: 0 -} -.js .needs-js { - opacity: 1; - transition: opacity .15s ease-in; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css deleted file mode 100644 index c0be3af61..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_print.css +++ /dev/null @@ -1,7 +0,0 @@ -@media print { - #page-footer, - body > footer, - body > nav { - display: none; - } -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css deleted file mode 100644 index 757457b2d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_right-sidebar.css +++ /dev/null @@ -1,11 +0,0 @@ -#right-sidebar { - scrollbar-width: none; /* hide scrollbar: Firefox */ - -ms-overflow-style: none; /* hide scrollbar: Internet Explorer 10+ */ - height: calc(100vh - 9rem); - overflow-y: auto; -} - -#right-sidebar::-webkit-scrollbar { /* hide scrollbar: WebKit */ - width: 0; - height: 0; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css deleted file mode 100644 index 634adbf06..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_shame.css +++ /dev/null @@ -1,20 +0,0 @@ -/* -Make h6 elements behave like dt elements. Initially implemented to support -linkable glossary entries. - -Yes, it's a hack. That's why it's in the shame file. -*/ - -h6 { - margin-top: 0; - margin-bottom: 0; - font-size: 1.125rem; -} - -h6:first-of-type { - margin-top: 3em; -} - -h6 ~ p { - margin: 0.5em 0 2em 0; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css deleted file mode 100644 index 6cfa7b1b4..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_social-icons.css +++ /dev/null @@ -1,52 +0,0 @@ -.facebook, -.twitter, -.instagram, -.youtube { - fill: #bababa; -} -.facebook:hover { - fill: #3b5998; -} - -.twitter { - fill: #55acee; -} - -.twitter:hover { - fill: #bababa; -} - -.instagram:hover { - fill: #e95950; -} - -.youtube:hover { - fill: #bb0000; -} - -.mstdn { - display: inline-block; - background-color: #282c37; - color: #d9e1e8; - text-decoration: none; - padding: 4px 10px 4px 30px; - border-radius: 4px; - font-size: 16px; - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"); - background-size: 16px; - background-repeat: no-repeat; - background-position: top 50% left 8px; - transition: all 0.5s; -} -.mstdn:hover { - background-color: #484c56; -} - -.mstdn > span { - color: #9baec8; - font-size: 12px; - padding-left: 3px; -} -.mstdn > span:before { - content: "@"; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css deleted file mode 100644 index 7759bed96..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_stickyheader.css +++ /dev/null @@ -1,15 +0,0 @@ - -@media (min-width: 75em) { - - [data-scrolldir="down"] .sticky { - position: fixed; - top:100px; - right:0; - } - - [data-scrolldir="up"] .sticky { - position: fixed; - top:100px; - right:0; - } -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css deleted file mode 100644 index 299a4a963..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_svg.css +++ /dev/null @@ -1 +0,0 @@ -.fill-current { fill: currentColor; } diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css deleted file mode 100644 index 6e0022cc9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tabs.css +++ /dev/null @@ -1,34 +0,0 @@ -.tab-button{ - margin-bottom:1px; - position: relative; - z-index: 1; - color:#333; - border-color:#ccc; - outline: none; - background-color:white; -} -.tab-pane code{ - background:#f1f2f2; - border-radius:0; -} -.tab-pane .chroma{ - background:none; - padding:0; -} -.tab-button.active{ - border-bottom-color:#f1f2f2; - background-color: #f1f2f2; -} -.tab-content .tab-pane{ - display: none; -} -.tab-content .tab-pane.active{ - display: block; -} -/* Treatment of copy buttons inside a tab module */ -.tab-content .copy, .tab-content .copied{ - display: none; -} -.tab-content .tab-pane.active + .copy, .tab-content .tab-pane.active + .copied{ - display: block; -} \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css deleted file mode 100644 index d697c4d85..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_tachyons.css +++ /dev/null @@ -1,94 +0,0 @@ -/*! TACHYONS v4.7.0 | http://tachyons.io */ - -/* - * NOTE: The Tachyons folder is for backup/reference only. This file references the module - * ________ ______ - * ___ __/_____ _________ /______ ______________________ - * __ / _ __ `/ ___/_ __ \_ / / / __ \_ __ \_ ___/ - * _ / / /_/ // /__ _ / / / /_/ // /_/ / / / /(__ ) - * /_/ \__,_/ \___/ /_/ /_/_\__, / \____//_/ /_//____/ - * /____/ - * - * TABLE OF CONTENTS - * - * 1. External Library Includes - * - Normalize.css | http://normalize.css.github.io - * 2. Tachyons Modules - * 3. Variables - * - Media Queries - * - Colors - * 4. Debugging - * - Debug all - * - Debug children - * - */ - - -/* External Library Includes */ -@import 'tachyons/src/_normalize'; - - -/* Modules */ -@import 'tachyons/src/_box-sizing'; -/*@import 'tachyons/src/_aspect-ratios';*/ -@import 'tachyons/src/_images'; -@import 'tachyons/src/_background-size'; -@import 'tachyons/src/_background-position'; -/*@import 'tachyons/src/_outlines';*/ -@import 'tachyons/src/_borders'; -@import 'tachyons/src/_border-colors'; -@import 'tachyons/src/_border-radius'; -@import 'tachyons/src/_border-style'; -@import 'tachyons/src/_border-widths'; -@import 'tachyons/src/_box-shadow'; -/*@import 'tachyons/src/_code';*/ -@import 'tachyons/src/_coordinates'; -@import 'tachyons/src/_clears'; -@import 'tachyons/src/_display'; -@import 'tachyons/src/_flexbox'; -@import 'tachyons/src/_floats'; -/*@import 'tachyons/src/_font-family';*/ -@import 'tachyons/src/_font-style'; -@import 'tachyons/src/_font-weight'; -@import 'tachyons/src/_forms'; -@import 'tachyons/src/_heights'; -@import 'tachyons/src/_letter-spacing'; -@import 'tachyons/src/_line-height'; -@import 'tachyons/src/_links'; -@import 'tachyons/src/_lists'; -@import 'tachyons/src/_max-widths'; -@import 'tachyons/src/_widths'; -@import 'tachyons/src/_overflow'; -@import 'tachyons/src/_position'; -@import 'tachyons/src/_opacity'; -/*@import 'tachyons/src/_rotations';*/ -@import 'tachyons/src/_skins'; -@import 'tachyons/src/_skins-pseudo'; -@import 'tachyons/src/_spacing'; -@import 'tachyons/src/_negative-margins'; -@import 'tachyons/src/_tables'; -@import 'tachyons/src/_text-decoration'; -@import 'tachyons/src/_text-align'; -@import 'tachyons/src/_text-transform'; -@import 'tachyons/src/_type-scale'; -@import 'tachyons/src/_typography'; -@import 'tachyons/src/_utilities'; -@import 'tachyons/src/_visibility'; -@import 'tachyons/src/_white-space'; -@import 'tachyons/src/_vertical-align'; -@import 'tachyons/src/_hovers'; -@import 'tachyons/src/_z-index'; -@import 'tachyons/src/_nested'; -/*@import 'tachyons/src/_styles';*/ - -/* Variables */ -/* Importing here will allow you to override any variables in the modules */ -@import 'tachyons/src/_colors'; -@import 'tachyons/src/_media-queries'; - -/* Debugging */ -/*@import 'tachyons/src/_debug-children'; -@import 'tachyons/src/_debug-grid';*/ - -/* Uncomment out the line below to help debug layout issues */ -/* @import 'tachyons/src/_debug'; */ diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css deleted file mode 100644 index 8701b1530..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/_variables.css +++ /dev/null @@ -1,16 +0,0 @@ -:root { - --primary-color: #0594CB; - --primary-color-dark: #0A1922; - --primary-color-light: #f9f9f9; - --accent-color: #EBB951; - --accent-color-light: #FF4088; - --accent-color-dark: #33ba91; - --text-color-primary: #373737; - --text-on-primary-color: #fff; - --text-color-secondary: #ccc; - --text-color-disabled: #F7f7f7; - --divider-color: #f6f6f6; - --warn-color: red; - - --blue: var(--primary-color); -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css deleted file mode 100644 index fd0f2a503..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/css/main.css +++ /dev/null @@ -1,39 +0,0 @@ -/*Base Styles*/ -@import '_tachyons'; - -/* purgecss start ignore */ -@import '_header-link'; -@import '_animation'; -@import '_documentation-styles'; - -@import 'docsearch.js/dist/cdn/docsearch.min'; -@import '_carousel'; -@import '_code'; -@import '_tabs'; -@import '_color-scheme'; -@import '_columns'; -@import '_content'; -@import '_content-tables'; -@import '_definition-lists'; -@import '_fluid-type'; -@import '_font-family'; -@import '_hugo-internal-template-styling'; -@import '_no-js'; -@import '_social-icons'; -@import '_stickyheader'; -@import '_right-sidebar'; -@import '_svg'; -@import '_chroma'; -@import '_variables'; -@import '_print'; -@import '_shame'; - -.nested-blockquote blockquote { - border-left: 4px solid var(--primary-color); - padding-left: 1em; -} - -.mw-90 { - max-width:90%; -} -/* purgecss end ignore */ diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg deleted file mode 100644 index 7060e856f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/images/sponsors/linode-logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js deleted file mode 100644 index c89af041b..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/index.js +++ /dev/null @@ -1,10 +0,0 @@ -require('typeface-muli'); -import styles from './css/main.css'; -import './js/clipboardjs.js'; -import './js/docsearch.js'; -import './js/lazysizes.js'; -import './js/menutoggle.js'; -import './js/scrolldir.js'; -import './js/smoothscroll.js'; -import './js/tabs.js'; -import './js/nojs.js'; diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js deleted file mode 100644 index ffae31c7f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/clipboardjs.js +++ /dev/null @@ -1,30 +0,0 @@ -var Clipboard = require('clipboard/dist/clipboard.js'); -new Clipboard('.copy', { - target: function(trigger) { - if(trigger.classList.contains('copy-toggle')){ - return trigger.previousElementSibling; - } - return trigger.nextElementSibling; - } - }).on('success', function(e) { - successMessage(e.trigger, 'Copied!'); - e.clearSelection(); - }).on('error', function(e) { - successMessage(e.trigger, fallbackMessage(e.action)); -}); - -function successMessage(elem, msg) { - elem.setAttribute('class', 'copied bg-primary-color-dark f6 absolute top-0 right-0 lh-solid hover-bg-primary-color-dark bn white ph3 pv2'); - elem.setAttribute('aria-label', msg); -} - -function fallbackMessage(elem, action) { - var actionMsg = ''; - var actionKey = (action === 'cut' ? 'X' : 'C'); - if (isMac) { - actionMsg = 'Press ⌘-' + actionKey; - } else { - actionMsg = 'Press Ctrl-' + actionKey; - } - return actionMsg; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js deleted file mode 100644 index e14fb2994..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/docsearch.js +++ /dev/null @@ -1,8 +0,0 @@ -var docsearch = require('docsearch.js/dist/cdn/docsearch.js'); -docsearch({ - appId: 'D1BPLZHGYQ', - apiKey: '6df94e1e5d55d258c56f60d974d10314', - indexName: 'hugodocs', - inputSelector: '#search-input', - debug: true, // Set debug to true if you want to inspect the dropdown -}); diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/filesaver.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/filesaver.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js deleted file mode 100644 index 4eb3950af..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/lazysizes.js +++ /dev/null @@ -1,3 +0,0 @@ -var lazysizes = require('lazysizes'); -// var lsnoscript = require('lazysizes/plugins/noscript/ls.noscript.js'); -var unveilhooks = require('lazysizes/plugins/unveilhooks/ls.unveilhooks.js'); diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js deleted file mode 100644 index f6d3eac9f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/main.js +++ /dev/null @@ -1,22 +0,0 @@ -import styles from './../css/main.css'; -import './clipboardjs.js' -import './codeblocks.js' -import './docsearch.js' -import './lazysizes.js' -import './menutoggle.js' -import './scrolldir.js' -import './smoothscroll.js' -import './tabs.js' -import './nojs.js' - -// TO use jQuery, just call the modules you want -// var $ = require('jquery/src/core'); -// require('jquery/src/core/init'); -// require('jquery/src/manipulation'); - -// OR, use all of them -// var $ = require('jquery/src/jquery'); - -// And write your code -// $('body').append('

Jquery is working

'); -// diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js deleted file mode 100644 index 40bda0ce9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/menutoggle.js +++ /dev/null @@ -1,31 +0,0 @@ -// Grab any element that has the 'js-toggle' class and add an event listener for the toggleClass function -var toggleBtns = document.getElementsByClassName('js-toggle') - for (var i = 0; i < toggleBtns.length; i++) { - toggleBtns[i].addEventListener('click', toggleClass, false) - } - -function toggleClass() { - // Define the data target via the dataset "target" (e.g. data-target=".docsmenu") - var content = this.dataset.target.split(' ') - // Find any menu items that are open - var mobileCurrentlyOpen = document.querySelector('.mobilemenu:not(.dn)') - var desktopCurrentlyOpen = document.querySelector('.desktopmenu:not(.dn)') - var desktopActive = document.querySelector('.desktopmenu:not(.dn)') - - // Loop through the targets' divs - for (var i = 0; i < content.length; i++) { - var matches = document.querySelectorAll(content[i]); - //for each, if the div has the 'dn' class (which is "display:none;"), remove it, otherwise, add that class - [].forEach.call(matches, function(dom) { - dom.classList.contains('dn') ? - dom.classList.remove('dn') : - dom.classList.add('dn'); - return false; - }); - // close the currently open menu items - if (mobileCurrentlyOpen) mobileCurrentlyOpen.classList.add('dn') - if (desktopCurrentlyOpen) desktopCurrentlyOpen.classList.add('dn') - if (desktopActive) desktopActive.classList.remove('db') - - } - } diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js deleted file mode 100644 index 50b5126a9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/nojs.js +++ /dev/null @@ -1 +0,0 @@ -document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/, 'js'); diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js deleted file mode 100644 index 0b69978cd..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/scrolldir.js +++ /dev/null @@ -1 +0,0 @@ -var scrollDir = require('scrolldir/dist/scrolldir.auto.min.js'); diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js deleted file mode 100644 index 4bb2d99b8..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/smoothscroll.js +++ /dev/null @@ -1,80 +0,0 @@ -// query selector targets Hugo TOC -(function() { - - 'use strict'; - - // Feature Test - if ('querySelector' in document && 'addEventListener' in window && Array.prototype.forEach) { - - // Function to animate the scroll - var smoothScroll = function(anchor, duration) { - - // Calculate how far and how fast to scroll - var startLocation = window.pageYOffset; - var endLocation = anchor.offsetTop; - var distance = endLocation - startLocation; - var increments = distance / (duration / 16); - var stopAnimation; - - // Scroll the page by an increment, and check if it's time to stop - var animateScroll = function() { - window.scrollBy(0, increments); - stopAnimation(); - }; - - // If scrolling down - if (increments >= 0) { - // Stop animation when you reach the anchor OR the bottom of the page - stopAnimation = function() { - var travelled = window.pageYOffset; - if ((travelled >= (endLocation - increments)) || ((window.innerHeight + travelled) >= document.body.offsetHeight)) { - clearInterval(runAnimation); - } - }; - } - // If scrolling up - else { - // Stop animation when you reach the anchor OR the top of the page - stopAnimation = function() { - var travelled = window.pageYOffset; - if (travelled <= (endLocation || 0)) { - clearInterval(runAnimation); - } - }; - } - - // Loop the animation function - var runAnimation = setInterval(animateScroll, 16); - - }; - - // Define smooth scroll links - var scrollToggle = document.querySelectorAll('#TableOfContents ul li a'); - - // For each smooth scroll link - [].forEach.call(scrollToggle, function(toggle) { - - // When the smooth scroll link is clicked - toggle.addEventListener('click', function(e) { - - // Prevent the default link behavior - e.preventDefault(); - - // Get anchor link and calculate distance from the top - var dataID = toggle.getAttribute('href'); - var dataTarget = document.querySelector(dataID); - var dataSpeed = toggle.getAttribute('data-speed'); - - // If the anchor exists - if (dataTarget) { - // Scroll to the anchor - smoothScroll(dataTarget, dataSpeed || 500); - } - - }, false); - - }); - - } - -})(); diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js deleted file mode 100644 index a689d474e..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/js/tabs.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Scripts which manages Code Toggle tabs. - */ -var i; -// store tabs variable -var allTabs = document.querySelectorAll("[data-toggle-tab]"); -var allPanes = document.querySelectorAll("[data-pane]"); - -function toggleTabs(event) { - - if(event.target){ - event.preventDefault(); - var clickedTab = event.currentTarget; - var targetKey = clickedTab.getAttribute("data-toggle-tab") - }else { - var targetKey = event - } - // We store the config language selected in users' localStorage - if(window.localStorage){ - window.localStorage.setItem("configLangPref", targetKey) - } - var selectedTabs = document.querySelectorAll("[data-toggle-tab='" + targetKey + "']"); - var selectedPanes = document.querySelectorAll("[data-pane='" + targetKey + "']"); - - for (var i = 0; i < allTabs.length; i++) { - allTabs[i].classList.remove("active"); - allPanes[i].classList.remove("active"); - } - - for (var i = 0; i < selectedTabs.length; i++) { - selectedTabs[i].classList.add("active"); - selectedPanes[i].classList.add("active"); - } - -} - -for (i = 0; i < allTabs.length; i++) { - allTabs[i].addEventListener("click", toggleTabs) -} -// Upon page load, if user has a preferred language in its localStorage, tabs are set to it. -if(window.localStorage.getItem('configLangPref')) { - toggleTabs(window.localStorage.getItem('configLangPref')) -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css deleted file mode 100644 index 5e0b0c708..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/css/app.css +++ /dev/null @@ -1,5350 +0,0 @@ -/* muli-200normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 200; - src: - local('Muli Extra Light '), - local('Muli-Extra Light'), - url(/fonts/muli-latin-200.woff2) format('woff2'), - url(/fonts/muli-latin-200.woff) format('woff'); /* Modern Browsers */ -} -/* muli-200italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 200; - src: - local('Muli Extra Light italic'), - local('Muli-Extra Lightitalic'), - url(/fonts/muli-latin-200italic.woff2) format('woff2'), - url(/fonts/muli-latin-200italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-300normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 300; - src: - local('Muli Light '), - local('Muli-Light'), - url(/fonts/muli-latin-300.woff2) format('woff2'), - url(/fonts/muli-latin-300.woff) format('woff'); /* Modern Browsers */ -} -/* muli-300italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 300; - src: - local('Muli Light italic'), - local('Muli-Lightitalic'), - url(/fonts/muli-latin-300italic.woff2) format('woff2'), - url(/fonts/muli-latin-300italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-400normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 400; - src: - local('Muli Regular '), - local('Muli-Regular'), - url(/fonts/muli-latin-400.woff2) format('woff2'), - url(/fonts/muli-latin-400.woff) format('woff'); /* Modern Browsers */ -} -/* muli-400italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 400; - src: - local('Muli Regular italic'), - local('Muli-Regularitalic'), - url(/fonts/muli-latin-400italic.woff2) format('woff2'), - url(/fonts/muli-latin-400italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-600normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 600; - src: - local('Muli SemiBold '), - local('Muli-SemiBold'), - url(/fonts/muli-latin-600.woff2) format('woff2'), - url(/fonts/muli-latin-600.woff) format('woff'); /* Modern Browsers */ -} -/* muli-600italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 600; - src: - local('Muli SemiBold italic'), - local('Muli-SemiBolditalic'), - url(/fonts/muli-latin-600italic.woff2) format('woff2'), - url(/fonts/muli-latin-600italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-700normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 700; - src: - local('Muli Bold '), - local('Muli-Bold'), - url(/fonts/muli-latin-700.woff2) format('woff2'), - url(/fonts/muli-latin-700.woff) format('woff'); /* Modern Browsers */ -} -/* muli-700italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 700; - src: - local('Muli Bold italic'), - local('Muli-Bolditalic'), - url(/fonts/muli-latin-700italic.woff2) format('woff2'), - url(/fonts/muli-latin-700italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-800normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 800; - src: - local('Muli ExtraBold '), - local('Muli-ExtraBold'), - url(/fonts/muli-latin-800.woff2) format('woff2'), - url(/fonts/muli-latin-800.woff) format('woff'); /* Modern Browsers */ -} -/* muli-800italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 800; - src: - local('Muli ExtraBold italic'), - local('Muli-ExtraBolditalic'), - url(/fonts/muli-latin-800italic.woff2) format('woff2'), - url(/fonts/muli-latin-800italic.woff) format('woff'); /* Modern Browsers */ -} -/* muli-900normal - latin */ -@font-face { - font-family: 'Muli'; - font-style: normal; - font-display: swap; - font-weight: 900; - src: - local('Muli Black '), - local('Muli-Black'), - url(/fonts/muli-latin-900.woff2) format('woff2'), - url(/fonts/muli-latin-900.woff) format('woff'); /* Modern Browsers */ -} -/* muli-900italic - latin */ -@font-face { - font-family: 'Muli'; - font-style: italic; - font-display: swap; - font-weight: 900; - src: - local('Muli Black italic'), - local('Muli-Blackitalic'), - url(/fonts/muli-latin-900italic.woff2) format('woff2'), - url(/fonts/muli-latin-900italic.woff) format('woff'); /* Modern Browsers */ -} - - -/*Base Styles*/ -/*! TACHYONS v4.7.0 | http://tachyons.io */ -/* - * NOTE: The Tachyons folder is for backup/reference only. This file references the module - * ________ ______ - * ___ __/_____ _________ /______ ______________________ - * __ / _ __ `/ ___/_ __ \_ / / / __ \_ __ \_ ___/ - * _ / / /_/ // /__ _ / / / /_/ // /_/ / / / /(__ ) - * /_/ \__,_/ \___/ /_/ /_/_\__, / \____//_/ /_//____/ - * /____/ - * - * TABLE OF CONTENTS - * - * 1. External Library Includes - * - Normalize.css | http://normalize.css.github.io - * 2. Tachyons Modules - * 3. Variables - * - Media Queries - * - Colors - * 4. Debugging - * - Debug all - * - Debug children - * - */ -/* External Library Includes */ -/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ -/* Document - ========================================================================== */ -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} -/* Sections - ========================================================================== */ -/** - * Remove the margin in all browsers. - */ -body { - margin: 0; -} -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ -h1 { - font-size: 2em; - margin: 0.67em 0; -} -/* Grouping content - ========================================================================== */ -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -/* Text-level semantics - ========================================================================== */ -/** - * Remove the gray background on active links in IE 10. - */ -a { - background-color: transparent; -} -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; /* 2 */ -} -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ -b, -strong { - font-weight: bolder; -} -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} -/** - * Add the correct font size in all browsers. - */ -small { - font-size: 80%; -} -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -/* Embedded content - ========================================================================== */ -/** - * Remove the border on images inside links in IE 10. - */ -img { - border-style: none; -} -/* Forms - ========================================================================== */ -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ -button, -input { /* 1 */ - overflow: visible; -} -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ -button, -select { /* 1 */ - text-transform: none; -} -/** - * Correct the inability to style clickable types in iOS and Safari. - */ -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} -/** - * Remove the inner border and padding in Firefox. - */ -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} -/** - * Restore the focus styles unset by the previous rule. - */ -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} -/** - * Correct the padding in Firefox. - */ -fieldset { - padding: 0.35em 0.75em 0.625em; -} -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ -legend { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ -progress { - vertical-align: baseline; -} -/** - * Remove the default vertical scrollbar in IE 10+. - */ -textarea { - overflow: auto; -} -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ -[type="checkbox"], -[type="radio"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} -/* Interactive - ========================================================================== */ -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ -details { - display: block; -} -/* - * Add the correct display in all browsers. - */ -summary { - display: list-item; -} -/* Misc - ========================================================================== */ -/** - * Add the correct display in IE 10+. - */ -template { - display: none; -} -/** - * Add the correct display in IE 10. - */ -[hidden] { - display: none; -} -/* Modules */ -/* - - BOX SIZING - -*/ -html, -body, -div, -article, -aside, -section, -main, -nav, -footer, -header, -form, -fieldset, -legend, -pre, -code, -a, -h1,h2,h3,h4,h5,h6, -p, -ul, -ol, -li, -dl, -dt, -dd, -blockquote, -figcaption, -figure, -textarea, -table, -td, -th, -tr, -input[type="email"], -input[type="number"], -input[type="password"], -input[type="tel"], -input[type="text"], -input[type="url"], -.border-box { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -/*@import 'tachyons/src/_aspect-ratios';*/ -/* - - IMAGES - Docs: http://tachyons.io/docs/elements/images/ - -*/ -/* Responsive images! */ -img { max-width: 100%; } -/* - - BACKGROUND SIZE - Docs: http://tachyons.io/docs/themes/background-size/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* - Often used in combination with background image set as an inline style - on an html element. -*/ -.cover { background-size: cover!important; } -.contain { background-size: contain!important; } -@media screen and (min-width: 30em) { - .cover-ns { background-size: cover!important; } - .contain-ns { background-size: contain!important; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .cover-m { background-size: cover!important; } - .contain-m { background-size: contain!important; } -} -@media screen and (min-width: 60em) { - .cover-l { background-size: cover!important; } - .contain-l { background-size: contain!important; } -} -/* - - BACKGROUND POSITION - - Base: - bg = background - - Modifiers: - -center = center center - -top = top center - -right = center right - -bottom = bottom center - -left = center left - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.bg-center { - background-repeat: no-repeat; - background-position: center center; -} -.bg-top { - background-repeat: no-repeat; - background-position: top center; -} -.bg-right { - background-repeat: no-repeat; - background-position: center right; -} -.bg-bottom { - background-repeat: no-repeat; - background-position: bottom center; -} -.bg-left { - background-repeat: no-repeat; - background-position: center left; -} -@media screen and (min-width: 30em) { - .bg-center-ns { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-ns { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-ns { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-ns { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-ns { - background-repeat: no-repeat; - background-position: center left; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .bg-center-m { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-m { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-m { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-m { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-m { - background-repeat: no-repeat; - background-position: center left; - } -} -@media screen and (min-width: 60em) { - .bg-center-l { - background-repeat: no-repeat; - background-position: center center; - } - - .bg-top-l { - background-repeat: no-repeat; - background-position: top center; - } - - .bg-right-l { - background-repeat: no-repeat; - background-position: center right; - } - - .bg-bottom-l { - background-repeat: no-repeat; - background-position: bottom center; - } - - .bg-left-l { - background-repeat: no-repeat; - background-position: center left; - } -} -/*@import 'tachyons/src/_outlines';*/ -/* - - BORDERS - Docs: http://tachyons.io/docs/themes/borders/ - - Base: - b = border - - Modifiers: - a = all - t = top - r = right - b = bottom - l = left - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ba { border-style: solid; border-width: 1px; } -.bt { border-top-style: solid; border-top-width: 1px; } -.br { border-right-style: solid; border-right-width: 1px; } -.bb { border-bottom-style: solid; border-bottom-width: 1px; } -.bl { border-left-style: solid; border-left-width: 1px; } -.bn { border-style: none; border-width: 0; } -@media screen and (min-width: 30em) { - .ba-ns { border-style: solid; border-width: 1px; } - .bt-ns { border-top-style: solid; border-top-width: 1px; } - .br-ns { border-right-style: solid; border-right-width: 1px; } - .bb-ns { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-ns { border-left-style: solid; border-left-width: 1px; } - .bn-ns { border-style: none; border-width: 0; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ba-m { border-style: solid; border-width: 1px; } - .bt-m { border-top-style: solid; border-top-width: 1px; } - .br-m { border-right-style: solid; border-right-width: 1px; } - .bb-m { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-m { border-left-style: solid; border-left-width: 1px; } - .bn-m { border-style: none; border-width: 0; } -} -@media screen and (min-width: 60em) { - .ba-l { border-style: solid; border-width: 1px; } - .bt-l { border-top-style: solid; border-top-width: 1px; } - .br-l { border-right-style: solid; border-right-width: 1px; } - .bb-l { border-bottom-style: solid; border-bottom-width: 1px; } - .bl-l { border-left-style: solid; border-left-width: 1px; } - .bn-l { border-style: none; border-width: 0; } -} -/* - - BORDER COLORS - Docs: http://tachyons.io/docs/themes/borders/ - - Border colors can be used to extend the base - border classes ba,bt,bb,br,bl found in the _borders.css file. - - The base border class by default will set the color of the border - to that of the current text color. These classes are for the cases - where you desire for the text and border colors to be different. - - Base: - b = border - - Modifiers: - --color-name = each color variable name is also a border color name - -*/ -.b--black { border-color: #000; } -.b--near-black { border-color: #111; } -.b--dark-gray { border-color: #333; } -.b--mid-gray { border-color: #555; } -.b--gray { border-color: #777; } -.b--silver { border-color: #999; } -.b--light-silver { border-color: #aaa; } -.b--moon-gray { border-color: #ccc; } -.b--light-gray { border-color: #eee; } -.b--near-white { border-color: #f4f4f4; } -.b--white { border-color: #fff; } -.b--white-90 { border-color: rgba(255, 255, 255, .9); } -.b--white-80 { border-color: rgba(255, 255, 255, .8); } -.b--white-70 { border-color: rgba(255, 255, 255, .7); } -.b--white-60 { border-color: rgba(255, 255, 255, .6); } -.b--white-50 { border-color: rgba(255, 255, 255, .5); } -.b--white-40 { border-color: rgba(255, 255, 255, .4); } -.b--white-30 { border-color: rgba(255, 255, 255, .3); } -.b--white-20 { border-color: rgba(255, 255, 255, .2); } -.b--white-10 { border-color: rgba(255, 255, 255, .1); } -.b--white-05 { border-color: rgba(255, 255, 255, .05); } -.b--white-025 { border-color: rgba(255, 255, 255, .025); } -.b--white-0125 { border-color: rgba(255, 255, 255, .0125); } -.b--black-90 { border-color: rgba(0, 0, 0, .9); } -.b--black-80 { border-color: rgba(0, 0, 0, .8); } -.b--black-70 { border-color: rgba(0, 0, 0, .7); } -.b--black-60 { border-color: rgba(0, 0, 0, .6); } -.b--black-50 { border-color: rgba(0, 0, 0, .5); } -.b--black-40 { border-color: rgba(0, 0, 0, .4); } -.b--black-30 { border-color: rgba(0, 0, 0, .3); } -.b--black-20 { border-color: rgba(0, 0, 0, .2); } -.b--black-10 { border-color: rgba(0, 0, 0, .1); } -.b--black-05 { border-color: rgba(0, 0, 0, .05); } -.b--black-025 { border-color: rgba(0, 0, 0, .025); } -.b--black-0125 { border-color: rgba(0, 0, 0, .0125); } -.b--dark-red { border-color: #e7040f; } -.b--red { border-color: #ff4136; } -.b--light-red { border-color: #ff725c; } -.b--orange { border-color: #ff6300; } -.b--gold { border-color: #ffb700; } -.b--yellow { border-color: #ffd700; } -.b--light-yellow { border-color: #fbf1a9; } -.b--purple { border-color: #5e2ca5; } -.b--light-purple { border-color: #a463f2; } -.b--dark-pink { border-color: #d5008f; } -.b--hot-pink { border-color: #ff41b4; } -.b--pink { border-color: #ff80cc; } -.b--light-pink { border-color: #ffa3d7; } -.b--dark-green { border-color: #137752; } -.b--green { border-color: #19a974; } -.b--light-green { border-color: #9eebcf; } -.b--navy { border-color: #001b44; } -.b--dark-blue { border-color: #00449e; } -.b--blue { border-color: #0594CB; } -.b--light-blue { border-color: #96ccff; } -.b--lightest-blue { border-color: #cdecff; } -.b--washed-blue { border-color: #f6fffe; } -.b--washed-green { border-color: #e8fdf5; } -.b--washed-yellow { border-color: #fffceb; } -.b--washed-red { border-color: #ffdfdf; } -.b--transparent { border-color: transparent; } -.b--inherit { border-color: inherit; } -/* - - BORDER RADIUS - Docs: http://tachyons.io/docs/themes/border-radius/ - - Base: - br = border-radius - - Modifiers: - 0 = 0/none - 1 = 1st step in scale - 2 = 2nd step in scale - 3 = 3rd step in scale - 4 = 4th step in scale - - Literal values: - -100 = 100% - -pill = 9999px - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.br0 { border-radius: 0; } -.br1 { border-radius: .125rem; } -.br2 { border-radius: .25rem; } -.br3 { border-radius: .5rem; } -.br4 { border-radius: 1rem; } -.br-100 { border-radius: 100%; } -.br-pill { border-radius: 9999px; } -.br--bottom { - border-top-left-radius: 0; - border-top-right-radius: 0; - } -.br--top { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } -.br--right { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -.br--left { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -@media screen and (min-width: 30em) { - .br0-ns { border-radius: 0; } - .br1-ns { border-radius: .125rem; } - .br2-ns { border-radius: .25rem; } - .br3-ns { border-radius: .5rem; } - .br4-ns { border-radius: 1rem; } - .br-100-ns { border-radius: 100%; } - .br-pill-ns { border-radius: 9999px; } - .br--bottom-ns { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-ns { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-ns { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-ns { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .br0-m { border-radius: 0; } - .br1-m { border-radius: .125rem; } - .br2-m { border-radius: .25rem; } - .br3-m { border-radius: .5rem; } - .br4-m { border-radius: 1rem; } - .br-100-m { border-radius: 100%; } - .br-pill-m { border-radius: 9999px; } - .br--bottom-m { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-m { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-m { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-m { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -@media screen and (min-width: 60em) { - .br0-l { border-radius: 0; } - .br1-l { border-radius: .125rem; } - .br2-l { border-radius: .25rem; } - .br3-l { border-radius: .5rem; } - .br4-l { border-radius: 1rem; } - .br-100-l { border-radius: 100%; } - .br-pill-l { border-radius: 9999px; } - .br--bottom-l { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - .br--top-l { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - .br--right-l { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .br--left-l { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} -/* - - BORDER STYLES - Docs: http://tachyons.io/docs/themes/borders/ - - Depends on base border module in _borders.css - - Base: - b = border-style - - Modifiers: - --none = none - --dotted = dotted - --dashed = dashed - --solid = solid - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.b--dotted { border-style: dotted; } -.b--dashed { border-style: dashed; } -.b--solid { border-style: solid; } -.b--none { border-style: none; } -@media screen and (min-width: 30em) { - .b--dotted-ns { border-style: dotted; } - .b--dashed-ns { border-style: dashed; } - .b--solid-ns { border-style: solid; } - .b--none-ns { border-style: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .b--dotted-m { border-style: dotted; } - .b--dashed-m { border-style: dashed; } - .b--solid-m { border-style: solid; } - .b--none-m { border-style: none; } -} -@media screen and (min-width: 60em) { - .b--dotted-l { border-style: dotted; } - .b--dashed-l { border-style: dashed; } - .b--solid-l { border-style: solid; } - .b--none-l { border-style: none; } -} -/* - - BORDER WIDTHS - Docs: http://tachyons.io/docs/themes/borders/ - - Base: - bw = border-width - - Modifiers: - 0 = 0 width border - 1 = 1st step in border-width scale - 2 = 2nd step in border-width scale - 3 = 3rd step in border-width scale - 4 = 4th step in border-width scale - 5 = 5th step in border-width scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.bw0 { border-width: 0; } -.bw1 { border-width: .125rem; } -.bw2 { border-width: .25rem; } -.bw3 { border-width: .5rem; } -.bw4 { border-width: 1rem; } -.bw5 { border-width: 2rem; } -/* Resets */ -.bt-0 { border-top-width: 0; } -.br-0 { border-right-width: 0; } -.bb-0 { border-bottom-width: 0; } -.bl-0 { border-left-width: 0; } -@media screen and (min-width: 30em) { - .bw0-ns { border-width: 0; } - .bw1-ns { border-width: .125rem; } - .bw2-ns { border-width: .25rem; } - .bw3-ns { border-width: .5rem; } - .bw4-ns { border-width: 1rem; } - .bw5-ns { border-width: 2rem; } - .bt-0-ns { border-top-width: 0; } - .br-0-ns { border-right-width: 0; } - .bb-0-ns { border-bottom-width: 0; } - .bl-0-ns { border-left-width: 0; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .bw0-m { border-width: 0; } - .bw1-m { border-width: .125rem; } - .bw2-m { border-width: .25rem; } - .bw3-m { border-width: .5rem; } - .bw4-m { border-width: 1rem; } - .bw5-m { border-width: 2rem; } - .bt-0-m { border-top-width: 0; } - .br-0-m { border-right-width: 0; } - .bb-0-m { border-bottom-width: 0; } - .bl-0-m { border-left-width: 0; } -} -@media screen and (min-width: 60em) { - .bw0-l { border-width: 0; } - .bw1-l { border-width: .125rem; } - .bw2-l { border-width: .25rem; } - .bw3-l { border-width: .5rem; } - .bw4-l { border-width: 1rem; } - .bw5-l { border-width: 2rem; } - .bt-0-l { border-top-width: 0; } - .br-0-l { border-right-width: 0; } - .bb-0-l { border-bottom-width: 0; } - .bl-0-l { border-left-width: 0; } -} -/* - - BOX-SHADOW - Docs: http://tachyons.io/docs/themes/box-shadow/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.shadow-1 { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } -.shadow-2 { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } -.shadow-3 { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } -.shadow-4 { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } -.shadow-5 { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -@media screen and (min-width: 30em) { - .shadow-1-ns { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-ns { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-ns { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-ns { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-ns { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .shadow-1-m { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-m { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-m { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-m { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-m { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -@media screen and (min-width: 60em) { - .shadow-1-l { -webkit-box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, .2); } - .shadow-2-l { -webkit-box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); box-shadow: 0px 0px 8px 2px rgba(0, 0, 0, .2); } - .shadow-3-l { -webkit-box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, .2); } - .shadow-4-l { -webkit-box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, .2); } - .shadow-5-l { -webkit-box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); box-shadow: 4px 4px 8px 0px rgba(0, 0, 0, .2); } -} -/*@import 'tachyons/src/_code';*/ -/* - - COORDINATES - Docs: http://tachyons.io/docs/layout/position/ - - Use in combination with the position module. - - Base: - top - bottom - right - left - - Modifiers: - -0 = literal value 0 - -1 = literal value 1 - -2 = literal value 2 - --1 = literal value -1 - --2 = literal value -2 - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.top-0 { top: 0; } -.right-0 { right: 0; } -.bottom-0 { bottom: 0; } -.left-0 { left: 0; } -.top-1 { top: 1rem; } -.right-1 { right: 1rem; } -.bottom-1 { bottom: 1rem; } -.left-1 { left: 1rem; } -.top-2 { top: 2rem; } -.right-2 { right: 2rem; } -.bottom-2 { bottom: 2rem; } -.left-2 { left: 2rem; } -.top--1 { top: -1rem; } -.right--1 { right: -1rem; } -.bottom--1 { bottom: -1rem; } -.left--1 { left: -1rem; } -.top--2 { top: -2rem; } -.right--2 { right: -2rem; } -.bottom--2 { bottom: -2rem; } -.left--2 { left: -2rem; } -.absolute--fill { - top: 0; - right: 0; - bottom: 0; - left: 0; -} -@media screen and (min-width: 30em) { - .top-0-ns { top: 0; } - .left-0-ns { left: 0; } - .right-0-ns { right: 0; } - .bottom-0-ns { bottom: 0; } - .top-1-ns { top: 1rem; } - .left-1-ns { left: 1rem; } - .right-1-ns { right: 1rem; } - .bottom-1-ns { bottom: 1rem; } - .top-2-ns { top: 2rem; } - .left-2-ns { left: 2rem; } - .right-2-ns { right: 2rem; } - .bottom-2-ns { bottom: 2rem; } - .top--1-ns { top: -1rem; } - .right--1-ns { right: -1rem; } - .bottom--1-ns { bottom: -1rem; } - .left--1-ns { left: -1rem; } - .top--2-ns { top: -2rem; } - .right--2-ns { right: -2rem; } - .bottom--2-ns { bottom: -2rem; } - .left--2-ns { left: -2rem; } - .absolute--fill-ns { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .top-0-m { top: 0; } - .left-0-m { left: 0; } - .right-0-m { right: 0; } - .bottom-0-m { bottom: 0; } - .top-1-m { top: 1rem; } - .left-1-m { left: 1rem; } - .right-1-m { right: 1rem; } - .bottom-1-m { bottom: 1rem; } - .top-2-m { top: 2rem; } - .left-2-m { left: 2rem; } - .right-2-m { right: 2rem; } - .bottom-2-m { bottom: 2rem; } - .top--1-m { top: -1rem; } - .right--1-m { right: -1rem; } - .bottom--1-m { bottom: -1rem; } - .left--1-m { left: -1rem; } - .top--2-m { top: -2rem; } - .right--2-m { right: -2rem; } - .bottom--2-m { bottom: -2rem; } - .left--2-m { left: -2rem; } - .absolute--fill-m { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -@media screen and (min-width: 60em) { - .top-0-l { top: 0; } - .left-0-l { left: 0; } - .right-0-l { right: 0; } - .bottom-0-l { bottom: 0; } - .top-1-l { top: 1rem; } - .left-1-l { left: 1rem; } - .right-1-l { right: 1rem; } - .bottom-1-l { bottom: 1rem; } - .top-2-l { top: 2rem; } - .left-2-l { left: 2rem; } - .right-2-l { right: 2rem; } - .bottom-2-l { bottom: 2rem; } - .top--1-l { top: -1rem; } - .right--1-l { right: -1rem; } - .bottom--1-l { bottom: -1rem; } - .left--1-l { left: -1rem; } - .top--2-l { top: -2rem; } - .right--2-l { right: -2rem; } - .bottom--2-l { bottom: -2rem; } - .left--2-l { left: -2rem; } - .absolute--fill-l { - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} -/* - - CLEARFIX - http://tachyons.io/docs/layout/clearfix/ - -*/ -/* Nicolas Gallaghers Clearfix solution - Ref: http://nicolasgallagher.com/micro-clearfix-hack/ */ -.cf:before, -.cf:after { content: " "; display: table; } -.cf:after { clear: both; } -.cf { *zoom: 1; } -.cl { clear: left; } -.cr { clear: right; } -.cb { clear: both; } -.cn { clear: none; } -@media screen and (min-width: 30em) { - .cl-ns { clear: left; } - .cr-ns { clear: right; } - .cb-ns { clear: both; } - .cn-ns { clear: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .cl-m { clear: left; } - .cr-m { clear: right; } - .cb-m { clear: both; } - .cn-m { clear: none; } -} -@media screen and (min-width: 60em) { - .cl-l { clear: left; } - .cr-l { clear: right; } - .cb-l { clear: both; } - .cn-l { clear: none; } -} -/* - - DISPLAY - Docs: http://tachyons.io/docs/layout/display - - Base: - d = display - - Modifiers: - n = none - b = block - ib = inline-block - it = inline-table - t = table - tc = table-cell - t-row = table-row - t-columm = table-column - t-column-group = table-column-group - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.dn { display: none; } -.di { display: inline; } -.db { display: block; } -.dib { display: inline-block; } -.dit { display: inline-table; } -.dt { display: table; } -.dtc { display: table-cell; } -.dt-row { display: table-row; } -.dt-row-group { display: table-row-group; } -.dt-column { display: table-column; } -.dt-column-group { display: table-column-group; } -/* - This will set table to full width and then - all cells will be equal width -*/ -.dt--fixed { - table-layout: fixed; - width: 100%; -} -@media screen and (min-width: 30em) { - .dn-ns { display: none; } - .di-ns { display: inline; } - .db-ns { display: block; } - .dib-ns { display: inline-block; } - .dit-ns { display: inline-table; } - .dt-ns { display: table; } - .dtc-ns { display: table-cell; } - .dt-row-ns { display: table-row; } - .dt-row-group-ns { display: table-row-group; } - .dt-column-ns { display: table-column; } - .dt-column-group-ns { display: table-column-group; } - - .dt--fixed-ns { - table-layout: fixed; - width: 100%; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .dn-m { display: none; } - .di-m { display: inline; } - .db-m { display: block; } - .dib-m { display: inline-block; } - .dit-m { display: inline-table; } - .dt-m { display: table; } - .dtc-m { display: table-cell; } - .dt-row-m { display: table-row; } - .dt-row-group-m { display: table-row-group; } - .dt-column-m { display: table-column; } - .dt-column-group-m { display: table-column-group; } - - .dt--fixed-m { - table-layout: fixed; - width: 100%; - } -} -@media screen and (min-width: 60em) { - .dn-l { display: none; } - .di-l { display: inline; } - .db-l { display: block; } - .dib-l { display: inline-block; } - .dit-l { display: inline-table; } - .dt-l { display: table; } - .dtc-l { display: table-cell; } - .dt-row-l { display: table-row; } - .dt-row-group-l { display: table-row-group; } - .dt-column-l { display: table-column; } - .dt-column-group-l { display: table-column-group; } - - .dt--fixed-l { - table-layout: fixed; - width: 100%; - } -} -/* - - FLEXBOX - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.flex { display: -webkit-box; display: -ms-flexbox; display: flex; } -.inline-flex { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } -/* 1. Fix for Chrome 44 bug. - * https://code.google.com/p/chromium/issues/detail?id=506893 */ -.flex-auto { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ -} -.flex-none { -webkit-box-flex: 0; -ms-flex: none; flex: none; } -.flex-column { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } -.flex-row { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } -.flex-wrap { -ms-flex-wrap: wrap; flex-wrap: wrap; } -.flex-nowrap { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } -.flex-wrap-reverse { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } -.flex-column-reverse { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } -.flex-row-reverse { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } -.items-start { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } -.items-end { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } -.items-center { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } -.items-baseline { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } -.items-stretch { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } -.self-start { -ms-flex-item-align: start; align-self: flex-start; } -.self-end { -ms-flex-item-align: end; align-self: flex-end; } -.self-center { -ms-flex-item-align: center; align-self: center; } -.self-baseline { -ms-flex-item-align: baseline; align-self: baseline; } -.self-stretch { -ms-flex-item-align: stretch; align-self: stretch; } -.justify-start { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } -.justify-end { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } -.justify-center { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } -.justify-between { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } -.justify-around { -ms-flex-pack: distribute; justify-content: space-around; } -.content-start { -ms-flex-line-pack: start; align-content: flex-start; } -.content-end { -ms-flex-line-pack: end; align-content: flex-end; } -.content-center { -ms-flex-line-pack: center; align-content: center; } -.content-between { -ms-flex-line-pack: justify; align-content: space-between; } -.content-around { -ms-flex-line-pack: distribute; align-content: space-around; } -.content-stretch { -ms-flex-line-pack: stretch; align-content: stretch; } -.order-0 { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } -.order-1 { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } -.order-2 { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } -.order-3 { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } -.order-4 { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } -.order-5 { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } -.order-6 { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } -.order-7 { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } -.order-8 { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } -.order-last { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } -.flex-grow-0 { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } -.flex-grow-1 { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } -.flex-shrink-0 { -ms-flex-negative: 0; flex-shrink: 0; } -.flex-shrink-1 { -ms-flex-negative: 1; flex-shrink: 1; } -@media screen and (min-width: 30em) { - .flex-ns { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-ns { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-ns { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-ns { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-ns { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-ns { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-ns { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-ns { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-ns { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-ns { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-ns { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - .items-start-ns { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-ns { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-ns { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-ns { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-ns { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-ns { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-ns { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-ns { -ms-flex-item-align: center; align-self: center; } - .self-baseline-ns { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-ns { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-ns { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-ns { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-ns { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-ns { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-ns { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-ns { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-ns { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-ns { -ms-flex-line-pack: center; align-content: center; } - .content-between-ns { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-ns { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-ns { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-ns { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-ns { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-ns { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-ns { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-ns { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-ns { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-ns { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-ns { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-ns { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-ns { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-ns { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-ns { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-ns { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-ns { -ms-flex-negative: 1; flex-shrink: 1; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .flex-m { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-m { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-m { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-m { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-m { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-m { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-m { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-m { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-m { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-m { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-m { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - .items-start-m { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-m { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-m { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-m { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-m { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-m { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-m { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-m { -ms-flex-item-align: center; align-self: center; } - .self-baseline-m { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-m { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-m { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-m { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-m { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-m { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-m { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-m { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-m { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-m { -ms-flex-line-pack: center; align-content: center; } - .content-between-m { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-m { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-m { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-m { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-m { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-m { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-m { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-m { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-m { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-m { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-m { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-m { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-m { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-m { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-m { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-m { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-m { -ms-flex-negative: 1; flex-shrink: 1; } -} -@media screen and (min-width: 60em) { - .flex-l { display: -webkit-box; display: -ms-flexbox; display: flex; } - .inline-flex-l { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } - .flex-auto-l { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; /* 1 */ - min-height: 0; /* 1 */ - } - .flex-none-l { -webkit-box-flex: 0; -ms-flex: none; flex: none; } - .flex-column-l { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } - .flex-row-l { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; } - .flex-wrap-l { -ms-flex-wrap: wrap; flex-wrap: wrap; } - .flex-nowrap-l { -ms-flex-wrap: nowrap; flex-wrap: nowrap; } - .flex-wrap-reverse-l { -ms-flex-wrap: wrap-reverse; flex-wrap: wrap-reverse; } - .flex-column-reverse-l { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -ms-flex-direction: column-reverse; flex-direction: column-reverse; } - .flex-row-reverse-l { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -ms-flex-direction: row-reverse; flex-direction: row-reverse; } - - .items-start-l { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; } - .items-end-l { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; } - .items-center-l { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } - .items-baseline-l { -webkit-box-align: baseline; -ms-flex-align: baseline; align-items: baseline; } - .items-stretch-l { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } - - .self-start-l { -ms-flex-item-align: start; align-self: flex-start; } - .self-end-l { -ms-flex-item-align: end; align-self: flex-end; } - .self-center-l { -ms-flex-item-align: center; align-self: center; } - .self-baseline-l { -ms-flex-item-align: baseline; align-self: baseline; } - .self-stretch-l { -ms-flex-item-align: stretch; align-self: stretch; } - - .justify-start-l { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } - .justify-end-l { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } - .justify-center-l { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } - .justify-between-l { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } - .justify-around-l { -ms-flex-pack: distribute; justify-content: space-around; } - - .content-start-l { -ms-flex-line-pack: start; align-content: flex-start; } - .content-end-l { -ms-flex-line-pack: end; align-content: flex-end; } - .content-center-l { -ms-flex-line-pack: center; align-content: center; } - .content-between-l { -ms-flex-line-pack: justify; align-content: space-between; } - .content-around-l { -ms-flex-line-pack: distribute; align-content: space-around; } - .content-stretch-l { -ms-flex-line-pack: stretch; align-content: stretch; } - - .order-0-l { -webkit-box-ordinal-group: 1; -ms-flex-order: 0; order: 0; } - .order-1-l { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } - .order-2-l { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } - .order-3-l { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } - .order-4-l { -webkit-box-ordinal-group: 5; -ms-flex-order: 4; order: 4; } - .order-5-l { -webkit-box-ordinal-group: 6; -ms-flex-order: 5; order: 5; } - .order-6-l { -webkit-box-ordinal-group: 7; -ms-flex-order: 6; order: 6; } - .order-7-l { -webkit-box-ordinal-group: 8; -ms-flex-order: 7; order: 7; } - .order-8-l { -webkit-box-ordinal-group: 9; -ms-flex-order: 8; order: 8; } - .order-last-l { -webkit-box-ordinal-group: 100000; -ms-flex-order: 99999; order: 99999; } - - .flex-grow-0-l { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; } - .flex-grow-1-l { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } - - .flex-shrink-0-l { -ms-flex-negative: 0; flex-shrink: 0; } - .flex-shrink-1-l { -ms-flex-negative: 1; flex-shrink: 1; } -} -/* - - FLOATS - http://tachyons.io/docs/layout/floats/ - - 1. Floated elements are automatically rendered as block level elements. - Setting floats to display inline will fix the double margin bug in - ie6. You know... just in case. - - 2. Don't forget to clearfix your floats with .cf - - Base: - f = float - - Modifiers: - l = left - r = right - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.fl { float: left; _display: inline; } -.fr { float: right; _display: inline; } -.fn { float: none; } -@media screen and (min-width: 30em) { - .fl-ns { float: left; _display: inline; } - .fr-ns { float: right; _display: inline; } - .fn-ns { float: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .fl-m { float: left; _display: inline; } - .fr-m { float: right; _display: inline; } - .fn-m { float: none; } -} -@media screen and (min-width: 60em) { - .fl-l { float: left; _display: inline; } - .fr-l { float: right; _display: inline; } - .fn-l { float: none; } -} -/*@import 'tachyons/src/_font-family';*/ -/* - - FONT STYLE - Docs: http://tachyons.io/docs/typography/font-style/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.i { font-style: italic; } -.fs-normal { font-style: normal; } -@media screen and (min-width: 30em) { - .i-ns { font-style: italic; } - .fs-normal-ns { font-style: normal; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .i-m { font-style: italic; } - .fs-normal-m { font-style: normal; } -} -@media screen and (min-width: 60em) { - .i-l { font-style: italic; } - .fs-normal-l { font-style: normal; } -} -/* - - FONT WEIGHT - Docs: http://tachyons.io/docs/typography/font-weight/ - - Base - fw = font-weight - - Modifiers: - 1 = literal value 100 - 2 = literal value 200 - 3 = literal value 300 - 4 = literal value 400 - 5 = literal value 500 - 6 = literal value 600 - 7 = literal value 700 - 8 = literal value 800 - 9 = literal value 900 - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.normal { font-weight: normal; } -.b { font-weight: bold; } -.fw1 { font-weight: 100; } -.fw2 { font-weight: 200; } -.fw3 { font-weight: 300; } -.fw4 { font-weight: 400; } -.fw5 { font-weight: 500; } -.fw6 { font-weight: 600; } -.fw7 { font-weight: 700; } -.fw8 { font-weight: 800; } -.fw9 { font-weight: 900; } -@media screen and (min-width: 30em) { - .normal-ns { font-weight: normal; } - .b-ns { font-weight: bold; } - .fw1-ns { font-weight: 100; } - .fw2-ns { font-weight: 200; } - .fw3-ns { font-weight: 300; } - .fw4-ns { font-weight: 400; } - .fw5-ns { font-weight: 500; } - .fw6-ns { font-weight: 600; } - .fw7-ns { font-weight: 700; } - .fw8-ns { font-weight: 800; } - .fw9-ns { font-weight: 900; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .normal-m { font-weight: normal; } - .b-m { font-weight: bold; } - .fw1-m { font-weight: 100; } - .fw2-m { font-weight: 200; } - .fw3-m { font-weight: 300; } - .fw4-m { font-weight: 400; } - .fw5-m { font-weight: 500; } - .fw6-m { font-weight: 600; } - .fw7-m { font-weight: 700; } - .fw8-m { font-weight: 800; } - .fw9-m { font-weight: 900; } -} -@media screen and (min-width: 60em) { - .normal-l { font-weight: normal; } - .b-l { font-weight: bold; } - .fw1-l { font-weight: 100; } - .fw2-l { font-weight: 200; } - .fw3-l { font-weight: 300; } - .fw4-l { font-weight: 400; } - .fw5-l { font-weight: 500; } - .fw6-l { font-weight: 600; } - .fw7-l { font-weight: 700; } - .fw8-l { font-weight: 800; } - .fw9-l { font-weight: 900; } -} -/* - - FORMS - -*/ -.input-reset { - -webkit-appearance: none; - -moz-appearance: none; -} -.button-reset::-moz-focus-inner, -.input-reset::-moz-focus-inner { - border: 0; - padding: 0; -} -/* - - HEIGHTS - Docs: http://tachyons.io/docs/layout/heights/ - - Base: - h = height - min-h = min-height - min-vh = min-height vertical screen height - vh = vertical screen height - - Modifiers - 1 = 1st step in height scale - 2 = 2nd step in height scale - 3 = 3rd step in height scale - 4 = 4th step in height scale - 5 = 5th step in height scale - - -25 = literal value 25% - -50 = literal value 50% - -75 = literal value 75% - -100 = literal value 100% - - -auto = string value of auto - -inherit = string value of inherit - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Height Scale */ -.h1 { height: 1rem; } -.h2 { height: 2rem; } -.h3 { height: 4rem; } -.h4 { height: 8rem; } -.h5 { height: 16rem; } -/* Height Percentages - Based off of height of parent */ -.h-25 { height: 25%; } -.h-50 { height: 50%; } -.h-75 { height: 75%; } -.h-100 { height: 100%; } -.min-h-100 { min-height: 100%; } -/* Screen Height Percentage */ -.vh-25 { height: 25vh; } -.vh-50 { height: 50vh; } -.vh-75 { height: 75vh; } -.vh-100 { height: 100vh; } -.min-vh-100 { min-height: 100vh; } -/* String Properties */ -.h-auto { height: auto; } -.h-inherit { height: inherit; } -@media screen and (min-width: 30em) { - .h1-ns { height: 1rem; } - .h2-ns { height: 2rem; } - .h3-ns { height: 4rem; } - .h4-ns { height: 8rem; } - .h5-ns { height: 16rem; } - .h-25-ns { height: 25%; } - .h-50-ns { height: 50%; } - .h-75-ns { height: 75%; } - .h-100-ns { height: 100%; } - .min-h-100-ns { min-height: 100%; } - .vh-25-ns { height: 25vh; } - .vh-50-ns { height: 50vh; } - .vh-75-ns { height: 75vh; } - .vh-100-ns { height: 100vh; } - .min-vh-100-ns { min-height: 100vh; } - .h-auto-ns { height: auto; } - .h-inherit-ns { height: inherit; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .h1-m { height: 1rem; } - .h2-m { height: 2rem; } - .h3-m { height: 4rem; } - .h4-m { height: 8rem; } - .h5-m { height: 16rem; } - .h-25-m { height: 25%; } - .h-50-m { height: 50%; } - .h-75-m { height: 75%; } - .h-100-m { height: 100%; } - .min-h-100-m { min-height: 100%; } - .vh-25-m { height: 25vh; } - .vh-50-m { height: 50vh; } - .vh-75-m { height: 75vh; } - .vh-100-m { height: 100vh; } - .min-vh-100-m { min-height: 100vh; } - .h-auto-m { height: auto; } - .h-inherit-m { height: inherit; } -} -@media screen and (min-width: 60em) { - .h1-l { height: 1rem; } - .h2-l { height: 2rem; } - .h3-l { height: 4rem; } - .h4-l { height: 8rem; } - .h5-l { height: 16rem; } - .h-25-l { height: 25%; } - .h-50-l { height: 50%; } - .h-75-l { height: 75%; } - .h-100-l { height: 100%; } - .min-h-100-l { min-height: 100%; } - .vh-25-l { height: 25vh; } - .vh-50-l { height: 50vh; } - .vh-75-l { height: 75vh; } - .vh-100-l { height: 100vh; } - .min-vh-100-l { min-height: 100vh; } - .h-auto-l { height: auto; } - .h-inherit-l { height: inherit; } -} -/* - - LETTER SPACING - Docs: http://tachyons.io/docs/typography/tracking/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.tracked { letter-spacing: .1em; } -.tracked-tight { letter-spacing: -.05em; } -.tracked-mega { letter-spacing: .25em; } -@media screen and (min-width: 30em) { - .tracked-ns { letter-spacing: .1em; } - .tracked-tight-ns { letter-spacing: -.05em; } - .tracked-mega-ns { letter-spacing: .25em; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .tracked-m { letter-spacing: .1em; } - .tracked-tight-m { letter-spacing: -.05em; } - .tracked-mega-m { letter-spacing: .25em; } -} -@media screen and (min-width: 60em) { - .tracked-l { letter-spacing: .1em; } - .tracked-tight-l { letter-spacing: -.05em; } - .tracked-mega-l { letter-spacing: .25em; } -} -/* - - LINE HEIGHT / LEADING - Docs: http://tachyons.io/docs/typography/line-height - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.lh-solid { line-height: 1; } -.lh-title { line-height: 1.25; } -.lh-copy { line-height: 1.5; } -@media screen and (min-width: 30em) { - .lh-solid-ns { line-height: 1; } - .lh-title-ns { line-height: 1.25; } - .lh-copy-ns { line-height: 1.5; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .lh-solid-m { line-height: 1; } - .lh-title-m { line-height: 1.25; } - .lh-copy-m { line-height: 1.5; } -} -@media screen and (min-width: 60em) { - .lh-solid-l { line-height: 1; } - .lh-title-l { line-height: 1.25; } - .lh-copy-l { line-height: 1.5; } -} -/* - - LINKS - Docs: http://tachyons.io/docs/elements/links/ - -*/ -.link { - text-decoration: none; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:link, -.link:visited { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:hover { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:active { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.link:focus { - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; - outline: 1px dotted currentColor; -} -/* - - LISTS - http://tachyons.io/docs/elements/lists/ - -*/ -.list { list-style-type: none; } -/* - - MAX WIDTHS - Docs: http://tachyons.io/docs/layout/max-widths/ - - Base: - mw = max-width - - Modifiers - 1 = 1st step in width scale - 2 = 2nd step in width scale - 3 = 3rd step in width scale - 4 = 4th step in width scale - 5 = 5th step in width scale - 6 = 6st step in width scale - 7 = 7nd step in width scale - 8 = 8rd step in width scale - 9 = 9th step in width scale - - -100 = literal value 100% - - -none = string value none - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Max Width Percentages */ -.mw-100 { max-width: 100%; } -/* Max Width Scale */ -.mw1 { max-width: 1rem; } -.mw2 { max-width: 2rem; } -.mw3 { max-width: 4rem; } -.mw4 { max-width: 8rem; } -.mw5 { max-width: 16rem; } -.mw6 { max-width: 32rem; } -.mw7 { max-width: 48rem; } -.mw8 { max-width: 64rem; } -.mw9 { max-width: 96rem; } -/* Max Width String Properties */ -.mw-none { max-width: none; } -@media screen and (min-width: 30em) { - .mw-100-ns { max-width: 100%; } - - .mw1-ns { max-width: 1rem; } - .mw2-ns { max-width: 2rem; } - .mw3-ns { max-width: 4rem; } - .mw4-ns { max-width: 8rem; } - .mw5-ns { max-width: 16rem; } - .mw6-ns { max-width: 32rem; } - .mw7-ns { max-width: 48rem; } - .mw8-ns { max-width: 64rem; } - .mw9-ns { max-width: 96rem; } - - .mw-none-ns { max-width: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .mw-100-m { max-width: 100%; } - - .mw1-m { max-width: 1rem; } - .mw2-m { max-width: 2rem; } - .mw3-m { max-width: 4rem; } - .mw4-m { max-width: 8rem; } - .mw5-m { max-width: 16rem; } - .mw6-m { max-width: 32rem; } - .mw7-m { max-width: 48rem; } - .mw8-m { max-width: 64rem; } - .mw9-m { max-width: 96rem; } - - .mw-none-m { max-width: none; } -} -@media screen and (min-width: 60em) { - .mw-100-l { max-width: 100%; } - - .mw1-l { max-width: 1rem; } - .mw2-l { max-width: 2rem; } - .mw3-l { max-width: 4rem; } - .mw4-l { max-width: 8rem; } - .mw5-l { max-width: 16rem; } - .mw6-l { max-width: 32rem; } - .mw7-l { max-width: 48rem; } - .mw8-l { max-width: 64rem; } - .mw9-l { max-width: 96rem; } - - .mw-none-l { max-width: none; } -} -/* - - WIDTHS - Docs: http://tachyons.io/docs/layout/widths/ - - Base: - w = width - - Modifiers - 1 = 1st step in width scale - 2 = 2nd step in width scale - 3 = 3rd step in width scale - 4 = 4th step in width scale - 5 = 5th step in width scale - - -10 = literal value 10% - -20 = literal value 20% - -25 = literal value 25% - -30 = literal value 30% - -33 = literal value 33% - -34 = literal value 34% - -40 = literal value 40% - -50 = literal value 50% - -60 = literal value 60% - -70 = literal value 70% - -75 = literal value 75% - -80 = literal value 80% - -90 = literal value 90% - -100 = literal value 100% - - -third = 100% / 3 (Not supported in opera mini or IE8) - -two-thirds = 100% / 1.5 (Not supported in opera mini or IE8) - -auto = string value auto - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Width Scale */ -.w1 { width: 1rem; } -.w2 { width: 2rem; } -.w3 { width: 4rem; } -.w4 { width: 8rem; } -.w5 { width: 16rem; } -.w-10 { width: 10%; } -.w-20 { width: 20%; } -.w-25 { width: 25%; } -.w-30 { width: 30%; } -.w-33 { width: 33%; } -.w-34 { width: 34%; } -.w-40 { width: 40%; } -.w-50 { width: 50%; } -.w-60 { width: 60%; } -.w-70 { width: 70%; } -.w-75 { width: 75%; } -.w-80 { width: 80%; } -.w-90 { width: 90%; } -.w-100 { width: 100%; } -.w-third { width: 33.33333%; } -.w-two-thirds { width: 66.66667%; } -.w-auto { width: auto; } -@media screen and (min-width: 30em) { - .w1-ns { width: 1rem; } - .w2-ns { width: 2rem; } - .w3-ns { width: 4rem; } - .w4-ns { width: 8rem; } - .w5-ns { width: 16rem; } - .w-10-ns { width: 10%; } - .w-20-ns { width: 20%; } - .w-25-ns { width: 25%; } - .w-30-ns { width: 30%; } - .w-33-ns { width: 33%; } - .w-34-ns { width: 34%; } - .w-40-ns { width: 40%; } - .w-50-ns { width: 50%; } - .w-60-ns { width: 60%; } - .w-70-ns { width: 70%; } - .w-75-ns { width: 75%; } - .w-80-ns { width: 80%; } - .w-90-ns { width: 90%; } - .w-100-ns { width: 100%; } - .w-third-ns { width: 33.33333%; } - .w-two-thirds-ns { width: 66.66667%; } - .w-auto-ns { width: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .w1-m { width: 1rem; } - .w2-m { width: 2rem; } - .w3-m { width: 4rem; } - .w4-m { width: 8rem; } - .w5-m { width: 16rem; } - .w-10-m { width: 10%; } - .w-20-m { width: 20%; } - .w-25-m { width: 25%; } - .w-30-m { width: 30%; } - .w-33-m { width: 33%; } - .w-34-m { width: 34%; } - .w-40-m { width: 40%; } - .w-50-m { width: 50%; } - .w-60-m { width: 60%; } - .w-70-m { width: 70%; } - .w-75-m { width: 75%; } - .w-80-m { width: 80%; } - .w-90-m { width: 90%; } - .w-100-m { width: 100%; } - .w-third-m { width: 33.33333%; } - .w-two-thirds-m { width: 66.66667%; } - .w-auto-m { width: auto; } -} -@media screen and (min-width: 60em) { - .w1-l { width: 1rem; } - .w2-l { width: 2rem; } - .w3-l { width: 4rem; } - .w4-l { width: 8rem; } - .w5-l { width: 16rem; } - .w-10-l { width: 10%; } - .w-20-l { width: 20%; } - .w-25-l { width: 25%; } - .w-30-l { width: 30%; } - .w-33-l { width: 33%; } - .w-34-l { width: 34%; } - .w-40-l { width: 40%; } - .w-50-l { width: 50%; } - .w-60-l { width: 60%; } - .w-70-l { width: 70%; } - .w-75-l { width: 75%; } - .w-80-l { width: 80%; } - .w-90-l { width: 90%; } - .w-100-l { width: 100%; } - .w-third-l { width: 33.33333%; } - .w-two-thirds-l { width: 66.66667%; } - .w-auto-l { width: auto; } -} -/* - - OVERFLOW - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - - */ -.overflow-visible { overflow: visible; } -.overflow-hidden { overflow: hidden; } -.overflow-scroll { overflow: scroll; } -.overflow-auto { overflow: auto; } -.overflow-x-visible { overflow-x: visible; } -.overflow-x-hidden { overflow-x: hidden; } -.overflow-x-scroll { overflow-x: scroll; } -.overflow-x-auto { overflow-x: auto; } -.overflow-y-visible { overflow-y: visible; } -.overflow-y-hidden { overflow-y: hidden; } -.overflow-y-scroll { overflow-y: scroll; } -.overflow-y-auto { overflow-y: auto; } -@media screen and (min-width: 30em) { - .overflow-visible-ns { overflow: visible; } - .overflow-hidden-ns { overflow: hidden; } - .overflow-scroll-ns { overflow: scroll; } - .overflow-auto-ns { overflow: auto; } - .overflow-x-visible-ns { overflow-x: visible; } - .overflow-x-hidden-ns { overflow-x: hidden; } - .overflow-x-scroll-ns { overflow-x: scroll; } - .overflow-x-auto-ns { overflow-x: auto; } - - .overflow-y-visible-ns { overflow-y: visible; } - .overflow-y-hidden-ns { overflow-y: hidden; } - .overflow-y-scroll-ns { overflow-y: scroll; } - .overflow-y-auto-ns { overflow-y: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .overflow-visible-m { overflow: visible; } - .overflow-hidden-m { overflow: hidden; } - .overflow-scroll-m { overflow: scroll; } - .overflow-auto-m { overflow: auto; } - - .overflow-x-visible-m { overflow-x: visible; } - .overflow-x-hidden-m { overflow-x: hidden; } - .overflow-x-scroll-m { overflow-x: scroll; } - .overflow-x-auto-m { overflow-x: auto; } - - .overflow-y-visible-m { overflow-y: visible; } - .overflow-y-hidden-m { overflow-y: hidden; } - .overflow-y-scroll-m { overflow-y: scroll; } - .overflow-y-auto-m { overflow-y: auto; } -} -@media screen and (min-width: 60em) { - .overflow-visible-l { overflow: visible; } - .overflow-hidden-l { overflow: hidden; } - .overflow-scroll-l { overflow: scroll; } - .overflow-auto-l { overflow: auto; } - - .overflow-x-visible-l { overflow-x: visible; } - .overflow-x-hidden-l { overflow-x: hidden; } - .overflow-x-scroll-l { overflow-x: scroll; } - .overflow-x-auto-l { overflow-x: auto; } - - .overflow-y-visible-l { overflow-y: visible; } - .overflow-y-hidden-l { overflow-y: hidden; } - .overflow-y-scroll-l { overflow-y: scroll; } - .overflow-y-auto-l { overflow-y: auto; } -} -/* - - POSITIONING - Docs: http://tachyons.io/docs/layout/position/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.static { position: static; } -.relative { position: relative; } -.absolute { position: absolute; } -.fixed { position: fixed; } -@media screen and (min-width: 30em) { - .static-ns { position: static; } - .relative-ns { position: relative; } - .absolute-ns { position: absolute; } - .fixed-ns { position: fixed; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .static-m { position: static; } - .relative-m { position: relative; } - .absolute-m { position: absolute; } - .fixed-m { position: fixed; } -} -@media screen and (min-width: 60em) { - .static-l { position: static; } - .relative-l { position: relative; } - .absolute-l { position: absolute; } - .fixed-l { position: fixed; } -} -/* - - OPACITY - Docs: http://tachyons.io/docs/themes/opacity/ - -*/ -.o-100 { opacity: 1; } -.o-90 { opacity: .9; } -.o-80 { opacity: .8; } -.o-70 { opacity: .7; } -.o-60 { opacity: .6; } -.o-50 { opacity: .5; } -.o-40 { opacity: .4; } -.o-30 { opacity: .3; } -.o-20 { opacity: .2; } -.o-10 { opacity: .1; } -.o-05 { opacity: .05; } -.o-025 { opacity: .025; } -.o-0 { opacity: 0; } -/*@import 'tachyons/src/_rotations';*/ -/* - - SKINS - Docs: http://tachyons.io/docs/themes/skins/ - - Classes for setting foreground and background colors on elements. - If you haven't declared a border color, but set border on an element, it will - be set to the current text color. - -*/ -/* Text colors */ -.black-90 { color: rgba(0, 0, 0, .9); } -.black-80 { color: rgba(0, 0, 0, .8); } -.black-70 { color: rgba(0, 0, 0, .7); } -.black-60 { color: rgba(0, 0, 0, .6); } -.black-50 { color: rgba(0, 0, 0, .5); } -.black-40 { color: rgba(0, 0, 0, .4); } -.black-30 { color: rgba(0, 0, 0, .3); } -.black-20 { color: rgba(0, 0, 0, .2); } -.black-10 { color: rgba(0, 0, 0, .1); } -.black-05 { color: rgba(0, 0, 0, .05); } -.white-90 { color: rgba(255, 255, 255, .9); } -.white-80 { color: rgba(255, 255, 255, .8); } -.white-70 { color: rgba(255, 255, 255, .7); } -.white-60 { color: rgba(255, 255, 255, .6); } -.white-50 { color: rgba(255, 255, 255, .5); } -.white-40 { color: rgba(255, 255, 255, .4); } -.white-30 { color: rgba(255, 255, 255, .3); } -.white-20 { color: rgba(255, 255, 255, .2); } -.white-10 { color: rgba(255, 255, 255, .1); } -.black { color: #000; } -.near-black { color: #111; } -.dark-gray { color: #333; } -.mid-gray { color: #555; } -.gray { color: #777; } -.silver { color: #999; } -.light-silver { color: #aaa; } -.moon-gray { color: #ccc; } -.light-gray { color: #eee; } -.near-white { color: #f4f4f4; } -.white { color: #fff; } -.dark-red { color: #e7040f; } -.red { color: #ff4136; } -.light-red { color: #ff725c; } -.orange { color: #ff6300; } -.gold { color: #ffb700; } -.yellow { color: #ffd700; } -.light-yellow { color: #fbf1a9; } -.purple { color: #5e2ca5; } -.light-purple { color: #a463f2; } -.dark-pink { color: #d5008f; } -.hot-pink { color: #ff41b4; } -.pink { color: #ff80cc; } -.light-pink { color: #ffa3d7; } -.dark-green { color: #137752; } -.green { color: #19a974; } -.light-green { color: #9eebcf; } -.navy { color: #001b44; } -.dark-blue { color: #00449e; } -.blue { color: #0594CB; } -.light-blue { color: #96ccff; } -.lightest-blue { color: #cdecff; } -.washed-blue { color: #f6fffe; } -.washed-green { color: #e8fdf5; } -.washed-yellow { color: #fffceb; } -.washed-red { color: #ffdfdf; } -.color-inherit { color: inherit; } -.bg-black-90 { background-color: rgba(0, 0, 0, .9); } -.bg-black-80 { background-color: rgba(0, 0, 0, .8); } -.bg-black-70 { background-color: rgba(0, 0, 0, .7); } -.bg-black-60 { background-color: rgba(0, 0, 0, .6); } -.bg-black-50 { background-color: rgba(0, 0, 0, .5); } -.bg-black-40 { background-color: rgba(0, 0, 0, .4); } -.bg-black-30 { background-color: rgba(0, 0, 0, .3); } -.bg-black-20 { background-color: rgba(0, 0, 0, .2); } -.bg-black-10 { background-color: rgba(0, 0, 0, .1); } -.bg-black-05 { background-color: rgba(0, 0, 0, .05); } -.bg-white-90 { background-color: rgba(255, 255, 255, .9); } -.bg-white-80 { background-color: rgba(255, 255, 255, .8); } -.bg-white-70 { background-color: rgba(255, 255, 255, .7); } -.bg-white-60 { background-color: rgba(255, 255, 255, .6); } -.bg-white-50 { background-color: rgba(255, 255, 255, .5); } -.bg-white-40 { background-color: rgba(255, 255, 255, .4); } -.bg-white-30 { background-color: rgba(255, 255, 255, .3); } -.bg-white-20 { background-color: rgba(255, 255, 255, .2); } -.bg-white-10 { background-color: rgba(255, 255, 255, .1); } -/* Background colors */ -.bg-black { background-color: #000; } -.bg-near-black { background-color: #111; } -.bg-dark-gray { background-color: #333; } -.bg-mid-gray { background-color: #555; } -.bg-gray { background-color: #777; } -.bg-silver { background-color: #999; } -.bg-light-silver { background-color: #aaa; } -.bg-moon-gray { background-color: #ccc; } -.bg-light-gray { background-color: #eee; } -.bg-near-white { background-color: #f4f4f4; } -.bg-white { background-color: #fff; } -.bg-transparent { background-color: transparent; } -.bg-dark-red { background-color: #e7040f; } -.bg-red { background-color: #ff4136; } -.bg-light-red { background-color: #ff725c; } -.bg-orange { background-color: #ff6300; } -.bg-gold { background-color: #ffb700; } -.bg-yellow { background-color: #ffd700; } -.bg-light-yellow { background-color: #fbf1a9; } -.bg-purple { background-color: #5e2ca5; } -.bg-light-purple { background-color: #a463f2; } -.bg-dark-pink { background-color: #d5008f; } -.bg-hot-pink { background-color: #ff41b4; } -.bg-pink { background-color: #ff80cc; } -.bg-light-pink { background-color: #ffa3d7; } -.bg-dark-green { background-color: #137752; } -.bg-green { background-color: #19a974; } -.bg-light-green { background-color: #9eebcf; } -.bg-navy { background-color: #001b44; } -.bg-dark-blue { background-color: #00449e; } -.bg-blue { background-color: #0594CB; } -.bg-light-blue { background-color: #96ccff; } -.bg-lightest-blue { background-color: #cdecff; } -.bg-washed-blue { background-color: #f6fffe; } -.bg-washed-green { background-color: #e8fdf5; } -.bg-washed-yellow { background-color: #fffceb; } -.bg-washed-red { background-color: #ffdfdf; } -.bg-inherit { background-color: inherit; } -/* - - SKINS:PSEUDO - - Customize the color of an element when - it is focused or hovered over. - - */ -.hover-black:hover, -.hover-black:focus { color: #000; } -.hover-near-black:hover, -.hover-near-black:focus { color: #111; } -.hover-dark-gray:hover, -.hover-dark-gray:focus { color: #333; } -.hover-mid-gray:hover, -.hover-mid-gray:focus { color: #555; } -.hover-gray:hover, -.hover-gray:focus { color: #777; } -.hover-silver:hover, -.hover-silver:focus { color: #999; } -.hover-light-silver:hover, -.hover-light-silver:focus { color: #aaa; } -.hover-moon-gray:hover, -.hover-moon-gray:focus { color: #ccc; } -.hover-light-gray:hover, -.hover-light-gray:focus { color: #eee; } -.hover-near-white:hover, -.hover-near-white:focus { color: #f4f4f4; } -.hover-white:hover, -.hover-white:focus { color: #fff; } -.hover-black-90:hover, -.hover-black-90:focus { color: rgba(0, 0, 0, .9); } -.hover-black-80:hover, -.hover-black-80:focus { color: rgba(0, 0, 0, .8); } -.hover-black-70:hover, -.hover-black-70:focus { color: rgba(0, 0, 0, .7); } -.hover-black-60:hover, -.hover-black-60:focus { color: rgba(0, 0, 0, .6); } -.hover-black-50:hover, -.hover-black-50:focus { color: rgba(0, 0, 0, .5); } -.hover-black-40:hover, -.hover-black-40:focus { color: rgba(0, 0, 0, .4); } -.hover-black-30:hover, -.hover-black-30:focus { color: rgba(0, 0, 0, .3); } -.hover-black-20:hover, -.hover-black-20:focus { color: rgba(0, 0, 0, .2); } -.hover-black-10:hover, -.hover-black-10:focus { color: rgba(0, 0, 0, .1); } -.hover-white-90:hover, -.hover-white-90:focus { color: rgba(255, 255, 255, .9); } -.hover-white-80:hover, -.hover-white-80:focus { color: rgba(255, 255, 255, .8); } -.hover-white-70:hover, -.hover-white-70:focus { color: rgba(255, 255, 255, .7); } -.hover-white-60:hover, -.hover-white-60:focus { color: rgba(255, 255, 255, .6); } -.hover-white-50:hover, -.hover-white-50:focus { color: rgba(255, 255, 255, .5); } -.hover-white-40:hover, -.hover-white-40:focus { color: rgba(255, 255, 255, .4); } -.hover-white-30:hover, -.hover-white-30:focus { color: rgba(255, 255, 255, .3); } -.hover-white-20:hover, -.hover-white-20:focus { color: rgba(255, 255, 255, .2); } -.hover-white-10:hover, -.hover-white-10:focus { color: rgba(255, 255, 255, .1); } -.hover-inherit:hover, -.hover-inherit:focus { color: inherit; } -.hover-bg-black:hover, -.hover-bg-black:focus { background-color: #000; } -.hover-bg-near-black:hover, -.hover-bg-near-black:focus { background-color: #111; } -.hover-bg-dark-gray:hover, -.hover-bg-dark-gray:focus { background-color: #333; } -.hover-bg-mid-gray:hover, -.hover-bg-mid-gray:focus { background-color: #555; } -.hover-bg-gray:hover, -.hover-bg-gray:focus { background-color: #777; } -.hover-bg-silver:hover, -.hover-bg-silver:focus { background-color: #999; } -.hover-bg-light-silver:hover, -.hover-bg-light-silver:focus { background-color: #aaa; } -.hover-bg-moon-gray:hover, -.hover-bg-moon-gray:focus { background-color: #ccc; } -.hover-bg-light-gray:hover, -.hover-bg-light-gray:focus { background-color: #eee; } -.hover-bg-near-white:hover, -.hover-bg-near-white:focus { background-color: #f4f4f4; } -.hover-bg-white:hover, -.hover-bg-white:focus { background-color: #fff; } -.hover-bg-transparent:hover, -.hover-bg-transparent:focus { background-color: transparent; } -.hover-bg-black-90:hover, -.hover-bg-black-90:focus { background-color: rgba(0, 0, 0, .9); } -.hover-bg-black-80:hover, -.hover-bg-black-80:focus { background-color: rgba(0, 0, 0, .8); } -.hover-bg-black-70:hover, -.hover-bg-black-70:focus { background-color: rgba(0, 0, 0, .7); } -.hover-bg-black-60:hover, -.hover-bg-black-60:focus { background-color: rgba(0, 0, 0, .6); } -.hover-bg-black-50:hover, -.hover-bg-black-50:focus { background-color: rgba(0, 0, 0, .5); } -.hover-bg-black-40:hover, -.hover-bg-black-40:focus { background-color: rgba(0, 0, 0, .4); } -.hover-bg-black-30:hover, -.hover-bg-black-30:focus { background-color: rgba(0, 0, 0, .3); } -.hover-bg-black-20:hover, -.hover-bg-black-20:focus { background-color: rgba(0, 0, 0, .2); } -.hover-bg-black-10:hover, -.hover-bg-black-10:focus { background-color: rgba(0, 0, 0, .1); } -.hover-bg-white-90:hover, -.hover-bg-white-90:focus { background-color: rgba(255, 255, 255, .9); } -.hover-bg-white-80:hover, -.hover-bg-white-80:focus { background-color: rgba(255, 255, 255, .8); } -.hover-bg-white-70:hover, -.hover-bg-white-70:focus { background-color: rgba(255, 255, 255, .7); } -.hover-bg-white-60:hover, -.hover-bg-white-60:focus { background-color: rgba(255, 255, 255, .6); } -.hover-bg-white-50:hover, -.hover-bg-white-50:focus { background-color: rgba(255, 255, 255, .5); } -.hover-bg-white-40:hover, -.hover-bg-white-40:focus { background-color: rgba(255, 255, 255, .4); } -.hover-bg-white-30:hover, -.hover-bg-white-30:focus { background-color: rgba(255, 255, 255, .3); } -.hover-bg-white-20:hover, -.hover-bg-white-20:focus { background-color: rgba(255, 255, 255, .2); } -.hover-bg-white-10:hover, -.hover-bg-white-10:focus { background-color: rgba(255, 255, 255, .1); } -.hover-dark-red:hover, -.hover-dark-red:focus { color: #e7040f; } -.hover-red:hover, -.hover-red:focus { color: #ff4136; } -.hover-light-red:hover, -.hover-light-red:focus { color: #ff725c; } -.hover-orange:hover, -.hover-orange:focus { color: #ff6300; } -.hover-gold:hover, -.hover-gold:focus { color: #ffb700; } -.hover-yellow:hover, -.hover-yellow:focus { color: #ffd700; } -.hover-light-yellow:hover, -.hover-light-yellow:focus { color: #fbf1a9; } -.hover-purple:hover, -.hover-purple:focus { color: #5e2ca5; } -.hover-light-purple:hover, -.hover-light-purple:focus { color: #a463f2; } -.hover-dark-pink:hover, -.hover-dark-pink:focus { color: #d5008f; } -.hover-hot-pink:hover, -.hover-hot-pink:focus { color: #ff41b4; } -.hover-pink:hover, -.hover-pink:focus { color: #ff80cc; } -.hover-light-pink:hover, -.hover-light-pink:focus { color: #ffa3d7; } -.hover-dark-green:hover, -.hover-dark-green:focus { color: #137752; } -.hover-green:hover, -.hover-green:focus { color: #19a974; } -.hover-light-green:hover, -.hover-light-green:focus { color: #9eebcf; } -.hover-navy:hover, -.hover-navy:focus { color: #001b44; } -.hover-dark-blue:hover, -.hover-dark-blue:focus { color: #00449e; } -.hover-blue:hover, -.hover-blue:focus { color: #0594CB; } -.hover-light-blue:hover, -.hover-light-blue:focus { color: #96ccff; } -.hover-lightest-blue:hover, -.hover-lightest-blue:focus { color: #cdecff; } -.hover-washed-blue:hover, -.hover-washed-blue:focus { color: #f6fffe; } -.hover-washed-green:hover, -.hover-washed-green:focus { color: #e8fdf5; } -.hover-washed-yellow:hover, -.hover-washed-yellow:focus { color: #fffceb; } -.hover-washed-red:hover, -.hover-washed-red:focus { color: #ffdfdf; } -.hover-bg-dark-red:hover, -.hover-bg-dark-red:focus { background-color: #e7040f; } -.hover-bg-red:hover, -.hover-bg-red:focus { background-color: #ff4136; } -.hover-bg-light-red:hover, -.hover-bg-light-red:focus { background-color: #ff725c; } -.hover-bg-orange:hover, -.hover-bg-orange:focus { background-color: #ff6300; } -.hover-bg-gold:hover, -.hover-bg-gold:focus { background-color: #ffb700; } -.hover-bg-yellow:hover, -.hover-bg-yellow:focus { background-color: #ffd700; } -.hover-bg-light-yellow:hover, -.hover-bg-light-yellow:focus { background-color: #fbf1a9; } -.hover-bg-purple:hover, -.hover-bg-purple:focus { background-color: #5e2ca5; } -.hover-bg-light-purple:hover, -.hover-bg-light-purple:focus { background-color: #a463f2; } -.hover-bg-dark-pink:hover, -.hover-bg-dark-pink:focus { background-color: #d5008f; } -.hover-bg-hot-pink:hover, -.hover-bg-hot-pink:focus { background-color: #ff41b4; } -.hover-bg-pink:hover, -.hover-bg-pink:focus { background-color: #ff80cc; } -.hover-bg-light-pink:hover, -.hover-bg-light-pink:focus { background-color: #ffa3d7; } -.hover-bg-dark-green:hover, -.hover-bg-dark-green:focus { background-color: #137752; } -.hover-bg-green:hover, -.hover-bg-green:focus { background-color: #19a974; } -.hover-bg-light-green:hover, -.hover-bg-light-green:focus { background-color: #9eebcf; } -.hover-bg-navy:hover, -.hover-bg-navy:focus { background-color: #001b44; } -.hover-bg-dark-blue:hover, -.hover-bg-dark-blue:focus { background-color: #00449e; } -.hover-bg-blue:hover, -.hover-bg-blue:focus { background-color: #0594CB; } -.hover-bg-light-blue:hover, -.hover-bg-light-blue:focus { background-color: #96ccff; } -.hover-bg-lightest-blue:hover, -.hover-bg-lightest-blue:focus { background-color: #cdecff; } -.hover-bg-washed-blue:hover, -.hover-bg-washed-blue:focus { background-color: #f6fffe; } -.hover-bg-washed-green:hover, -.hover-bg-washed-green:focus { background-color: #e8fdf5; } -.hover-bg-washed-yellow:hover, -.hover-bg-washed-yellow:focus { background-color: #fffceb; } -.hover-bg-washed-red:hover, -.hover-bg-washed-red:focus { background-color: #ffdfdf; } -.hover-bg-inherit:hover, -.hover-bg-inherit:focus { background-color: inherit; } -/* Variables */ -/* - SPACING - Docs: http://tachyons.io/docs/layout/spacing/ - - An eight step powers of two scale ranging from 0 to 16rem. - - Base: - p = padding - m = margin - - Modifiers: - a = all - h = horizontal - v = vertical - t = top - r = right - b = bottom - l = left - - 0 = none - 1 = 1st step in spacing scale - 2 = 2nd step in spacing scale - 3 = 3rd step in spacing scale - 4 = 4th step in spacing scale - 5 = 5th step in spacing scale - 6 = 6th step in spacing scale - 7 = 7th step in spacing scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.pa0 { padding: 0; } -.pa1 { padding: .25rem; } -.pa2 { padding: .5rem; } -.pa3 { padding: 1rem; } -.pa4 { padding: 2rem; } -.pa5 { padding: 4rem; } -.pa6 { padding: 8rem; } -.pa7 { padding: 16rem; } -.pl0 { padding-left: 0; } -.pl1 { padding-left: .25rem; } -.pl2 { padding-left: .5rem; } -.pl3 { padding-left: 1rem; } -.pl4 { padding-left: 2rem; } -.pl5 { padding-left: 4rem; } -.pl6 { padding-left: 8rem; } -.pl7 { padding-left: 16rem; } -.pr0 { padding-right: 0; } -.pr1 { padding-right: .25rem; } -.pr2 { padding-right: .5rem; } -.pr3 { padding-right: 1rem; } -.pr4 { padding-right: 2rem; } -.pr5 { padding-right: 4rem; } -.pr6 { padding-right: 8rem; } -.pr7 { padding-right: 16rem; } -.pb0 { padding-bottom: 0; } -.pb1 { padding-bottom: .25rem; } -.pb2 { padding-bottom: .5rem; } -.pb3 { padding-bottom: 1rem; } -.pb4 { padding-bottom: 2rem; } -.pb5 { padding-bottom: 4rem; } -.pb6 { padding-bottom: 8rem; } -.pb7 { padding-bottom: 16rem; } -.pt0 { padding-top: 0; } -.pt1 { padding-top: .25rem; } -.pt2 { padding-top: .5rem; } -.pt3 { padding-top: 1rem; } -.pt4 { padding-top: 2rem; } -.pt5 { padding-top: 4rem; } -.pt6 { padding-top: 8rem; } -.pt7 { padding-top: 16rem; } -.pv0 { - padding-top: 0; - padding-bottom: 0; -} -.pv1 { - padding-top: .25rem; - padding-bottom: .25rem; -} -.pv2 { - padding-top: .5rem; - padding-bottom: .5rem; -} -.pv3 { - padding-top: 1rem; - padding-bottom: 1rem; -} -.pv4 { - padding-top: 2rem; - padding-bottom: 2rem; -} -.pv5 { - padding-top: 4rem; - padding-bottom: 4rem; -} -.pv6 { - padding-top: 8rem; - padding-bottom: 8rem; -} -.pv7 { - padding-top: 16rem; - padding-bottom: 16rem; -} -.ph0 { - padding-left: 0; - padding-right: 0; -} -.ph1 { - padding-left: .25rem; - padding-right: .25rem; -} -.ph2 { - padding-left: .5rem; - padding-right: .5rem; -} -.ph3 { - padding-left: 1rem; - padding-right: 1rem; -} -.ph4 { - padding-left: 2rem; - padding-right: 2rem; -} -.ph5 { - padding-left: 4rem; - padding-right: 4rem; -} -.ph6 { - padding-left: 8rem; - padding-right: 8rem; -} -.ph7 { - padding-left: 16rem; - padding-right: 16rem; -} -.ma0 { margin: 0; } -.ma1 { margin: .25rem; } -.ma2 { margin: .5rem; } -.ma3 { margin: 1rem; } -.ma4 { margin: 2rem; } -.ma5 { margin: 4rem; } -.ma6 { margin: 8rem; } -.ma7 { margin: 16rem; } -.ml0 { margin-left: 0; } -.ml1 { margin-left: .25rem; } -.ml2 { margin-left: .5rem; } -.ml3 { margin-left: 1rem; } -.ml4 { margin-left: 2rem; } -.ml5 { margin-left: 4rem; } -.ml6 { margin-left: 8rem; } -.ml7 { margin-left: 16rem; } -.mr0 { margin-right: 0; } -.mr1 { margin-right: .25rem; } -.mr2 { margin-right: .5rem; } -.mr3 { margin-right: 1rem; } -.mr4 { margin-right: 2rem; } -.mr5 { margin-right: 4rem; } -.mr6 { margin-right: 8rem; } -.mr7 { margin-right: 16rem; } -.mb0 { margin-bottom: 0; } -.mb1 { margin-bottom: .25rem; } -.mb2 { margin-bottom: .5rem; } -.mb3 { margin-bottom: 1rem; } -.mb4 { margin-bottom: 2rem; } -.mb5 { margin-bottom: 4rem; } -.mb6 { margin-bottom: 8rem; } -.mb7 { margin-bottom: 16rem; } -.mt0 { margin-top: 0; } -.mt1 { margin-top: .25rem; } -.mt2 { margin-top: .5rem; } -.mt3 { margin-top: 1rem; } -.mt4 { margin-top: 2rem; } -.mt5 { margin-top: 4rem; } -.mt6 { margin-top: 8rem; } -.mt7 { margin-top: 16rem; } -.mv0 { - margin-top: 0; - margin-bottom: 0; -} -.mv1 { - margin-top: .25rem; - margin-bottom: .25rem; -} -.mv2 { - margin-top: .5rem; - margin-bottom: .5rem; -} -.mv3 { - margin-top: 1rem; - margin-bottom: 1rem; -} -.mv4 { - margin-top: 2rem; - margin-bottom: 2rem; -} -.mv5 { - margin-top: 4rem; - margin-bottom: 4rem; -} -.mv6 { - margin-top: 8rem; - margin-bottom: 8rem; -} -.mv7 { - margin-top: 16rem; - margin-bottom: 16rem; -} -.mh0 { - margin-left: 0; - margin-right: 0; -} -.mh1 { - margin-left: .25rem; - margin-right: .25rem; -} -.mh2 { - margin-left: .5rem; - margin-right: .5rem; -} -.mh3 { - margin-left: 1rem; - margin-right: 1rem; -} -.mh4 { - margin-left: 2rem; - margin-right: 2rem; -} -.mh5 { - margin-left: 4rem; - margin-right: 4rem; -} -.mh6 { - margin-left: 8rem; - margin-right: 8rem; -} -.mh7 { - margin-left: 16rem; - margin-right: 16rem; -} -@media screen and (min-width: 30em) { - .pa0-ns { padding: 0; } - .pa1-ns { padding: .25rem; } - .pa2-ns { padding: .5rem; } - .pa3-ns { padding: 1rem; } - .pa4-ns { padding: 2rem; } - .pa5-ns { padding: 4rem; } - .pa6-ns { padding: 8rem; } - .pa7-ns { padding: 16rem; } - - .pl0-ns { padding-left: 0; } - .pl1-ns { padding-left: .25rem; } - .pl2-ns { padding-left: .5rem; } - .pl3-ns { padding-left: 1rem; } - .pl4-ns { padding-left: 2rem; } - .pl5-ns { padding-left: 4rem; } - .pl6-ns { padding-left: 8rem; } - .pl7-ns { padding-left: 16rem; } - - .pr0-ns { padding-right: 0; } - .pr1-ns { padding-right: .25rem; } - .pr2-ns { padding-right: .5rem; } - .pr3-ns { padding-right: 1rem; } - .pr4-ns { padding-right: 2rem; } - .pr5-ns { padding-right: 4rem; } - .pr6-ns { padding-right: 8rem; } - .pr7-ns { padding-right: 16rem; } - - .pb0-ns { padding-bottom: 0; } - .pb1-ns { padding-bottom: .25rem; } - .pb2-ns { padding-bottom: .5rem; } - .pb3-ns { padding-bottom: 1rem; } - .pb4-ns { padding-bottom: 2rem; } - .pb5-ns { padding-bottom: 4rem; } - .pb6-ns { padding-bottom: 8rem; } - .pb7-ns { padding-bottom: 16rem; } - - .pt0-ns { padding-top: 0; } - .pt1-ns { padding-top: .25rem; } - .pt2-ns { padding-top: .5rem; } - .pt3-ns { padding-top: 1rem; } - .pt4-ns { padding-top: 2rem; } - .pt5-ns { padding-top: 4rem; } - .pt6-ns { padding-top: 8rem; } - .pt7-ns { padding-top: 16rem; } - - .pv0-ns { - padding-top: 0; - padding-bottom: 0; - } - .pv1-ns { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-ns { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-ns { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-ns { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-ns { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-ns { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-ns { - padding-top: 16rem; - padding-bottom: 16rem; - } - .ph0-ns { - padding-left: 0; - padding-right: 0; - } - .ph1-ns { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-ns { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-ns { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-ns { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-ns { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-ns { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-ns { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-ns { margin: 0; } - .ma1-ns { margin: .25rem; } - .ma2-ns { margin: .5rem; } - .ma3-ns { margin: 1rem; } - .ma4-ns { margin: 2rem; } - .ma5-ns { margin: 4rem; } - .ma6-ns { margin: 8rem; } - .ma7-ns { margin: 16rem; } - - .ml0-ns { margin-left: 0; } - .ml1-ns { margin-left: .25rem; } - .ml2-ns { margin-left: .5rem; } - .ml3-ns { margin-left: 1rem; } - .ml4-ns { margin-left: 2rem; } - .ml5-ns { margin-left: 4rem; } - .ml6-ns { margin-left: 8rem; } - .ml7-ns { margin-left: 16rem; } - - .mr0-ns { margin-right: 0; } - .mr1-ns { margin-right: .25rem; } - .mr2-ns { margin-right: .5rem; } - .mr3-ns { margin-right: 1rem; } - .mr4-ns { margin-right: 2rem; } - .mr5-ns { margin-right: 4rem; } - .mr6-ns { margin-right: 8rem; } - .mr7-ns { margin-right: 16rem; } - - .mb0-ns { margin-bottom: 0; } - .mb1-ns { margin-bottom: .25rem; } - .mb2-ns { margin-bottom: .5rem; } - .mb3-ns { margin-bottom: 1rem; } - .mb4-ns { margin-bottom: 2rem; } - .mb5-ns { margin-bottom: 4rem; } - .mb6-ns { margin-bottom: 8rem; } - .mb7-ns { margin-bottom: 16rem; } - - .mt0-ns { margin-top: 0; } - .mt1-ns { margin-top: .25rem; } - .mt2-ns { margin-top: .5rem; } - .mt3-ns { margin-top: 1rem; } - .mt4-ns { margin-top: 2rem; } - .mt5-ns { margin-top: 4rem; } - .mt6-ns { margin-top: 8rem; } - .mt7-ns { margin-top: 16rem; } - - .mv0-ns { - margin-top: 0; - margin-bottom: 0; - } - .mv1-ns { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-ns { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-ns { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-ns { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-ns { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-ns { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-ns { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-ns { - margin-left: 0; - margin-right: 0; - } - .mh1-ns { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-ns { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-ns { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-ns { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-ns { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-ns { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-ns { - margin-left: 16rem; - margin-right: 16rem; - } - -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .pa0-m { padding: 0; } - .pa1-m { padding: .25rem; } - .pa2-m { padding: .5rem; } - .pa3-m { padding: 1rem; } - .pa4-m { padding: 2rem; } - .pa5-m { padding: 4rem; } - .pa6-m { padding: 8rem; } - .pa7-m { padding: 16rem; } - - .pl0-m { padding-left: 0; } - .pl1-m { padding-left: .25rem; } - .pl2-m { padding-left: .5rem; } - .pl3-m { padding-left: 1rem; } - .pl4-m { padding-left: 2rem; } - .pl5-m { padding-left: 4rem; } - .pl6-m { padding-left: 8rem; } - .pl7-m { padding-left: 16rem; } - - .pr0-m { padding-right: 0; } - .pr1-m { padding-right: .25rem; } - .pr2-m { padding-right: .5rem; } - .pr3-m { padding-right: 1rem; } - .pr4-m { padding-right: 2rem; } - .pr5-m { padding-right: 4rem; } - .pr6-m { padding-right: 8rem; } - .pr7-m { padding-right: 16rem; } - - .pb0-m { padding-bottom: 0; } - .pb1-m { padding-bottom: .25rem; } - .pb2-m { padding-bottom: .5rem; } - .pb3-m { padding-bottom: 1rem; } - .pb4-m { padding-bottom: 2rem; } - .pb5-m { padding-bottom: 4rem; } - .pb6-m { padding-bottom: 8rem; } - .pb7-m { padding-bottom: 16rem; } - - .pt0-m { padding-top: 0; } - .pt1-m { padding-top: .25rem; } - .pt2-m { padding-top: .5rem; } - .pt3-m { padding-top: 1rem; } - .pt4-m { padding-top: 2rem; } - .pt5-m { padding-top: 4rem; } - .pt6-m { padding-top: 8rem; } - .pt7-m { padding-top: 16rem; } - - .pv0-m { - padding-top: 0; - padding-bottom: 0; - } - .pv1-m { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-m { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-m { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-m { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-m { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-m { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-m { - padding-top: 16rem; - padding-bottom: 16rem; - } - - .ph0-m { - padding-left: 0; - padding-right: 0; - } - .ph1-m { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-m { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-m { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-m { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-m { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-m { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-m { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-m { margin: 0; } - .ma1-m { margin: .25rem; } - .ma2-m { margin: .5rem; } - .ma3-m { margin: 1rem; } - .ma4-m { margin: 2rem; } - .ma5-m { margin: 4rem; } - .ma6-m { margin: 8rem; } - .ma7-m { margin: 16rem; } - - .ml0-m { margin-left: 0; } - .ml1-m { margin-left: .25rem; } - .ml2-m { margin-left: .5rem; } - .ml3-m { margin-left: 1rem; } - .ml4-m { margin-left: 2rem; } - .ml5-m { margin-left: 4rem; } - .ml6-m { margin-left: 8rem; } - .ml7-m { margin-left: 16rem; } - - .mr0-m { margin-right: 0; } - .mr1-m { margin-right: .25rem; } - .mr2-m { margin-right: .5rem; } - .mr3-m { margin-right: 1rem; } - .mr4-m { margin-right: 2rem; } - .mr5-m { margin-right: 4rem; } - .mr6-m { margin-right: 8rem; } - .mr7-m { margin-right: 16rem; } - - .mb0-m { margin-bottom: 0; } - .mb1-m { margin-bottom: .25rem; } - .mb2-m { margin-bottom: .5rem; } - .mb3-m { margin-bottom: 1rem; } - .mb4-m { margin-bottom: 2rem; } - .mb5-m { margin-bottom: 4rem; } - .mb6-m { margin-bottom: 8rem; } - .mb7-m { margin-bottom: 16rem; } - - .mt0-m { margin-top: 0; } - .mt1-m { margin-top: .25rem; } - .mt2-m { margin-top: .5rem; } - .mt3-m { margin-top: 1rem; } - .mt4-m { margin-top: 2rem; } - .mt5-m { margin-top: 4rem; } - .mt6-m { margin-top: 8rem; } - .mt7-m { margin-top: 16rem; } - - .mv0-m { - margin-top: 0; - margin-bottom: 0; - } - .mv1-m { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-m { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-m { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-m { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-m { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-m { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-m { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-m { - margin-left: 0; - margin-right: 0; - } - .mh1-m { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-m { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-m { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-m { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-m { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-m { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-m { - margin-left: 16rem; - margin-right: 16rem; - } - -} -@media screen and (min-width: 60em) { - .pa0-l { padding: 0; } - .pa1-l { padding: .25rem; } - .pa2-l { padding: .5rem; } - .pa3-l { padding: 1rem; } - .pa4-l { padding: 2rem; } - .pa5-l { padding: 4rem; } - .pa6-l { padding: 8rem; } - .pa7-l { padding: 16rem; } - - .pl0-l { padding-left: 0; } - .pl1-l { padding-left: .25rem; } - .pl2-l { padding-left: .5rem; } - .pl3-l { padding-left: 1rem; } - .pl4-l { padding-left: 2rem; } - .pl5-l { padding-left: 4rem; } - .pl6-l { padding-left: 8rem; } - .pl7-l { padding-left: 16rem; } - - .pr0-l { padding-right: 0; } - .pr1-l { padding-right: .25rem; } - .pr2-l { padding-right: .5rem; } - .pr3-l { padding-right: 1rem; } - .pr4-l { padding-right: 2rem; } - .pr5-l { padding-right: 4rem; } - .pr6-l { padding-right: 8rem; } - .pr7-l { padding-right: 16rem; } - - .pb0-l { padding-bottom: 0; } - .pb1-l { padding-bottom: .25rem; } - .pb2-l { padding-bottom: .5rem; } - .pb3-l { padding-bottom: 1rem; } - .pb4-l { padding-bottom: 2rem; } - .pb5-l { padding-bottom: 4rem; } - .pb6-l { padding-bottom: 8rem; } - .pb7-l { padding-bottom: 16rem; } - - .pt0-l { padding-top: 0; } - .pt1-l { padding-top: .25rem; } - .pt2-l { padding-top: .5rem; } - .pt3-l { padding-top: 1rem; } - .pt4-l { padding-top: 2rem; } - .pt5-l { padding-top: 4rem; } - .pt6-l { padding-top: 8rem; } - .pt7-l { padding-top: 16rem; } - - .pv0-l { - padding-top: 0; - padding-bottom: 0; - } - .pv1-l { - padding-top: .25rem; - padding-bottom: .25rem; - } - .pv2-l { - padding-top: .5rem; - padding-bottom: .5rem; - } - .pv3-l { - padding-top: 1rem; - padding-bottom: 1rem; - } - .pv4-l { - padding-top: 2rem; - padding-bottom: 2rem; - } - .pv5-l { - padding-top: 4rem; - padding-bottom: 4rem; - } - .pv6-l { - padding-top: 8rem; - padding-bottom: 8rem; - } - .pv7-l { - padding-top: 16rem; - padding-bottom: 16rem; - } - - .ph0-l { - padding-left: 0; - padding-right: 0; - } - .ph1-l { - padding-left: .25rem; - padding-right: .25rem; - } - .ph2-l { - padding-left: .5rem; - padding-right: .5rem; - } - .ph3-l { - padding-left: 1rem; - padding-right: 1rem; - } - .ph4-l { - padding-left: 2rem; - padding-right: 2rem; - } - .ph5-l { - padding-left: 4rem; - padding-right: 4rem; - } - .ph6-l { - padding-left: 8rem; - padding-right: 8rem; - } - .ph7-l { - padding-left: 16rem; - padding-right: 16rem; - } - - .ma0-l { margin: 0; } - .ma1-l { margin: .25rem; } - .ma2-l { margin: .5rem; } - .ma3-l { margin: 1rem; } - .ma4-l { margin: 2rem; } - .ma5-l { margin: 4rem; } - .ma6-l { margin: 8rem; } - .ma7-l { margin: 16rem; } - - .ml0-l { margin-left: 0; } - .ml1-l { margin-left: .25rem; } - .ml2-l { margin-left: .5rem; } - .ml3-l { margin-left: 1rem; } - .ml4-l { margin-left: 2rem; } - .ml5-l { margin-left: 4rem; } - .ml6-l { margin-left: 8rem; } - .ml7-l { margin-left: 16rem; } - - .mr0-l { margin-right: 0; } - .mr1-l { margin-right: .25rem; } - .mr2-l { margin-right: .5rem; } - .mr3-l { margin-right: 1rem; } - .mr4-l { margin-right: 2rem; } - .mr5-l { margin-right: 4rem; } - .mr6-l { margin-right: 8rem; } - .mr7-l { margin-right: 16rem; } - - .mb0-l { margin-bottom: 0; } - .mb1-l { margin-bottom: .25rem; } - .mb2-l { margin-bottom: .5rem; } - .mb3-l { margin-bottom: 1rem; } - .mb4-l { margin-bottom: 2rem; } - .mb5-l { margin-bottom: 4rem; } - .mb6-l { margin-bottom: 8rem; } - .mb7-l { margin-bottom: 16rem; } - - .mt0-l { margin-top: 0; } - .mt1-l { margin-top: .25rem; } - .mt2-l { margin-top: .5rem; } - .mt3-l { margin-top: 1rem; } - .mt4-l { margin-top: 2rem; } - .mt5-l { margin-top: 4rem; } - .mt6-l { margin-top: 8rem; } - .mt7-l { margin-top: 16rem; } - - .mv0-l { - margin-top: 0; - margin-bottom: 0; - } - .mv1-l { - margin-top: .25rem; - margin-bottom: .25rem; - } - .mv2-l { - margin-top: .5rem; - margin-bottom: .5rem; - } - .mv3-l { - margin-top: 1rem; - margin-bottom: 1rem; - } - .mv4-l { - margin-top: 2rem; - margin-bottom: 2rem; - } - .mv5-l { - margin-top: 4rem; - margin-bottom: 4rem; - } - .mv6-l { - margin-top: 8rem; - margin-bottom: 8rem; - } - .mv7-l { - margin-top: 16rem; - margin-bottom: 16rem; - } - - .mh0-l { - margin-left: 0; - margin-right: 0; - } - .mh1-l { - margin-left: .25rem; - margin-right: .25rem; - } - .mh2-l { - margin-left: .5rem; - margin-right: .5rem; - } - .mh3-l { - margin-left: 1rem; - margin-right: 1rem; - } - .mh4-l { - margin-left: 2rem; - margin-right: 2rem; - } - .mh5-l { - margin-left: 4rem; - margin-right: 4rem; - } - .mh6-l { - margin-left: 8rem; - margin-right: 8rem; - } - .mh7-l { - margin-left: 16rem; - margin-right: 16rem; - } -} -/* - NEGATIVE MARGINS - - Base: - n = negative - - Modifiers: - a = all - t = top - r = right - b = bottom - l = left - - 1 = 1st step in spacing scale - 2 = 2nd step in spacing scale - 3 = 3rd step in spacing scale - 4 = 4th step in spacing scale - 5 = 5th step in spacing scale - 6 = 6th step in spacing scale - 7 = 7th step in spacing scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.na1 { margin: -0.25rem; } -.na2 { margin: -0.5rem; } -.na3 { margin: -1rem; } -.na4 { margin: -2rem; } -.na5 { margin: -4rem; } -.na6 { margin: -8rem; } -.na7 { margin: -16rem; } -.nl1 { margin-left: -0.25rem; } -.nl2 { margin-left: -0.5rem; } -.nl3 { margin-left: -1rem; } -.nl4 { margin-left: -2rem; } -.nl5 { margin-left: -4rem; } -.nl6 { margin-left: -8rem; } -.nl7 { margin-left: -16rem; } -.nr1 { margin-right: -0.25rem; } -.nr2 { margin-right: -0.5rem; } -.nr3 { margin-right: -1rem; } -.nr4 { margin-right: -2rem; } -.nr5 { margin-right: -4rem; } -.nr6 { margin-right: -8rem; } -.nr7 { margin-right: -16rem; } -.nb1 { margin-bottom: -0.25rem; } -.nb2 { margin-bottom: -0.5rem; } -.nb3 { margin-bottom: -1rem; } -.nb4 { margin-bottom: -2rem; } -.nb5 { margin-bottom: -4rem; } -.nb6 { margin-bottom: -8rem; } -.nb7 { margin-bottom: -16rem; } -.nt1 { margin-top: -0.25rem; } -.nt2 { margin-top: -0.5rem; } -.nt3 { margin-top: -1rem; } -.nt4 { margin-top: -2rem; } -.nt5 { margin-top: -4rem; } -.nt6 { margin-top: -8rem; } -.nt7 { margin-top: -16rem; } -@media screen and (min-width: 30em) { - - .na1-ns { margin: -0.25rem; } - .na2-ns { margin: -0.5rem; } - .na3-ns { margin: -1rem; } - .na4-ns { margin: -2rem; } - .na5-ns { margin: -4rem; } - .na6-ns { margin: -8rem; } - .na7-ns { margin: -16rem; } - - .nl1-ns { margin-left: -0.25rem; } - .nl2-ns { margin-left: -0.5rem; } - .nl3-ns { margin-left: -1rem; } - .nl4-ns { margin-left: -2rem; } - .nl5-ns { margin-left: -4rem; } - .nl6-ns { margin-left: -8rem; } - .nl7-ns { margin-left: -16rem; } - - .nr1-ns { margin-right: -0.25rem; } - .nr2-ns { margin-right: -0.5rem; } - .nr3-ns { margin-right: -1rem; } - .nr4-ns { margin-right: -2rem; } - .nr5-ns { margin-right: -4rem; } - .nr6-ns { margin-right: -8rem; } - .nr7-ns { margin-right: -16rem; } - - .nb1-ns { margin-bottom: -0.25rem; } - .nb2-ns { margin-bottom: -0.5rem; } - .nb3-ns { margin-bottom: -1rem; } - .nb4-ns { margin-bottom: -2rem; } - .nb5-ns { margin-bottom: -4rem; } - .nb6-ns { margin-bottom: -8rem; } - .nb7-ns { margin-bottom: -16rem; } - - .nt1-ns { margin-top: -0.25rem; } - .nt2-ns { margin-top: -0.5rem; } - .nt3-ns { margin-top: -1rem; } - .nt4-ns { margin-top: -2rem; } - .nt5-ns { margin-top: -4rem; } - .nt6-ns { margin-top: -8rem; } - .nt7-ns { margin-top: -16rem; } - -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .na1-m { margin: -0.25rem; } - .na2-m { margin: -0.5rem; } - .na3-m { margin: -1rem; } - .na4-m { margin: -2rem; } - .na5-m { margin: -4rem; } - .na6-m { margin: -8rem; } - .na7-m { margin: -16rem; } - - .nl1-m { margin-left: -0.25rem; } - .nl2-m { margin-left: -0.5rem; } - .nl3-m { margin-left: -1rem; } - .nl4-m { margin-left: -2rem; } - .nl5-m { margin-left: -4rem; } - .nl6-m { margin-left: -8rem; } - .nl7-m { margin-left: -16rem; } - - .nr1-m { margin-right: -0.25rem; } - .nr2-m { margin-right: -0.5rem; } - .nr3-m { margin-right: -1rem; } - .nr4-m { margin-right: -2rem; } - .nr5-m { margin-right: -4rem; } - .nr6-m { margin-right: -8rem; } - .nr7-m { margin-right: -16rem; } - - .nb1-m { margin-bottom: -0.25rem; } - .nb2-m { margin-bottom: -0.5rem; } - .nb3-m { margin-bottom: -1rem; } - .nb4-m { margin-bottom: -2rem; } - .nb5-m { margin-bottom: -4rem; } - .nb6-m { margin-bottom: -8rem; } - .nb7-m { margin-bottom: -16rem; } - - .nt1-m { margin-top: -0.25rem; } - .nt2-m { margin-top: -0.5rem; } - .nt3-m { margin-top: -1rem; } - .nt4-m { margin-top: -2rem; } - .nt5-m { margin-top: -4rem; } - .nt6-m { margin-top: -8rem; } - .nt7-m { margin-top: -16rem; } - -} -@media screen and (min-width: 60em) { - .na1-l { margin: -0.25rem; } - .na2-l { margin: -0.5rem; } - .na3-l { margin: -1rem; } - .na4-l { margin: -2rem; } - .na5-l { margin: -4rem; } - .na6-l { margin: -8rem; } - .na7-l { margin: -16rem; } - - .nl1-l { margin-left: -0.25rem; } - .nl2-l { margin-left: -0.5rem; } - .nl3-l { margin-left: -1rem; } - .nl4-l { margin-left: -2rem; } - .nl5-l { margin-left: -4rem; } - .nl6-l { margin-left: -8rem; } - .nl7-l { margin-left: -16rem; } - - .nr1-l { margin-right: -0.25rem; } - .nr2-l { margin-right: -0.5rem; } - .nr3-l { margin-right: -1rem; } - .nr4-l { margin-right: -2rem; } - .nr5-l { margin-right: -4rem; } - .nr6-l { margin-right: -8rem; } - .nr7-l { margin-right: -16rem; } - - .nb1-l { margin-bottom: -0.25rem; } - .nb2-l { margin-bottom: -0.5rem; } - .nb3-l { margin-bottom: -1rem; } - .nb4-l { margin-bottom: -2rem; } - .nb5-l { margin-bottom: -4rem; } - .nb6-l { margin-bottom: -8rem; } - .nb7-l { margin-bottom: -16rem; } - - .nt1-l { margin-top: -0.25rem; } - .nt2-l { margin-top: -0.5rem; } - .nt3-l { margin-top: -1rem; } - .nt4-l { margin-top: -2rem; } - .nt5-l { margin-top: -4rem; } - .nt6-l { margin-top: -8rem; } - .nt7-l { margin-top: -16rem; } -} -/* - - TABLES - Docs: http://tachyons.io/docs/elements/tables/ - -*/ -.collapse { - border-collapse: collapse; - border-spacing: 0; -} -.striped--light-silver:nth-child(odd) { - background-color: #aaa; -} -.striped--moon-gray:nth-child(odd) { - background-color: #ccc; -} -.striped--light-gray:nth-child(odd) { - background-color: #eee; -} -.striped--near-white:nth-child(odd) { - background-color: #f4f4f4; -} -.stripe-light:nth-child(odd) { - background-color: rgba(255, 255, 255, .1); -} -.stripe-dark:nth-child(odd) { - background-color: rgba(0, 0, 0, .1); -} -/* - - TEXT DECORATION - Docs: http://tachyons.io/docs/typography/text-decoration/ - - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.strike { text-decoration: line-through; } -.underline { text-decoration: underline; } -.no-underline { text-decoration: none; } -@media screen and (min-width: 30em) { - .strike-ns { text-decoration: line-through; } - .underline-ns { text-decoration: underline; } - .no-underline-ns { text-decoration: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .strike-m { text-decoration: line-through; } - .underline-m { text-decoration: underline; } - .no-underline-m { text-decoration: none; } -} -@media screen and (min-width: 60em) { - .strike-l { text-decoration: line-through; } - .underline-l { text-decoration: underline; } - .no-underline-l { text-decoration: none; } -} -/* - - TEXT ALIGN - Docs: http://tachyons.io/docs/typography/text-align/ - - Base - t = text-align - - Modifiers - l = left - r = right - c = center - j = justify - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.tl { text-align: left; } -.tr { text-align: right; } -.tc { text-align: center; } -.tj { text-align: justify; } -@media screen and (min-width: 30em) { - .tl-ns { text-align: left; } - .tr-ns { text-align: right; } - .tc-ns { text-align: center; } - .tj-ns { text-align: justify; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .tl-m { text-align: left; } - .tr-m { text-align: right; } - .tc-m { text-align: center; } - .tj-m { text-align: justify; } -} -@media screen and (min-width: 60em) { - .tl-l { text-align: left; } - .tr-l { text-align: right; } - .tc-l { text-align: center; } - .tj-l { text-align: justify; } -} -/* - - TEXT TRANSFORM - Docs: http://tachyons.io/docs/typography/text-transform/ - - Base: - tt = text-transform - - Modifiers - c = capitalize - l = lowercase - u = uppercase - n = none - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ttc { text-transform: capitalize; } -.ttl { text-transform: lowercase; } -.ttu { text-transform: uppercase; } -.ttn { text-transform: none; } -@media screen and (min-width: 30em) { - .ttc-ns { text-transform: capitalize; } - .ttl-ns { text-transform: lowercase; } - .ttu-ns { text-transform: uppercase; } - .ttn-ns { text-transform: none; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ttc-m { text-transform: capitalize; } - .ttl-m { text-transform: lowercase; } - .ttu-m { text-transform: uppercase; } - .ttn-m { text-transform: none; } -} -@media screen and (min-width: 60em) { - .ttc-l { text-transform: capitalize; } - .ttl-l { text-transform: lowercase; } - .ttu-l { text-transform: uppercase; } - .ttn-l { text-transform: none; } -} -/* - - TYPE SCALE - Docs: http://tachyons.io/docs/typography/scale/ - - Base: - f = font-size - - Modifiers - 1 = 1st step in size scale - 2 = 2nd step in size scale - 3 = 3rd step in size scale - 4 = 4th step in size scale - 5 = 5th step in size scale - 6 = 6th step in size scale - 7 = 7th step in size scale - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large -*/ -/* - * For Hero/Marketing Titles - * - * These generally are too large for mobile - * so be careful using them on smaller screens. - * */ -.f-6, -.f-headline { - font-size: 6rem; -} -.f-5, -.f-subheadline { - font-size: 5rem; -} -/* Type Scale */ -.f1 { font-size: 3rem; } -.f2 { font-size: 2.25rem; } -.f3 { font-size: 1.5rem; } -.f4 { font-size: 1.25rem; } -.f5 { font-size: 1rem; } -.f6 { font-size: .875rem; } -.f7 { font-size: .75rem; } -/* Small and hard to read for many people so use with extreme caution */ -@media screen and (min-width: 30em){ - .f-6-ns, - .f-headline-ns { font-size: 6rem; } - .f-5-ns, - .f-subheadline-ns { font-size: 5rem; } - .f1-ns { font-size: 3rem; } - .f2-ns { font-size: 2.25rem; } - .f3-ns { font-size: 1.5rem; } - .f4-ns { font-size: 1.25rem; } - .f5-ns { font-size: 1rem; } - .f6-ns { font-size: .875rem; } - .f7-ns { font-size: .75rem; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .f-6-m, - .f-headline-m { font-size: 6rem; } - .f-5-m, - .f-subheadline-m { font-size: 5rem; } - .f1-m { font-size: 3rem; } - .f2-m { font-size: 2.25rem; } - .f3-m { font-size: 1.5rem; } - .f4-m { font-size: 1.25rem; } - .f5-m { font-size: 1rem; } - .f6-m { font-size: .875rem; } - .f7-m { font-size: .75rem; } -} -@media screen and (min-width: 60em) { - .f-6-l, - .f-headline-l { - font-size: 6rem; - } - .f-5-l, - .f-subheadline-l { - font-size: 5rem; - } - .f1-l { font-size: 3rem; } - .f2-l { font-size: 2.25rem; } - .f3-l { font-size: 1.5rem; } - .f4-l { font-size: 1.25rem; } - .f5-l { font-size: 1rem; } - .f6-l { font-size: .875rem; } - .f7-l { font-size: .75rem; } -} -/* - - TYPOGRAPHY - http://tachyons.io/docs/typography/measure/ - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Measure is limited to ~66 characters */ -.measure { - max-width: 30em; -} -/* Measure is limited to ~80 characters */ -.measure-wide { - max-width: 34em; -} -/* Measure is limited to ~45 characters */ -.measure-narrow { - max-width: 20em; -} -/* Book paragraph style - paragraphs are indented with no vertical spacing. */ -.indent { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; -} -.small-caps { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; -} -/* Combine this class with a width to truncate text (or just leave as is to truncate at width of containing element. */ -.truncate { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -@media screen and (min-width: 30em) { - .measure-ns { - max-width: 30em; - } - .measure-wide-ns { - max-width: 34em; - } - .measure-narrow-ns { - max-width: 20em; - } - .indent-ns { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-ns { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-ns { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .measure-m { - max-width: 30em; - } - .measure-wide-m { - max-width: 34em; - } - .measure-narrow-m { - max-width: 20em; - } - .indent-m { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-m { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-m { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -@media screen and (min-width: 60em) { - .measure-l { - max-width: 30em; - } - .measure-wide-l { - max-width: 34em; - } - .measure-narrow-l { - max-width: 20em; - } - .indent-l { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; - } - .small-caps-l { - -webkit-font-feature-settings: "c2sc"; - font-feature-settings: "c2sc"; - font-variant: small-caps; - } - .truncate-l { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} -/* - - UTILITIES - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* Equivalent to .overflow-y-scroll */ -.overflow-container { - overflow-y: scroll; -} -.center { - margin-right: auto; - margin-left: auto; -} -.mr-auto { margin-right: auto; } -.ml-auto { margin-left: auto; } -@media screen and (min-width: 30em){ - .center-ns { - margin-right: auto; - margin-left: auto; - } - .mr-auto-ns { margin-right: auto; } - .ml-auto-ns { margin-left: auto; } -} -@media screen and (min-width: 30em) and (max-width: 60em){ - .center-m { - margin-right: auto; - margin-left: auto; - } - .mr-auto-m { margin-right: auto; } - .ml-auto-m { margin-left: auto; } -} -@media screen and (min-width: 60em){ - .center-l { - margin-right: auto; - margin-left: auto; - } - .mr-auto-l { margin-right: auto; } - .ml-auto-l { margin-left: auto; } -} -/* - - VISIBILITY - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -/* - Text that is hidden but accessible - Ref: http://snook.ca/archives/html_and_css/hiding-content-for-accessibility -*/ -.clip { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); -} -@media screen and (min-width: 30em) { - .clip-ns { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .clip-m { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -@media screen and (min-width: 60em) { - .clip-l { - position: fixed !important; - _position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ - clip: rect(1px, 1px, 1px, 1px); - } -} -/* - - WHITE SPACE - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.ws-normal { white-space: normal; } -.nowrap { white-space: nowrap; } -.pre { white-space: pre; } -@media screen and (min-width: 30em) { - .ws-normal-ns { white-space: normal; } - .nowrap-ns { white-space: nowrap; } - .pre-ns { white-space: pre; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .ws-normal-m { white-space: normal; } - .nowrap-m { white-space: nowrap; } - .pre-m { white-space: pre; } -} -@media screen and (min-width: 60em) { - .ws-normal-l { white-space: normal; } - .nowrap-l { white-space: nowrap; } - .pre-l { white-space: pre; } -} -/* - - VERTICAL ALIGN - - Media Query Extensions: - -ns = not-small - -m = medium - -l = large - -*/ -.v-base { vertical-align: baseline; } -.v-mid { vertical-align: middle; } -.v-top { vertical-align: top; } -.v-btm { vertical-align: bottom; } -@media screen and (min-width: 30em) { - .v-base-ns { vertical-align: baseline; } - .v-mid-ns { vertical-align: middle; } - .v-top-ns { vertical-align: top; } - .v-btm-ns { vertical-align: bottom; } -} -@media screen and (min-width: 30em) and (max-width: 60em) { - .v-base-m { vertical-align: baseline; } - .v-mid-m { vertical-align: middle; } - .v-top-m { vertical-align: top; } - .v-btm-m { vertical-align: bottom; } -} -@media screen and (min-width: 60em) { - .v-base-l { vertical-align: baseline; } - .v-mid-l { vertical-align: middle; } - .v-top-l { vertical-align: top; } - .v-btm-l { vertical-align: bottom; } -} -/* - - HOVER EFFECTS - Docs: http://tachyons.io/docs/themes/hovers/ - - - Dim - - Glow - - Hide Child - - Underline text - - Grow - - Pointer - - Shadow - -*/ -/* - - Dim element on hover by adding the dim class. - -*/ -.dim { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.dim:hover, -.dim:focus { - opacity: .5; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.dim:active { - opacity: .8; -webkit-transition: opacity .15s ease-out; transition: opacity .15s ease-out; -} -/* - - Animate opacity to 100% on hover by adding the glow class. - -*/ -.glow { - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.glow:hover, -.glow:focus { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -/* - - Hide child & reveal on hover: - - Put the hide-child class on a parent element and any nested element with the - child class will be hidden and displayed on hover or focus. - -
-
Hidden until hover or focus
-
Hidden until hover or focus
-
Hidden until hover or focus
-
Hidden until hover or focus
-
-*/ -.hide-child .child { - opacity: 0; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.hide-child:hover .child, -.hide-child:focus .child, -.hide-child:active .child { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.underline-hover:hover, -.underline-hover:focus { - text-decoration: underline; -} -/* Can combine this with overflow-hidden to make background images grow on hover - * even if you are using background-size: cover */ -.grow { - -moz-osx-font-smoothing: grayscale; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-transition: -webkit-transform 0.25s ease-out; - transition: -webkit-transform 0.25s ease-out; - transition: transform 0.25s ease-out; - transition: transform 0.25s ease-out, -webkit-transform 0.25s ease-out; -} -.grow:hover, -.grow:focus { - -webkit-transform: scale(1.05); - transform: scale(1.05); -} -.grow:active { - -webkit-transform: scale(.90); - transform: scale(.90); -} -.grow-large { - -moz-osx-font-smoothing: grayscale; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-transition: -webkit-transform .25s ease-in-out; - transition: -webkit-transform .25s ease-in-out; - transition: transform .25s ease-in-out; - transition: transform .25s ease-in-out, -webkit-transform .25s ease-in-out; -} -.grow-large:hover, -.grow-large:focus { - -webkit-transform: scale(1.2); - transform: scale(1.2); -} -.grow-large:active { - -webkit-transform: scale(.95); - transform: scale(.95); -} -/* Add pointer on hover */ -.pointer:hover { - cursor: pointer; -} -/* - Add shadow on hover. - - Performant box-shadow animation pattern from - http://tobiasahlin.com/blog/how-to-animate-box-shadow/ -*/ -.shadow-hover { - cursor: pointer; - position: relative; - -webkit-transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); - transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); -} -.shadow-hover::after { - content: ''; - -webkit-box-shadow: 0px 0px 16px 2px rgba(0, 0, 0, .2); - box-shadow: 0px 0px 16px 2px rgba(0, 0, 0, .2); - border-radius: inherit; - opacity: 0; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - -webkit-transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); - transition: opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1); -} -.shadow-hover:hover::after, -.shadow-hover:focus::after { - opacity: 1; -} -/* Combine with classes in skins and skins-pseudo for - * many different transition possibilities. */ -.bg-animate, -.bg-animate:hover, -.bg-animate:focus { - -webkit-transition: background-color .15s ease-in-out; - transition: background-color .15s ease-in-out; -} -/* - - Z-INDEX - - Base - z = z-index - - Modifiers - -0 = literal value 0 - -1 = literal value 1 - -2 = literal value 2 - -3 = literal value 3 - -4 = literal value 4 - -5 = literal value 5 - -999 = literal value 999 - -9999 = literal value 9999 - - -max = largest accepted z-index value as integer - - -inherit = string value inherit - -initial = string value initial - -unset = string value unset - - MDN: https://developer.mozilla.org/en/docs/Web/CSS/z-index - Spec: http://www.w3.org/TR/CSS2/zindex.html - Articles: - https://philipwalton.com/articles/what-no-one-told-you-about-z-index/ - - Tips on extending: - There might be a time worth using negative z-index values. - Or if you are using tachyons with another project, you might need to - adjust these values to suit your needs. - -*/ -.z-0 { z-index: 0; } -.z-1 { z-index: 1; } -.z-2 { z-index: 2; } -.z-3 { z-index: 3; } -.z-4 { z-index: 4; } -.z-5 { z-index: 5; } -.z-999 { z-index: 999; } -.z-9999 { z-index: 9999; } -.z-max { - z-index: 2147483647; -} -.z-inherit { z-index: inherit; } -.z-initial { z-index: auto; z-index: initial; } -.z-unset { z-index: unset; } -/* - - NESTED - Tachyons module for styling nested elements - that are generated by a cms. - -*/ -.nested-copy-line-height p, -.nested-copy-line-height ul, -.nested-copy-line-height ol { - line-height: 1.5; -} -.nested-headline-line-height h1, -.nested-headline-line-height h2, -.nested-headline-line-height h3, -.nested-headline-line-height h4, -.nested-headline-line-height h5, -.nested-headline-line-height h6 { - line-height: 1.25; -} -.nested-list-reset ul, -.nested-list-reset ol { - padding-left: 0; - margin-left: 0; - list-style-type: none; -} -.nested-copy-indent p+p { - text-indent: 1em; - margin-top: 0; - margin-bottom: 0; -} -.nested-copy-separator p+p { - margin-top: 1.5em; -} -.nested-img img { - width: 100%; - max-width: 100%; - display: block; -} -.nested-links a { - color: #0594CB; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -.nested-links a:hover, -.nested-links a:focus { - color: #96ccff; - -webkit-transition: color .15s ease-in; - transition: color .15s ease-in; -} -/*@import 'tachyons/src/_styles';*/ -/* Variables */ -/* Importing here will allow you to override any variables in the modules */ -/* - - Tachyons - COLOR VARIABLES - - Grayscale - - Solids - - Transparencies - Colors - -*/ -/* - - CUSTOM MEDIA QUERIES - - Media query values can be changed to fit your own content. - There are no magic bullets when it comes to media query width values. - They should be declared in em units - and they should be set to meet - the needs of your content. You can also add additional media queries, - or remove some of the existing ones. - - These media queries can be referenced like so: - - @media (--breakpoint-not-small) { - .medium-and-larger-specific-style { - background-color: red; - } - } - - @media (--breakpoint-medium) { - .medium-screen-specific-style { - background-color: red; - } - } - - @media (--breakpoint-large) { - .large-and-larger-screen-specific-style { - background-color: red; - } - } - -*/ -/* Media Queries */ -/* Debugging */ -/*@import 'tachyons/src/_debug-children'; -@import 'tachyons/src/_debug-grid';*/ -/* Uncomment out the line below to help debug layout issues */ -/* @import 'tachyons/src/_debug'; */ -/* purgecss start ignore */ -.header-link:after { - position: relative; - left: 0.5em; - opacity: 0; - font-size: 0.8em; - -moz-transition: opacity 0.2s ease-in-out 0.1s; - -ms-transition: opacity 0.2s ease-in-out 0.1s; -} -h2:hover .header-link, -h3:hover .header-link, -h4:hover .header-link, -h5:hover .header-link, -h6:hover .header-link { - opacity: 1; -} -.animated { - -webkit-animation-duration: .5s; - animation-duration: .5s; - -webkit-animation-fill-mode: forwards; - animation-fill-mode: forwards; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; -} -@-webkit-keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -.fadeIn { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; -} -.animated-delay-1 { - -webkit-animation-delay: 0.5s; - animation-delay: 0.5s; -} -.note, -.warning { - - border-left-width: 4px; - border-left-style: solid; - position: relative; - border-color: #0594CB; - - display: block; -} -.note #exclamation-icon, -.warning #exclamation-icon { - - fill: #0594CB; - position: absolute; - top: 35%; - left: -12px; - /*background-color: white;*/ -} -.admonition-content { - display: block; - margin: 0px; - padding: .125em 1em; - /*margin-left: 1em;*/ - margin-top: 2em; - margin-bottom: 2em; - overflow-x: auto; - /*font-size: .9375em;*/ - background-color: rgba(0, 0, 0, .05); - } -.hide-child-menu .child-menu { - display: none; - } -.hide-child-menu:hover .child-menu, - .hide-child-menu:focus .child-menu, - .hide-child-menu:active .child-menu { - display: block; - } -/*documentation-copy headings exaggerate spacing and size to chunk content */ -.documentation-copy h2 { - margin-top: 3em - } -.documentation-copy h2.minor { - font-size: inherit; - margin-top: inherit; - border-bottom: none; -} -.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;visibility:visible!important} -.searchbox .algolia-autocomplete{display:block;width:100%;height:100%} -.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative} -.searchbox__input{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:background .4s ease,-webkit-box-shadow .4s ease;transition:background .4s ease,-webkit-box-shadow .4s ease;transition:box-shadow .4s ease,background .4s ease;transition:box-shadow .4s ease,background .4s ease,-webkit-box-shadow .4s ease;border:0;border-radius:16px;-webkit-box-shadow:inset 0 0 0 1px #ccc;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none} -.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none} -.searchbox__input:hover{-webkit-box-shadow:inset 0 0 0 1px #b3b3b3;box-shadow:inset 0 0 0 1px #b3b3b3} -.searchbox__input:active,.searchbox__input:focus{outline:0;-webkit-box-shadow:inset 0 0 0 1px #aaa;box-shadow:inset 0 0 0 1px #aaa;background:#fff} -.searchbox__input::-webkit-input-placeholder{color:#aaa} -.searchbox__input:-ms-input-placeholder{color:#aaa} -.searchbox__input::-ms-input-placeholder{color:#aaa} -.searchbox__input::placeholder{color:#aaa} -.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69, 142, 225, 0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0} -.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""} -.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer} -.searchbox__submit:focus{outline:0} -.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96} -.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0, 0, 0, .5)} -.searchbox__reset.hide{display:none} -.searchbox__reset:focus{outline:0} -.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px} -.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s} -@-webkit-keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}} -@keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}} -.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important} -.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px} -.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important} -.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px} -.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;-webkit-box-shadow:0 1px 0 0 rgba(0, 0, 0, .2),0 2px 3px 0 rgba(0, 0, 0, .1);box-shadow:0 1px 0 0 rgba(0, 0, 0, .2),0 2px 3px 0 rgba(0, 0, 0, .1)} -.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer} -.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69, 142, 225, .05)} -.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px} -.algolia-autocomplete .ds-dropdown-menu *{-webkit-box-sizing:border-box;box-sizing:border-box} -.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden} -.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143, 187, 237, .1);padding:.1em .05em} -.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;-webkit-box-shadow:inset 0 -2px 0 0 rgba(69, 142, 225, .8);box-shadow:inset 0 -2px 0 0 rgba(69, 142, 225, .8);color:inherit} -.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer} -.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px} -.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d} -.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0} -.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none} -.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700} -.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d} -.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em} -.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none} -.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace} -.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none} -.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block} -@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}} -@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}} -.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8} -.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;-webkit-box-shadow:none;box-shadow:none} -.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0} -.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} -/* These styles enhance the home page carousel, located here: themes/gohugoioTheme/layouts/partials/home-page-sections/showcase.html */ -.overflow-x-scroll{ - -webkit-overflow-scrolling: touch; -} -.row { - -webkit-transition: 450ms -webkit-transform; - transition: 450ms -webkit-transform; - transition: 450ms transform; - transition: 450ms transform, 450ms -webkit-transform; - font-size: 0; -} -.tile { - -webkit-transition: 450ms all; - transition: 450ms all; -} -.details { - background: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, .9)), to(rgba(0, 0, 0, 0))); - background: linear-gradient(to top, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, 0) 100%); - -webkit-transition: 450ms opacity; - transition: 450ms opacity; -} -.tile:hover .details { - opacity: 1; -} -.row:hover .tile { - opacity: 0.3; -} -.row:hover .tile:hover { - opacity: 1; -} -.chroma .lntable pre { - padding: 0; - margin: 0; - border: 0; -} -.chroma .lntable pre code { - padding: 0; - margin: 0; -} -code { - padding: 0.2em; - margin: 0; - font-size: 85%; - background-color: rgba(27, 31, 35, .05); - border-radius: 3px; -} -pre code { - display: block; - padding: 1.5em 1.5em; - font-size: .875rem; - line-height: 2; - overflow-x: auto; -} -pre { - background-color: #fff; - color: #333; - white-space: pre; - -webkit-hyphens: none; - -ms-hyphens: none; - hyphens: none; - position: relative; - border-width: 1px; - border-color: #ccc; - border-style: solid; -} -/* The Pygments highlighter comes with its own styles. */ -.highlight pre { - background-color: inherit; - color: inherit; - padding: 0.5em; - font-size: .875rem; -} -/*We are adding the copy button content here so we can change it with javascript. See the "Clipboard scripts"*/ -.copy:after { - content: "Copy" -} -.copied:after { - content: "Copied" -} -@media screen and (min-width: 60em) { - .full-width - { - /*width: 100vw; - position: relative; - left: 50%; - right: 50%; - margin-left: -50vw; - margin-right: -50vw;*/ - /*width: 60vw;*/ - /*position: relative; - left: 50%; - right: 50%;*/ - /*margin-left: -30vw;*/ - margin-right: -30vw; - max-width: 100vw; - } -} -.code-block .line-numbers-rows { - background: #2f3a46; - border: none; - bottom: -50px; - color: #98a4b3; - left: -178px; - padding: 50px 0; - top: -50px; - width: 138px -} -.code-block .line-numbers-rows>span:before { - color: inherit; - padding-right: 30px -} -.tab-button{ - margin-bottom:1px; - position: relative; - z-index: 1; - color:#333; - border-color:#ccc; - outline: none; - background-color:white; -} -.tab-pane code{ - background:#f1f2f2; - border-radius:0; -} -.tab-pane .chroma{ - background:none; - padding:0; -} -.tab-button.active{ - border-bottom-color:#f1f2f2; - background-color: #f1f2f2; -} -.tab-content .tab-pane{ - display: none; -} -.tab-content .tab-pane.active{ - display: block; -} -/* Treatment of copy buttons inside a tab module */ -.tab-content .copy, .tab-content .copied{ - display: none; -} -.tab-content .tab-pane.active + .copy, .tab-content .tab-pane.active + .copied{ - display: block; -} -.primary-color {color: #0594CB} -.bg-primary-color {background-color: #0594CB} -.hover-bg-primary-color:hover {background-color: #0594CB} -.primary-color-dark {color: #0A1922} -.bg-primary-color-dark {background-color: #0A1922} -.hover-bg-primary-color-dark:hover {background-color: #0A1922} -.primary-color-light {color: #f9f9f9} -.bg-primary-color-light {background-color: #f9f9f9} -.hover-bg-primary-color-light:hover {background-color: #f9f9f9} -.accent-color {color: #EBB951} -.bg-accent-color {background-color: #EBB951} -.hover-bg-accent-color:hover {background-color: #EBB951} -.accent-color-light {color: #FF4088} -.hover-accent-color-light:hover {color: #FF4088} -.bg-accent-color-light {background-color: #FF4088} -.hover-bg-accent-color-light:hover {background-color: #FF4088} -.accent-color-dark {color: #33ba91} -.bg-accent-color-dark {background-color: #33ba91} -.hover-bg-accent-color-dark:hover {background-color: #33ba91} -.text-color-primary {color: #373737} -.text-on-primary-color {color: #fff} -.text-color-secondary {color: #ccc} -.text-color-disabled {color: #F7f7f7} -.divider-color {color: #f6f6f6} -.warn-color {color: red} -.nested-links a { - color: #0594CB; - text-decoration: none; - -} -.column-count-2 {-webkit-column-count: 1;column-count: 1} -.column-gap-1 {-webkit-column-gap: 0;column-gap: 0} -.break-inside-avoid {-webkit-column-break-inside: auto;break-inside: auto} -@media screen and (min-width: 60em) { - .column-count-3-l {-webkit-column-count: 3;column-count: 3} - .column-count-2-l {-webkit-column-count: 2;column-count: 2} - .column-gap-1-l {-webkit-column-gap: 1;column-gap: 1} - .break-inside-avoid-l {-webkit-column-break-inside: avoid;break-inside: avoid} -} -.prose ul, .prose ol { - margin-bottom: 2em; -} -.prose ul li, .prose ol li { - margin-bottom: .5em; -} -.prose li:hover { - background-color: #eee -} -.prose ::selection { - background: #0594CB; /* WebKit/Blink Browsers */ - color: white; -} -.prose-glossary h3 { - margin-top: 0; - font-size: 1.125rem; -} -.prose-glossary h3:first-of-type { - margin-top: 3em; -} -.prose-glossary h3 ~ p { - margin: 0.5em 0 2em 0; -} -body { - -line-height: 1.45; - -} -p {margin-bottom: 1.3em;} -h1, h2, h3, h4 { -margin: 1.414em 0 0.5em; - -line-height: 1.2; -} -h1 { -margin-top: 0; -font-size: 2.441em; -} -h2 {font-size: 1.953em;} -h3 {font-size: 1.563em;} -h4 {font-size: 1.25em;} -small, .font_small {font-size: 0.8em;} -.prose table { - width: 100%; - margin-bottom: 3em; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; - border: 1px solid #eee - -} -.prose table th { - background-color: #0594CB; - border-bottom: 1px solid #0594CB; - color: white; - font-weight: 400; - text-align: left; - padding: .375em .5em; -} -.prose table td, .prose table tc { - padding: .75em .5em; - text-align: left; - border-right: 1px solid #eee; -} -.prose table tr:nth-child(even) { - background-color: #eee; -} -dl dt { - font-weight: bold; - font-size: 1.125rem; -} -dd { - margin: .5em 0 2em 0; - padding: 0; -} -.f2-fluid { - font-size: 2.25rem; -} -@media screen and (min-width: 60em) { - .f2-fluid { - font-size: 1.25rem; - font-size: calc(0.70833rem + 0.83333vw); - } -} -/* From https://www.cssfontstack.com */ -code, .code, pre code, .highlight pre { - font-family: 'inconsolata',Menlo,Monaco,'Courier New',monospace; -} -.sans-serif { - font-family: 'Muli', - avenir, - 'helvetica neue', helvetica, - ubuntu, - roboto, noto, - 'segoe ui', arial, - sans-serif; -} -.serif { - font-family: Palatino,"Palatino Linotype","Palatino LT STD","Book Antiqua",Georgia,serif; -} -/* Monospaced Typefaces (for code) */ -.courier { - font-family: 'Courier Next', - courier, - monospace; -} -/* Sans-Serif Typefaces */ -.helvetica { - font-family: 'helvetica neue', helvetica, - sans-serif; -} -.avenir { - font-family: 'avenir next', avenir, - sans-serif; -} -/* Serif Typefaces */ -.athelas { - font-family: athelas, - georgia, - serif; -} -.georgia { - font-family: georgia, - serif; -} -.times { - font-family: times, - serif; -} -.bodoni { - font-family: "Bodoni MT", - serif; -} -.calisto { - font-family: "Calisto MT", - serif; -} -.garamond { - font-family: garamond, - serif; -} -.baskerville { - font-family: baskerville, - serif; -} -/* pagination.html: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/template_embedded.go#L117 */ -.pagination { - margin: 3rem 0; -} -.pagination li { - display: inline-block; - margin-right: .375rem; - font-size: .875rem; - margin-bottom: 2.5em; -} -.pagination li a { - padding: .5rem .625rem; - background-color: white; - color: #333; - border: 1px solid #ddd; - border-radius: 3px; - text-decoration: none; -} -.pagination li.disabled { - display: none; -} -.pagination li.active a:link, -.pagination li.active a:active, -.pagination li.active a:visited { - background-color: #ddd; -} -/* Hides non-meaningful TOC items*/ -#TableOfContents ul li ul li ul li{ - display: none; - } -#TableOfContents ul li { - color: black; - display: block; - margin-bottom: .375em; - line-height: 1.375; -} -#TableOfContents ul li a{ - width: 100%; - padding: .25em .375em; - margin-left: -.375em; - -} -#TableOfContents ul li a:hover { - background-color: #999; - color: white; - -} -.no-js .needs-js { - opacity: 0 -} -.js .needs-js { - opacity: 1; - -webkit-transition: opacity .15s ease-in; - transition: opacity .15s ease-in; -} -.facebook, -.twitter, -.instagram, -.youtube { - fill: #bababa; -} -.facebook:hover { - fill: #3b5998; -} -.twitter { - fill: #55acee; -} -.twitter:hover { - fill: #bababa; -} -.instagram:hover { - fill: #e95950; -} -.youtube:hover { - fill: #bb0000; -} -.mstdn { - display: inline-block; - background-color: #282c37; - color: #d9e1e8; - text-decoration: none; - padding: 4px 10px 4px 30px; - border-radius: 4px; - font-size: 16px; - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2261.076954mm%22%20height%3D%2265.47831mm%22%20viewBox%3D%220%200%20216.4144%20232.00976%22%3E%3Cpath%20d%3D%22M211.80734%20139.0875c-3.18125%2016.36625-28.4925%2034.2775-57.5625%2037.74875-15.15875%201.80875-30.08375%203.47125-45.99875%202.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125%200%202.53375.15625%204.94625.46875%207.2025%203.38375%2025.68625%2025.47%2027.225%2046.39125%2027.9425%2021.11625.7225%2039.91875-5.20625%2039.91875-5.20625l.8675%2019.09s-14.77%207.93125-41.08125%209.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234%20213.82%201.40609%20165.31125.20859%20116.09125c-.365-14.61375-.14-28.39375-.14-39.91875%200-50.33%2032.97625-65.0825%2032.97625-65.0825C49.67234%203.45375%2078.20359.2425%20107.86484%200h.72875c29.66125.2425%2058.21125%203.45375%2074.8375%2011.09%200%200%2032.975%2014.7525%2032.975%2065.0825%200%200%20.41375%2037.13375-4.59875%2062.915%22%20fill%3D%22%233088d4%22%2F%3E%3Cpath%20d%3D%22M177.50984%2080.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025%200-17.4175%207.5075-17.4175%2022.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375%200-15.74%206.32875-15.74%2018.7975v59.15H38.90484V80.077c0-12.455%203.17125-22.3525%209.54125-29.675%206.56875-7.3225%2015.17125-11.07625%2025.85-11.07625%2012.355%200%2021.71125%204.74875%2027.8975%2014.2475l6.01375%2010.08125%206.015-10.08125c6.185-9.49875%2015.54125-14.2475%2027.8975-14.2475%2010.6775%200%2019.28%203.75375%2025.85%2011.07625%206.36875%207.3225%209.54%2017.22%209.54%2029.675%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"); - background-size: 16px; - background-repeat: no-repeat; - background-position: top 50% left 8px; - -webkit-transition: all 0.5s; - transition: all 0.5s; -} -.mstdn:hover { - background-color: #484c56; -} -.mstdn > span { - color: #9baec8; - font-size: 12px; - padding-left: 3px; -} -.mstdn > span:before { - content: "@"; -} -@media (min-width: 75em) { - - [data-scrolldir="down"] .sticky { - position: fixed; - top:100px; - right:0; - } - - [data-scrolldir="up"] .sticky { - position: fixed; - top:100px; - right:0; - } -} -#right-sidebar { - scrollbar-width: none; /* hide scrollbar: Firefox */ - -ms-overflow-style: none; /* hide scrollbar: Internet Explorer 10+ */ - height: calc(100vh - 9rem); - overflow-y: auto; -} -#right-sidebar::-webkit-scrollbar { /* hide scrollbar: WebKit */ - width: 0; - height: 0; -} -.fill-current { fill: currentColor; } -/* Background */ -.chroma { background-color: #ffffff } -/* Error */ -.chroma .err { color: #a61717; background-color: #e3d2d2 } -/* LineTableTD */ -.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } -/* LineTable */ -.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; } -/* LineHighlight */ -.chroma .hl { display: block; width: 100%;background-color: #ffffcc } -/* LineNumbersTable */ -.chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* LineNumbers */ -.chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* Keyword */ -.chroma .k { font-weight: bold } -/* KeywordConstant */ -.chroma .kc { font-weight: bold } -/* KeywordDeclaration */ -.chroma .kd { font-weight: bold } -/* KeywordNamespace */ -.chroma .kn { font-weight: bold } -/* KeywordPseudo */ -.chroma .kp { font-weight: bold } -/* KeywordReserved */ -.chroma .kr { font-weight: bold } -/* KeywordType */ -.chroma .kt { color: #445588; font-weight: bold } -/* NameAttribute */ -.chroma .na { color: #008080 } -/* NameBuiltin */ -.chroma .nb { color: #999999 } -/* NameClass */ -.chroma .nc { color: #445588; font-weight: bold } -/* NameConstant */ -.chroma .no { color: #008080 } -/* NameEntity */ -.chroma .ni { color: #800080 } -/* NameException */ -.chroma .ne { color: #990000; font-weight: bold } -/* NameFunction */ -.chroma .nf { color: #990000; font-weight: bold } -/* NameNamespace */ -.chroma .nn { color: #555555 } -/* NameTag */ -.chroma .nt { color: #000080 } -/* NameVariable */ -.chroma .nv { color: #008080 } -/* LiteralString */ -.chroma .s { color: #bb8844 } -/* LiteralStringAffix */ -.chroma .sa { color: #bb8844 } -/* LiteralStringBacktick */ -.chroma .sb { color: #bb8844 } -/* LiteralStringChar */ -.chroma .sc { color: #bb8844 } -/* LiteralStringDelimiter */ -.chroma .dl { color: #bb8844 } -/* LiteralStringDoc */ -.chroma .sd { color: #bb8844 } -/* LiteralStringDouble */ -.chroma .s2 { color: #bb8844 } -/* LiteralStringEscape */ -.chroma .se { color: #bb8844 } -/* LiteralStringHeredoc */ -.chroma .sh { color: #bb8844 } -/* LiteralStringInterpol */ -.chroma .si { color: #bb8844 } -/* LiteralStringOther */ -.chroma .sx { color: #bb8844 } -/* LiteralStringRegex */ -.chroma .sr { color: #808000 } -/* LiteralStringSingle */ -.chroma .s1 { color: #bb8844 } -/* LiteralStringSymbol */ -.chroma .ss { color: #bb8844 } -/* LiteralNumber */ -.chroma .m { color: #009999 } -/* LiteralNumberBin */ -.chroma .mb { color: #009999 } -/* LiteralNumberFloat */ -.chroma .mf { color: #009999 } -/* LiteralNumberHex */ -.chroma .mh { color: #009999 } -/* LiteralNumberInteger */ -.chroma .mi { color: #009999 } -/* LiteralNumberIntegerLong */ -.chroma .il { color: #009999 } -/* LiteralNumberOct */ -.chroma .mo { color: #009999 } -/* Operator */ -.chroma .o { font-weight: bold } -/* OperatorWord */ -.chroma .ow { font-weight: bold } -/* Comment */ -.chroma .c { color: #999988; font-style: italic } -/* CommentHashbang */ -.chroma .ch { color: #999988; font-style: italic } -/* CommentMultiline */ -.chroma .cm { color: #999988; font-style: italic } -/* CommentSingle */ -.chroma .c1 { color: #999988; font-style: italic } -/* CommentSpecial */ -.chroma .cs { color: #999999; font-weight: bold; font-style: italic } -/* CommentPreproc */ -.chroma .cp { color: #999999; font-weight: bold } -/* CommentPreprocFile */ -.chroma .cpf { color: #999999; font-weight: bold } -/* GenericDeleted */ -.chroma .gd { color: #000000; background-color: #ffdddd } -/* GenericEmph */ -.chroma .ge { font-style: italic } -/* GenericError */ -.chroma .gr { color: #aa0000 } -/* GenericHeading */ -.chroma .gh { color: #999999 } -/* GenericInserted */ -.chroma .gi { color: #000000; background-color: #ddffdd } -/* GenericOutput */ -.chroma .go { color: #888888 } -/* GenericPrompt */ -.chroma .gp { color: #555555 } -/* GenericStrong */ -.chroma .gs { font-weight: bold } -/* GenericSubheading */ -.chroma .gu { color: #aaaaaa } -/* GenericTraceback */ -.chroma .gt { color: #aa0000 } -/* TextWhitespace */ -.chroma .w { color: #bbbbbb } -@media print { - #page-footer, - body > footer, - body > nav { - display: none; - } -} -/* -Make h6 elements behave like dt elements. Initially implemented to support -linkable glossary entries. - -Yes, it's a hack. That's why it's in the shame file. -*/ -h6 { - margin-top: 0; - margin-bottom: 0; - font-size: 1.125rem; -} -h6:first-of-type { - margin-top: 3em; -} -h6 ~ p { - margin: 0.5em 0 2em 0; -} -.nested-blockquote blockquote { - border-left: 4px solid #0594CB; - padding-left: 1em; -} -.mw-90 { - max-width:90%; -} -/* purgecss end ignore */ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js deleted file mode 100644 index a3e1801f8..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/assets/output/js/app.js +++ /dev/null @@ -1,17 +0,0 @@ -!function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=1)}([function(t,e,n){!function(e,n){var r=function(t,e,n){"use strict";var r,i;if(function(){var e,n={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in i=t.lazySizesConfig||t.lazysizesConfig||{},n)e in i||(i[e]=n[e])}(),!e||!e.getElementsByClassName)return{init:function(){},cfg:i,noSupport:!0};var o=e.documentElement,s=t.HTMLPictureElement,a=t.addEventListener.bind(t),u=t.setTimeout,c=t.requestAnimationFrame||u,l=t.requestIdleCallback,h=/^picture$/i,f=["load","error","lazyincluded","_lazyloaded"],d={},p=Array.prototype.forEach,g=function(t,e){return d[e]||(d[e]=new RegExp("(\\s|^)"+e+"(\\s|$)")),d[e].test(t.getAttribute("class")||"")&&d[e]},m=function(t,e){g(t,e)||t.setAttribute("class",(t.getAttribute("class")||"").trim()+" "+e)},y=function(t,e){var n;(n=g(t,e))&&t.setAttribute("class",(t.getAttribute("class")||"").replace(n," "))},v=function(t,e,n){var r=n?"addEventListener":"removeEventListener";n&&v(t,e),f.forEach((function(n){t[r](n,e)}))},b=function(t,n,i,o,s){var a=e.createEvent("Event");return i||(i={}),i.instance=r,a.initEvent(n,!o,!s),a.detail=i,t.dispatchEvent(a),a},w=function(e,n){var r;!s&&(r=t.picturefill||i.pf)?(n&&n.src&&!e.getAttribute("srcset")&&e.setAttribute("srcset",n.src),r({reevaluate:!0,elements:[e]})):n&&n.src&&(e.src=n.src)},_=function(t,e){return(getComputedStyle(t,null)||{})[e]},x=function(t,e,n){for(n=n||t.offsetWidth;n0)&&"visible"!=_(i,"overflow")&&(r=i.getBoundingClientRect(),s=z>r.left&&Fr.top-1&&H500&&o.clientWidth>500?500:370:i.expand,r._defEx=d,p=d*i.expFactor,g=i.hFac,U=null,W2&&D>2&&!e.hidden?(W=p,X=0):W=D>1&&X>1&&Q<6?d:0),f!==c&&($=innerWidth+c*g,M=innerHeight+c,l=-1*c,f=c),s=m[n].getBoundingClientRect(),(B=s.bottom)>=l&&(H=s.top)<=M&&(z=s.right)>=l*g&&(F=s.left)<=$&&(B||z||F||H)&&(i.loadHidden||Z(m[n]))&&(R&&Q<3&&!h&&(D<3||X<4)||Y(m[n],c))){if(at(m[n]),u=!0,Q>9)break}else!u&&R&&!a&&Q<4&&X<4&&D>2&&(L[0]||i.preloadAfterLoad)&&(L[0]||!h&&(B||z||F||H||"auto"!=m[n].getAttribute(i.sizesAttr)))&&(a=L[0]||m[n]);a&&!u&&at(a)}},et=function(t){var e,r=0,o=i.throttleDelay,s=i.ricTimeout,a=function(){e=!1,r=n.now(),t()},c=l&&s>49?function(){l(a,{timeout:s}),s!==i.ricTimeout&&(s=i.ricTimeout)}:C((function(){u(a)}),!0);return function(t){var i;(t=!0===t)&&(s=33),e||(e=!0,(i=o-(n.now()-r))<0&&(i=0),t||i<9?c():u(c,i))}}(tt),nt=function(t){var e=t.target;e._lazyCache?delete e._lazyCache:(G(t),m(e,i.loadedClass),y(e,i.loadingClass),v(e,it),b(e,"lazyloaded"))},rt=C(nt),it=function(t){rt({target:t.target})},ot=function(t){var e,n=t.getAttribute(i.srcsetAttr);(e=i.customMedia[t.getAttribute("data-media")||t.getAttribute("media")])&&t.setAttribute("media",e),n&&t.setAttribute("srcset",n)},st=C((function(t,e,n,r,o){var s,a,c,l,f,d;(f=b(t,"lazybeforeunveil",e)).defaultPrevented||(r&&(n?m(t,i.autosizesClass):t.setAttribute("sizes",r)),a=t.getAttribute(i.srcsetAttr),s=t.getAttribute(i.srcAttr),o&&(l=(c=t.parentNode)&&h.test(c.nodeName||"")),d=e.firesLoad||"src"in t&&(a||s||l),f={target:t},m(t,i.loadingClass),d&&(clearTimeout(P),P=u(G,2500),v(t,it,!0)),l&&p.call(c.getElementsByTagName("source"),ot),a?t.setAttribute("srcset",a):s&&!l&&(K.test(t.nodeName)?function(t,e){var n=t.getAttribute("data-load-mode")||i.iframeLoadMode;0==n?t.contentWindow.location.replace(e):1==n&&(t.src=e)}(t,s):t.src=s),o&&(a||l)&&w(t,{src:s})),t._lazyRace&&delete t._lazyRace,y(t,i.lazyClass),S((function(){var e=t.complete&&t.naturalWidth>1;d&&!e||(e&&m(t,i.fastLoadedClass),nt(f),t._lazyCache=!0,u((function(){"_lazyCache"in t&&delete t._lazyCache}),9)),"lazy"==t.loading&&Q--}),!0)})),at=function(t){if(!t._lazyRace){var e,n=V.test(t.nodeName),r=n&&(t.getAttribute(i.sizesAttr)||t.getAttribute("sizes")),o="auto"==r;(!o&&R||!n||!t.getAttribute("src")&&!t.srcset||t.complete||g(t,i.errorClass)||!g(t,i.lazyClass))&&(e=b(t,"lazyunveilread").detail,o&&T.updateElem(t,!0,t.offsetWidth),t._lazyRace=!0,Q++,st(t,e,o,r,n))}},ut=A((function(){i.loadMode=3,et()})),ct=function(){3==i.loadMode&&(i.loadMode=2),ut()},lt=function(){R||(n.now()-q<999?u(lt,999):(R=!0,i.loadMode=3,et(),a("scroll",ct,!0)))},{_:function(){q=n.now(),r.elements=e.getElementsByClassName(i.lazyClass),L=e.getElementsByClassName(i.lazyClass+" "+i.preloadClass),a("scroll",et,!0),a("resize",et,!0),a("pageshow",(function(t){if(t.persisted){var n=e.querySelectorAll("."+i.loadingClass);n.length&&n.forEach&&c((function(){n.forEach((function(t){t.complete&&at(t)}))}))}})),t.MutationObserver?new MutationObserver(et).observe(o,{childList:!0,subtree:!0,attributes:!0}):(o.addEventListener("DOMNodeInserted",et,!0),o.addEventListener("DOMAttrModified",et,!0),setInterval(et,999)),a("hashchange",et,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach((function(t){e.addEventListener(t,et,!0)})),/d$|^c/.test(e.readyState)?lt():(a("load",lt),e.addEventListener("DOMContentLoaded",et),u(lt,2e4)),r.elements.length?(tt(),S._lsFlush()):et()},checkElems:et,unveil:at,_aLSL:ct}),T=(N=C((function(t,e,n,r){var i,o,s;if(t._lazysizesWidth=r,r+="px",t.setAttribute("sizes",r),h.test(e.nodeName||""))for(o=0,s=(i=e.getElementsByTagName("source")).length;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===r(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,a.default)(t,"click",(function(t){return e.onClick(t)}))}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new o.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return l("action",t)}},{key:"defaultTarget",value:function(t){var e=l("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return l("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach((function(t){n=n&&!!document.queryCommandSupported(t)})),n}}]),e}(s.default);function l(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}t.exports=c},function(t,e,n){"use strict";var r,i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,a.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,a.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=u},function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(t),r.removeAllRanges(),r.addRange(i),e=r.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var r=this.e||(this.e={});return(r[t]||(r[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var r=this;function i(){r.off(t,i),e.apply(n,arguments)}return i._=e,this.on(t,i,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),r=0,i=n.length;r";var r=document.createElement("div");r.appendChild(document.createTextNode(e)),n=n||"";var i=document.createElement("div");i.appendChild(document.createTextNode(n));var s=document.createElement("div");return s.appendChild(document.createTextNode(t)),s.innerHTML.replace(RegExp(o(r.innerHTML),"g"),e).replace(RegExp(o(i.innerHTML),"g"),n)}}},function(t,e,n){"use strict";t.exports={element:null}},function(t,e){var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString;t.exports=function(t,e,i){if("[object Function]"!==r.call(e))throw new TypeError("iterator must be a function");var o=t.length;if(o===+o)for(var s=0;s was loaded but did not call our provided callback"),JSONPScriptError:o("JSONPScriptError"," -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html deleted file mode 100644 index dec9ae48b..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/icon-link.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html deleted file mode 100644 index f1fb6d059..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/maintenance-pages-table.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - {{ range . }} - - - - - - {{ end }} - -
LastModLinkGitHub
{{ .Lastmod.Format "2006-01-02" }} - {{ .Title }} - - - {{ with .GitInfo }}{{ .Subject }}{{ else }}Source{{ end }} - -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html deleted file mode 100644 index b1eb5c8db..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/math.html +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html deleted file mode 100644 index a8fc27e21..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs-mobile.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ $currentPage := . }} -{{ $menu := .Site.Menus.docs.ByWeight }} -
    - {{ range $menu }}{{ $post := printf "%s" .Post }} -
  • - - {{ .Name }} - -
  • - {{end}} -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html deleted file mode 100644 index 61aa11dde..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-docs.html +++ /dev/null @@ -1,23 +0,0 @@ -{{ $currentPage := . }} - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html deleted file mode 100644 index 6ad98923e..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links-global-mobile.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ $currentPage := . }} -{{ $menu := .Site.Menus.global }} - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html deleted file mode 100644 index af3790b16..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-links.html +++ /dev/null @@ -1,37 +0,0 @@ -{{ $currentPage := . }} -{{ $.Scratch.Add "listlinkClasses" "f6 link primary-color-dark hover-white db brand-font ma0 w-100 pv3 ph4" }} - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html deleted file mode 100644 index b04866e52..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-mobile.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- {{ partial "nav-links-docs-mobile.html" . }} -
- -
- - - -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html deleted file mode 100644 index f64111409..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/nav-top.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ $currentPage := . }} -
- - - {{ partial "nav-links.html" .}} -
- {{ partial "nav-button-open.html" .}} -
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html deleted file mode 100644 index 79b315a44..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/get-featured-image.html +++ /dev/null @@ -1,24 +0,0 @@ -{{ $images := $.Resources.ByType "image" }} -{{ $featured := $images.GetMatch "*feature*" }} -{{ if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end }} -{{ if not $featured }} - {{ $featured = resources.Get "/opengraph/gohugoio-card-base-1.png" }} - {{ $size := 80 }} - {{ $title := $.LinkTitle }} - {{ if gt (len $title) 20 }} - {{ $size = 70 }} - {{ end }} - - {{ $text := $title }} - {{ $textOptions := dict - "color" "#FFF" - "size" $size - "lineSpacing" 10 - "x" 65 "y" 80 - "font" (resources.Get "/opengraph/mulish-black.ttf") - }} - - {{ $featured = $featured | images.Filter (images.Text $text $textOptions) }} -{{ end }} - -{{ return $featured }} \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html deleted file mode 100644 index 6d195ede6..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/opengraph.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - -{{- with $.Params.images -}} -{{- range first 6 . }}{{ end -}} -{{- else -}} -{{- $featured := partial "opengraph/get-featured-image.html" . }} -{{- with $featured -}} - -{{- else -}} -{{- with $.Site.Params.images }}{{ end -}} -{{- end -}} -{{- end -}} - -{{- if .IsPage }} -{{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} - -{{ with .PublishDate }}{{ end }} -{{ with .Lastmod }}{{ end }} -{{- end -}} - -{{- with .Params.audio }}{{ end }} -{{- with .Params.locale }}{{ end }} -{{- with .Site.Params.title }}{{ end }} -{{- with .Params.videos }}{{- range . }} - -{{ end }}{{ end }} - -{{- /* If it is part of a series, link to related articles */}} -{{- $permalink := .Permalink }} -{{- $siteSeries := .Site.Taxonomies.series }} -{{ with .Params.series }}{{- range $name := . }} - {{- $series := index $siteSeries ($name | urlize) }} - {{- range $page := first 6 $series.Pages }} - {{- if ne $page.Permalink $permalink }}{{ end }} - {{- end }} -{{ end }}{{ end }} - -{{- /* Facebook Page Admin ID for Domain Insights */}} -{{- with site.Params.social.facebook_admin }}{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html deleted file mode 100644 index 456f87b1c..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/opengraph/twitter_cards.html +++ /dev/null @@ -1,22 +0,0 @@ -{{- with $.Params.images -}} - - -{{ else -}} -{{- $featured := partial "opengraph/get-featured-image.html" . }} -{{- with $featured -}} - - -{{- else -}} -{{- with $.Site.Params.images -}} - - -{{ else -}} - -{{- end -}} -{{- end -}} -{{- end }} - - -{{ with site.Params.social.twitter -}} - -{{ end -}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html deleted file mode 100644 index f41e5b791..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-edit.html +++ /dev/null @@ -1,3 +0,0 @@ -Improve this page diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html deleted file mode 100644 index dcc96242f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/page-header.html +++ /dev/null @@ -1,20 +0,0 @@ -{{ $currentPage := . }} -{{ $currentURL := .RelPermalink }} -
-
    - -
  • - - News: - -
  • - {{ range $name, $taxonomy := .Site.Taxonomies.categories }} - {{ $link := $name | printf "%s%s" "/categories/" | printf "%s/" }} -
  • - - {{ $name | humanize }} - -
  • - {{ end }} -
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html deleted file mode 100644 index e6b644b2f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/pagelayout.html +++ /dev/null @@ -1,36 +0,0 @@ -{{ $section_to_display := .section_to_display }} -
- -
-
- {{ partial "nav-links-docs.html" .context }} -
- -
- - -
- {{ $interior_classes := .context.Site.Params.flex_box_interior_classes }} -
- {{ $pages := $section_to_display }} - {{ if in (slice "functions" "methods") $.context.Type }} - {{ $pages = $.context.Pages }} - {{ end }} - {{ range $pages }} - {{ partial "boxes-section-summaries.html" (dict "context" . "classes" $interior_classes "fullcontent" true) }} - {{ end }} -
-
- -
- -
- -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html deleted file mode 100644 index 71a14c0ef..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section-with-title.html +++ /dev/null @@ -1,14 +0,0 @@ -{{ if or .PrevInSection .NextInSection }} -{{/* this div holds these a tags as a unit for flex-box display */}} - -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html deleted file mode 100644 index af9f4aac1..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links-in-section.html +++ /dev/null @@ -1,16 +0,0 @@ -{{ if or .PrevInSection .NextInSection }} -{{/* this div holds these a tags as a unit for flex-box display */}} - -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html deleted file mode 100644 index cd43dd840..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/previous-next-links.html +++ /dev/null @@ -1,25 +0,0 @@ -{{if .Prev }} - - {{ partial "svg/ic_chevron_left_black_24px.svg" (dict "size" "30px") }} {{ .Prev.Title }} - -{{end}} - -{{if .Next }} - - {{ .Next.Title }} {{ partial "svg/ic_chevron_right_black_24px.svg" (dict "size" "30px") }} - -{{end}} - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html deleted file mode 100644 index ff7435668..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/related.html +++ /dev/null @@ -1,22 +0,0 @@ -{{- $heading := "See also" }} -{{- $related := site.RegularPages.Related . | first 5 }} - -{{- if in (slice "functions" "methods") .Type }} - {{- $related = slice }} - {{- range .Params.action.related }} - {{- with site.GetPage (lower .) }} - {{- $related = $related | append . }} - {{- else }} - {{- errorf "The 'related' partial was unable to get page %s" . }} - {{- end }} - {{- end }} -{{- end }} - -{{- with $related }} -

{{ $heading }}

- -{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html deleted file mode 100644 index ecdbeb33f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/right-sidebar.html +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html deleted file mode 100644 index 09c013361..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-footer.html +++ /dev/null @@ -1,48 +0,0 @@ -
-
-
- - - -
- Hugo Logo -
- - - - - - {{ with getenv "REPOSITORY_URL" -}} -

Netlify badge

- {{- end }} - -
- -
  - {{ partial "home-page-sections/sponsors.html" (dict "cx" . "gtag" "footer" "classes_section" "pb3 w-100" "classes_copy" "f7 w-90-ns") }} -
- -
- -
  -

The Hugo logos are copyright © Steve Francia 2013–{{ now.Year }}.

-

The Hugo Gopher is based on an original work by Renée French.

-
- - - -
- {{- partial "nav-mobile.html" . -}} -
- -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html deleted file mode 100644 index 54472ba16..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-manifest.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html deleted file mode 100644 index f387d66f3..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-nav.html +++ /dev/null @@ -1,38 +0,0 @@ -{{ $currentPage := . }} - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html deleted file mode 100644 index 7dec9de18..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-scripts.html +++ /dev/null @@ -1,17 +0,0 @@ - -{{ $scripts := resources.Get "output/js/app.js" }} -{{ $isDev := eq hugo.Environment "development" }} -{{ if not $isDev }} -{{ $scripts = $scripts | fingerprint }} -{{ end }} -{{ with $scripts }} - {{ if $isDev }} - - {{ else }} - - {{ end }} - {{ $.Scratch.Set "scripts" . }} -{{end}} - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html deleted file mode 100644 index 8c97ac454..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/site-search.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html deleted file mode 100644 index 7451c15d6..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/social-follow.html +++ /dev/null @@ -1,21 +0,0 @@ - -gohugoio -Star diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html deleted file mode 100644 index 0f140cf70..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/summary.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
- {{ humanize .Section }} -

- - {{ .Title }} - -

- -
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg deleted file mode 100644 index da9438414..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/Twitter_Logo_Blue.svg +++ /dev/null @@ -1 +0,0 @@ -Twitter_Logo_Blue diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg deleted file mode 100644 index 6f3c20f76..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/apple.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg deleted file mode 100644 index e1b170359..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clipboard.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg deleted file mode 100644 index e1b170359..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/clippy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg deleted file mode 100644 index 2ea15de87..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/cloud.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg deleted file mode 100644 index bc696b90b..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/content.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg deleted file mode 100644 index 9f9d71769..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/design.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/exclamation.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/exclamation.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg deleted file mode 100644 index 6e6af44a2..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/facebook.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg deleted file mode 100644 index ed2c929b4..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/focus.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg deleted file mode 100644 index 842be09a1..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/freebsd.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg deleted file mode 100644 index 717a35686..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/functions.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg deleted file mode 100644 index 29bc57ad3..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-corner.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg deleted file mode 100644 index dabc741e0..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/github-squared.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg deleted file mode 100644 index 9c2de7da2..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gitter.svg +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg deleted file mode 100644 index 9ab114aa3..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gme.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html deleted file mode 100644 index 1a6b82159..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/godoc-icon.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg deleted file mode 100644 index 961221f18..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-2.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg deleted file mode 100644 index 0f8fbe0d9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-front.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg deleted file mode 100644 index 36d9f1c41..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-homepage.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg deleted file mode 100644 index 05cfb84d1..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-side_path.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg deleted file mode 100644 index bc1e5010c..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher-small.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg deleted file mode 100644 index 7f6ec255c..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/gopher.svg +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg deleted file mode 100644 index ea72a6f51..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo-h-only.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg deleted file mode 100644 index 58d025596..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/hugo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg deleted file mode 100644 index 3ba28c3f5..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_down.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg deleted file mode 100644 index 8ec2eb766..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_arrow_drop_up.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg deleted file mode 100644 index da37757cf..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_left_black_24px.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg deleted file mode 100644 index 47689a91e..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/ic_chevron_right_black_24px.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg deleted file mode 100644 index 5c2ccc2f4..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/idea.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg deleted file mode 100644 index ae915113b..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/instagram.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg deleted file mode 100644 index b0e2f5b0d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/javascript.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg deleted file mode 100644 index d2ba6d0fc..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/json.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg deleted file mode 100644 index ba9400b7f..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-ext.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg deleted file mode 100644 index f5de52d02..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/link-permalink.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg deleted file mode 100644 index f1a794565..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/md.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg deleted file mode 100644 index d0d9ae938..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/mdsolid.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Svg Vector Icons : http://www.onlinewebfonts.com/icon - - \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg deleted file mode 100644 index 83b706383..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/newlogo.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg deleted file mode 100644 index da3d9cfcf..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/sass.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg deleted file mode 100644 index 181789b54..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/search.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg deleted file mode 100644 index 247ca9062..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/twitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg deleted file mode 100644 index 2bdcf5f94..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/website.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg deleted file mode 100644 index fe3bf0296..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/windows.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg deleted file mode 100644 index 59eeb71c2..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/svg/yaml.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html deleted file mode 100644 index 59e3e51a0..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/tags.html +++ /dev/null @@ -1,37 +0,0 @@ -{{ $currentPageUrl := .RelPermalink }} -{{ if and .Params.tags .Site.Taxonomies.tags }} - {{ $name := index .Params.tags 0 }} - {{ $name := $name | urlize }} - {{ $tags := index .Site.Taxonomies.tags $name }} - -
- -
-{{end}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt deleted file mode 100644 index 25b9e9a0d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/robots.txt +++ /dev/null @@ -1,8 +0,0 @@ -User-agent: * -# robotstxt.org - if ENV production variable is false robots will be disallowed. -{{ if eq (getenv "HUGO_ENV") "production" }} - Disallow: admin/ - Disallow: -{{ else }} - Disallow: / -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html deleted file mode 100644 index 2755b1e2d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/articlelist.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - {{ range $ind, $art := $.Site.Data.articles.article }} - - - - - - {{ end }} - -
Title - Author - Date -
{{$art.title | markdownify }}{{ $art.author | markdownify }}{{ $art.date }}
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html deleted file mode 100644 index 2e10c3dee..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/chroma-lexers.html +++ /dev/null @@ -1,6 +0,0 @@ -
- {{ range .Site.Data.docs.chroma.lexers }} -
{{ .Name }}
-
{{ with .Aliases }}{{ delimit . ", " }}{{ end }}
- {{ end }} -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html deleted file mode 100644 index d1131132d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code-toggle.html +++ /dev/null @@ -1,101 +0,0 @@ -{{- /* - Renders syntax-highlighted configuration data in JSON, TOML, and YAML formats. - - @param {string} [config] The section of site.Data.docs.config to render. - @param {bool} [copy=false] If true, display a copy to clipboard button. - @param {string} [file] The file name to display above the rendered code. - @param {bool} [fm=false] If true, render the code as front matter. - @param {bool} [skipHeader=false] If false, omit top level key(s) when rendering a section of site.Data.docs.config. - - @returns {template.HTML} -*/}} - -{{- /* Initialize. */}} -{{- $config := "" }} -{{- $dataKey := "" }} -{{- $copy := false }} -{{- $file := "" }} -{{- $fm := false }} -{{- $skipHeader := false }} - -{{- /* Get parameters. */}} -{{- $config = .Get "config" }} -{{- $dataKey = .Get "dataKey" }} -{{- $file = .Get "file" }} -{{- if in (slice "false" false 0) (.Get "copy") }} - {{- $copy = false }} -{{- else if in (slice "true" true 1) (.Get "copy") }} - {{- $copy = true }} -{{- end }} -{{- if in (slice "false" false 0) (.Get "fm") }} - {{- $fm = false }} -{{- else if in (slice "true" true 1) (.Get "fm") }} - {{- $fm = true }} -{{- end }} -{{- if in (slice "false" false 0) (.Get "skipHeader") }} - {{- $skipHeader = false }} -{{- else if in (slice "true" true 1) (.Get "skipHeader") }} - {{- $skipHeader = true }} -{{- end }} - -{{- /* Define constants. */}} -{{- $delimiters := dict "toml" "+++" "yaml" "---" }} -{{- $langs := slice "yaml" "toml" "json" }} -{{- $placeHolder := "#-hugo-placeholder-#" }} - -{{- /* Render. */}} -{{- $code := "" }} -{{- if $config }} - {{- $file = $file | default "hugo" }} - {{- $sections := (split $config ".") }} - {{- $configSection := index $.Site.Data.docs.config $sections }} - {{- $code = dict $sections $configSection }} - {{- if $skipHeader }} - {{- $code = $configSection }} - {{- end }} -{{- else if $dataKey }} - {{- $file = $file | default $dataKey }} - {{- $sections := (split $dataKey ".") }} - {{- $code = index $.Site.Data.docs $sections }} -{{- else }} - {{- $code = $.Inner }} -{{- end }} -
-
- {{- with $file }} -
- {{ . }}{{ if not $fm }}.{{ end }} -
- {{- end }} - {{- range $langs }} - -   - {{- end }} -
-
- {{- range $langs }} -
- {{- $hCode := $code | transform.Remarshal . }} - {{- if and $fm (in (slice "toml" "yaml") .) }} - {{- $hCode = printf "%s\n%s\n%s" $placeHolder $hCode $placeHolder }} - {{- end }} - {{- $hCode = $hCode | replaceRE `\n+` "\n" }} - {{ highlight $hCode . "" | replaceRE $placeHolder (index $delimiters .) | safeHTML }} -
- {{- if $copy }} - - {{- /* Functionality located within filesaver.js The copy here is located in the css with .copy class so it can be replaced with JS on success */}} - {{- end }} - {{- end }} -
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html deleted file mode 100644 index dd21551cb..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/code.html +++ /dev/null @@ -1,35 +0,0 @@ -{{- /* -Renders syntax highlighted code. - -@param {bool} [copy=false] If true, display a copy to clipboard button. -@param {string} [file] The file name to display above the rendered code. -@param {string} [lang] The code language of the inner content. - -@returns {template.HTML} -*/}} - -{{- /* Get parameters. */}} -{{- $copy := false }} -{{- if in (slice "false" false 0) (.Get "copy") }} - {{- $copy = false }} -{{- else if in (slice "true" true 1) (.Get "copy")}} - {{- $copy = true }} -{{- end }} -{{- $file := or (.Get "file") " " }} -{{- $lang := or (.Get "lang") (path.Ext $file | strings.TrimPrefix ".") "text" }} - -{{- /* Use the go-html-template Chroma lexer for HTML. */}} -{{- if eq $lang "html" }} - {{- $lang = "go-html-template" }} -{{- end }} - -{{- /* Render. */}} -
-
{{ $file | htmlUnescape }}
- {{- if $copy }} - - {{- end }} -
- {{- highlight (trim .Inner "\n\r") $lang }} -
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable-filtered.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable-filtered.html deleted file mode 100644 index ff3f299bd..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable-filtered.html +++ /dev/null @@ -1,39 +0,0 @@ -{{ $package := (index .Params 0) }} -{{ $listname := (index .Params 1) }} -{{ $filter := split (index .Params 2) " " }} -{{ $filter1 := index $filter 0 }} -{{ $filter2 := index $filter 1 }} -{{ $filter3 := index $filter 2 }} - -{{ $list := (index (index .Site.Data.docs $package) $listname) }} -{{ $fields := after 3 .Params }} -{{ $list := where $list $filter1 $filter2 $filter3 }} - - - - {{ range $fields }} - - {{ end }} - - {{ range $list }} - - {{ range $k, $v := . }} - {{ $.Scratch.Set $k $v }} - {{ end }} - {{ range $k, $v := $fields }} - - {{ end }} - - {{ end }} -
{{ . }}
- {{ $tdContent := $.Scratch.Get . }} - {{ if eq $k 3 }} - {{ printf "%v" $tdContent | - strings.ReplaceRE `\[` "
  1. " | - strings.ReplaceRE `\s` "
  2. " | - strings.ReplaceRE `\]` "
" | - safeHTML }} - {{ else }} - {{ $tdContent }} - {{ end}} -
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable.html deleted file mode 100644 index 12054f89d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/datatable.html +++ /dev/null @@ -1,33 +0,0 @@ -{{ $package := (index .Params 0) }} -{{ $listname := (index .Params 1) }} -{{ $list := (index (index .Site.Data.docs $package) $listname) }} -{{ $fields := after 2 .Params }} - - - - - {{ range $fields }} - {{ $s := . }} - {{ if eq $s "_key" }} - {{ $s = "Type" }} - {{ end }} - - {{ end }} - - {{ range $k1, $v1 := $list }} - - {{ range $k2, $v2 := . }} - {{ $.Scratch.Set $k2 $v2 }} - {{ end }} - {{ range $fields }} - {{ $s := "" }} - {{ if eq . "_key" }} - {{ $s = $k1 }} - {{ else }} - {{ $s = $.Scratch.Get . }} - {{ end }} - - {{ end }} - - {{ end }} -
{{ $s }}
{{ $s }}
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html deleted file mode 100644 index 7219d7f54..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/deprecated-in.html +++ /dev/null @@ -1,14 +0,0 @@ -{{ $_hugo_config := `{ "version": 1 }` }} - -{{ with .Get 0 }} - {{ $version := printf "v%v" (strings.TrimLeft "vV" .) }} - {{ $href := printf "https://github.com/gohugoio/hugo/releases/tag/%s" $version }} - -{{ else }} - {{ errorf "The %q shortcode requires a single positional parameter indicating version. See %s" .Name .Position }} -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html deleted file mode 100644 index 4e96504ab..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/funcsig.html +++ /dev/null @@ -1,4 +0,0 @@ -

Syntax

-
-  {{- .Inner -}}
-
diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html deleted file mode 100644 index 31d860394..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/gomodules-info.html +++ /dev/null @@ -1,18 +0,0 @@ -{{ $text := ` -Most of the commands for **Hugo Modules** require a newer version (>= 1.18) of Go installed (see https://golang.org/dl/) and the relevant VCS client (e.g. Git, see https://git-scm.com/downloads/ ). -If you have an "older" site running on Netlify, you may have to set GO_VERSION to 1.19 or newer in your Environment settings. - -For more information about Go Modules, see: - -* https://github.com/golang/go/wiki/Modules -* https://blog.golang.org/using-go-modules -` }} - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/imgproc.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/imgproc.html deleted file mode 100644 index c09133ba8..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/imgproc.html +++ /dev/null @@ -1,37 +0,0 @@ -{{- /* -Renders the given image using the given process specification. - -@param {string} (positional parameter 0) The path to the image, relative to the current page. The image must be a page resource. -@param {string}} (positional parameter 1) The image processing specification. - -@returns template.HTML - -@example {{< imgproc "sunset.jpg" "resize 300x" />}} -*/}} - -{{- with $.Get 0 }} - {{- with $i := $.Page.Resources.Get . }} - {{- with $spec := $.Get 1 }} - {{- with $i.Process . }} -
- -
- - {{- with $.Inner }} - {{ . }} - {{- else }} - {{ $spec }} - {{- end }} - -
-
- {{- end }} - {{- else }} - {{- errorf "The %q shortcode requires a positional parameter (1) containing the image processing specification. See %s" $.Name $.Position }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a positional parameter (0) indicating the image path, relative to the current page. See %s" $.Name $.Position }} -{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/list-pages-in-section.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/list-pages-in-section.html deleted file mode 100644 index 73e7f85a9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/list-pages-in-section.html +++ /dev/null @@ -1,96 +0,0 @@ -{{- /* -Renders a description list of the pages in the given section. - -Render a subset of the pages in the section by specifying a predefined filter, -and whether to include those pages. - -Filters are defined in the data directory, in the file named page_filters. Each -filter is an array of paths to a file, relative to the root of the content -directory. Hugo will throw an error if the specified filter does not exist, or -if any of the pages in the filter do not exist. - -The definition term elements (dt) have an id attribute derived from the title -of the page. This is probably unique, because pages of the same title in the -same section is unlikely. - -If you render a complete list on a page, then call the shortcode again to -render a subset, you will generate duplicate element ids. In this case, set -omitElementIDs to true for the subset. - -@param {string} path The path to the section. -@param {string} [filter=""] The name of filter list. -@param {string} [filterType=""] The type of filter, either include or exclude. -@param {string} [omitElementIDs=false] Whether to omit dt element ids. -@param {string} [titlePrefix=""] The string to prepend to the link title. - -@returns template.HTML - -@example {{< list-pages-in-section path=/methods/resources >}} -@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude >}} -@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude titlePrefix=foo >}} -@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude titlePrefix=foo omitElementIDs=true >}} -*/}} - -{{- /* Initialize. */}} -{{- $filter := or "" (.Get "filter" | lower)}} -{{- $filterType := or (.Get "filterType") "none" | lower }} -{{- $filteredPages := slice }} -{{- $titlePrefix := or (.Get "titlePrefix") "" }} -{{- $omitElementIDs := false }} - -{{- /* Get boolean parameters. */}} -{{- if in (slice "false" false 0) (.Get "omitElementIDs") }} - {{- $omitElementIDs = false }} -{{- else if in (slice "true" true 1) (.Get "omitElementIDs")}} - {{- $omitElementIDs = true }} -{{- end }} - -{{- /* Build slice of filtered pages. */}} -{{- with $filter }} - {{- with index site.Data.page_filters . }} - {{- range . }} - {{- with site.GetPage . }} - {{- $filteredPages = $filteredPages | append . }} - {{- else }} - {{- errorf "The %q shortcode was unable to find %q as specified in the page_filters data file. See %s" $.Name . $.Position }} - {{- end }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to find the %q filter in the page_filters data file. See %s" $.Name . $.Position }} - {{- end }} -{{- end }} - -{{- /* Render */}} -{{- with $sectionPath := .Get "path" }} - {{- with site.GetPage . }} - {{- with .RegularPages }} -
- {{- range $page := .ByTitle }} - {{- if or - (and (eq $filterType "include") (in $filteredPages $page)) - (and (eq $filterType "exclude") (not (in $filteredPages $page))) - (eq $filterType "none") - }} - {{- $linkTitle := .LinkTitle }} - {{- with $titlePrefix }} - {{- $linkTitle = printf "%s%s" . $linkTitle }} - {{- end }} - {{- $idAttribute := "" }} - {{- if not $omitElementIDs }} - {{- $id := path.Join .File.Dir .File.ContentBaseName | replaceRE `[\|/]` ":" | lower }} - {{- $idAttribute = printf " id=%q" $id }} - {{- end }} -
{{ $linkTitle }}
-
{{- $page.Description | $page.RenderString }}
- {{- end }} - {{- end }} -
- {{- else }} - {{- warnf "The %q shortcode found no pages in the %q section. See %s" $.Name $sectionPath $.Position }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name $sectionPath $.Position }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a 'path' parameter indicating the path to the section. See %s" $.Name $.Position }} -{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/module-mounts-note.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/module-mounts-note.html deleted file mode 100644 index e8b2a7a7e..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/module-mounts-note.html +++ /dev/null @@ -1 +0,0 @@ -Also see [Module Mounts Config](/hugo-modules/configuration/#module-configuration-mounts) for an alternative way to configure this directory (from Hugo 0.56). diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html deleted file mode 100644 index 606d2219c..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/new-in.html +++ /dev/null @@ -1,34 +0,0 @@ -{{- /* -Renders a "new in" button indicating the version in which a feature was added. - -When comparing the current version to the specified version, the "new in" -button will be hidden if any of the following conditions is true: - -- The major version difference exceeds the majorVersionDiffThreshold -- The minor version difference exceeds the minorVersionDiffThreshold - -@param {string} version The semantic version string, with or without a leading v. -@returns {template.HTML} - -@example {{< new-in 0.100.0 >}} -*/}} - -{{- /* Set defaults. */}} -{{- $majorVersionDiffThreshold := 0 }} -{{- $minorVersionDiffThreshold := 30 }} -{{- $displayExpirationWarning := true }} - -{{- /* Render. */}} -{{- with $version := .Get 0 | strings.TrimPrefix "v" }} - {{- $majorVersionDiff := sub (index (split hugo.Version ".") 0 | int) (index (split $version ".") 0 | int) }} - {{- $minorVersionDiff := sub (index (split hugo.Version ".") 1 | int) (index (split $version ".") 1 | int) }} - {{- if or (gt $majorVersionDiff $majorVersionDiffThreshold) (gt $minorVersionDiff $minorVersionDiffThreshold) }} - {{- if $displayExpirationWarning }} - {{- warnf "This call to the %q shortcode should be removed: %s. The button is now hidden because the specified version (%s) is older than the display threshold." $.Name $.Position $version }} - {{- end }} - {{- else }} - New in v{{ $version }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a positional parameter (version). See %s" .Name .Position }} -{{- end -}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html deleted file mode 100644 index 99818114e..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/note.html +++ /dev/null @@ -1,7 +0,0 @@ -{{ $_hugo_config := `{ "version": 1 }` }} - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/quick-reference.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/quick-reference.html deleted file mode 100644 index fc53c93bd..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/quick-reference.html +++ /dev/null @@ -1,39 +0,0 @@ -{{/* -Renders the child sections of the given top-level section, listing each child's immediate descendants. - -@param {string} section The top-level section to render. -@returns template.HTML - -@example {{% quick-reference section="functions" %}} -*/}} - -{{ $section := "" }} -{{ with .Get "section" }} - {{ $section = . }} -{{ else }} - {{ errorf "The %q shortcodes requires a 'section' parameter. See %s" .Name .Position }} -{{ end }} - -{{/* Do not change the markdown indentation, and do not remove blank lines. */}} -{{ with site.GetPage $section }} - {{ range .Sections }} - -## {{ .LinkTitle }} -{{ .RawContent }} - - {{ range .Pages }} - {{ $aliases := "" }} - {{ if eq .Section "functions" }} - {{ with .Params.action.aliases }} - {{ $aliases = delimit . " or " }} - {{ end }} - {{ end }} - -[{{ .LinkTitle }}]({{ .RelPermalink }}) {{ with $aliases }}({{ . }}){{ end }} -: {{ .Description }} - - {{ end }} - {{ end }} -{{ else }} - {{ errorf "The %q shortcodes was unable to find the %q section. See %s" .Name $section .Position }} -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html deleted file mode 100644 index de8083443..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/readfile.html +++ /dev/null @@ -1,29 +0,0 @@ -{{- $highlight := or (.Get "highlight") "" }} - -{{- $markdown := false }} -{{- if in (slice "false" false 0) (.Get "markdown") }} - {{- $markdown = false }} -{{- else if in (slice "true" true 1) (.Get "markdown") }} - {{- $markdown = true }} -{{- end }} - -{{- with .Get "file" }} - {{- if os.FileExists . }} - {{- with os.ReadFile . }} - {{- $content := trim . "\n\r" }} - {{- if $markdown }} - {{- $content | markdownify }} - {{- else if $highlight }} - {{- highlight $content $highlight }} - {{- else }} - {{- $content | safeHTML }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to read %q. See %s" $.Name . $.Position }} - {{- end }} - {{- else }} - {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} - {{- end }} -{{- else }} - {{- errorf "The %q shortcode requires a 'file' parameter. See %s" $.Name $.Position }} -{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html deleted file mode 100644 index 50a099267..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/todo.html +++ /dev/null @@ -1 +0,0 @@ -{{ if .Inner }}{{ end }} \ No newline at end of file diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html deleted file mode 100644 index bff52ad8d..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/list.html +++ /dev/null @@ -1,46 +0,0 @@ -{{ define "main" }} -
-
-

- {{ .Title }} -

-
- {{ .Content }} -
-
-
- {{ range (.Paginate (.Pages | shuffle ) 20).Pages }} - {{template "showcase_items" .}} - {{ end }} -
- -
The Showcase articles are copyrighted by their respective content authors. Any open source license will be attached.
-
-{{ end }} - - -{{define "showcase_items"}} - -
- {{ $img := (.Resources.ByType "image").GetMatch "*featured*" }} - {{ with $img }} - {{ $big := .Fill "1024x512 top" }} - {{ $small := $big.Resize "512x" }} - - {{end}} -
{{/* the margin aligns to the bottom */}} -

- {{- .Title -}} -

-
-
-
- - -{{end}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html deleted file mode 100644 index 5ae1e07a7..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/showcase/single.html +++ /dev/null @@ -1,106 +0,0 @@ -{{ define "title" }} -Showcase: {{ .Title }} -{{ end }} - -{{ define "main" }} -
- - -
- -
- {{template "sc-details" .}} -
- -
- {{template "sc-main-column" .}} -
- - - -
- -
{{/* bottom row */}} - Last Update: {{ .Lastmod.Format "January 2, 2006" }}
- {{ partial "page-edit.html" . }} -
-
The Showcase articles are copyright the content authors. Any open source license will be attached.
-
-{{ end }} - - - -{{define "sc-main-column"}} - {{ $img := (.Resources.ByType "image").GetMatch "*featured*" }} - {{ with $img }} - {{ $big := .Fill "1024x512 top" }} - {{ $small := $big.Resize "512x" }} - {{ $img.Title }} - {{ end }} - - -{{end}} - -{{define "sc-details"}} - -{{end}} - -{{define "sc-navigation"}} - {{$section := where .Site.RegularPages "Section" .Section}} - {{$number_of_entries := $section | len}} - -{{end}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html b/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html deleted file mode 100644 index 8b5f0da85..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/template-func/page.html +++ /dev/null @@ -1,55 +0,0 @@ -{{ $pkg := .Params.package}} -{{ $funcs := index site.Data.docs.tpl.funcs $pkg }} - -{{ range $k, $v := $funcs }} - {{ if $v.Description }} - {{ $func := printf "%s.%s" $pkg $k }} - {{ $id := $func | anchorize | safeURL }} -

- - - - - - - {{ $func }} -

- {{ with $v.Description }} -

- {{ . | $.RenderString | safeHTML }} -

- {{ end }} -

- Syntax -

-
- {{ $pkg }}.{{ $k }} - {{ with $v.Args }} - - {{ delimit $v.Args ", "}} - - {{ end }} - -
- {{ if $v.Examples }} -

- Examples -

- {{ end }} - {{ range $v.Examples }} - {{ $input := index . 0 }} - {{ $result := index . 1 }} - {{ $example := printf "%s ---> %s" $input $result }} - - {{ highlight $example "go-html-template" "" }} - {{ end }} - {{ with $v.Aliases }} -

- Aliases -

-

- {{ delimit . ", "}} -

- {{ end }} - {{ end }} -{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/package.json b/docs/_vendor/github.com/gohugoio/gohugoioTheme/package.json deleted file mode 100644 index 14d128910..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "gohugo-default-styles", - "version": "1.1.0", - "description": "Default Theme for Hugo Sites", - "main": "index.js", - "repository": "", - "author": "budparr", - "license": "MIT", - "scripts": { - "build": "NODE_ENV=production webpack", - "build-dev": "NODE_ENV=development webpack --progress --watch", - "start": "npm run build-dev" - }, - "devDependencies": { - "clean-webpack-plugin": "^1.0.0", - "clipboard": "^2.0.4", - "css-loader": "^1.0.1", - "docsearch.js": "^2.6.1", - "file-loader": "^2.0.0", - "glob-all": "^3.1.0", - "lazysizes": "^5.3.2", - "mini-css-extract-plugin": "^0.4.4", - "postcss": "^7.0.5", - "postcss-cssnext": "^3.1.0", - "postcss-import": "^12.0.1", - "postcss-loader": "^3.0.0", - "purgecss-webpack-plugin": "^1.3.1", - "scrolldir": "^1.4.0", - "tachyons": "^4.7.0", - "typeface-muli": "0.0.54", - "webpack": "^4.25.1", - "webpack-command": "^0.4.2" - } -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png b/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png deleted file mode 100644 index ecf1fc020..000000000 Binary files a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/apple-touch-icon.png and /dev/null differ diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/browserconfig.xml b/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/browserconfig.xml deleted file mode 100644 index 62400c5f2..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/browserconfig.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - #2d89ef - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/dist/app.bundle.js b/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/dist/app.bundle.js deleted file mode 100644 index 6391e71e9..000000000 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/dist/app.bundle.js +++ /dev/null @@ -1,22 +0,0 @@ -!function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=11)}([function(t,e,n){"use strict";var r=function(t){var e=document.createElement("a");return e.className="header-link",e.href="#"+t,e.innerHTML=' ',e},i=function(t,e){for(var n=e.getElementsByTagName("h"+t),i=0;i0&&p.parentNode.classList.add("expand")}}catch(t){a=!0,u=t}finally{try{!s&&l.return&&l.return()}finally{if(a)throw u}}}},function(t,e,n){"use strict";n(13)({apiKey:"167e7998590aebda7f9fedcf86bc4a55",indexName:"hugodocs",inputSelector:"#search-input",debug:!0})},function(t,e,n){"use strict";n(14),n(15)},function(t,e,n){"use strict";function r(){for(var t=this.dataset.target.split(" "),e=document.querySelector(".mobilemenu:not(.dn)"),n=document.querySelector(".desktopmenu:not(.dn)"),r=document.querySelector(".desktopmenu:not(.dn)"),i=0;i=0?function(){var t=window.pageYOffset;(t>=i-s||window.innerHeight+t>=document.body.offsetHeight)&&clearInterval(u)}:function(){window.pageYOffset<=(i||0)&&clearInterval(u)};var u=setInterval(a,16)},e=document.querySelectorAll("#TableOfContents ul li a");[].forEach.call(e,function(e){e.addEventListener("click",function(n){n.preventDefault();var r=e.getAttribute("href"),i=document.querySelector(r),o=e.getAttribute("data-speed");i&&t(i,o||500)},!1)})}}()},function(t,e,n){"use strict";function r(t){if(t.target){t.preventDefault();var e=t.currentTarget,n=e.getAttribute("data-toggle-tab")}else var n=t;window.localStorage&&window.localStorage.setItem("configLangPref",n);for(var r=document.querySelectorAll("[data-toggle-tab='"+n+"']"),i=document.querySelectorAll("[data-pane='"+n+"']"),a=0;a0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=s})},{select:5}],8:[function(e,n,r){!function(i,o){if("function"==typeof t&&t.amd)t(["module","./clipboard-action","tiny-emitter","good-listener"],o);else if(void 0!==r)o(n,e("./clipboard-action"),e("tiny-emitter"),e("good-listener"));else{var s={exports:{}};o(s,i.clipboardAction,i.tinyEmitter,i.goodListener),i.clipboard=s.exports}}(this,function(t,e,n,r){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function u(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}var c=i(e),l=i(n),h=i(r),f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},p=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===f(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,h.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new c.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(l.default);t.exports=d})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)})},function(t,e,n){/*! docsearch 2.4.1 | © Algolia | github.com/algolia/docsearch */ -!function(e,n){t.exports=n()}(0,function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=46)}([function(t,e,n){"use strict";function r(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var i=n(1);t.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(){return!!/(msie|trident)/i.test(navigator.userAgent)&&navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]},escapeRegExChars:function(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(t){return"number"==typeof t},toStr:function(t){return void 0===t||null===t?"":t+""},cloneDeep:function(t){var e=this.mixin({},t),n=this;return this.each(e,function(t,r){t&&(n.isArray(t)?e[r]=[].concat(t):n.isObject(t)&&(e[r]=n.cloneDeep(t)))}),e},error:function(t){throw new Error(t)},every:function(t,e){var n=!0;return t?(this.each(t,function(r,i){if(!(n=e.call(null,r,i,t)))return!1}),!!n):n},any:function(t,e){var n=!1;return t?(this.each(t,function(r,i){if(e.call(null,r,i,t))return n=!0,!1}),n):n},getUniqueId:function(){var t=0;return function(){return t++}}(),templatify:function(t){if(this.isFunction(t))return t;var e=i.element(t);return"SCRIPT"===e.prop("tagName")?function(){return e.text()}:function(){return String(t)}},defer:function(t){setTimeout(t,0)},noop:function(){},formatPrefix:function(t,e){return e?"":t+"-"},className:function(t,e,n){return(n?"":".")+t+e},escapeHighlightedString:function(t,e,n){e=e||"";var i=document.createElement("div");i.appendChild(document.createTextNode(e)),n=n||"";var o=document.createElement("div");o.appendChild(document.createTextNode(n));var s=document.createElement("div");return s.appendChild(document.createTextNode(t)),s.innerHTML.replace(RegExp(r(i.innerHTML),"g"),e).replace(RegExp(r(o.innerHTML),"g"),n)}}},function(t,e,n){"use strict";t.exports={element:null}},function(t,e){var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString;t.exports=function(t,e,i){if("[object Function]"!==r.call(e))throw new TypeError("iterator must be a function");var o=t.length;if(o===+o)for(var s=0;s was loaded but did not call our provided callback"),JSONPScriptError:i("JSONPScriptError"," -{{< /code >}} +``` The delimiters above must match the delimiters in your site configuration. -###### Step 3 +### Step 3 Conditionally call the partial template from the base template. -{{< code file=layouts/_default/baseof.html >}} +```go-html-template {file="layouts/_default/baseof.html"} ... {{ if .Param "math" }} @@ -116,15 +116,15 @@ Conditionally call the partial template from the base template. {{ end }} ... -{{< /code >}} +``` The example above loads the partial template if you have set the `math` parameter in front matter to `true`. If you have not set the `math` parameter in front matter, the conditional statement falls back to the `math` parameter in your site configuration. -###### Step 4 +### Step 4 -Include mathematical equations and expressions in your Markdown using LaTeX or TeX typesetting syntax. +Include mathematical equations and expressions in Markdown using LaTeX markup. -{{< code file=content/math-examples.md copy=true >}} +```text {file="content/math-examples.md" copy=true} This is an inline \(a^*=x-b^*\) equation. These are block equations: @@ -137,7 +137,7 @@ These are block equations: a^*=x-b^* \] -These are block equations using alternate delimiters: +These are also block equations: $$a^*=x-b^*$$ @@ -146,7 +146,7 @@ $$ a^*=x-b^* $$ $$ a^*=x-b^* $$ -{{< /code >}} +``` If you set the `math` parameter to `false` in your site configuration, you must set the `math` parameter to `true` in front matter. For example: @@ -167,26 +167,40 @@ If you add the `$...$` delimiter pair to your configuration and JavaScript, you A \\$5 bill _saved_ is a \\$5 bill _earned_. ``` -{{% note %}} -If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation](https://github.com/KaTeX/KaTeX/issues/437). -{{% /note %}} +> [!note] +> If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation](https://github.com/KaTeX/KaTeX/issues/437). ## Engines -MathJax and KaTeX are open-source JavaScript display engines. Both engines are fast, but at the time of this writing MathJax v3.2.2 is slightly faster than KaTeX v0.16.9. +MathJax and KaTeX are open-source JavaScript display engines. Both engines are fast, but at the time of this writing MathJax v3.2.2 is slightly faster than KaTeX v0.16.11. -{{% note %}} -If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation](https://github.com/KaTeX/KaTeX/issues/437). - -See the [inline delimiters](#inline-delimiters) section for details. -{{% /note %}} +> [!note] +> If you use the `$...$` delimiter pair for inline equations, and occasionally use the `$` symbol outside of math contexts, you must use MathJax instead of KaTeX to avoid unintended formatting caused by [this KaTeX limitation](https://github.com/KaTeX/KaTeX/issues/437). +> +>See the [inline delimiters](#inline-delimiters) section for details. To use KaTeX instead of MathJax, replace the partial template from [Step 2] with this: -{{< code file=layouts/partials/math.html copy=true >}} - - - +```go-html-template {file="layouts/partials/math.html" copy=true} + + + -{{< /code >}} +``` The delimiters above must match the delimiters in your site configuration. @@ -215,14 +229,10 @@ $$C_p[\ce{H2O(l)}] = \pu{75.3 J // mol K}$$ As shown in [Step 2] above, MathJax supports chemical equations without additional configuration. To add chemistry support to KaTeX, enable the mhchem extension as described in the KaTeX [documentation](https://katex.org/docs/libs). +[`transform.ToMath`]: /functions/transform/tomath/ [KaTeX]: https://katex.org/ [LaTeX]: https://www.latex-project.org/ [MathJax]: https://www.mathjax.org/ -[Microsoft VS Code]: https://code.visualstudio.com/ -[Obsidian]: https://obsidian.md/ -[Step 1]: #step-1 +[passthrough extension]: /configuration/markup/#passthrough [Step 2]: #step-2 [Step 3]: #step-3 -[TeX]: https://en.wikipedia.org/wiki/TeX -[Typora]: https://typora.io/ -[passthrough extension]: https://github.com/gohugoio/hugo-goldmark-extensions diff --git a/docs/content/en/content-management/menus.md b/docs/content/en/content-management/menus.md index 169b6eb05..6d01173dc 100644 --- a/docs/content/en/content-management/menus.md +++ b/docs/content/en/content-management/menus.md @@ -1,14 +1,8 @@ --- title: Menus -description: Create menus by defining entries, localizing each entry, and rendering the resulting data structure. -categories: [content management] -keywords: [menus] -menu: - docs: - parent: content-management - weight: 190 -weight: 190 -toc: true +description: Create menus by defining entries, localizing each entry, and rendering the resulting data structure. +categories: [] +keywords: [] aliases: [/extras/menus/] --- @@ -17,8 +11,8 @@ aliases: [/extras/menus/] To create a menu for your site: 1. Define the menu entries -2. [Localize] each entry -3. Render the menu with a [template] +1. [Localize](multilingual/#menus) each entry +1. Render the menu with a [template] Create multiple menus, either flat or nested. For example, create a main menu for the header, and a separate menu for the footer. @@ -28,13 +22,12 @@ There are three ways to define menu entries: 1. In front matter 1. In site configuration -{{% note %}} -Although you can use these methods in combination when defining a menu, the menu will be easier to conceptualize and maintain if you use one method throughout the site. -{{% /note %}} +> [!note] +> Although you can use these methods in combination when defining a menu, the menu will be easier to conceptualize and maintain if you use one method throughout the site. ## Define automatically -To automatically define a menu entry for each top-level [section] of your site, enable the section pages menu in your site configuration. +To automatically define a menu entry for each top-level [section](g) of your site, enable the section pages menu in your site configuration. {{< code-toggle file=hugo >}} sectionPagesMenu = "main" @@ -62,45 +55,22 @@ menus = ['main','footer'] Access the entry with `site.Menus.main` and `site.Menus.footer` in your templates. See [menu templates] for details. -{{% note %}} -The configuration key in the examples above is `menus`. The `menu` (singular) configuration key is an alias for `menus`. -{{% /note %}} +> [!note] +> The configuration key in the examples above is `menus`. The `menu` (singular) configuration key is an alias for `menus`. -### Properties {#properties-front-matter} +### Properties Use these properties when defining menu entries in front matter: -identifier -: (`string`) Required when two or more menu entries have the same `name`, or when localizing the `name` using translation tables. Must start with a letter, followed by letters, digits, or underscores. +{{% include "/_common/menu-entry-properties.md" %}} -name -: (`string`) The text to display when rendering the menu entry. - -params -: (`map`) User-defined properties for the menu entry. - -parent -: (`string`) The `identifier` of the parent menu entry. If `identifier` is not defined, use `name`. Required for child entries in a nested menu. - -post -: (`string`) The HTML to append when rendering the menu entry. - -pre -: (`string`) The HTML to prepend when rendering the menu entry. - -title -: (`string`) The HTML `title` attribute of the rendered menu entry. - -weight -: (`int`) A non-zero integer indicating the entry's position relative the root of the menu, or to its parent for a child entry. Lighter entries float to the top, while heavier entries sink to the bottom. - -### Example {#example-front-matter} +### Example This front matter menu entry demonstrates some of the available properties: {{< code-toggle file=content/products/software.md fm=true >}} title = 'Software' -[[menus.main]] +[menus.main] parent = 'Products' weight = 20 pre = '' @@ -112,111 +82,7 @@ Access the entry with `site.Menus.main` in your templates. See [menu templates] ## Define in site configuration -To define entries for the "main" menu: - -{{< code-toggle file=hugo >}} -[[menus.main]] -name = 'Home' -pageRef = '/' -weight = 10 - -[[menus.main]] -name = 'Products' -pageRef = '/products' -weight = 20 - -[[menus.main]] -name = 'Services' -pageRef = '/services' -weight = 30 -{{< /code-toggle >}} - -This creates a menu structure that you can access with `site.Menus.main` in your templates. See [menu templates] for details. - -To define entries for the "footer" menu: - -{{< code-toggle file=hugo >}} -[[menus.footer]] -name = 'Terms' -pageRef = '/terms' -weight = 10 - -[[menus.footer]] -name = 'Privacy' -pageRef = '/privacy' -weight = 20 -{{< /code-toggle >}} - -This creates a menu structure that you can access with `site.Menus.footer` in your templates. See [menu templates] for details. - -{{% note %}} -The configuration key in the examples above is `menus`. The `menu` (singular) configuration key is an alias for `menus`. -{{% /note %}} - -### Properties {#properties-site-configuration} - -{{% note %}} -The [properties available to entries defined in front matter] are also available to entries defined in site configuration. - -[properties available to entries defined in front matter]: /content-management/menus/#properties-front-matter -{{% /note %}} - -Each menu entry defined in site configuration requires two or more properties: - -- Specify `name` and `pageRef` for internal links -- Specify `name` and `url` for external links - -pageRef -: (`string`) The logical path of the target page, relative to the `content` directory. Omit language code and file extension. Required for *internal* links. - -Kind|pageRef -:--|:-- -home|`/` -page|`/books/book-1` -section|`/books` -taxonomy|`/tags` -term|`/tags/foo` - -url -: (`string`) Required for *external* links. - -### Example {#example-site-configuration} - -This nested menu demonstrates some of the available properties: - -{{< code-toggle file=hugo >}} -[[menus.main]] -name = 'Products' -pageRef = '/products' -weight = 10 - -[[menus.main]] -name = 'Hardware' -pageRef = '/products/hardware' -parent = 'Products' -weight = 1 - -[[menus.main]] -name = 'Software' -pageRef = '/products/software' -parent = 'Products' -weight = 2 - -[[menus.main]] -name = 'Services' -pageRef = '/services' -weight = 20 - -[[menus.main]] -name = 'Hugo' -pre = '' -url = 'https://gohugo.io/' -weight = 30 -[menus.main.params] -rel = 'external' -{{< /code-toggle >}} - -This creates a menu structure that you can access with `site.Menus.main` in your templates. See [menu templates] for details. +See [configure menus](/configuration/menus/). ## Localize @@ -226,8 +92,6 @@ Hugo provides two methods to localize your menu entries. See [multilingual]. See [menu templates]. -[localize]: /content-management/multilingual/#menus [menu templates]: /templates/menu/ [multilingual]: /content-management/multilingual/#menus -[section]: /getting-started/glossary/#section [template]: /templates/menu/ diff --git a/docs/content/en/content-management/multilingual.md b/docs/content/en/content-management/multilingual.md index b8ba80dfd..d419f4381 100644 --- a/docs/content/en/content-management/multilingual.md +++ b/docs/content/en/content-management/multilingual.md @@ -2,211 +2,14 @@ title: Multilingual mode linkTitle: Multilingual description: Localize your project for each language and region, including translations, images, dates, currencies, numbers, percentages, and collation sequence. Hugo's multilingual framework supports single-host and multihost configurations. -categories: [content management] -keywords: [multilingual,i18n,internationalization] -menu: - docs: - parent: content-management - weight: 230 -weight: 230 -toc: true +categories: [] +keywords: [] aliases: [/content/multilingual/,/tutorials/create-a-multilingual-site/] --- -## Configure languages +## Configuration -This is the default language configuration: - -{{< code-toggle config=languages />}} - -In the above, `en` is the language key. - - -Language keys must conform to the syntax described in [RFC 5646]. For example: - -- `en` -- `en-US` - -Artificial languages with private use subtags as defined in [RFC 5646 § 2.2.7] are also supported. Omit the `art-x-` prefix from the language key. For example: - -- `hugolang` - -{{% note %}} -Private use subtags must not exceed 8 alphanumeric characters. -{{% /note %}} - -[RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 -[RFC 5646 § 2.2.7]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.2.7 - -This is an example of a site configuration for a multilingual project. Any key not defined in a `languages` object will fall back to the global value in the root of your site configuration. - -{{< code-toggle file=hugo >}} -defaultContentLanguage = 'de' -defaultContentLanguageInSubdir = true - -[languages.de] -contentDir = 'content/de' -disabled = false -languageCode = 'de-DE' -languageDirection = 'ltr' -languageName = 'Deutsch' -title = 'Projekt Dokumentation' -weight = 1 - -[languages.de.params] -subtitle = 'Referenz, Tutorials und Erklärungen' - -[languages.en] -contentDir = 'content/en' -disabled = false -languageCode = 'en-US' -languageDirection = 'ltr' -languageName = 'English' -title = 'Project Documentation' -weight = 2 - -[languages.en.params] -subtitle = 'Reference, Tutorials, and Explanations' -{{< /code-toggle >}} - -defaultContentLanguage -: (`string`) The project's default language key, conforming to the syntax described in [RFC 5646]. This value must match one of the defined language keys. Examples: - -- `en` -- `en-GB` -- `pt-BR` - -defaultContentLanguageInSubdir -: (`bool`) If `true`, Hugo renders the default language site in a subdirectory matching the `defaultContentLanguage`. Default is `false`. - -contentDir -: (`string`) The content directory for this language. Omit if [translating by file name]. - -disabled -: (`bool`) If `true`, Hugo will not render content for this language. Default is `false`. - -languageCode -: (`string`) The language tag as described in [RFC 5646]. This value does not affect localization or URLs. Hugo uses this value to populate the `language` element in the [built-in RSS template], and the `lang` attribute of the `html` element in the [built-in alias template]. Examples: - -- `en` -- `en-GB` -- `pt-BR` - -languageDirection -: (`string`) The language direction, either left-to-right (`ltr`) or right-to-left (`rtl`). Use this value in your templates with the global [`dir`] HTML attribute. - -languageName -: (`string`) The language name, typically used when rendering a language switcher. - -title -: (`string`) The site title for this language (optional). - -weight -: (`int`) The language weight. When set to a non-zero value, this is the primary sort criteria for this language. - -[`dir`]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir -[built-in RSS template]: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml -[built-in alias template]: https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/alias.html -[RFC 5646]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 -[translating by file name]: #translation-by-file-name - -### Changes in Hugo 0.112.0 - -{{< new-in 0.112.0 >}} - -In Hugo `v0.112.0` we consolidated all configuration options, and improved how the languages and their parameters are merged with the main configuration. But while testing this on Hugo sites out there, we received some error reports and reverted some of the changes in favor of deprecation warnings: - -1. `site.Language.Params` is deprecated. Use `site.Params` directly. -1. Adding custom parameters to the top level language configuration is deprecated. Define custom parameters within `languages.xx.params`. See `color` in the example below. - -{{< code-toggle file=hugo >}} - -title = "My blog" -languageCode = "en-us" - -[languages] -[languages.sv] -title = "Min blogg" -languageCode = "sv" -[languages.en.params] -color = "blue" -{{< /code-toggle >}} - -In the example above, all settings except `color` below `params` map to predefined configuration options in Hugo for the site and its language, and should be accessed via the documented accessors: - -```go-html-template -{{ site.Title }} -{{ site.LanguageCode }} -{{ site.Params.color }} -``` - -### Disable a language - -To disable a language within a `languages` object in your site configuration: - -{{< code-toggle file=hugo >}} -[languages.es] -disabled = true -{{< /code-toggle >}} - -To disable one or more languages in the root of your site configuration: - -{{< code-toggle file=hugo >}} -disableLanguages = ["es", "fr"] -{{< /code-toggle >}} - -To disable one or more languages using an environment variable: - -```sh -HUGO_DISABLELANGUAGES="es fr" hugo -``` - -Note that you cannot disable the default content language. - -### Configure multilingual multihost - - -Hugo supports multiple languages in a multihost configuration. This means you can configure a `baseURL` per `language`. - -{{% note %}} -If a `baseURL` is set on the `language` level, then all languages must have one and they must all be different. -{{% /note %}} - -Example: - -{{< code-toggle file=hugo >}} -[languages] - [languages.en] - baseURL = 'https://en.example.org/' - languageName = 'English' - title = 'In English' - weight = 2 - [languages.fr] - baseURL = 'https://fr.example.org' - languageName = 'Français' - title = 'En Français' - weight = 1 -{{}} - -With the above, the two sites will be generated into `public` with their own root: - -```text -public -├── en -└── fr -``` - -**All URLs (i.e `.Permalink` etc.) will be generated from that root. So the English home page above will have its `.Permalink` set to `https://example.org/`.** - -When you run `hugo server` we will start multiple HTTP servers. You will typically see something like this in the console: - -```text -Web Server is available at 127.0.0.1:1313 (bind address 127.0.0.1) fr -Web Server is available at 127.0.0.1:1314 (bind address 127.0.0.1) en -Press Ctrl+C to stop -``` - -Live reload and `--navigateToChanged` between the servers work as expected. +See [configure languages](/configuration/languages/). ## Translate your content @@ -217,7 +20,7 @@ There are two ways to manage your content translations. Both ensure each page is Considering the following example: 1. `/content/about.en.md` -2. `/content/about.fr.md` +1. `/content/about.fr.md` The first file is assigned the English language and is linked to the second. The second file is assigned the French language and is linked to the first. @@ -226,13 +29,12 @@ Their language is __assigned__ according to the language code added as a __suffi By having the same **path and base file name**, the content pieces are __linked__ together as translated pages. -{{% note %}} -If a file has no language code, it will be assigned the default language. -{{% /note %}} +> [!note] +> If a file has no language code, it will be assigned the default language. ### Translation by content directory -This system uses different content directories for each of the languages. Each language's content directory is set using the `contentDir` parameter. +This system uses different content directories for each of the languages. Each language's `content` directory is set using the `contentDir` parameter. {{< code-toggle file=hugo >}} languages: @@ -251,14 +53,14 @@ The value of `contentDir` can be any valid path -- even absolute path references Considering the following example in conjunction with the configuration above: 1. `/content/english/about.md` -2. `/content/french/about.md` +1. `/content/french/about.md` The first file is assigned the English language and is linked to the second. The second file is assigned the French language and is linked to the first. -Their language is __assigned__ according to the content directory they are __placed__ in. +Their language is __assigned__ according to the `content` directory they are __placed__ in. -By having the same **path and basename** (relative to their language content directory), the content pieces are __linked__ together as translated pages. +By having the same **path and basename** (relative to their language `content` directory), the content pieces are __linked__ together as translated pages. ### Bypassing default linking @@ -267,10 +69,10 @@ Any pages sharing the same `translationKey` set in front matter will be linked a Considering the following example: 1. `/content/about-us.en.md` -2. `/content/om.nn.md` -3. `/content/presentation/a-propos.fr.md` +1. `/content/om.nn.md` +1. `/content/presentation/a-propos.fr.md` -{{< code-toggle >}} +{{< code-toggle file=hugo >}} translationKey: "about" {{< /code-toggle >}} @@ -285,9 +87,6 @@ To localize URLs: - For a regular page, set either [`slug`] or [`url`] in front matter - For a section page, set [`url`] in front matter -[`slug`]: /content-management/urls/#slug -[`url`]: /content-management/urls/#url - For example, a French translation can have its own localized slug. {{< code-toggle file=content/about.fr.md fm=true >}} @@ -299,24 +98,23 @@ At render, Hugo will build both `/about/` and `/fr/a-propos/` without affecting ### Page bundles -To avoid the burden of having to duplicate files, each Page Bundle inherits the resources of its linked translated pages' bundles except for the content files (Markdown files, HTML files etc...). +To avoid the burden of having to duplicate files, each Page Bundle inherits the resources of its linked translated pages' bundles except for the content files (Markdown files, HTML files etc.). Therefore, from within a template, the page will have access to the files from all linked pages' bundles. If, across the linked bundles, two or more files share the same basename, only one will be included and chosen as follows: -* File from current language bundle, if present. -* First file found across bundles by order of language `Weight`. +- File from current language bundle, if present. +- First file found across bundles by order of language `Weight`. -{{% note %}} -Page Bundle resources follow the same language assignment logic as content files, both by file name (`image.jpg`, `image.fr.jpg`) and by directory (`english/about/header.jpg`, `french/about/header.jpg`). -{{%/ note %}} +> [!note] +> Page Bundle resources follow the same language assignment logic as content files, both by file name (`image.jpg`, `image.fr.jpg`) and by directory (`english/about/header.jpg`, `french/about/header.jpg`). ## Reference translated content To create a list of links to translated content, use a template similar to the following: -{{< code file=layouts/partials/i18nlist.html >}} +```go-html-template {file="layouts/partials/i18nlist.html"} {{ if .IsTranslated }}

{{ i18n "translations" }}

    @@ -327,7 +125,7 @@ To create a list of links to translated content, use a template similar to the f {{ end }}
{{ end }} -{{< /code >}} +``` The above can be put in a `partial` (i.e., inside `layouts/partials/`) and included in any template. It will not print anything if there are no translations for a given page. @@ -337,20 +135,18 @@ The above also uses the [`i18n` function][i18func] described in the next section `.AllTranslations` on a `Page` can be used to list all translations, including the page itself. On the home page it can be used to build a language navigator: -{{< code file=layouts/partials/allLanguages.html >}} +```go-html-template {file="layouts/partials/allLanguages.html"} -{{< /code >}} +``` ## Translation of strings See the [`lang.Translate`] template function. -[`lang.Translate`]: /functions/lang/translate - ## Localization The following localization examples assume your site's primary language is English, with translations to French and German. @@ -378,7 +174,7 @@ weight = 3 With this front matter: -{{< code-toggle >}} +{{< code-toggle file=hugo >}} date = 2021-11-03T12:34:56+01:00 {{< /code-toggle >}} @@ -532,8 +328,6 @@ pageRef = '/services' weight = 20 {{< /code-toggle >}} -[configuration directory]: /getting-started/configuration/#configuration-directory - ### Use translation tables When rendering the text that appears in menu each entry, the [example menu template] does this: @@ -571,20 +365,14 @@ products = 'Produkte' services = 'Leistungen' {{< / code-toggle >}} -[example menu template]: /templates/menu/#example -[automatically]: /content-management/menus/#define-automatically -[in front matter]: /content-management/menus/#define-in-front-matter -[in site configuration]: /content-management/menus/#define-in-site-configuration - ## Missing translations If a string does not have a translation for the current language, Hugo will use the value from the default language. If no default value is set, an empty string will be shown. While translating a Hugo website, it can be handy to have a visual indicator of missing translations. The [`enableMissingTranslationPlaceholders` configuration option][config] will flag all untranslated strings with the placeholder `[i18n] identifier`, where `identifier` is the id of the missing translation. -{{% note %}} -Hugo will generate your website with these missing translation placeholders. It might not be suitable for production environments. -{{% /note %}} +> [!note] +> Hugo will generate your website with these missing translation placeholders. It might not be suitable for production environments. For merging of content from other languages (i.e. missing content translations), see [lang.Merge]. @@ -599,8 +387,8 @@ i18n|MISSING_TRANSLATION|en|wordCount To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there is more than one language, URLs must meet the following criteria: -* Come from the built-in `.Permalink` or `.RelPermalink` -* Be constructed with the [`relLangURL`] or [`absLangURL`] template function, or be prefixed with `{{ .LanguagePrefix }}` +- Come from the built-in `.Permalink` or `.RelPermalink` +- Be constructed with the [`relLangURL`] or [`absLangURL`] template function, or be prefixed with `{{ .LanguagePrefix }}` If there is more than one language defined, the `LanguagePrefix` method will return `/en` (or whatever the current language is). If not enabled, it will be an empty string (and is therefore harmless for single-language Hugo websites). @@ -620,19 +408,22 @@ hugo new content content/en/post/test.md hugo new content content/de/post/test.md ``` -[`abslangurl`]: /functions/urls/abslangurl/ -[config]: /getting-started/configuration/ -[go-i18n-source]: https://github.com/nicksnyder/go-i18n -[go-i18n]: https://github.com/nicksnyder/go-i18n -[Hugo Multilingual Part 1: Content translation]: https://regisphilibert.com/blog/2018/08/hugo-multilingual-part-1-managing-content-translation/ +[`absLangURL`]: /functions/urls/abslangurl/ +[`lang.Translate`]: /functions/lang/translate +[`relLangURL`]: /functions/urls/rellangurl/ +[`slug`]: /content-management/urls/#slug +[`time.Format`]: /functions/time/format/ +[`url`]: /content-management/urls/#url +[automatically]: /content-management/menus/#define-automatically +[config]: /configuration/ +[configuration directory]: /configuration/introduction/#configuration-directory +[example menu template]: /templates/menu/#example [i18func]: /functions/lang/translate/ +[in front matter]: /content-management/menus/#define-in-front-matter +[in site configuration]: /content-management/menus/#define-in-site-configuration [lang.FormatAccounting]: /functions/lang/formataccounting/ [lang.FormatCurrency]: /functions/lang/formatcurrency/ [lang.FormatNumber]: /functions/lang/formatnumber/ [lang.FormatNumberCustom]: /functions/lang/formatnumbercustom/ [lang.FormatPercent]: /functions/lang/formatpercent/ [lang.Merge]: /functions/lang/merge/ -[menus]: /content-management/menus/ -[OS environment]: /getting-started/configuration/#configure-with-environment-variables -[`rellangurl`]: /functions/urls/rellangurl/ -[`time.Format`]: /functions/time/format/ diff --git a/docs/content/en/content-management/organization/1-featured-content-bundles.png b/docs/content/en/content-management/organization/1-featured-content-bundles.png deleted file mode 100644 index 501e671e2..000000000 Binary files a/docs/content/en/content-management/organization/1-featured-content-bundles.png and /dev/null differ diff --git a/docs/content/en/content-management/organization/index.md b/docs/content/en/content-management/organization/index.md index 99a3224bf..a7682bfad 100644 --- a/docs/content/en/content-management/organization/index.md +++ b/docs/content/en/content-management/organization/index.md @@ -2,14 +2,8 @@ title: Content organization linkTitle: Organization description: Hugo assumes that the same structure that works to organize your source content is used to organize the rendered site. -categories: [content management,fundamentals] -keywords: [sections,content,organization,bundle,resources] -menu: - docs: - parent: content-management - weight: 20 -weight: 20 -toc: true +categories: [] +keywords: [] aliases: [/content/sections/] --- @@ -71,11 +65,10 @@ The following demonstrates the relationships between your content organization a ### Index pages: `_index.md` -`_index.md` has a special role in Hugo. It allows you to add front matter and content to `home`, `section`, `taxonomy`, and `term` pages. +`_index.md` has a special role in Hugo. It allows you to add front matter and content to `home`, `section`, `taxonomy`, and `term` pages. -{{% note %}} -**Tip:** You can get a reference to the content and metadata in `_index.md` using the [`.Site.GetPage` function](/methods/page/getpage). -{{% /note %}} +> [!note] +> Access the content and metadata within an `_index.md` file by invoking the `GetPage` method on a `Site` or `Page` object. You can create one `_index.md` for your home page and one in each of your content sections, taxonomies, and terms. The following shows typical placement of an `_index.md` that would contain content and front matter for a `posts` section list page on a Hugo website: @@ -143,20 +136,16 @@ The `slug` is the last segment of the URL path, defined by the file name and opt ### `path` -A content's `path` is determined by the section's path to the file. The file `path` +A content's `path` is determined by the section's path to the file. The file `path`: -* is based on the path to the content's location AND -* does not include the slug +- Is based on the path to the content's location AND +- Does not include the slug ### `url` The `url` is the entire URL path, defined by the file path and optionally overridden by a `url` value in front matter. See [URL Management](/content-management/urls/#slug) for details. -[config]: /getting-started/configuration/ -[formats]: /content-management/formats/ -[front matter]: /content-management/front-matter/ -[getpage]: /methods/page/getpage/ +[config]: /configuration/ [pretty]: /content-management/urls/#appearance [sections]: /content-management/sections/ [single template]: /templates/types/#single -[urls]: /content-management/urls/ diff --git a/docs/content/en/content-management/page-bundles.md b/docs/content/en/content-management/page-bundles.md index b83bb0ba5..f6a5cf771 100644 --- a/docs/content/en/content-management/page-bundles.md +++ b/docs/content/en/content-management/page-bundles.md @@ -1,14 +1,8 @@ --- title: Page bundles description: Use page bundles to logically associate one or more resources with content. -categories: [content management] -keywords: [page,bundle,leaf,branch] -menu : - docs: - parent: content-management - weight: 30 -weight: 30 -toc: true +categories: [] +keywords: [] --- ## Introduction @@ -30,16 +24,13 @@ The "about" page is a page bundle. It logically associates a resource with conte Page bundles are either _leaf bundles_ or _branch bundles_. leaf bundle -: A _leaf bundle_ is a directory that contains an index.md file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. +: A _leaf bundle_ is a directory that contains an `index.md` file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. branch bundle -: A _branch bundle_ is a directory that contains an _index.md file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top level directories with or without _index.md files are also branch bundles. This includes the home page. +: A _branch bundle_ is a directory that contains an `_index.md` file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top-level directories with or without `_index.md` files are also branch bundles. This includes the home page. -{{% note %}} -In the definitions above and the examples below, the extension of the index file depends on the [content format]. For example, use index.md for Markdown content, index.html for HTML content, index.adoc for AsciiDoc content, etc. - -[content format]: /getting-started/glossary/#content-format -{{% /note %}} +> [!note] +> In the definitions above and the examples below, the extension of the index file depends on the [content format](g). For example, use `index.md` for Markdown content, `index.html` for HTML content, `index.adoc` for AsciiDoc content, etc. ## Comparison @@ -47,25 +38,19 @@ Page bundle characteristics vary by bundle type. | | Leaf bundle | Branch bundle | |---------------------|---------------------------------------------------------|---------------------------------------------------------| -| Index file | index.md | _index.md | -| Example | content/about/index.md | content/posts/_index.md | -| [Page kinds] | `page` | `home`, `section`, `taxonomy`, or `term` | -| Template types | [single] | [home], [section], [taxonomy], or [term] | +| Index file | `index.md` | `_index.md` | +| Example | `content/about/index.md` | `content/posts/_index.md ` | +| [Page kinds](g) | `page` | `home`, `section`, `taxonomy`, or `term` | +| Template types | [single] | [home], [section], [taxonomy], or [term] | | Descendant pages | None | Zero or more | | Resource location | Adjacent to the index file or in a nested subdirectory | Same as a leaf bundles, but excludes descendant bundles | -| [Resource types] | `page`, `image`, `video`, etc. | all but `page` | +| [Resource types](g) | `page`, `image`, `video`, etc. | all but `page` | -[single]: /templates/types/#single -[home]: /templates/types/#home -[section]: /templates/types/#section -[taxonomy]: /templates/types/#taxonomy -[term]: /templates/types/#term - -Files with [resource type] `page` include content written in Markdown, HTML, AsciiDoc, Pandoc, reStructuredText, and Emacs Org Mode. In a leaf bundle, excluding the index file, these files are only accessible as page resources. In a branch bundle, these files are only accessible as content pages. +Files with [resource type](g) `page` include content written in Markdown, HTML, AsciiDoc, Pandoc, reStructuredText, and Emacs Org Mode. In a leaf bundle, excluding the index file, these files are only accessible as page resources. In a branch bundle, these files are only accessible as content pages. ## Leaf bundles -A _leaf bundle_ is a directory that contains an index.md file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. +A _leaf bundle_ is a directory that contains an `index.md` file and zero or more resources. Analogous to a physical leaf, a leaf bundle is at the end of a branch. It has no descendants. ```text content/ @@ -94,15 +79,15 @@ about : This leaf bundle does not contain any page resources. my-post -: This leaf bundle contains an index file, two resources of [resource type] `page`, and two resources of resource type `image`. +: This leaf bundle contains an index file, two resources of [resource type](g) `page`, and two resources of resource type `image`. -- content-1, content-2 + - content-1, content-2 - These are resources of resource type `page`, accessible via the [`Resources`] method on the `Page` object. Hugo will not render these as individual pages. + These are resources of resource type `page`, accessible via the [`Resources`] method on the `Page` object. Hugo will not render these as individual pages. -- image-1, image-2 + - image-1, image-2 - These are resources of resource type `image`, accessible via the `Resources` method on the `Page` object + These are resources of resource type `image`, accessible via the `Resources` method on the `Page` object my-other-post : This leaf bundle does not contain any page resources. @@ -110,13 +95,12 @@ my-other-post another-leaf-bundle : This leaf bundle does not contain any page resources. -{{% note %}} -Create leaf bundles at any depth within the content directory, but a leaf bundle may not contain another bundle. Leaf bundles do not have descendants. -{{% /note %}} +> [!note] +> Create leaf bundles at any depth within the `content` directory, but a leaf bundle may not contain another bundle. Leaf bundles do not have descendants. ## Branch bundles -A _branch bundle_ is a directory that contains an _index.md file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top level directories with or without _index.md files are also branch bundles. This includes the home page. +A _branch bundle_ is a directory that contains an `_index.md` file and zero or more resources. Analogous to a physical branch, a branch bundle may have descendants including leaf bundles and other branch bundles. Top-level directories with or without `_index.md` files are also branch bundles. This includes the home page. ```text content/ @@ -139,23 +123,23 @@ home page : This branch bundle contains an index file, two descendant branch bundles, and no resources. branch-bundle-1 -: This branch bundle contains an index file, two resources of [resource type] `page`, and two resources of resource type `image`. +: This branch bundle contains an index file, two resources of [resource type](g) `page`, and two resources of resource type `image`. branch-bundle-2 : This branch bundle contains an index file and a leaf bundle. -{{% note %}} -Create branch bundles at any depth within the content directory, but a leaf bundle may not contain another bundle. Leaf bundles do not have descendants. -{{% /note %}} - +> [!note] +> Create branch bundles at any depth within the `content` directory. Branch bundles may have descendants. ## Headless bundles Use [build options] in front matter to create an unpublished leaf or branch bundle whose content and resources you can include in other pages. [`Resources`]: /methods/page/resources/ -[build options]: content-management/build-options/ -[page kinds]: /getting-started/glossary/#page-kind +[build options]: /content-management/build-options/ +[home]: /templates/types/#home [page resources]: /content-management/page-resources/ -[resource type]: /getting-started/glossary/#resource-type -[resource types]: /getting-started/glossary/#resource-type +[section]: /templates/types/#section +[single]: /templates/types/#single +[taxonomy]: /templates/types/#taxonomy +[term]: /templates/types/#term diff --git a/docs/content/en/content-management/page-resources.md b/docs/content/en/content-management/page-resources.md index 4f902a67f..204ca5301 100644 --- a/docs/content/en/content-management/page-resources.md +++ b/docs/content/en/content-management/page-resources.md @@ -1,18 +1,12 @@ --- title: Page resources description: Use page resources to logically associate assets with a page. -categories: [content management] -keywords: [bundle,content,resources] -menu: - docs: - parent: content-management - weight: 80 -weight: 80 -toc: true +categories: [] +keywords: [] --- Page resources are only accessible from [page bundles](/content-management/page-bundles), those directories with `index.md` or -`_index.md` files at their root. Page resources are only available to the +`_index.md` files at their root. Page resources are only available to the page with which they are bundled. In this example, `first-post` is a page bundle with access to 10 page resources including audio, data, documents, images, and video. Although `second-post` is also a page bundle, it has no page resources and is unable to directly access the page resources associated with `first-post`. @@ -46,13 +40,7 @@ Use any of these methods on a `Page` object to capture page resources: - [`Resources.GetMatch`] - [`Resources.Match`] - Once you have captured a resource, use any of the applicable [`Resource`] methods to return a value or perform an action. - -[`Resource`]: /methods/resource -[`Resources.ByType`]: /methods/page/resources#bytype -[`Resources.GetMatch`]: /methods/page/resources#getmatch -[`Resources.Get`]: /methods/page/resources#get -[`Resources.Match`]: /methods/page/resources#match + Once you have captured a resource, use any of the applicable [`Resource`] methods to return a value or perform an action. The following examples assume this content structure: @@ -120,16 +108,14 @@ List the titles in the data file, and throw an error if the file does not exist. The page resources' metadata is managed from the corresponding page's front matter with an array/table parameter named `resources`. You can batch assign values using [wildcards](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm). -{{% note %}} -Resources of type `page` get `Title` etc. from their own front matter. -{{% /note %}} +> [!note] +> Resources of type `page` get `Title` etc. from their own front matter. name : (`string`) Sets the value returned in `Name`. -{{% note %}} -The methods `Match`, `Get` and `GetMatch` use `Name` to match the resources. -{{% /note %}} +> [!note] +> The methods `Match`, `Get` and `GetMatch` use `Name` to match the resources. title : (`string`) Sets the value returned in `Title` @@ -173,9 +159,8 @@ From the example above: - All `PDF` files will get a new `Name`. The `name` parameter contains a special placeholder [`:counter`](#the-counter-placeholder-in-name-and-title), so the `Name` will be `pdf-file-1`, `pdf-file-2`, `pdf-file-3`. - Every docx in the bundle will receive the `word` icon. -{{% note %}} -The order matters; only the first set values of the `title`, `name` and `params` keys will be used. Consecutive parameters will be set only for the ones not already set. In the above example, `.Params.icon` is first set to `"photo"` in `src = "documents/photo_specs.pdf"`. So that would not get overridden to `"pdf"` by the later set `src = "**.pdf"` rule. -{{% /note %}} +> [!note] +> The order matters; only the first set values of the `title`, `name` and `params` keys will be used. Consecutive parameters will be set only for the ones not already set. In the above example, `.Params.icon` is first set to `"photo"` in `src = "documents/photo_specs.pdf"`. So that would not get overridden to `"pdf"` by the later set `src = "**.pdf"` rule. ### The `:counter` placeholder in `name` and `title` @@ -206,15 +191,12 @@ the `Name` and `Title` will be assigned to the resource files as follows: ## Multilingual -{{< new-in 0.123.0 >}} +{{< new-in 0.123.0 />}} By default, with a multilingual single-host site, Hugo does not duplicate shared page resources when building the site. -{{% note %}} -This behavior is limited to Markdown content. Shared page resources for other [content formats] are copied into each language bundle. - -[content formats]: /content-management/formats/ -{{% /note %}} +> [!note] +> This behavior is limited to Markdown content. Shared page resources for other [content formats] are copied into each language bundle. Consider this site configuration: @@ -289,18 +271,12 @@ public/ This approach reduces build times, storage requirements, bandwidth consumption, and deployment times, ultimately reducing cost. -{{% note %}} -To resolve Markdown link and image destinations to the correct location, you must use link and image render hooks that capture the page resource with the [`Resources.Get`] method, and then invoke its [`RelPermalink`] method. - -By default, with multilingual single-host sites, Hugo enables its [embedded link render hook] and [embedded image render hook] to resolve Markdown link and image destinations. - -You may override the embedded render hooks as needed, provided they capture the resource as described above. - -[embedded link render hook]: /render-hooks/links/#default -[embedded image render hook]: /render-hooks/images/#default -[`Resources.Get`]: /methods/page/resources/#get -[`RelPermalink`]: /methods/resource/relpermalink/ -{{% /note %}} +> [!note] +> To resolve Markdown link and image destinations to the correct location, you must use link and image render hooks that capture the page resource with the [`Resources.Get`] method, and then invoke its [`RelPermalink`] method. +> +> By default, with multilingual single-host sites, Hugo enables its [embedded link render hook] and [embedded image render hook] to resolve Markdown link and image destinations. +> +> You may override the embedded render hooks as needed, provided they capture the resource as described above. Although duplicating shared page resources is inefficient, you can enable this feature in your site configuration if desired: @@ -308,3 +284,14 @@ Although duplicating shared page resources is inefficient, you can enable this f [markup.goldmark] duplicateResourceFiles = true {{< /code-toggle >}} + +[`RelPermalink`]: /methods/resource/relpermalink/ +[`Resource`]: /methods/resource +[`Resources.ByType`]: /methods/page/resources#bytype +[`Resources.Get`]: /methods/page/resources#get +[`Resources.Get`]: /methods/page/resources/#get +[`Resources.GetMatch`]: /methods/page/resources#getmatch +[`Resources.Match`]: /methods/page/resources#match +[content formats]: /content-management/formats/ +[embedded image render hook]: /render-hooks/images/#default +[embedded link render hook]: /render-hooks/links/#default diff --git a/docs/content/en/content-management/related-content.md b/docs/content/en/content-management/related-content.md new file mode 100644 index 000000000..d7b18dab0 --- /dev/null +++ b/docs/content/en/content-management/related-content.md @@ -0,0 +1,102 @@ +--- +title: Related content +description: List related content in "See Also" sections. +categories: [] +keywords: [] +aliases: [/content/related/,/related/,/content-management/related/] +--- + +Hugo uses a set of factors to identify a page's related content based on front matter parameters. This can be tuned to the desired set of indices and parameters or left to Hugo's default [related content configuration](/configuration/related-content/). + +## List related content + +To list up to 5 related pages (which share the same _date_ or _keyword_ parameters) is as simple as including something similar to this partial in your template: + +```go-html-template {file="layouts/partials/related.html" copy=true} +{{ with site.RegularPages.Related . | first 5 }} +

Related content:

+ +{{ end }} +``` + +The `Related` method takes one argument which may be a `Page` or an options map. The options map has these options: + +indices +: (`slice`) The indices to search within. + +document +: (`page`) The page for which to find related content. Required when specifying an options map. + +namedSlices +: (`slice`) The keywords to search for, expressed as a slice of `KeyValues` using the [`keyVals`] function. + +fragments +: (`slice`) A list of special keywords that is used for indices configured as type "fragments". This will match the [fragment](g) identifiers of the documents. + +A fictional example using all of the above options: + +```go-html-template +{{ $page := . }} +{{ $opts := dict + "indices" (slice "tags" "keywords") + "document" $page + "namedSlices" (slice (keyVals "tags" "hugo" "rocks") (keyVals "date" $page.Date)) + "fragments" (slice "heading-1" "heading-2") +}} +``` + +> [!note] +> We improved and simplified this feature in Hugo 0.111.0. Before this we had 3 different methods: `Related`, `RelatedTo` and `RelatedIndices`. Now we have only one method: `Related`. The old methods are still available but deprecated. Also see [this blog article](https://regisphilibert.com/blog/2018/04/hugo-optmized-relashionships-with-related-content/) for a great explanation of more advanced usage of this feature. + +## Index content headings + +Hugo can index the headings in your content and use this to find related content. You can enable this by adding a index of type `fragments` to your `related` configuration: + +{{< code-toggle file=hugo >}} +[related] +threshold = 20 +includeNewer = true +toLower = false +[[related.indices]] +name = "fragmentrefs" +type = "fragments" +applyFilter = true +weight = 80 +{{< /code-toggle >}} + +- The `name` maps to a optional front matter slice attribute that can be used to link from the page level down to the fragment/heading level. +- If `applyFilter` is enabled, the `.HeadingsFiltered` on each page in the result will reflect the filtered headings. This is useful if you want to show the headings in the related content listing: + +```go-html-template +{{ $related := .Site.RegularPages.Related . | first 5 }} +{{ with $related }} +

See Also

+
    + {{ range $i, $p := . }} +
  • + {{ .LinkTitle }} + {{ with .HeadingsFiltered }} +
      + {{ range . }} + {{ $link := printf "%s#%s" $p.RelPermalink .ID | safeURL }} +
    • + {{ .Title }} +
    • + {{ end }} +
    + {{ end }} +
  • + {{ end }} +
+{{ end }} +``` + +## Configuration + +See [configure related content](/configuration/related-content/). + +[`keyVals`]: /functions/collections/keyvals/ diff --git a/docs/content/en/content-management/related.md b/docs/content/en/content-management/related.md deleted file mode 100644 index 0a97bd7cb..000000000 --- a/docs/content/en/content-management/related.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Related content -description: List related content in "See Also" sections. -categories: [content management] -keywords: [content] -menu: - docs: - parent: content-management - weight: 110 -weight: 110 -toc: true -aliases: [/content/related/,/related/] ---- - -Hugo uses a set of factors to identify a page's related content based on front matter parameters. This can be tuned to the desired set of indices and parameters or left to Hugo's default [Related Content configuration](#configure-related-content). - -## List related content - -To list up to 5 related pages (which share the same _date_ or _keyword_ parameters) is as simple as including something similar to this partial in your template: - -{{< code file=layouts/partials/related.html >}} -{{ $related := .Site.RegularPages.Related . | first 5 }} -{{ with $related }} -

See Also

- -{{ end }} -{{< /code >}} - -The `Related` method takes one argument which may be a `Page` or a options map. The options map have these options: - -indices -: (`slice`) The indices to search within. - -document -: (`page`) The page for which to find related content. Required when specifying an options map. - -namedSlices -: (`slice`) The keywords to search for, expressed as a slice of `KeyValues` using the [`keyVals`] function. - -fragments -: (`slice`) A list of special keywords that is used for indices configured as type "fragments". This will match the [fragment] identifiers of the documents. - -[fragment]: /getting-started/glossary/#fragment -[`keyVals`]: /functions/collections/keyvals/ - -A fictional example using all of the above options: - -```go-html-template -{{ $page := . }} -{{ $opts := dict - "indices" (slice "tags" "keywords") - "document" $page - "namedSlices" (slice (keyVals "tags" "hugo" "rocks") (keyVals "date" $page.Date)) - "fragments" (slice "heading-1" "heading-2") -}} -``` - -{{% note %}} -We improved and simplified this feature in Hugo 0.111.0. Before this we had 3 different methods: `Related`, `RelatedTo` and `RelatedIndices`. Now we have only one method: `Related`. The old methods are still available but deprecated. Also see [this blog article](https://regisphilibert.com/blog/2018/04/hugo-optmized-relashionships-with-related-content/) for a great explanation of more advanced usage of this feature. -{{% /note %}} - -## Index content headings in related content - -{{< new-in 0.111.0 >}} - -Hugo can index the headings in your content and use this to find related content. You can enable this by adding a index of type `fragments` to your `related` configuration: - -{{< code-toggle file=hugo >}} -[related] -threshold = 20 -includeNewer = true -toLower = false -[[related.indices]] -name = "fragmentrefs" -type = "fragments" -applyFilter = true -weight = 80 -{{< /code-toggle >}} - -* The `name` maps to a optional front matter slice attribute that can be used to link from the page level down to the fragment/heading level. -* If `applyFilter`is enabled, the `.HeadingsFiltered` on each page in the result will reflect the filtered headings. This is useful if you want to show the headings in the related content listing: - -```go-html-template -{{ $related := .Site.RegularPages.Related . | first 5 }} -{{ with $related }} -

See Also

-
    - {{ range $i, $p := . }} -
  • - {{ .LinkTitle }} - {{ with .HeadingsFiltered }} -
      - {{ range . }} - {{ $link := printf "%s#%s" $p.RelPermalink .ID | safeURL }} -
    • - {{ .Title }} -
    • - {{ end }} -
    - {{ end }} -
  • - {{ end }} -
-{{ end }} -``` - -## Configure related content - -Hugo provides a sensible default configuration of Related Content, but you can fine-tune this in your configuration, on the global or language level if needed. - -### Default configuration - -Without any `related` configuration set on the project, Hugo's Related Content methods will use the following. - -{{< code-toggle config=related />}} - -Custom configuration should be set using the same syntax. - -{{% note %}} -If you add a `related` configuration section, you need to add a complete configuration. It is not possible to just set, say, `includeNewer` and use the rest from the Hugo defaults. -{{% /note %}} - -### Top level configuration options - -threshold -: (`int`) A value between 0-100. Lower value will give more, but maybe not so relevant, matches. - -includeNewer -: (`bool`) Set to `true` to include **pages newer than the current page** in the related content listing. This will mean that the output for older posts may change as new related content gets added. - -toLower -: (`bool`) Set to `true` to lower case keywords in both the indexes and the queries. This may give more accurate results at a slight performance penalty. Note that this can also be set per index. - -### Configuration options per index - -name -: (`string`) The index name. This value maps directly to a page parameter. Hugo supports string values (`author` in the example) and lists (`tags`, `keywords` etc.) and time and date objects. - -type {{< new-in 0.111.0 >}} -: (`string`) One of `basic`(default) or `fragments`. - -applyFilter {{< new-in 0.111.0 >}} -: (`string`) Apply a `type` specific filter to the result of a search. This is currently only used for the `fragments` type. - -weight -: (`int`) An integer weight that indicates _how important_ this parameter is relative to the other parameters. It can be `0`, which has the effect of turning this index off, or even negative. Test with different values to see what fits your content best. - -cardinalityThreshold {{< new-in 0.111.0 >}} -: (`int`) If between 1 and 100, this is a percentage. All keywords that are used in more than this percentage of documents are removed. For example, setting this to `60` will remove all keywords that are used in more than 60% of the documents in the index. If `0`, no keyword is removed from the index. Default is `0`. - -pattern -: (`string`) This is currently only relevant for dates. When listing related content, we may want to list content that is also close in time. Setting "2006" (default value for date indexes) as the pattern for a date index will add weight to pages published in the same year. For busier blogs, "200601" (year and month) may be a better default. - -toLower -: (`bool`) See above. - -## Performance considerations - -**Fast is Hugo's middle name** and we would not have released this feature had it not been blistering fast. - -This feature has been in the back log and requested by many for a long time. The development got this recent kick start from this Twitter thread: - -{{< tweet user="scott_lowe" id="898398437527363585" >}} - -Scott S. Lowe removed the "Related Content" section built using the `intersect` template function on tags, and the build time dropped from 30 seconds to less than 2 seconds on his 1700 content page sized blog. - -He should now be able to add an improved version of that "Related Content" section without giving up the fast live-reloads. But it's worth noting that: - -* If you don't use any of the `Related` methods, you will not use the Relate Content feature, and performance will be the same as before. -* Calling `.RegularPages.Related` etc. will create one inverted index, also sometimes named posting list, that will be reused for any lookups in that same page collection. Doing that in addition to, as an example, calling `.Pages.Related` will work as expected, but will create one additional inverted index. This should still be very fast, but worth having in mind, especially for bigger sites. - -{{% note %}} -We currently do not index **Page content**. We thought we would release something that will make most people happy before we start solving [Sherlock's last case](https://github.com/joearms/sherlock). -{{% /note %}} diff --git a/docs/content/en/content-management/sections.md b/docs/content/en/content-management/sections.md index 03655c90a..f7a2296f5 100644 --- a/docs/content/en/content-management/sections.md +++ b/docs/content/en/content-management/sections.md @@ -2,26 +2,14 @@ title: Sections description: Organize content into sections. -categories: [content management] -keywords: [lists,sections,content types,organization] -menu: - docs: - parent: content-management - weight: 120 -weight: 120 -toc: true +categories: [] +keywords: [] aliases: [/content/sections/] --- ## Overview -A section is a top-level content directory, or any content directory with an _index.md file. A content directory with an _index.md file is also known as a [branch bundle](/getting-started/glossary/#branch-bundle). Section templates receive one or more page [collections](/getting-started/glossary/#collection) in [context](/getting-started/glossary/#context). - -{{% note %}} -Although top-level directories without _index.md files are sections, we recommend creating _index.md files in _all_ sections. -{{% /note %}} - -A typical site consists of one or more sections. For example: +{{% glossary-term "section" %}} ```text content/ @@ -74,14 +62,8 @@ Have list pages|:heavy_check_mark:|:x: With the file structure from the [example above](#overview): 1. The list page for the articles section includes all articles, regardless of directory structure; none of the subdirectories are sections. - 1. The articles/2022 and articles/2023 directories do not have list pages; they are not sections. - 1. The list page for the products section, by default, includes product-1 and product-2, but not their descendant pages. To include descendant pages, use the `RegularPagesRecursive` method instead of the `Pages` method in the list template. - -[`Pages`]: /methods/page/pages/ -[`RegularPagesRecursive`]: /methods/page/regularpagesrecursive/ - 1. All directories in the products section have list pages; each directory is a section. ## Template selection @@ -92,21 +74,18 @@ With the file structure from the [example above](#overview): Content directory|Section template :--|:-- -content/products|layouts/products/list.html -content/products/product-1|layouts/products/list.html -content/products/product-1/benefits|layouts/products/list.html +`content/products`|`layouts/products/list.html` +`content/products/product-1`|`layouts/products/list.html` +`content/products/product-1/benefits`|`layouts/products/list.html` Content directory|Single template :--|:-- -content/products|layouts/products/single.html -content/products/product-1|layouts/products/single.html -content/products/product-1/benefits|layouts/products/single.html +`content/products`|`layouts/products/single.html` +`content/products/product-1`|`layouts/products/single.html` +`content/products/product-1/benefits`|`layouts/products/single.html` If you need to use a different template for a subsection, specify `type` and/or `layout` in front matter. -[lookup rules]: /templates/lookup-order/#lookup-rules -[lookup order]: /templates/lookup-order/ - ## Ancestors and descendants A section has one or more ancestors (including the home page), and zero or more descendants. With the file structure from the [example above](#overview): @@ -119,7 +98,7 @@ The content file (benefit-1.md) has four ancestors: benefits, product-1, product For example, use the `.Ancestors` method to render breadcrumb navigation. -{{< code file=layouts/partials/breadcrumb.html >}} +```go-html-template {file="layouts/partials/breadcrumb.html"} -{{< /code >}} +``` With this CSS: @@ -156,9 +135,5 @@ Hugo renders this, where each breadcrumb is a link to the corresponding page: Home » Products » Product 1 » Benefits » Benefit 1 ``` -[archetype]: /content-management/archetypes/ -[content type]: /content-management/types/ -[directory structure]: /getting-started/directory-structure/ -[section templates]: /templates/types/#section -[leaf bundles]: /content-management/page-bundles/#leaf-bundles -[branch bundles]: /content-management/page-bundles/#branch-bundles +[lookup order]: /templates/lookup-order/ +[lookup rules]: /templates/lookup-order/#lookup-rules diff --git a/docs/content/en/content-management/shortcodes.md b/docs/content/en/content-management/shortcodes.md index 847ba2bbb..2de387f39 100644 --- a/docs/content/en/content-management/shortcodes.md +++ b/docs/content/en/content-management/shortcodes.md @@ -1,477 +1,230 @@ --- title: Shortcodes -description: Shortcodes are simple snippets inside your content files calling built-in or custom templates. -categories: [content management] -keywords: [markdown,content,shortcodes] -menu: - docs: - parent: content-management - weight: 100 -weight: 100 -toc: true +description: Use embedded, custom, or inline shortcodes to insert elements such as videos, images, and social media embeds into your content. +categories: [] +keywords: [] aliases: [/extras/shortcodes/] -testparam: "Hugo Rocks!" --- -## What a shortcode is +## Introduction -Hugo loves Markdown because of its simple content format, but there are times when Markdown falls short. Often, content authors are forced to add raw HTML (e.g., video ` +### Argument collection + +Use the [`Params`] method to access the arguments as a collection. + +When using named arguments, the `Params` method returns a map: + +```text {file="content/example/index.md"} +{{}} +``` + +```go-html-template {file="layouts/shortcodes/image.html"} +{{ .Params.path }} → a.jpg +{{ .Params.width }} → 300 +{{ .Params.alt }} → A white kitten +``` + + When using positional arguments, the `Params` method returns a slice: + +```text {file="content/example/index.md"} +{{}} +``` + +```go-html-template {file="layouts/shortcodes/image.html"} +{{ index .Params 0 }} → a.jpg +{{ index .Params 1 }} → 300 +{{ index .Params 1 }} → A white kitten +``` + +Combine the `Params` method with the [`collections.IsSet`] function to determine if a parameter is set, even if its value is falsy. + +### Inner content + +Extract the content enclosed within shortcode tags using the [`Inner`] method. This example demonstrates how to pass both content and a title to a shortcode. The shortcode then generates a `div` element containing an `h2` element (displaying the title) and the provided content. + +```text {file="content/example.md"} +{{}} +This is a **bold** word, and this is an _emphasized_ word. +{{}} +``` + +```go-html-template {file="layouts/shortcodes/contrived.html"} +
+

{{ .Get "title" }}

+ {{ .Inner | .Page.RenderString }}
-{{< /code >}} - -{{< code file=youtube-embed.html >}} -
- -
-{{< /code >}} - -### Single named example: `image` - -Let's say you want to create your own `img` shortcode rather than use Hugo's built-in [`figure` shortcode][figure]. Your goal is to be able to call the shortcode as follows in your content files: - -{{< code file=content-image.md >}} -{{}} -{{< /code >}} - -You have created the shortcode at `/layouts/shortcodes/img.html`, which loads the following shortcode template: - -{{< code file=layouts/shortcodes/img.html >}} - -
- {{ with .Get "link" }}{{ end }} - - {{ if .Get "link" }}{{ end }} - {{ if or (or (.Get "title") (.Get "caption")) (.Get "attr") }} -
{{ if isset .Params "title" }} -

{{ .Get "title" }}

{{ end }} - {{ if or (.Get "caption") (.Get "attr") }}

- {{ .Get "caption" }} - {{ with .Get "attrlink" }} {{ end }} - {{ .Get "attr" }} - {{ if .Get "attrlink" }} {{ end }} -

{{ end }} -
- {{ end }} -
- -{{< /code >}} - -Would be rendered as: - -{{< code file=img-output.html >}} -
- -
-

Steve Francia

-
-
-{{< /code >}} - -### Single flexible example: `vimeo` - -```go-html-template -{{}} -{{}} ``` -Would load the template found at `/layouts/shortcodes/vimeo.html`: +The preceding example called the shortcode using [standard notation], requiring us to process the inner content with the [`RenderString`] method to convert the Markdown to HTML. This conversion is unnecessary when calling a shortcode using [Markdown notation]. -{{< code file=layouts/shortcodes/vimeo.html >}} -{{ if .IsNamedParams }} -
- -
-{{ else }} -
- -
-{{ end }} -{{< /code >}} +### Nesting -Would be rendered as: - -{{< code file=vimeo-iframes.html >}} -
- -
-
- -
-{{< /code >}} - -### Paired example: `highlight` - -The following is taken from `highlight`, which is a [built-in shortcode] that ships with Hugo. - -{{< code file=highlight-example.md >}} -{{}} - - This HTML - -{{}} -{{< /code >}} - -The template for the `highlight` shortcode uses the following code, which is already included in Hugo: - -```go-html-template -{{ .Get 0 | highlight .Inner }} -``` - -The rendered output of the HTML example code block will be as follows: - -{{< code file=syntax-highlighted.html >}} -
<html>
-    <body> This HTML </body>
-</html>
-
-{{< /code >}} - -### Nested shortcode: image gallery - -Hugo's [`.Parent`] shortcode method provides access to the parent shortcode context when the shortcode in question is called within the context of a parent shortcode. This provides an inheritance model. +The [`Parent`] method provides access to the parent shortcode context when the shortcode in question is called within the context of a parent shortcode. This provides an inheritance model. The following example is contrived but demonstrates the concept. Assume you have a `gallery` shortcode that expects one named `class` argument: -{{< code file=layouts/shortcodes/gallery.html >}} +```go-html-template {file="layouts/shortcodes/gallery.html"}
{{ .Inner }}
-{{< /code >}} +``` You also have an `img` shortcode with a single named `src` argument that you want to call inside of `gallery` and other shortcodes, so that the parent defines the context of each `img`: -{{< code file=layouts/shortcodes/img.html >}} -{{- $src := .Get "src" -}} -{{- with .Parent -}} +```go-html-template {file="layouts/shortcodes/img.html"} +{{ $src := .Get "src" }} +{{ with .Parent }} -{{- else -}} +{{ else }} -{{- end -}} -{{< /code >}} +{{ end }} +``` You can then call your shortcode in your content as follows: -```go-html-template +```text {file="content/example.md"} {{}} {{}} {{}} @@ -355,68 +285,54 @@ This will output the following HTML. Note how the first two `img` shortcodes inh ```html ``` -## Error handling in shortcodes +### Other examples -Use the [`errorf`] template function with the [`Name`] and [`Position`] shortcode methods to generate useful error messages: +For guidance, consider examining Hugo's embedded shortcodes. The source code, available on [GitHub], can provide a useful model. -{{< code file=layouts/shortcodes/greeting.html >}} -{{ with .Get "name" }} -

Hello, my name is {{ . }}.

-{{ else }} - {{ errorf "The %q shortcode requires a 'name' argument. See %s" .Name .Position }} -{{ end }} -{{< /code >}} +## Detection -When the above fails, you will see an `ERROR` message such as: +The [`HasShortcode`] method allows you to check if a specific shortcode has been called on a page. For example, consider a custom audio shortcode: -```sh -ERROR The "greeting" shortcode requires a 'name' argument. See "/home/user/project/content/_index.md:12:1" +```text {file="content/example.md"} +{{}} ``` -## Inline shortcodes +You can use the `HasShortcode` method in your base template to conditionally load CSS if the audio shortcode was used on the page: -You can also implement your shortcodes inline -- e.g. where you use them in the content file. This can be useful for scripting that you only need in one place. - -This feature is disabled by default, but can be enabled in your site configuration: - -{{< code-toggle file=hugo >}} -[security] -enableInlineShortcodes = true -{{< /code-toggle >}} - -It is disabled by default for security reasons. The security model used by Hugo's template handling assumes that template authors are trusted, but that the content files are not, so the templates are injection-safe from malformed input data. But in most situations you have full control over the content, too, and then `enableInlineShortcodes = true` would be considered safe. But it's something to be aware of: It allows ad-hoc [Go Text templates](https://golang.org/pkg/text/template/) to be executed from the content files. - -And once enabled, you can do this in your content files: - - ```go-html-template - {{}}{{ now }}{{}} - ``` - -The above will print the current date and time. - -Note that an inline shortcode's inner content is parsed and executed as a Go text template with the same context as a regular shortcode template. - -This means that the current page can be accessed via `.Page.Title` etc. This also means that there are no concept of "nested inline shortcodes". - -The same inline shortcode can be reused later in the same content file, with different arguments if needed, using the self-closing syntax: - - ```go-html-template -{{}} +```go-html-template {file="layouts/_default/baseof.html"} + + ... + {{ if .HasShortcode "audio" }} + + {{ end }} + ... + ``` -[`.Parent`]: /methods/shortcode/parent/ -[`errorf`]: /functions/fmt/errorf/ +[`collections.IsSet`]: /functions/collections/isset/ +[`compare.Conditional`]: /functions/compare/conditional/ +[`Get`]: /methods/shortcode/get/ +[`HasShortcode`]: /methods/page/hasshortcode/ +[`Inner`]: /methods/shortcode/inner/ +[`IsNamedParams`]: /methods/shortcode/isnamedparams/ [`Name`]: /methods/shortcode/name/ +[`Params`]: /methods/shortcode/params/ +[`Parent`]: /methods/shortcode/parent/ [`Position`]: /methods/shortcode/position/ -[built-in shortcode]: /content-management/shortcodes/ -[figure]: /content-management/shortcodes/#figure -[lookup order]: /templates/lookup-order/ -[source organization]: /getting-started/directory-structure/ -[vimeoexample]: #single-flexible-example-vimeo -[youtubeshortcode]: /content-management/shortcodes/#youtube +[`RenderString`]: /methods/page/renderstring/ +[`with`]: /functions/go-template/with/ +[content management]: /content-management/shortcodes/ +[embedded shortcodes]: /shortcodes/ +[GitHub]: https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/_shortcodes +[introduction to templating]: /templates/introduction/ +[Markdown notation]: /content-management/shortcodes/#markdown-notation +[named or positional]: /content-management/shortcodes/#arguments +[shortcodes]: /content-management/shortcodes/ +[standard notation]: /content-management/shortcodes/#standard-notation +[whitespace]: /templates/introduction/#whitespace diff --git a/docs/content/en/templates/single.md b/docs/content/en/templates/single.md index f6292ca03..6f244ef10 100644 --- a/docs/content/en/templates/single.md +++ b/docs/content/en/templates/single.md @@ -1,14 +1,9 @@ --- title: Single templates description: Create a single template to render a single page. -categories: [templates] +categories: [] keywords: [] -menu: - docs: - parent: templates - weight: 70 -weight: 70 -toc: true +weight: 60 aliases: [/layout/content/,/templates/single-page-templates/] --- @@ -16,12 +11,12 @@ The single template below inherits the site's shell from the [base template]. [base template]: /templates/types/ -{{< code file=layouts/_default/single.html >}} +```go-html-template {file="layouts/_default/single.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} {{ end }} -{{< /code >}} +``` Review the [template lookup order] to select a template path that provides the desired level of specificity. @@ -29,7 +24,7 @@ Review the [template lookup order] to select a template path that provides the d The single template below inherits the site's shell from the base template, and renders the page title, creation date, content, and a list of associated terms in the "tags" taxonomy. -{{< code file=layouts/_default/single.html >}} +```go-html-template {file="layouts/_default/single.html"} {{ define "main" }}

{{ .Title }}

@@ -53,4 +48,4 @@ The single template below inherits the site's shell from the base template, and
{{ end }} -{{< /code >}} +``` diff --git a/docs/content/en/templates/sitemap.md b/docs/content/en/templates/sitemap.md index 9fc152210..bf0850eef 100644 --- a/docs/content/en/templates/sitemap.md +++ b/docs/content/en/templates/sitemap.md @@ -1,14 +1,9 @@ --- title: Sitemap templates description: Hugo provides built-in sitemap templates. -categories: [templates] +categories: [] keywords: [] -menu: - docs: - parent: templates - weight: 140 -weight: 140 -toc: true +weight: 130 aliases: [/layout/sitemap/,/templates/sitemap-template/] --- @@ -23,26 +18,9 @@ With a multilingual project, Hugo generates: - A sitemap.xml file in the root of each site (language) using the [embedded sitemap template] - A sitemap.xml file in the root of the [`publishDir`] using the [embedded sitemapindex template] -[embedded sitemap template]: {{% eturl sitemap %}} -[embedded sitemapindex template]: {{% eturl sitemapindex %}} - ## Configuration -These are the default sitemap configuration values. They apply to all pages unless overridden in front matter. - -{{< code-toggle config=sitemap />}} - -changefreq -: (`string`) How frequently a page is likely to change. Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, and `never`. With the default value of `""` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#changefreqdef). - -disable {{< new-in 0.125.0 >}} -: (`bool`) Whether to disable page inclusion. Default is `false`. Set to `true` in front matter to exclude the page. - -filename -: (`string`) The name of the generated file. Default is `sitemap.xml`. - -priority -: (`float`) The priority of a page relative to any other page on the site. Valid values range from 0.0 to 1.0. With the default value of `-1` Hugo will omit this field from the sitemap. See [details](https://www.sitemaps.org/protocol.html#priority). +See [configure sitemap](/configuration/sitemap). ## Override default values @@ -60,15 +38,15 @@ title = 'News' To override the built-in sitemap.xml template, create a new file in either of these locations: -- layouts/sitemap.xml -- layouts/_default/sitemap.xml +- `layouts/sitemap.xml` +- `layouts/_default/sitemap.xml` When ranging through the page collection, access the _change frequency_ and _priority_ with `.Sitemap.ChangeFreq` and `.Sitemap.Priority` respectively. To override the built-in sitemapindex.xml template, create a new file in either of these locations: -- layouts/sitemapindex.xml -- layouts/_default/sitemapindex.xml +- `layouts/sitemapindex.xml` +- `layouts/_default/sitemapindex.xml` ## Disable sitemap generation @@ -78,5 +56,7 @@ You may disable sitemap generation in your site configuration: disableKinds = ['sitemap'] {{}} -[`publishDir`]: /getting-started/configuration#publishdir -[sitemap protocol]: +[`publishDir`]: /configuration/all/#publishdir +[embedded sitemap template]: {{% eturl sitemap %}} +[embedded sitemapindex template]: {{% eturl sitemapindex %}} +[sitemap protocol]: https://www.sitemaps.org/protocol.html diff --git a/docs/content/en/templates/taxonomy.md b/docs/content/en/templates/taxonomy.md index d0f24b1b2..96c93ec95 100644 --- a/docs/content/en/templates/taxonomy.md +++ b/docs/content/en/templates/taxonomy.md @@ -1,24 +1,17 @@ --- title: Taxonomy templates description: Create a taxonomy template to render a list of terms. -categories: [templates] +categories: [] keywords: [] -menu: - docs: - parent: templates - weight: 90 -weight: 90 -toc: true +weight: 80 aliases: [/taxonomies/displaying/,/templates/terms/,/indexes/displaying/,/taxonomies/templates/,/indexes/ordering/, /templates/taxonomies/, /templates/taxonomy-templates/] --- -The [taxonomy] template below inherits the site's shell from the [base template], and renders a list of [terms] in the current taxonomy. +The [taxonomy](g) template below inherits the site's shell from the [base template], and renders a list of [terms](g) in the current taxonomy. -[taxonomy]: /getting-started/glossary/#taxonomy -[terms]: /getting-started/glossary/#term [base template]: /templates/types/ -{{< code file=layouts/_default/taxonomy.html >}} +```go-html-template {file="layouts/_default/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -26,7 +19,7 @@ The [taxonomy] template below inherits the site's shell from the [base template]

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` Review the [template lookup order] to select a template path that provides the desired level of specificity. @@ -57,9 +50,7 @@ Plural ``` Terms -: (`page.Taxonomy`) Returns the `Taxonomy` object, consisting of a map of terms and the [weighted pages] associated with each term. - -[weighted pages]: /getting-started/glossary/#weighted-page +: (`page.Taxonomy`) Returns the `Taxonomy` object, consisting of a map of terms and the [weighted pages](g) associated with each term. ```go-html-template {{ $taxonomyObject := .Data.Terms }} @@ -73,7 +64,7 @@ Once we have the `Taxonomy` object, we can call any of its [methods], allowing u The taxonomy template below inherits the site's shell from the base template, and renders a list of terms in the current taxonomy. Hugo sorts the list alphabetically by term, and displays the number of pages associated with each term. -{{< code file=layouts/_default/taxonomy.html >}} +```go-html-template {file="layouts/_default/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -81,13 +72,13 @@ The taxonomy template below inherits the site's shell from the base template, an

{{ .Page.LinkTitle }} ({{ .Count }})

{{ end }} {{ end }} -{{< /code >}} +``` ## Sort by term count The taxonomy template below inherits the site's shell from the base template, and renders a list of terms in the current taxonomy. Hugo sorts the list by the number of pages associated with each term, and displays the number of pages associated with each term. -{{< code file=layouts/_default/taxonomy.html >}} +```go-html-template {file="layouts/_default/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -95,19 +86,18 @@ The taxonomy template below inherits the site's shell from the base template, an

{{ .Page.LinkTitle }} ({{ .Count }})

{{ end }} {{ end }} -{{< /code >}} +``` ## Include content links -The [`Alphabetical`] and [`ByCount`] methods used in the previous examples return an [ordered taxonomy], so we can also list the content to which each term is assigned. +The [`Alphabetical`] and [`ByCount`] methods used in the previous examples return an [ordered taxonomy](g), so we can also list the content to which each term is assigned. -[ordered taxonomy]: /getting-started/glossary/#ordered-taxonomy [`Alphabetical`]: /methods/taxonomy/alphabetical/ [`ByCount`]: /methods/taxonomy/bycount/ The taxonomy template below inherits the site's shell from the base template, and renders a list of terms in the current taxonomy. Hugo sorts the list by the number of pages associated with each term, displays the number of pages associated with each term, then lists the content to which each term is assigned. -{{< code file=layouts/_default/taxonomy.html >}} +```go-html-template {file="layouts/_default/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -120,11 +110,11 @@ The taxonomy template below inherits the site's shell from the base template, an {{ end }} {{ end }} -{{< /code >}} +``` ## Display metadata -Display metadata about each term by creating a corresponding branch bundle in the content directory. +Display metadata about each term by creating a corresponding branch bundle in the `content` directory. For example, create an "authors" taxonomy: @@ -133,9 +123,7 @@ For example, create an "authors" taxonomy: author = 'authors' {{< /code-toggle >}} -Then create content with one [branch bundle] for each term: - -[branch bundle]: /getting-started/glossary/#branch-bundle +Then create content with one [branch bundle](g) for each term: ```text content/ @@ -157,7 +145,7 @@ affiliation = "University of Chicago" Then create a taxonomy template specific to the "authors" taxonomy: -{{< code file=layouts/authors/taxonomy.html >}} +```go-html-template {file="layouts/authors/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -171,6 +159,6 @@ Then create a taxonomy template specific to the "authors" taxonomy: {{ end }} {{ end }} {{ end }} -{{< /code >}} +``` In the example above we list each author including their affiliation and portrait. diff --git a/docs/content/en/templates/term.md b/docs/content/en/templates/term.md index 5becd0aa0..cf1097e86 100644 --- a/docs/content/en/templates/term.md +++ b/docs/content/en/templates/term.md @@ -1,22 +1,16 @@ --- title: Term templates description: Create a term template to render a list of pages associated with the current term. -categories: [templates] +categories: [] keywords: [] -menu: - docs: - parent: templates - weight: 100 -weight: 100 -toc: true +weight: 90 --- -The [term] template below inherits the site's shell from the [base template], and renders a list of pages associated with the current term. +The [term](g) template below inherits the site's shell from the [base template], and renders a list of pages associated with the current term. -[term]: /getting-started/glossary/#term [base template]: /templates/types/ -{{< code file=layouts/_default/term.html >}} +```go-html-template {file="layouts/_default/term.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -24,7 +18,7 @@ The [term] template below inherits the site's shell from the [base template], an

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` Review the [template lookup order] to select a template path that provides the desired level of specificity. @@ -63,7 +57,7 @@ Term ## Display metadata -Display metadata about each term by creating a corresponding branch bundle in the content directory. +Display metadata about each term by creating a corresponding branch bundle in the `content` directory. For example, create an "authors" taxonomy: @@ -72,9 +66,7 @@ For example, create an "authors" taxonomy: author = 'authors' {{< /code-toggle >}} -Then create content with one [branch bundle] for each term: - -[branch bundle]: /getting-started/glossary/#branch-bundle +Then create content with one [branch bundle](g) for each term: ```text content/ @@ -96,7 +88,7 @@ affiliation = "University of Chicago" Then create a term template specific to the "authors" taxonomy: -{{< code file=layouts/authors/term.html >}} +```go-html-template {file="layouts/authors/term.html"} {{ define "main" }}

{{ .Title }}

Affiliation: {{ .Params.affiliation }}

@@ -110,6 +102,6 @@ Then create a term template specific to the "authors" taxonomy:

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` In the example above we display the author with their affiliation and portrait, then a list of associated content. diff --git a/docs/content/en/templates/types/index.md b/docs/content/en/templates/types.md similarity index 72% rename from docs/content/en/templates/types/index.md rename to docs/content/en/templates/types.md index 934105d88..b44d3eb47 100644 --- a/docs/content/en/templates/types/index.md +++ b/docs/content/en/templates/types.md @@ -1,23 +1,15 @@ --- title: Template types -linkTitle: Template types description: Create templates of different types to render your content, resources, and data. -categories: [templates] +categories: [] keywords: [] -menu: - docs: - parent: templates - weight: 30 weight: 30 -toc: true aliases: ['/templates/lists/'] --- -[![structural diagram of a website](site-hierarchy.svg)](site-hierarchy.svg) - ## Structure -Create templates in the layouts directory in the root of your project. +Create templates in the `layouts` directory in the root of your project. Although your site may not require each of these templates, the example below is typical for a site of medium complexity. @@ -45,13 +37,8 @@ layouts/ Hugo's [template lookup order] determines the template path, allowing you to create unique templates for any page. -[template lookup order]: /templates/lookup-order/ - -{{% note %}} -You must have thorough understanding of the [template lookup order] when creating templates. Template selection is based on template type, page kind, content type, section, language, and output format. - -[template lookup order]: /templates/lookup-order/ -{{% /note %}} +> [!note] +> You must have thorough understanding of the template lookup order when creating templates. Template selection is based on template type, page kind, content type, section, language, and output format. The purpose of each template type is described below. @@ -61,10 +48,7 @@ Base templates reduce duplicate code by wrapping other templates within a shell. For example, the base template below calls the [partial] function to include partial templates for the `head`, `header`, and `footer` elements of each page, and it uses the [block] function to include `home`, `single`, `section`, `taxonomy`, and `term` templates within the `main` element of each page. -[block]: /functions/go-template/block/ -[partial]: /functions/partials/include/ - -{{< code file=layouts/_default/baseof.html >}} +```go-html-template {file="layouts/_default/baseof.html"} @@ -82,28 +66,26 @@ For example, the base template below calls the [partial] function to include par -{{< /code >}} +``` Learn more about [base templates](/templates/base/). ## Home -A home template renders your site's home page. For a single page site this is the only required template. +A home page template is used to render your site's home page, and is the only template required for a single-page website. For example, the home page template below inherits the site's shell from the base template and renders the home page content, such as a list of other pages. -For example, the home template below inherits the site's shell from the base template, and renders the home page content with a list of pages. - -{{< code file=layouts/_default/home.html >}} +```go-html-template {file="layouts/_default/home.html"} {{ define "main" }} {{ .Content }} {{ range site.RegularPages }}

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` -{{% include "templates/_common/filter-sort-group.md" %}} +{{% include "/_common/filter-sort-group.md" %}} -Learn more about [home templates](/templates/home/). +Learn more about [home page templates](/templates/home/). ## Single @@ -111,12 +93,12 @@ A single template renders a single page. For example, the single template below inherits the site's shell from the base template, and renders the title and content of each page. -{{< code file=layouts/_default/single.html >}} +```go-html-template {file="layouts/_default/single.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} {{ end }} -{{< /code >}} +``` Learn more about [single templates](/templates/single/). @@ -126,7 +108,7 @@ A section template typically renders a list of pages within a section. For example, the section template below inherits the site's shell from the base template, and renders a list of pages in the current section. -{{< code file=layouts/_default/section.html >}} +```go-html-template {file="layouts/_default/section.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -134,21 +116,19 @@ For example, the section template below inherits the site's shell from the base

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` -{{% include "templates/_common/filter-sort-group.md" %}} +{{% include "/_common/filter-sort-group.md" %}} Learn more about [section templates](/templates/section/). ## Taxonomy -A taxonomy template renders a list of terms in a [taxonomy]. - -[taxonomy]: /getting-started/glossary/#taxonomy +A taxonomy template renders a list of terms in a [taxonomy](g). For example, the taxonomy template below inherits the site's shell from the base template, and renders a list of terms in the current taxonomy. -{{< code file=layouts/_default/taxonomy.html >}} +```go-html-template {file="layouts/_default/taxonomy.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -156,21 +136,19 @@ For example, the taxonomy template below inherits the site's shell from the base

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` -{{% include "templates/_common/filter-sort-group.md" %}} +{{% include "/_common/filter-sort-group.md" %}} Learn more about [taxonomy templates](/templates/taxonomy/). ## Term -A term template renders a list of pages associated with a [term]. - -[term]: /getting-started/glossary/#term +A term template renders a list of pages associated with a [term](g). For example, the term template below inherits the site's shell from the base template, and renders a list of pages associated with the current term. -{{< code file=layouts/_default/term.html >}} +```go-html-template {file="layouts/_default/term.html"} {{ define "main" }}

{{ .Title }}

{{ .Content }} @@ -178,9 +156,9 @@ For example, the term template below inherits the site's shell from the base tem

{{ .LinkTitle }}

{{ end }} {{ end }} -{{< /code >}} +``` -{{% include "templates/_common/filter-sort-group.md" %}} +{{% include "/_common/filter-sort-group.md" %}} Learn more about [term templates](/templates/term/). @@ -188,17 +166,14 @@ Learn more about [term templates](/templates/term/). A partial template is typically used to render a component of your site, though you may also create partial templates that return values. -{{% note %}} -Unlike other template types, you cannot create partial templates to target a particular page kind, content type, section, language, or output format. Partial templates do not follow Hugo's [template lookup order]. - -[template lookup order]: /templates/lookup-order/ -{{% /note %}} +> [!note] +> Unlike other template types, you cannot create partial templates to target a particular page kind, content type, section, language, or output format. Partial templates do not follow Hugo's [template lookup order]. For example, the partial template below renders copyright information. -{{< code file=layouts/partials/footer.html >}} +```go-html-template {file="layouts/partials/footer.html"}

Copyright {{ now.Year }}. All rights reserved.

-{{< /code >}} +``` Learn more about [partial templates](/templates/partial/). @@ -209,11 +184,9 @@ A content view template is similar to a partial template, invoked by calling the - Automatically inherit the context of the current page - Follow a lookup order allowing you to target a given content type or section -[`Render`]: /methods/page/render/ - For example, the home template below inherits the site's shell from the base template, and renders a card component for each page within the "articles" section of your site. -{{< code file=layouts/_default/home.html >}} +```go-html-template {file="layouts/_default/home.html"} {{ define "main" }} {{ .Content }}
    @@ -222,14 +195,14 @@ For example, the home template below inherits the site's shell from the base tem {{ end }}
{{ end }} -{{< /code >}} +``` -{{< code file=layouts/articles/card.html >}} +```go-html-template {file="layouts/articles/card.html"}

{{ .LinkTitle }}

{{ .Summary }}
-{{< /code >}} +``` Learn more about [content view templates](/templates/content-view/). @@ -239,16 +212,16 @@ A render hook template overrides the conversion of Markdown to HTML. For example, the render hook template below adds a `rel` attribute to external links. -{{< code file=layouts/_default/_markup/render-link.html >}} +```go-html-template {file="layouts/_default/_markup/render-link.html"} {{- $u := urls.Parse .Destination -}} - {{- with .Text | safeHTML }}{{ . }}{{ end -}} + {{- with .Text }}{{ . }}{{ end -}} {{- /* chomp trailing newline */ -}} -{{< /code >}} +``` Learn more about [render hook templates](/render-hooks/). @@ -256,21 +229,19 @@ Learn more about [render hook templates](/render-hooks/). A shortcode template is used to render a component of your site. Unlike partial templates, shortcode templates are called from content pages. -For example, the shortcode template below renders an audio element from a [global resource]. +For example, the shortcode template below renders an audio element from a [global resource](g). -[global resource]: /getting-started/glossary/#global-resource - -{{< code file=layouts/shortcodes/audio.html >}} +```go-html-template {file="layouts/shortcodes/audio.html"} {{ with resources.Get (.Get "src") }} {{ end }} -{{< /code >}} +``` -Call the shortcode from your content page: +Then call the shortcode from within markup: -{{< code file=content/example.md >}} -{{}} -{{< /code >}} +```text {file="content/example.md"} +{{}} +``` Learn more about [shortcode templates](/templates/shortcode/). @@ -282,3 +253,9 @@ Use other specialized templates to create: - [RSS feeds](/templates/rss/) - [404 error pages](/templates/404/) - [robots.txt files](/templates/robots/) + +[`Render`]: /methods/page/render/ +[block]: /functions/go-template/block/ +[partial]: /functions/partials/include/ +[template lookup order]: /templates/lookup-order/ +[template lookup order]: /templates/lookup-order/ diff --git a/docs/content/en/templates/types/site-hierarchy.svg b/docs/content/en/templates/types/site-hierarchy.svg deleted file mode 100644 index 3c744871b..000000000 --- a/docs/content/en/templates/types/site-hierarchy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/content/en/tools/_index.md b/docs/content/en/tools/_index.md index 9cd72853a..3acc287ae 100644 --- a/docs/content/en/tools/_index.md +++ b/docs/content/en/tools/_index.md @@ -1,20 +1,7 @@ --- title: Developer tools -linkTitle: In this section -description: In addition to Hugo's powerful CLI, there is a large number of community-developed tool chains for Hugo developers. +description: Third-party tools to help you create and manage sites. categories: [] keywords: [] -menu: - docs: - identifier: developer-tools-in-this-section - parent: developer-tools - weight: 10 weight: 10 --- - -One of Hugo's greatest strengths is its passionate---and always evolving---developer community. With the exception of the `highlight` shortcode mentioned in [Syntax Highlighting][syntax], the tools and other projects featured in this section are offerings from both commercial services and open-source projects, many of which are developed by Hugo developers just like you. - -[See the popularity of Hugo compared with other static site generators.][staticgen] - -[staticgen]: https://staticgen.com -[syntax]: /content-management/syntax-highlighting/ diff --git a/docs/content/en/tools/editors.md b/docs/content/en/tools/editors.md index b2e2cc47b..c375fcba8 100644 --- a/docs/content/en/tools/editors.md +++ b/docs/content/en/tools/editors.md @@ -1,15 +1,9 @@ --- title: Editor plugins -linkTitle: Editor plugins description: The Hugo community uses a wide range of tools and has developed plugins for some of the most popular text editors to help automate parts of your workflow. -categories: [developer tools] -keywords: [editor,plugin] -menu: - docs: - parent: developer-tools - weight: 20 -weight: 20 -toc: true +categories: [] +keywords: [] +weight: 10 --- ## Visual Studio Code diff --git a/docs/content/en/tools/front-ends.md b/docs/content/en/tools/front-ends.md index 217f96e2c..0c52a4687 100644 --- a/docs/content/en/tools/front-ends.md +++ b/docs/content/en/tools/front-ends.md @@ -2,14 +2,9 @@ title: Front-end interfaces linkTitle: Front-ends description: Do you prefer a graphical user interface over a text editor? Give these front-ends a try. -categories: [developer tools] -keywords: [frontend, gui] -menu: - docs: - parent: developer-tools - weight: 30 -weight: 30 -toc: true +categories: [] +keywords: [] +weight: 20 aliases: [/tools/frontends/] --- diff --git a/docs/content/en/tools/migrations.md b/docs/content/en/tools/migrations.md index 99064e271..103e28b9e 100644 --- a/docs/content/en/tools/migrations.md +++ b/docs/content/en/tools/migrations.md @@ -2,14 +2,9 @@ title: Migrate to Hugo linkTitle: Migrations description: A list of community-developed tools for migrating from your existing static site generator or content management system to Hugo. -categories: [developer tools] -keywords: [migrations,jekyll,wordpress,drupal,ghost,contentful] -menu: - docs: - parent: developer-tools - weight: 50 -weight: 50 -toc: true +categories: [] +keywords: [] +weight: 40 aliases: [/developer-tools/migrations/, /developer-tools/migrated/] --- @@ -35,7 +30,7 @@ Alternatively, you can use the [Jekyll import command](/commands/hugo_import_jek ## DokuWiki [dokuwiki-to-hugo](https://github.com/wgroeneveld/dokuwiki-to-hugo) -: Migrates your DokuWiki source pages from [DokuWiki syntax](https://www.dokuwiki.org/wiki:syntax) to Hugo Markdown syntax. Includes extras like the TODO plugin. Written with extensibility in mind using Python 3. Also generates a TOML header for each page. Designed to copy-paste the wiki directory into your /content directory. +: Migrates your DokuWiki source pages from [DokuWiki syntax](https://www.dokuwiki.org/wiki:syntax) to Hugo Markdown syntax. Includes extras like the TODO plugin. Written with extensibility in mind using Python 3. Also generates a TOML header for each page. Designed to copy-paste the wiki directory into your `content` directory. ## WordPress @@ -49,7 +44,7 @@ Alternatively, you can use the [Jekyll import command](/commands/hugo_import_jek : A small utility written in Java that exports the entire WordPress site from the database and resource (e.g., images) files stored locally or remotely. Therefore, migration from the backup files is possible. Supports merging multiple WordPress sites into a single Hugo site. [wp2hugo](https://github.com/ashishb/wp2hugo) -: A Go-based CLI tool to migrate WordPress website to Hugo while preserving original URLs, GUIDs (for feeds), image URLs, code highlights, table of contents, YouTube embeds, Google Maps embeds, and original WordPress navigation categories. +: A Go-based CLI tool to migrate WordPress website to Hugo while preserving original URLs, GUIDs (for feeds), image URLs, code highlights, table of contents, YouTube embeds, Google Maps embeds, and original WordPress navigation categories. ## Medium @@ -68,7 +63,7 @@ Alternatively, you can use the [Jekyll import command](/commands/hugo_import_jek : Export all your Tumblr content to Hugo Markdown files with preserved original formatting. [Tumblr to Hugo](https://github.com/jipiboily/tumblr-to-hugo) -: A migration tool that converts each of your Tumblr posts to a content file with a proper title and path. It also generates a CSV file to help you set up URL redirects. +: A migration tool that converts each of your Tumblr posts to a content file with a proper title and path. It also generates a CSV file to help you set up URL redirects. ## Drupal diff --git a/docs/content/en/tools/other.md b/docs/content/en/tools/other.md index 8edfc4258..489d78506 100644 --- a/docs/content/en/tools/other.md +++ b/docs/content/en/tools/other.md @@ -2,13 +2,9 @@ title: Other community projects linkTitle: Other projects description: Some interesting projects developed by the Hugo community that don't quite fit into our other developer tool categories. -categories: [developer tools] -keywords: [frontend,gui] -menu: - docs: - parent: developer-tools - weight: 60 -weight: 60 +categories: [] +keywords: [] +weight: 50 --- And for all the other community projects around Hugo: diff --git a/docs/content/en/tools/search.md b/docs/content/en/tools/search.md index 614bc511c..2c392c75a 100644 --- a/docs/content/en/tools/search.md +++ b/docs/content/en/tools/search.md @@ -2,14 +2,9 @@ title: Search tools linkTitle: Search description: See some of the open-source and commercial search options for your newly created Hugo website. -categories: [developer tools] -keywords: [search] -menu: - docs: - parent: developer-tools - weight: 40 -weight: 40 -toc: true +categories: [] +keywords: [] +weight: 30 --- A static website with a dynamic search function? Yes, Hugo provides an alternative to embeddable scripts from Google or other search engines for static websites. Hugo allows you to provide your visitors with a custom search function by indexing your content files directly. @@ -29,7 +24,7 @@ A static website with a dynamic search function? Yes, Hugo provides an alternati : A bit like Hugo-lunr, but Hugo-lunr-zh can help you separate the Chinese keywords. [GitHub Gist for Fuse.js integration](https://gist.github.com/eddiewebb/735feb48f50f0ddd65ae5606a1cb41ae) -: This gist demonstrates how to leverage Hugo's existing build time processing to generate a searchable JSON index used by [Fuse.js](https://fusejs.io/) on the client-side. Although this gist uses Fuse.js for fuzzy matching, any client-side search tool capable of reading JSON indexes will work. Does not require npm, grunt, or other build-time tools except Hugo! +: This gist demonstrates how to leverage Hugo's existing build time processing to generate a searchable JSON index used by [Fuse.js](https://fusejs.io/) on the client side. Although this gist uses Fuse.js for fuzzy matching, any client-side search tool capable of reading JSON indexes will work. Does not require npm, grunt, or other build-time tools except Hugo! [hugo-search-index](https://www.npmjs.com/package/hugo-search-index) : A library containing Gulp tasks and a prebuilt browser script that implements search. Gulp generates a search index from project Markdown files. @@ -43,14 +38,13 @@ A static website with a dynamic search function? Yes, Hugo provides an alternati [Hugo Lyra](https://github.com/paolomainardi/hugo-lyra) : Hugo-Lyra is a JavaScript module to integrate [Lyra](https://github.com/LyraSearch/lyra) into a Hugo website. It contains the server-side part to generate the index and the client-side library (optional) to bootstrap the search engine easily. -[INFINI Pizza for WebAssembly](https://github.com/infinilabs/pizza-docsearch) +[INFINI Pizza for WebAssembly](https://github.com/infinilabs/pizza-docsearch) : Pizza is a super-lightweight yet fully featured search engine written in Rust. You can quickly add offline search functionality to your Hugo website in just five minutes with only three lines of code. For a step-by-step guide on integrating it with Hugo, check out [this blog tutorial](https://dev.to/medcl/adding-search-functionality-to-a-hugo-static-site-based-on-infini-pizza-for-webassembly-4h5e). - ## Commercial -[Algolia](https://www.algolia.com/) -: Algolia's Search API makes it easy to deliver a great search experience in your apps and websites. Algolia Search provides hosted full-text, numerical, faceted, and geolocalized search. +[Algolia DocSearch](https://docsearch.algolia.com/) +: Algolia DocSearch is free for public technical documentation sites and easy to set up. For other use cases, [Algolia's Search API](https://www.algolia.com) makes it easy to deliver a great search experience in your apps and websites. Algolia Search provides hosted full-text, numerical, faceted, and geolocalized search. [Bonsai](https://www.bonsai.io) : Bonsai is a fully-managed hosted Elasticsearch service that is fast, reliable, and simple to set up. Easily ingest your docs from Hugo into Elasticsearch following [this guide from the docs](https://bonsai.io/docs/hugo). diff --git a/docs/content/en/troubleshooting/_index.md b/docs/content/en/troubleshooting/_index.md index 92ba6bc6d..fd51b4fc8 100644 --- a/docs/content/en/troubleshooting/_index.md +++ b/docs/content/en/troubleshooting/_index.md @@ -1,16 +1,8 @@ --- title: Troubleshooting -linkTitle: In this section description: Use these techniques when troubleshooting your site. categories: [] keywords: [] -menu: - docs: - identifier: troubleshooting-in-this-section - parent: troubleshooting - weight: 10 weight: 10 aliases: [/templates/template-debugging/] --- - -Use these techniques when troubleshooting your site. diff --git a/docs/content/en/troubleshooting/audit/index.md b/docs/content/en/troubleshooting/audit/index.md index e0de0d5df..2efad55e3 100644 --- a/docs/content/en/troubleshooting/audit/index.md +++ b/docs/content/en/troubleshooting/audit/index.md @@ -2,20 +2,15 @@ title: Site audit linkTitle: Audit description: Run this audit before deploying your production site. -categories: [troubleshooting] +categories: [] keywords: [] -menu: - docs: - parent: troubleshooting - weight: 20 -weight: 20 --- There are several conditions that can produce errors in your published site which are not detected during the build. Run this audit before your final build. -{{< code copy=true >}} +```text {copy=true} HUGO_MINIFY_TDEWOLFF_HTML_KEEPCOMMENTS=true HUGO_ENABLEMISSINGTRANSLATIONPLACEHOLDERS=true hugo && grep -inorE "<\!-- raw HTML omitted -->|ZgotmplZ|\[i18n\]|\(\)|(<nil>)|hahahugo" public/ -{{< /code >}} +``` _Tested with GNU Bash 5.1 and GNU grep 3.7._ diff --git a/docs/content/en/troubleshooting/deprecation.md b/docs/content/en/troubleshooting/deprecation.md index aa4bb71a2..f2e5259a6 100644 --- a/docs/content/en/troubleshooting/deprecation.md +++ b/docs/content/en/troubleshooting/deprecation.md @@ -1,20 +1,15 @@ --- title: Deprecation description: The Hugo project follows a formal and consistent process to deprecate functions, methods, and configuration settings. -categories: [troubleshooting] +categories: [] keywords: [] -menu: - docs: - parent: troubleshooting - weight: 50 -weight: 50 --- When a project _deprecates_ something, they are telling its users: 1. Don't use Thing One anymore. -2. Use Thing Two instead. -3. We're going to remove Thing One at some point in the future. +1. Use Thing Two instead. +1. We're going to remove Thing One at some point in the future. [reasons for deprecation]: https://en.wikipedia.org/wiki/Deprecation @@ -29,9 +24,15 @@ Common [reasons for deprecation]: After the project team deprecates something in code, Hugo will: -1. Log an INFO message for 6 minor releases[^1] -2. Log a WARN message for another 6 minor releases -3. Log an ERROR message and fail the build thereafter +1. Log an INFO message for 3 minor releases[^1] +1. Log a WARN message for another 12 minor releases +1. Log an ERROR message and fail the build thereafter + +The project team will: + +1. On the deprecation date, update the documentation with a note describing the deprecation and any relevant alternatives. +1. Remove the code six or more minor releases after Hugo begins logging ERROR messages and failing the build. At that point, Hugo will throw an error, but the error message will no longer mention the deprecation. +1. Remove the corresponding documentation two years after the deprecation date. To see the INFO messages, you must use the `--logLevel` command line flag: diff --git a/docs/content/en/troubleshooting/faq.md b/docs/content/en/troubleshooting/faq.md index 0fd67264c..6992af5d3 100644 --- a/docs/content/en/troubleshooting/faq.md +++ b/docs/content/en/troubleshooting/faq.md @@ -2,142 +2,112 @@ title: Frequently asked questions linkTitle: FAQs description: These questions are frequently asked by new users. -categories: [troubleshooting] -keywords: [faq] -menu: - docs: - parent: troubleshooting - weight: 70 -weight: 70 -# Use level 6 headings for each question. +categories: [] +keywords: [] --- -Hugo’s [forum] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question. +Hugo's [forum] is an active community of users and developers who answer questions, share knowledge, and provide examples. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question. These are just a few of the questions most frequently asked by new users. -###### An error message indicates that a feature is not available. Why? +An error message indicates that a feature is not available. Why? +: + {{% include "/_common/installation/01-editions.md" %}} -Hugo is available in two editions: standard and extended. With the extended edition you can (a) encode to the WebP format when processing images, and (b) transpile Sass to CSS using the embedded LibSass transpiler. The extended edition is not required to use the Dart Sass transpiler. + When you attempt to use a feature that is not available in the edition that you installed, Hugo throws this error: -When you attempt to perform either of the operations above with the standard edition, Hugo throws this error: + ```go-html-template + this feature is not available in this edition of Hugo + ``` -```go-html-template -Error: this feature is not available in your current Hugo version -``` + To resolve, install a different edition based on the feature table above. See the [installation] section for details. -To resolve, uninstall the standard edition, then install the extended edition. See the [installation] section for details. - -###### Why do I see "Page Not Found" when visiting the home page? - -In the content/_index.md file: +Why do I see "Page Not Found" when visiting the home page? +: In the `content/_index.md` file: - Is `draft` set to `true`? - Is the `date` in the future? - Is the `publishDate` in the future? - Is the `expiryDate` in the past? -If the answer to any of these questions is yes, either change the field values, or use one of these command line flags: `--buildDrafts`, `--buildFuture`, or `--buildExpired`. + If the answer to any of these questions is yes, either change the field values, or use one of these command line flags: `--buildDrafts`, `--buildFuture`, or `--buildExpired`. -###### Why is a given page not published? - -In the content/section/page.md file, or in the content/section/page/index.md file: +Why is a given page not published? +: In the `content/section/page.md` file, or in the `content/section/page/index.md` file: - Is `draft` set to `true`? - Is the `date` in the future? - Is the `publishDate` in the future? - Is the `expiryDate` in the past? -If the answer to any of these questions is yes, either change the field values, or use one of these command line flags: `--buildDrafts`, `--buildFuture`, or `--buildExpired`. + If the answer to any of these questions is yes, either change the field values, or use one of these command line flags: `--buildDrafts`, `--buildFuture`, or `--buildExpired`. -###### Why can't I see any of a page's descendants? +Why can't I see any of a page's descendants? +: You may have an `index.md` file instead of an `_index.md` file. See [details](/content-management/page-bundles/). -You may have an index.md file instead of an _index.md file. See [details](/content-management/page-bundles/). +What is the difference between an `index.md` file and an `_index.md` file? +: A directory with an `index.md file` is a [leaf bundle](g). A directory with an `_index.md` file is a [branch bundle](g). See [details](/content-management/page-bundles/). -###### What is the difference between an index.md file and an _index.md file? +Why is my partial template not rendered as expected? +: You may have neglected to pass the required [context](g) when calling the partial. For example: -A directory with an index.md file is a [leaf bundle]. A directory with an _index.md file is a [branch bundle]. See [details](/content-management/page-bundles/). + ```go-html-template + {{/* incorrect */}} + {{ partial "_internal/pagination.html" }} -[branch bundle]: /getting-started/glossary/#branch-bundle -[leaf bundle]: /getting-started/glossary/#leaf-bundle + {{/* correct */}} + {{ partial "_internal/pagination.html" . }} + ``` -###### Why is my partial template not rendered as expected? {#foo} +In a template, what's the difference between `:=` and `=` when assigning values to variables? +: Use `:=` to initialize a variable, and use `=` to assign a value to a variable that has been previously initialized. See [details](https://pkg.go.dev/text/template#hdr-Variables). -You may have neglected to pass the required [context] when calling the partial. For example: +When I paginate a list page, why is the page collection not filtered as specified? +: You are probably invoking the [`Paginate`] or [`Paginator`] method more than once on the same page. See [details](/templates/pagination/). -```go-html-template -{{/* incorrect */}} -{{ partial "_internal/pagination.html" }} +Why are there two ways to call a shortcode? +: Use the `{{%/* shortcode */%}}` notation if the shortcode template, or the content between the opening and closing shortcode tags, contains Markdown. Otherwise use the\ +`{{}}` notation. See [details](/content-management/shortcodes/#notation). -{{/* correct */}} -{{ partial "_internal/pagination.html" . }} -``` +Can I use environment variables to control configuration? +: Yes. See [details](/configuration/introduction/#environment-variables). -###### In a template, what's the difference between `:=` and `=` when assigning values to variables? +Why am I seeing inconsistent output from one build to the next? +: The most common causes are page collisions (publishing two pages to the same path) and the effects of concurrency. Use the `--printPathWarnings` command line flag to check for page collisions, and create a topic on the [forum] if you suspect concurrency problems. -Use `:=` to initialize a variable, and use `=` to assign a value to a variable that has been previously initialized. See [details](https://pkg.go.dev/text/template#hdr-Variables). +Why isn't Hugo's development server detecting file changes? +: In its default configuration, Hugo's file watcher may not be able detect file changes when: -###### When I paginate a list page, why is the page collection not filtered as specified? + - Running Hugo within Windows Subsystem for Linux (WSL/WSL2) with project files on a Windows partition + - Running Hugo locally with project files on a removable drive + - Running Hugo locally with project files on a storage server accessed via the NFS, SMB, or CIFS protocols -You are probably invoking the [`Paginate`] or [`Paginator`] method more than once on the same page. See [details](/templates/pagination/). + In these cases, instead of monitoring native file system events, use the `--poll` command line flag. For example, to poll the project files every 700 milliseconds, use `--poll 700ms`. -###### Why are there two ways to call a shortcode? +Why is my page Scratch or Store missing a value? +: The [`Scratch`] and [`Store`] methods on a `Page` object allow you to create a [scratch pad](g) on the given page to store and manipulate data. Values are often set within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are not determinate until Hugo renders the page content. -Use the `{{%/* shortcode */%}}` notation if the shortcode template, or the content between the opening and closing shortcode tags, contains Markdown. Otherwise use the\ -`{{}}` notation. See [details](/content-management/shortcodes/). + If you need to access a scratch pad value from a parent template, and the parent template has not yet rendered the page content, you can trigger content rendering by assigning the returned value to a [noop](g) variable: -###### Can I use environment variables to control configuration? + ```go-html-template + {{ $noop := .Content }} + {{ .Store.Get "mykey" }} + ``` -Yes. See [details](/getting-started/configuration/#configure-with-environment-variables). + You can trigger content rendering with other methods as well. See next FAQ. -###### Why am I seeing inconsistent output from one build to the next? +Which page methods trigger content rendering? +: The following methods on a `Page` object trigger content rendering: `Content`, `ContentWithoutSummary`, `FuzzyWordCount`, `Len`, `Plain`, `PlainWords`, `ReadingTime`, `Summary`, `Truncated`, and `WordCount`. -The most common causes are page collisions (publishing two pages to the same path) and the effects of concurrency. Use the `--printPathWarnings` command line flag to check for page collisions, and create a topic on the [forum] if you suspect concurrency problems. - -###### Why isn't Hugo's development server detecting file changes? - -In its default configuration, Hugo's file watcher may not be able detect file changes when: - -- Running Hugo within Windows Subsystem for Linux (WSL/WSL2) with project files on a Windows partition -- Running Hugo locally with project files on a removable drive -- Running Hugo locally with project files on a storage server accessed via the NFS, SMB, or CIFS protocols - -In these cases, instead of monitoring native file system events, use the `--poll` command line flag. For example, to poll the project files every 700 milliseconds, use `--poll 700ms`. - -###### Why is my page Scratch or Store missing a value? - -The [`Scratch`] and [`Store`] methods on a `Page` object allow you to create a [scratch pad] on the given page to store and manipulate data. Values are often set within a shortcode, a partial template called by a shortcode, or by a Markdown render hook. In all three cases, the scratch pad values are not determinate until Hugo renders the page content. - -[scratch pad]: /getting-started/glossary/#scratch-pad - -If you need to access a scratch pad value from a parent template, and the parent template has not yet rendered the page content, you can trigger content rendering by assigning the returned value to a [noop] variable: - -[noop]: /getting-started/glossary/#noop - -```go-html-template -{{ $noop := .Content }} -{{ .Store.Get "mykey" }} -``` - -You can trigger content rendering with other methods as well. See next FAQ. - -[`Scratch`]: /methods/page/scratch -[`Store`]: /methods/page/store - -###### Which page methods trigger content rendering? - -The following methods on a `Page` object trigger content rendering: `Content`, `FuzzyWordCount`, `Len`, `Plain`, `PlainWords`, `ReadingTime`, `Summary`, `Truncated`, and `WordCount`. - -{{% note %}} -For other questions please visit the [forum]. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question. - -[forum]: https://discourse.gohugo.io -[requesting help]: https://discourse.gohugo.io/t/requesting-help/9132 -{{% /note %}} +> [!note] +> For other questions please visit the [forum]. A quick search of over 20,000 topics will often answer your question. Please be sure to read about [requesting help] before asking your first question. [`Paginate`]: /methods/page/paginate/ [`Paginator`]: /methods/page/paginator/ -[context]: /getting-started/glossary/#context +[`Scratch`]: /methods/page/scratch +[`Store`]: /methods/page/store +[forum]: https://discourse.gohugo.io [forum]: https://discourse.gohugo.io [installation]: /installation/ [requesting help]: https://discourse.gohugo.io/t/requesting-help/9132 +[requesting help]: https://discourse.gohugo.io/t/requesting-help/9132 diff --git a/docs/content/en/troubleshooting/inspection.md b/docs/content/en/troubleshooting/inspection.md index e9fc8ed0f..ea3c097f9 100644 --- a/docs/content/en/troubleshooting/inspection.md +++ b/docs/content/en/troubleshooting/inspection.md @@ -2,13 +2,8 @@ title: Data inspection linkTitle: Inspection description: Use template functions to inspect values and data structures. -categories: [troubleshooting] +categories: [] keywords: [] -menu: - docs: - parent: troubleshooting - weight: 40 -weight: 40 --- Use the [`debug.Dump`] function to inspect a data structure: @@ -39,6 +34,11 @@ Use the [`printf`] function (render) or [`warnf`] function (log to console) to i {{ printf "%[1]v (%[1]T)" $value }} → 42 (int) ``` +{{< new-in 0.146.0 />}} + +Use the [`templates.Current`] function to visually mark template execution boundaries or to display the template call stack. + [`debug.Dump`]: /functions/debug/dump/ [`printf`]: /functions/fmt/printf/ [`warnf`]: /functions/fmt/warnf/ +[`templates.Current`]: /functions/templates/current/ diff --git a/docs/content/en/troubleshooting/logging.md b/docs/content/en/troubleshooting/logging.md index fc6838069..0cd25d1ae 100644 --- a/docs/content/en/troubleshooting/logging.md +++ b/docs/content/en/troubleshooting/logging.md @@ -1,14 +1,8 @@ --- title: Logging description: Enable logging to inspect events while building your site. -categories: [troubleshooting] +categories: [] keywords: [] -menu: - docs: - parent: troubleshooting - weight: 30 -weight: 30 -toc: true --- ## Command line @@ -20,40 +14,39 @@ Hugo has four logging levels: error : Display error messages only. -```sh -hugo --logLevel error -``` + ```sh + hugo --logLevel error + ``` warn : Display warning and error messages. -```sh -hugo --logLevel warn -``` + ```sh + hugo --logLevel warn + ``` info : Display information, warning, and error messages. -```sh -hugo --logLevel info -``` + ```sh + hugo --logLevel info + ``` debug : Display debug, information, warning, and error messages. -```sh -hugo --logLevel debug -``` + ```sh + hugo --logLevel debug + ``` -{{% note %}} -If you do not specify a logging level with the `--logLevel` flag, warnings and errors are always displayed. -{{% /note %}} +> [!note] +> If you do not specify a logging level with the `--logLevel` flag, warnings and errors are always displayed. ## Template functions You can also use template functions to print warnings or errors to the console. These functions are typically used to report data validation errors, missing files, etc. -{{< list-pages-in-section path=/functions/fmt filter=functions_fmt_logging filterType=include >}} +{{% list-pages-in-section path=/functions/fmt filter=functions_fmt_logging filterType=include %}} ## LiveReload diff --git a/docs/content/en/troubleshooting/performance.md b/docs/content/en/troubleshooting/performance.md index a2df1b08e..e366eba81 100644 --- a/docs/content/en/troubleshooting/performance.md +++ b/docs/content/en/troubleshooting/performance.md @@ -1,14 +1,8 @@ --- title: Performance description: Tools and suggestions for evaluating and improving performance. -categories: [troubleshooting] +categories: [] keywords: [] -menu: - docs: - parent: troubleshooting - weight: 60 -weight: 60 -toc: true aliases: [/troubleshooting/build-performance/] --- @@ -24,9 +18,8 @@ For example, with Microsoft Defender Antivirus: Then type `hugo.exe` add press the **Add** button. -{{% note %}} -Virus scanning exclusions are common, but use caution when changing these settings. See the [Microsoft Defender Antivirus documentation](https://support.microsoft.com/en-us/topic/how-to-add-a-file-type-or-process-exclusion-to-windows-security-e524cbc2-3975-63c2-f9d1-7c2eb5331e53) for details. -{{% /note %}} +> [!note] +> Virus scanning exclusions are common, but use caution when changing these settings. See the [Microsoft Defender Antivirus documentation] for details. Other virus scanners have similar exclusion mechanisms. See their respective documentation. @@ -90,23 +83,22 @@ total count : The number of times the template was executed. template -: The path to the template, relative to the layouts directory. +: The path to the template, relative to the `layouts` directory. -[`partial`]: /functions/partials/include/ -[`partialCached`]: /functions/partials/includecached/ - -{{% note %}} -Hugo builds pages in parallel where multiple pages are generated simultaneously. Because of this parallelism, the sum of "cumulative duration" values is usually greater than the actual time it takes to build a site. -{{% /note %}} +> [!note] +> Hugo builds pages in parallel where multiple pages are generated simultaneously. Because of this parallelism, the sum of "cumulative duration" values is usually greater than the actual time it takes to build a site. ## Caching Some partial templates such as sidebars or menus are executed many times during a site build. Depending on the content within the partial template and the desired output, the template may benefit from caching to reduce the number of executions. The [`partialCached`] template function provides caching capabilities for partial templates. -{{% note %}} -Note that you can create cached variants of each partial by passing additional arguments to `partialCached` beyond the initial context. See the `partialCached` documentation for more details. -{{% /note %}} +> [!note] +> Note that you can create cached variants of each partial by passing additional arguments to `partialCached` beyond the initial context. See the `partialCached` documentation for more details. ## Timers -Use the `debug.Timer` function to determine execution time for a block of code, useful for finding performance bottle necks in templates. See [details](/functions/debug/timer/). +Use the `debug.Timer` function to determine execution time for a block of code, useful for finding performance bottlenecks in templates. See [details](/functions/debug/timer/). + +[`partial`]: /functions/partials/include/ +[`partialCached`]: /functions/partials/includecached/ +[Microsoft Defender Antivirus documentation]: https://support.microsoft.com/en-us/topic/how-to-add-a-file-type-or-process-exclusion-to-windows-security-e524cbc2-3975-63c2-f9d1-7c2eb5331e53 diff --git a/docs/data/docs.yaml b/docs/data/docs.yaml index 8d97c21e0..0db4c7fe9 100644 --- a/docs/data/docs.yaml +++ b/docs/data/docs.yaml @@ -54,6 +54,9 @@ chroma: - Aliases: - armasm Name: ArmAsm + - Aliases: + - atl + Name: ATL - Aliases: - autohotkey - ahk @@ -88,6 +91,9 @@ chroma: - dosbatch - winbatch Name: Batchfile + - Aliases: + - beef + Name: Beef - Aliases: - bib - bibtex @@ -190,6 +196,9 @@ chroma: - Aliases: - css Name: CSS + - Aliases: + - csv + Name: CSV - Aliases: - cue Name: CUE @@ -308,7 +317,7 @@ chroma: - Gherkin Name: Gherkin - Aliases: - - gleam> + - gleam Name: Gleam - Aliases: - glsl @@ -414,6 +423,12 @@ chroma: - Aliases: - json Name: JSON + - Aliases: + - jsonata + Name: JSONata + - Aliases: + - jsonnet + Name: Jsonnet - Aliases: - julia - jl @@ -464,7 +479,8 @@ chroma: Name: Matlab - Aliases: - mcfunction - Name: mcfunction + - mcf + Name: MCFunction - Aliases: - meson - meson.build @@ -521,6 +537,11 @@ chroma: - nixos - nix Name: Nix + - Aliases: + - nsis + - nsi + - nsh + Name: NSIS - Aliases: - objective-c - objectivec @@ -752,6 +773,9 @@ chroma: - Aliases: - smarty Name: Smarty + - Aliases: + - snbt + Name: SNBT - Aliases: - snobol Name: Snobol @@ -862,6 +886,9 @@ chroma: - Aliases: - typoscripthtmldata Name: TypoScriptHtmlData + - Aliases: + - typst + Name: Typst - Aliases: null Name: ucode - Aliases: @@ -904,6 +931,9 @@ chroma: - Aliases: - wgsl Name: WebGPU Shading Language + - Aliases: + - vtt + Name: WebVTT - Aliases: - whiley Name: Whiley @@ -928,6 +958,73 @@ chroma: - Aliases: - zig Name: Zig + styles: + - abap + - algol + - algol_nu + - arduino + - autumn + - average + - base16-snazzy + - borland + - bw + - catppuccin-frappe + - catppuccin-latte + - catppuccin-macchiato + - catppuccin-mocha + - colorful + - doom-one + - doom-one2 + - dracula + - emacs + - evergarden + - friendly + - fruity + - github + - github-dark + - gruvbox + - gruvbox-light + - hr_high_contrast + - hrdark + - igor + - lovelace + - manni + - modus-operandi + - modus-vivendi + - monokai + - monokailight + - murphy + - native + - nord + - nordic + - onedark + - onesenterprise + - paraiso-dark + - paraiso-light + - pastie + - perldoc + - pygments + - rainbow_dash + - rose-pine + - rose-pine-dawn + - rose-pine-moon + - rrt + - solarized-dark + - solarized-dark256 + - solarized-light + - swapoff + - tango + - tokyonight-day + - tokyonight-moon + - tokyonight-night + - tokyonight-storm + - trac + - vim + - vs + - vulcan + - witchhazel + - xcode + - xcode-dark config: HTTPCache: cache: @@ -989,10 +1086,18 @@ config: cascade: [] cleanDestinationDir: false contentDir: content + contentTypes: + text/asciidoc: {} + text/html: {} + text/markdown: {} + text/org: {} + text/pandoc: {} + text/rst: {} copyright: "" dataDir: data defaultContentLanguage: en defaultContentLanguageInSubdir: false + defaultOutputFormat: html deployment: confirm: false dryRun: false @@ -1005,6 +1110,7 @@ config: targets: null workers: 10 disableAliases: false + disableDefaultLanguageRedirect: false disableHugoGeneratorInject: false disableKinds: null disableLanguages: null @@ -1122,8 +1228,9 @@ config: attribute: block: false title: true + autoDefinitionTermID: false autoHeadingID: true - autoHeadingIDType: github + autoIDType: github wrapStandAloneImageWithinParagraph: true renderHooks: image: @@ -1145,9 +1252,9 @@ config: lineNos: false lineNumbersInTable: true noClasses: true - noHl: false style: monokai tabWidth: 4 + wrapperClass: highlight tableOfContents: endLevel: 3 ordered: false @@ -1368,6 +1475,7 @@ config: xml: keepWhitespace: false module: + auth: "" hugoVersion: extended: false max: "" @@ -1637,6 +1745,10 @@ config: disable: false enableDNT: false simple: false + x: + disable: false + enableDNT: false + simple: false youTube: disable: false privacyEnhanced: false @@ -1701,7 +1813,9 @@ config: headers: null redirects: - force: false - from: '**' + from: /** + fromHeaders: null + fromRe: "" status: 404 to: /404.html services: @@ -1716,6 +1830,8 @@ config: limit: -1 twitter: disableInlineCSS: false + x: + disableInlineCSS: false sitemap: changeFreq: "" disable: false @@ -1757,6 +1873,8 @@ config_helpers: _merge: none cascade: _merge: none + contenttypes: + _merge: none deployment: _merge: none frontmatter: @@ -2440,17 +2558,6 @@ tpl: treating values as key-value pairs. The number of values must be even. The keys can be string slices, which will create the needed nested structure. Examples: [] - EchoParam: - Aliases: - - echoParam - Args: - - c - - k - Description: |- - EchoParam returns the value in the collection c with key k if is set; otherwise, it returns an - empty string. - Deprecated: Use the index function instead. - Examples: [] First: Aliases: - first @@ -2571,8 +2678,9 @@ tpl: - querify Args: - params - Description: Querify encodes the given params in URL-encoded form ("bar=baz&foo=quux") - sorted by key. + Description: |- + Querify returns a URL query string composed of the given key-value pairs, + encoded and sorted by key. Examples: - - '{{ (querify "foo" 1 "bar" 2 "baz" "with spaces" "qux" "this&that=those") | safeHTML }}' @@ -3119,6 +3227,11 @@ tpl: Args: null Description: "" Examples: null + Store: + Aliases: null + Args: null + Description: "" + Examples: null Version: Aliases: null Args: null @@ -3199,6 +3312,11 @@ tpl: Args: null Description: "" Examples: null + Mask: + Aliases: null + Args: null + Description: "" + Examples: null Opacity: Aliases: null Args: null @@ -3224,6 +3342,11 @@ tpl: Args: null Description: "" Examples: null + QR: + Aliases: null + Args: null + Description: "" + Examples: null Saturation: Aliases: null Args: null @@ -3295,6 +3418,11 @@ tpl: - args Description: Babel processes the given Resource with Babel. Examples: [] + Batch: + Aliases: null + Args: null + Description: "" + Examples: null Build: Aliases: null Args: null @@ -3384,11 +3512,6 @@ tpl: Args: null Description: "" Examples: null - NumFmt: - Aliases: null - Args: null - Description: "" - Examples: null Translate: Aliases: - i18n @@ -4002,11 +4125,6 @@ tpl: Args: null Description: "" Examples: null - DisqusShortname: - Aliases: null - Args: null - Description: "" - Examples: null ForEeachIdentityByName: Aliases: null Args: null @@ -4017,11 +4135,6 @@ tpl: Args: null Description: "" Examples: null - GoogleAnalytics: - Aliases: null - Args: null - Description: "" - Examples: null Home: Aliases: null Args: null @@ -4037,11 +4150,6 @@ tpl: Args: null Description: "" Examples: null - IsServer: - Aliases: null - Args: null - Description: "" - Examples: null Key: Aliases: null Args: null @@ -4102,11 +4210,6 @@ tpl: Args: null Description: "" Examples: null - RSSLink: - Aliases: null - Args: null - Description: "" - Examples: null RegularPages: Aliases: null Args: null @@ -4132,6 +4235,11 @@ tpl: Args: null Description: "" Examples: null + Store: + Aliases: null + Args: null + Description: "" + Examples: null Taxonomies: Aliases: null Args: null @@ -4457,6 +4565,11 @@ tpl: Examples: - - '{{ "aabbaa" | strings.TrimRight "a" }}' - aabb + TrimSpace: + Aliases: null + Args: null + Description: "" + Examples: null TrimSuffix: Aliases: null Args: diff --git a/docs/data/embedded_template_urls.toml b/docs/data/embedded_template_urls.toml index 7bb2e4eee..b2a796cd1 100644 --- a/docs/data/embedded_template_urls.toml +++ b/docs/data/embedded_template_urls.toml @@ -4,33 +4,39 @@ # BaseURL 'base_url' = 'https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates' -# Templates -'alias' = 'alias.html' -'disqus' = 'disqus.html' -'google_analytics' = 'google_analytics.html' -'opengraph' = 'opengraph.html' -'pagination' = 'pagination.html' -'schema' = 'schema.html' -'twitter_cards' = 'twitter_cards.html' - -'robots' = '_default/robots.txt' -'rss' = '_default/rss.xml' -'sitemap' = '_default/sitemap.xml' -'sitemapindex' = '_default/sitemapindex.xml' +# Partials +'disqus' = '_partials/disqus.html' +'google_analytics' = '_partials/google_analytics.html' +'opengraph' = '_partials/opengraph.html' +'pagination' = '_partials/pagination.html' +'schema' = '_partials/schema.html' +'twitter_cards' = '_partials/twitter_cards.html' # Render hooks -'render-image' = '_default/_markup/render-image.html' -'render-link' = '_default/_markup/render-link.html' -'render-codeblock-goat' = '_default/_markup/render-codeblock-goat.html' +'render-codeblock-goat' = '_markup/render-codeblock-goat.html' +'render-image' = '_markup/render-image.html' +'render-link' = '_markup/render-link.html' +'render-table' = '_markup/render-table.html' # Shortcodes -'figure' = 'shortcodes/figure.html' -'gist' = 'shortcodes/gist.html' -'highlight' = 'shortcodes/highlight.html' -'instagram' = 'shortcodes/instagram.html' -'param' = 'shortcodes/param.html' -'ref' = 'shortcodes/ref.html' -'relref' = 'shortcodes/relref.html' -'twitter' = 'shortcodes/twitter.html' -'vimeo' = 'shortcodes/vimeo.html' -'youtube' = 'shortcodes/youtube.html' +'details' = '_shortcodes/details.html' +'figure' = '_shortcodes/figure.html' +'gist' = '_shortcodes/gist.html' +'highlight' = '_shortcodes/highlight.html' +'instagram' = '_shortcodes/instagram.html' +'param' = '_shortcodes/param.html' +'qr' = '_shortcodes/qr.html' +'ref' = '_shortcodes/ref.html' +'relref' = '_shortcodes/relref.html' +'vimeo' = '_shortcodes/vimeo.html' +'vimeo_simple' = '_shortcodes/vimeo_simple.html' +'x' = '_shortcodes/x.html' +'x_simple' = '_shortcodes/x_simple.html' +'youtube' = '_shortcodes/youtube.html' + +# Other +'alias' = 'alias.html' +'robots' = 'robots.txt' +'rss' = 'rss.xml' +'sitemap' = 'sitemap.xml' +'sitemapindex' = 'sitemapindex.xml' diff --git a/docs/data/keywords.yaml b/docs/data/keywords.yaml new file mode 100644 index 000000000..106929884 --- /dev/null +++ b/docs/data/keywords.yaml @@ -0,0 +1,12 @@ +# We use the front matter keywords field to determine related content. To +# ensure consistency, during site build we validate each keyword against the +# entries in data/keywords.yaml. + +# As of March 5, 2025, this feature is experimental, pending usability +# assessment. We anticipate that the number of additions to data/keywords.yaml +# will decrease over time, though the initial implementation will require some +# effort. + +- menu +- resource +- highlight diff --git a/docs/data/page_filters.yaml b/docs/data/page_filters.yaml index a4969e9b7..82de6168e 100644 --- a/docs/data/page_filters.yaml +++ b/docs/data/page_filters.yaml @@ -6,7 +6,7 @@ # # For example: # -# {{< list-pages-in-section path=/functions/images filter=functions_images_no_filters filterType=exclude >}} +# {{% list-pages-in-section path=/functions/images filter=functions_images_no_filters filterType=exclude %}} functions_fmt_logging: - /functions/fmt/errorf diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/data/sponsors.toml b/docs/data/sponsors.toml similarity index 62% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/data/sponsors.toml rename to docs/data/sponsors.toml index 33d550449..705ca9746 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/data/sponsors.toml +++ b/docs/data/sponsors.toml @@ -6,13 +6,12 @@ bgcolor = "#ffffff" [[banners]] - name = "Route4Me" - link = "https://route4me.com/" - title = "Route Planning & Route Optimization Software" + name = "GoLand" + title = "The complete IDE crafted for professional Go developers." no_query_params = true - utm_campaign = "hugosponsor" - bgcolor = "#334799" - link_attr = "style='color: #ffffff; font-weight: bold; text-decoration: none; text-align: center'" + link = "https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" + logo = "images/sponsors/goland.svg" + bgcolor = "#f4f4f4" [[banners]] name = "Your Company?" diff --git a/docs/go.mod b/docs/go.mod index 7fd269069..4b9e0a369 100644 --- a/docs/go.mod +++ b/docs/go.mod @@ -1,5 +1,3 @@ module github.com/gohugoio/hugoDocs -go 1.16 - -require github.com/gohugoio/gohugoioTheme v0.0.0-20240815082608-66ccd383a90f // indirect +go 1.22.0 diff --git a/docs/go.sum b/docs/go.sum index 4e5e32477..af9b5febf 100644 --- a/docs/go.sum +++ b/docs/go.sum @@ -1,16 +1,2 @@ -github.com/gohugoio/gohugoioTheme v0.0.0-20240426212330-f38e99e0d88d h1:EaFz80Aqh3Ej20VmUSNe3K+F0NbT8UueXLP/VqkK9Dw= -github.com/gohugoio/gohugoioTheme v0.0.0-20240426212330-f38e99e0d88d/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240508091825-b23e8e2d2419 h1:cQ/44eDHK0tVImTtSx/9sWWZv+RynH/oB4R7ASbQNAE= -github.com/gohugoio/gohugoioTheme v0.0.0-20240508091825-b23e8e2d2419/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240619093131-b595d5fb8c52 h1:dPJxUU4SevIZ7OS1DIVOrJ7p8I/QM00pXGRfAtKgQmU= -github.com/gohugoio/gohugoioTheme v0.0.0-20240619093131-b595d5fb8c52/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240622143740-53a4bdb8c0fb h1:gOIE1eFXILxCio/QOm3oLYcYmsis2CD099dXbXpjprA= -github.com/gohugoio/gohugoioTheme v0.0.0-20240622143740-53a4bdb8c0fb/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240623150114-cc7096eab3fd h1:I8X7c0oBRWXy83BL2ODSk7v0xPXDnp2hcFWpCcN+Kyc= -github.com/gohugoio/gohugoioTheme v0.0.0-20240623150114-cc7096eab3fd/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240728210410-d42c342ce472 h1:AYZUibKKFRBp2VCQpDHW+JmQKvCvyhX7z7/SOLUSCcw= -github.com/gohugoio/gohugoioTheme v0.0.0-20240728210410-d42c342ce472/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240812175901-cc0ef8e4a14a h1:E3JbZo69eqFBz6B+meQlKyy/ZBZQ73ldVDw8TADiIrQ= -github.com/gohugoio/gohugoioTheme v0.0.0-20240812175901-cc0ef8e4a14a/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= -github.com/gohugoio/gohugoioTheme v0.0.0-20240815082608-66ccd383a90f h1:Eo5z3uUYfmrtIxQvHm388dFOERZwWGTjLuUO6vobzLc= -github.com/gohugoio/gohugoioTheme v0.0.0-20240815082608-66ccd383a90f/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= +github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743 h1:gjoqq8+RnGwpuU/LQVYGGR/LsDplrfUjOabWwoROYsM= +github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743/go.mod h1:GOYeAPQJ/ok8z7oz1cjfcSlsFpXrmx6VkzQ5RpnyhZM= diff --git a/docs/hugo.toml b/docs/hugo.toml index 2a271b6e3..e8373a87c 100644 --- a/docs/hugo.toml +++ b/docs/hugo.toml @@ -1,10 +1,6 @@ -# This his the main configuration file. There are also environment specific configuration stored in the /config directory. - baseURL = "https://gohugo.io/" defaultContentLanguage = "en" enableEmoji = true -ignoreErrors = ["error-remote-getjson", "error-missing-instagram-accesstoken"] -languageCode = "en-us" pluralizeListTitles = false timeZone = "Europe/Oslo" title = "Hugo" @@ -12,91 +8,164 @@ title = "Hugo" # We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below). disableAliases = true -[pagination] -pagerSize = 100 - -[services.googleAnalytics] -ID = 'G-MBZGKNMDWC' - -[minify] - [minify.tdewolff] - [minify.tdewolff.html] - keepWhitespace = true - -[module] - [module.hugoVersion] - min = "0.56.0" - [[module.imports]] - path = "github.com/gohugoio/gohugoioTheme" - -[outputs] - home = ["HTML", "RSS", "REDIR", "HEADERS"] - section = ["HTML"] - -[mediaTypes] - [mediaTypes."text/netlify"] - delimiter = "" - -[outputFormats] - [outputFormats.REDIR] - mediatype = "text/netlify" - baseName = "_redirects" - isPlainText = true - notAlternative = true - [outputFormats.HEADERS] - mediatype = "text/netlify" - baseName = "_headers" - isPlainText = true - notAlternative = true +[build] + [build.buildStats] + disableIDs = true + enable = true + [[build.cachebusters]] + source = "assets/notwatching/hugo_stats\\.json" + target = "css" + [[build.cachebusters]] + source = "(postcss|tailwind)\\.config\\.js" + target = "css" [caches] - [caches.getjson] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.getcsv] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.images] - dir = ":cacheDir/images" - maxAge = "1440h" - [caches.assets] - dir = ":resourceDir/_gen" - maxAge = -1 - [caches.getresource] - dir = ":cacheDir/:project" - maxage = '1h' + [caches.images] + dir = ":cacheDir/images" + maxAge = "1440h" + [caches.getresource] + dir = ':cacheDir/:project' + maxAge = "1h" + +[cascade] + [cascade.params] + hide_in_this_section = true + show_publish_date = true + [cascade.target] + kind = 'page' + path = '{/news/**}' + +[frontmatter] + date = ['date'] # do not add publishdate; it will affect page sorting + expiryDate = ['expirydate'] + lastmod = [':git', 'lastmod', 'publishdate', 'date'] + publishDate = ['publishdate', 'date'] + +[languages] + [languages.en] + languageCode = "en-US" + languageName = "English" + weight = 1 + +[markup] + [markup.goldmark] + [markup.goldmark.extensions] + [markup.goldmark.extensions.typographer] + disable = false + [markup.goldmark.extensions.passthrough] + enable = true + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + inline = [['\(', '\)']] + [markup.goldmark.parser] + autoDefinitionTermID = true + [markup.goldmark.parser.attribute] + block = true + [markup.highlight] + lineNumbersInTable = false + noClasses = false + style = 'solarized-dark' + wrapperClass = 'highlight not-prose' + +[mediaTypes] + [mediaTypes."text/netlify"] + delimiter = "" + +[module] + [module.hugoVersion] + min = "0.144.0" + [[module.mounts]] + source = "assets" + target = "assets" + [[module.mounts]] + lang = 'en' + source = 'content/en' + target = 'content' + [[module.mounts]] + disableWatch = true + source = "hugo_stats.json" + target = "assets/notwatching/hugo_stats.json" + +[outputFormats] + [outputFormats.redir] + baseName = "_redirects" + isPlainText = true + mediatype = "text/netlify" + [outputFormats.headers] + baseName = "_headers" + isPlainText = true + mediatype = "text/netlify" + notAlternative = true + +[outputs] + home = ["html", "rss", "redir", "headers"] + page = ["html"] + section = ["html"] + taxonomy = ["html"] + term = ["html"] + +[params] + description = "The world’s fastest framework for building websites" + ghrepo = "https://github.com/gohugoio/hugoDocs/" + [params.render_hooks.link] + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) [related] - threshold = 80 - includeNewer = true - toLower = false - [[related.indices]] - name = "keywords" - weight = 60 - [[related.indices]] - # Can be used as a front matter slice to link to other page fragments (headings) using their ID. - # This isn't particular useful in the current docs, but we're planning on getting a auto generated - # reference section with a better ID setup. - # For now, we just use it to give pages with same headings some similarity score. - name = "fragmentrefs" - type = "fragments" - applyFilter = false - weight = 60 - cardinalityThreshold = 50 + includeNewer = true + threshold = 80 + toLower = true + [[related.indices]] + name = 'keywords' + weight = 1 -[imaging] - # See https://github.com/disintegration/imaging - # CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots. - # Note that you can also set this per image processing. - resampleFilter = "CatmullRom" - # Default JPEG quality setting. Default is 75. - quality = 75 - anchor = "smart" +[security] + [security.funcs] + getenv = ['^HUGO_', '^REPOSITORY_URL$', '^BRANCH$'] + +[server] + [[server.headers]] + for = "/*" + [server.headers.values] + X-Frame-Options = "DENY" + X-XSS-Protection = "1; mode=block" + X-Content-Type-Options = "nosniff" + Referrer-Policy = "no-referrer" + [[server.headers]] + for = "/**.{css,js}" + +[services] + [services.googleAnalytics] + ID = 'G-MBZGKNMDWC' [taxonomies] - category = "categories" +category = 'categories' -[[cascade]] -categories = ['commands'] -[cascade._target] -path = '/commands/**' +######## GLOBAL ITEMS TO BE SHARED WITH THE HUGO SITES ######## +[menus] + [[menus.global]] + identifier = 'news' + name = 'News' + pageRef = '/news/' + weight = 1 + [[menus.global]] + identifier = 'docs' + name = 'Docs' + url = '/documentation/' + weight = 5 + [[menus.global]] + identifier = 'themes' + name = 'Themes' + url = 'https://themes.gohugo.io/' + weight = 10 + [[menus.global]] + identifier = 'community' + name = 'Community' + post = 'external' + url = 'https://discourse.gohugo.io/' + weight = 150 + [[menus.global]] + identifier = 'github' + name = 'GitHub' + post = 'external' + url = 'https://github.com/gohugoio/hugo' + weight = 200 diff --git a/docs/hugo.work b/docs/hugo.work index a3c91ef28..02c0ba91f 100644 --- a/docs/hugo.work +++ b/docs/hugo.work @@ -1,4 +1,4 @@ -go 1.19 +go 1.22.0 use . -use ../gohugoioTheme + diff --git a/docs/hugo_stats.json b/docs/hugo_stats.json deleted file mode 100644 index ca27d8991..000000000 --- a/docs/hugo_stats.json +++ /dev/null @@ -1,820 +0,0 @@ -{ - "htmlElements": { - "tags": [ - "", - "a", - "article", - "aside", - "blockquote", - "body", - "br", - "button", - "circle", - "code", - "date", - "dd", - "div", - "dl", - "dt", - "em", - "figcaption", - "figure", - "footer", - "form", - "g", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "head", - "header", - "hr", - "html", - "i", - "iframe", - "img", - "li", - "link", - "main", - "meta", - "nav", - "noscript", - "ol", - "p", - "path", - "pre", - "script", - "section", - "small", - "span", - "strong", - "style", - "sup", - "svg", - "table", - "tbody", - "td", - "thead", - "time", - "title", - "tr", - "ul" - ], - "classes": [ - "!('about'", - "!('content-management'", - "!('contribute'", - "!('functions'", - "!('getting-started'", - "!('hosting-and-deployment'", - "!('modules'", - "!('pipes'", - "!('templates'", - "!('tools'", - "!('troubleshooting'", - "!('variables'", - "\u0026\u0026", - "(false", - "(true", - "-ml-px", - "-mr-12", - "-mr-3", - "-translate-x-3", - "-translate-y-2", - "absolute", - "absolute-l", - "active", - "admonition", - "admonition-content", - "admonition-icon", - "anchor", - "b--moon-gray", - "benchstat", - "better", - "bg-accent-color-dark", - "bg-animate", - "bg-black", - "bg-carrot-500", - "bg-cover", - "bg-gradient-to-b", - "bg-gray-100", - "bg-gray-200", - "bg-gray-300", - "bg-gray-50", - "bg-gray-600", - "bg-gray-900", - "bg-green-100", - "bg-mango-300", - "bg-mango-50", - "bg-near-white", - "bg-opacity-20", - "bg-opacity-75", - "bg-orange-500", - "bg-steel-200", - "bg-steel-500", - "bg-steel-600", - "bg-steel-800", - "bg-steel-900", - "bg-white", - "blTK", - "black", - "block", - "bmt1", - "border", - "border-0", - "border-2", - "border-b", - "border-gray-100", - "border-gray-200", - "border-gray-300", - "border-l", - "border-none", - "border-r", - "border-solid", - "border-t", - "border-transparent", - "bottom-0", - "break-inside-avoid-l", - "btn-primary", - "c", - "c1", - "chroma", - "clearfix", - "cm", - "code-copy-content", - "code-toggle", - "column-count-3-l", - "column-gap-1-l", - "configs", - "copy", - "cp", - "cursor-pointer", - "dark:bg-red-800", - "dark:border-gray-800", - "delta", - "details", - "dim", - "disabled", - "divide-gray-200", - "divide-x", - "err", - "f2-fluid", - "f6", - "filename", - "fill-current", - "fixed", - "fixed-lTK", - "flex", - "flex-1", - "flex-auto", - "flex-auto-ns", - "flex-col", - "flex-column", - "flex-none", - "flex-shrink-0", - "flex-wrap", - "fn", - "focus:border-steel-500", - "focus:outline-none", - "focus:ring-1", - "focus:ring-2", - "focus:ring-inset", - "focus:ring-offset-2", - "focus:ring-steel-500", - "focus:ring-white", - "focus:z-10", - "font-black", - "font-bold", - "font-extrabold", - "font-extralight", - "font-medium", - "font-mono", - "font-normal", - "font-sans", - "font-semibold", - "footnote-backref", - "footnote-ref", - "footnotes", - "from-primarydark", - "gap-4", - "ge", - "grid", - "grid-cols-1", - "group", - "grow", - "gs", - "gu", - "h-0", - "h-0.5", - "h-10", - "h-12", - "h-16", - "h-2", - "h-32", - "h-5", - "h-6", - "h-64", - "h-8", - "h-full", - "h-screen", - "h6", - "hidden", - "highlight", - "hl", - "hover", - "hover-bg-green", - "hover-bg-near-white", - "hover-bg-primary-color", - "hover-bg-primary-color-dark", - "hover-blue", - "hover:bg-gray-300", - "hover:bg-gray-50", - "hover:bg-steel-500", - "hover:bg-steel-700", - "hover:border", - "hover:text-gray-200", - "hover:text-gray-900", - "hover:text-hotpink-400", - "hover:text-hotpink-600", - "hover:text-limegreen-900", - "hover:text-royalblue-700", - "hover:text-steel-500", - "hover:text-white", - "img", - "in", - "inline-block", - "inline-flex", - "inset-0", - "inset-x-0", - "instagram-media", - "items-center", - "items-start", - "justify-between", - "justify-center", - "justify-end", - "k", - "kc", - "kd", - "kr", - "kt", - "l", - "language-asciidoc", - "language-bash", - "language-go", - "language-go-html-template", - "language-go-text-template", - "language-html", - "language-js", - "language-json", - "language-markdown", - "language-md", - "language-ps1", - "language-sh", - "language-svg", - "language-text", - "language-toml", - "language-txt", - "language-xml", - "language-yaml", - "language-yml", - "lazyload", - "ld", - "lead", - "leading-none", - "leading-normal", - "leading-relaxed", - "leading-snug", - "leading-tight", - "left-0", - "lg:bg-steel-700", - "lg:block", - "lg:flex", - "lg:flex-grow", - "lg:flex-shrink-0", - "lg:hidden", - "lg:inline-block", - "lg:items-center", - "lg:max-w-lg", - "lg:mb-0", - "lg:mr-auto", - "lg:mt-0", - "lg:p-4", - "lg:pb-5", - "lg:prose-lg", - "lg:pt-0", - "lg:px-4", - "lg:px-5", - "lg:px-8", - "lg:py-5", - "lg:py-8", - "lg:rounded-md", - "lg:shadow-lg", - "lg:space-x-4", - "lg:text-5xl", - "lg:w-1/2", - "lg:w-1/4", - "lg:w-1/5", - "lg:w-11/12", - "lg:w-3/5", - "lg:w-4/5", - "lg:w-auto", - "light-gray", - "link", - "list-reset", - "lnt", - "lntable", - "lntd", - "m", - "m-0", - "m-1", - "max-w-6xl", - "max-w-lg", - "max-w-xs", - "mb-0", - "mb-1", - "mb-2", - "mb-3", - "mb-4", - "mb-8", - "mb5", - "mb7", - "md:flex", - "md:flex-col", - "md:flex-grow", - "md:grid-cols-2", - "md:mt-8", - "md:pb-12", - "menu))", - "menu['about']", - "menu['content-management']", - "menu['contribute']", - "menu['functions']", - "menu['getting-started']", - "menu['hosting-and-deployment']", - "menu['modules']", - "menu['pipes']", - "menu['templates']", - "menu['tools']", - "menu['troubleshooting']", - "menu['variables']", - "mf", - "mi", - "min-h-screen", - "min-w-0", - "minor", - "ml-1", - "ml-10", - "ml-4", - "ml-6", - "ml1", - "mr-1.5", - "mr-10", - "mr-3", - "mr-4", - "mt-0", - "mt-1", - "mt-2", - "mt-4", - "mt-5", - "mt-6", - "mt-8", - "mt3", - "mt4", - "mv2", - "mv3", - "mv4", - "mv6", - "mw-100", - "mw5-l", - "mx-auto", - "my-0", - "n", - "na", - "navbar-menu", - "nb", - "needs-js", - "nested-blockquote", - "nested-copy-seperator", - "nested-img", - "nested-links", - "nested-linksTK", - "nested-list-reset", - "nf", - "ni", - "nightwind", - "nightwind-prevent", - "nightwind-prevent-block", - "nn", - "no-js", - "no-underline", - "nodelta", - "note", - "note-icon", - "nt", - "nt3", - "nv", - "nx", - "o", - "o-0", - "o-80", - "oldnew", - "opacity-60", - "open", - "order-0", - "order-0-l", - "order-1", - "order-1-l", - "order-2", - "output-content", - "overflow-hidden", - "overflow-x-scroll", - "overflow-y-auto", - "p", - "p-0", - "p-2", - "p-3", - "p-4", - "p-5", - "p-8", - "pa4-m", - "page-item", - "page-link", - "pagination", - "pb-1", - "pb-2", - "pb-3", - "pb-4", - "pb-5", - "pb-7", - "pb-8", - "pb2", - "ph1", - "ph2", - "ph4", - "pl-0", - "pl-1", - "pl-2", - "pl-3", - "pl-6", - "pl5-l", - "pr-2", - "pr1", - "primary-color", - "prose", - "pt-0", - "pt-1", - "pt-2", - "pt-3", - "pt-4", - "pt-5", - "pv1", - "px-0", - "px-2", - "px-3", - "px-4", - "py-0", - "py-0.5", - "py-1.5", - "py-2", - "py-3", - "py-4", - "py-6", - "relative", - "right-0", - "rounded", - "rounded-full", - "rounded-l-lg", - "rounded-l-md", - "rounded-lg", - "rounded-md", - "rounded-r-md", - "row", - "s", - "s1", - "s2", - "san-serif", - "se", - "shadow", - "shadow-lg", - "shadow-md", - "shadow-sm", - "show", - "sm:flex", - "sm:grid-cols-2", - "sm:mb-0", - "sm:mt-0", - "sm:mt-8", - "sm:p-4", - "sm:pb-0", - "sm:pb-6", - "sm:pt-3", - "sm:pt-5", - "sm:px-4", - "sm:px-5", - "sm:px-6", - "sm:py-0", - "sm:py-4", - "sm:py-5", - "sm:py-6", - "sm:text-2xl", - "sm:text-4xl", - "sm:text-base", - "sm:text-center", - "sm:text-left", - "sm:w-1/2", - "sm:w-1/5", - "sm:w-11/12", - "sm:w-4/5", - "space-x-4", - "space-x-8", - "space-y-1", - "sr-only", - "table", - "table-bordered", - "tc", - "text-2xl", - "text-3xl", - "text-4xl", - "text-5xl", - "text-base", - "text-black", - "text-center", - "text-gray-200", - "text-gray-300", - "text-gray-400", - "text-gray-500", - "text-gray-600", - "text-gray-900", - "text-lg", - "text-limegreen-600", - "text-limegreen-700", - "text-mango-100", - "text-mango-300", - "text-md", - "text-royalblue-500", - "text-royalblue-600", - "text-sm", - "text-steel-100", - "text-steel-500", - "text-steel-900", - "text-white", - "text-xl", - "text-xs", - "tile", - "tip", - "tip-icon", - "to-steel-800", - "top-0", - "top-2", - "tracked", - "tracking-normal", - "tracking-tight", - "transform", - "twitter-tweet", - "unchanged", - "uppercase", - "v-base", - "v-mid", - "v-top", - "w", - "w-1/5", - "w-10", - "w-11/12", - "w-12", - "w-14", - "w-2", - "w-2/3", - "w-30-l", - "w-32", - "w-5", - "w-50-m", - "w-6", - "w-64", - "w-8", - "w-80-nsTK", - "w-96", - "w-auto", - "w-full", - "w-two-third-l", - "warning", - "whitespace-no-wrap", - "worse", - "x", - "xl:flex", - "xl:flex-col", - "z-0", - "z-40", - "z-999", - "||" - ], - "ids": [ - ".gitlab-ci.yml", - "/blog/greatest-city/index.html", - "/content/actors/bruce-willis/_index.md", - "/layouts/shortcodes/img.html", - "/layouts/shortcodes/vimeo.html", - "/layouts/shortcodes/year.html", - "/layouts/shortcodes/youtube.html", - "/themes/yourtheme/layouts/review/single.html", - "404.html", - "TableOfContents", - "addrobotstxt.sh", - "all-taxonomies-keys-and-pages.html", - "all-taxonomies.html", - "archetype-example.sh", - "archetypes/functions.md", - "archetypes/newsletter.md", - "articles.html", - "asciicast-3mf1JGaN0AX0Z7j5kLGl3hSh8", - "asciicast-7naKerRYUGVPj8kiDmdh5k5h9", - "asciicast-BvJBsF6egk9c163bMsObhuNXj", - "asciicast-ItACREbFgvJ0HjnSNeTknxWy9", - "asciicast-Lc5iwTVny2kuUC8lqvNnL6oDU", - "asciicast-eUojYCfRTZvkEiqc52fUsJRBR", - "bad-url-sidebar-menu-output.html", - "base-64-output.html", - "base64-input.html", - "baseof.html", - "bf-config.toml", - "bf-config.yml", - "boxfile.yml", - "breadcrumb.html", - "check-title-length.html", - "clone-herring-cove-theme.sh", - "config.toml", - "content-header.html", - "content-image.md", - "content/blog/greatest-city.md", - "content/posts/_index.md", - "content/posts/default-function-example.md", - "content/posts/my-awesome-post.md", - "content/posts/my-post.md", - "content/posts/old-post.md", - "content/posts/old-url.md", - "content/tutorials/learn-html.md", - "correct-url-sidebar-menu-output.html", - "delimit-example-front-matter.toml", - "delimit-page-tags-final-and-input.html", - "delimit-page-tags-final-and-output.html", - "delimit-page-tags-input.html", - "delimit-page-tags-output.html", - "disqus.html", - "dot-notation-default-return-value.html", - "dot-notation-default-value.html", - "example-tweet-input.md", - "example-tweet-output.html", - "example-vimeo-input.md", - "example-vimeo-output.html", - "example-youtube-input-with-autoplay.md", - "example-youtube-input-with-title.md", - "example-youtube-input.md", - "example-youtube-output.html", - "example.com/posts/index.html", - "example.com/quote/index.html", - "external-links.svg", - "figure-input-example.md", - "figure-output-example.html", - "first-and-where-together.html", - "fn:1", - "fn:2", - "fnref:1", - "fnref:2", - "footer.html", - "from-gh.sh", - "gist-input.md", - "gist-output.html", - "gitignore.sh", - "gohugoio", - "grab-top-two-tags.html", - "header.html", - "highlight-example.md", - "how-many-posts.html", - "hugo-new-site.sh", - "if-instead-of-default.html", - "img-output.html", - "index.html", - "instagram-hide-caption-output.html", - "instagram-input-hide-caption.md", - "instagram-input.md", - "install-brew.sh", - "install-extended-with-chocolatey.ps1", - "install-go.sh", - "install-openssh.sh", - "install-with-chocolatey.ps1", - "install-with-homebrew.sh", - "install-with-linuxbrew.sh", - "install-with-macports.sh", - "install.sh", - "layout/_default/section.html", - "layout/_default/single.html", - "layouts/404.html", - "layouts/_default/_markup/render-heading.html", - "layouts/_default/_markup/render-image.html", - "layouts/_default/_markup/render-link.html", - "layouts/_default/baseof.html", - "layouts/_default/li.html", - "layouts/_default/list.html", - "layouts/_default/section.html", - "layouts/_default/single.html", - "layouts/_default/summary.html", - "layouts/_default/taxonomy.html", - "layouts/index.html", - "layouts/partials/all-taxonomies.html", - "layouts/partials/alllanguages.html", - "layouts/partials/bad-url-sidebar-menu.html", - "layouts/partials/breadcrumb.html", - "layouts/partials/by-date-reverse.html", - "layouts/partials/by-date.html", - "layouts/partials/by-expiry-date.html", - "layouts/partials/by-group-by-page.html", - "layouts/partials/by-last-mod.html", - "layouts/partials/by-length.html", - "layouts/partials/by-link-title.html", - "layouts/partials/by-nested-param.html", - "layouts/partials/by-page-date.html", - "layouts/partials/by-page-expiry-date.html", - "layouts/partials/by-page-field.html", - "layouts/partials/by-page-lastmod.html", - "layouts/partials/by-page-param-as-date.html", - "layouts/partials/by-page-param.html", - "layouts/partials/by-page-publish-date.html", - "layouts/partials/by-publish-date.html", - "layouts/partials/by-rating.html", - "layouts/partials/by-title.html", - "layouts/partials/by-weight.html", - "layouts/partials/content-header.html", - "layouts/partials/correct-url-sidebar-menu.html", - "layouts/partials/default-order.html", - "layouts/partials/disqus.html", - "layouts/partials/footer.html", - "layouts/partials/get-csv.html", - "layouts/partials/groups.html", - "layouts/partials/head.html", - "layouts/partials/header.html", - "layouts/partials/i18nlist.html", - "layouts/partials/post-tag-link.html", - "layouts/partials/post-tag-list.html", - "layouts/partials/related.html", - "layouts/partials/schemaorg-metadata.html", - "layouts/partials/sidebar.html", - "layouts/partials/svgs/external-links.svg", - "layouts/partials/toc.html", - "layouts/partials/twitter.html", - "layouts/partials/upcoming-events.html", - "layouts/posts/single.html", - "layouts/robots.txt", - "layouts/section/articles.html", - "layouts/section/posts.html", - "layouts/shortcodes/gallery.html", - "layouts/shortcodes/img.html", - "layouts/shortcodes/imgproc.html", - "li.html", - "links-to-all-tags.html", - "list.html", - "netlify.toml", - "note-with-heading.html", - "note-with-heading.md", - "page-list-with-summaries.html", - "partial-cached-example.html", - "partials/templates/random-tweets.html", - "post-tag-list.html", - "prose", - "push-wecker-to-gh.sh", - "range-through-tags-w-global.html", - "remove-herring-cove-git.sh", - "robots.txt", - "schemaorg-metadata.html", - "section.html", - "setup-gh-repo.sh", - "shuffle-input.html", - "shuffle-output.html", - "sidebar.html", - "single.html", - "slice.html", - "summary.html", - "syntax-highlighted.html", - "tags-range-with-page-variable.html", - "taxonomy.html", - "time-passed.html", - "tip-output.html", - "toc.html", - "tutorials/learn-html/index.html", - "tweets.html", - "unix-to-month-integer.html", - "upcoming-events.html", - "using-tip.md", - "variable-as-default-value.html", - "vimeo-iframes.html", - "warning-admonition-input.md", - "warning-admonition-output.html", - "wercker-build-step.yml", - "wercker.yml", - "where-intersect-variables.html", - "with-instead-of-default.html", - "yourbaseurl/review/book01/index.html", - "youtube-embed.html" - ] - } -} \ No newline at end of file diff --git a/docs/hugoreleaser.toml b/docs/hugoreleaser.toml deleted file mode 100644 index 3ee1aad13..000000000 --- a/docs/hugoreleaser.toml +++ /dev/null @@ -1,29 +0,0 @@ -project = "hugoDocs" - -[release_settings] - name = "${HUGORELEASER_TAG}" - type = "github" - repository = "hugoDocs" - repository_owner = "gohugoio" - - draft = true - prerelease = false - - [release_settings.release_notes_settings] - generate = true - generate_on_host = false - short_threshold = 10 - short_title = "What's Changed" - - groups = [ - { regexp = "snapcraft:|Merge commit|Merge branch|netlify:|release:|Squashed", ignore = true }, - { title = "Typo fixes", regexp = "typo", ordinal = 20 }, - { title = "Dependency Updates", regexp = "deps", ordinal = 30 }, - { title = "Improvements", regexp = ".*", ordinal = 10 }, - ] - -[[releases]] - paths = ["archives/**"] - # In this file we have only one release, but path can be used to partition the release step, e.g.: - # hugoreleaser release -paths "releases/myrelease" - path = "myrelease" diff --git a/docs/hugoreleaser.yaml b/docs/hugoreleaser.yaml new file mode 100644 index 000000000..9f8671e06 --- /dev/null +++ b/docs/hugoreleaser.yaml @@ -0,0 +1,29 @@ +project: hugoDocs +release_settings: + name: ${HUGORELEASER_TAG} + type: github + repository: hugoDocs + repository_owner: gohugoio + draft: true + prerelease: false + release_notes_settings: + generate: true + generate_on_host: false + short_threshold: 10 + short_title: What's Changed + groups: + - regexp: snapcraft:|Merge commit|Merge branch|netlify:|release:|Squashed + ignore: true + - title: Typo fixes + regexp: typo + ordinal: 20 + - title: Dependency Updates + regexp: deps + ordinal: 30 + - title: Improvements + regexp: .* + ordinal: 10 +releases: + - paths: + - archives/** + path: myrelease diff --git a/docs/layouts/404.html b/docs/layouts/404.html new file mode 100644 index 000000000..6d962ffc0 --- /dev/null +++ b/docs/layouts/404.html @@ -0,0 +1,22 @@ +{{ define "main" }} +
+
+

+ Page not found + gopher +

+ + +
+
+{{ end }} diff --git a/docs/layouts/_markup/render-blockquote.html b/docs/layouts/_markup/render-blockquote.html new file mode 100644 index 000000000..98019e12d --- /dev/null +++ b/docs/layouts/_markup/render-blockquote.html @@ -0,0 +1,33 @@ +{{- if eq .Type "alert" }} + {{- $alerts := dict + "caution" (dict "color" "red" "icon" "exclamation-triangle") + "important" (dict "color" "blue" "icon" "exclamation-circle") + "note" (dict "color" "blue" "icon" "information-circle") + "tip" (dict "color" "green" "icon" "light-bulb") + "warning" (dict "color" "orange" "icon" "exclamation-triangle") + }} + + {{- $alertTypes := slice }} + {{- range $k, $_ := $alerts }} + {{- $alertTypes = $alertTypes | append $k }} + {{- end }} + {{- $alertTypes = $alertTypes | sort }} + + {{- $alertType := strings.ToLower .AlertType }} + {{- if in $alertTypes $alertType }} + {{- partial "layouts/blocks/alert.html" (dict + "color" (or ((index $alerts $alertType).color) "blue") + "icon" (or ((index $alerts $alertType).icon) "information-circle") + "text" .Text + "title" .AlertTitle + "class" .Attributes.class + ) + }} + {{- else }} + {{- errorf `Invalid blockquote alert type. Received %s. Expected one of %s (case-insensitive). See %s.` .AlertType (delimit $alertTypes ", " ", or ") .Page.String }} + {{- end }} +{{- else }} +
+ {{ .Text }} +
+{{- end }} diff --git a/docs/layouts/_markup/render-codeblock.html b/docs/layouts/_markup/render-codeblock.html new file mode 100644 index 000000000..13725ffcd --- /dev/null +++ b/docs/layouts/_markup/render-codeblock.html @@ -0,0 +1,98 @@ +{{/* prettier-ignore-start */}} +{{/* +Renders a highlighted code block using the given options and attributes. + +In addition to the options available to the transform.Highlight function, you +may also specify the following parameters: + +@param {bool} [copy=false] Whether to display a copy-to-clipboard button. +@param {string} [file] The file name to display above the rendered code. +@param {bool} [details=false] Whether to wrap the highlighted code block within a details element. +@param {bool} [open=false] Whether to initially display the content of the details element. +@param {string} [summary=Details] The content of the details summary element rendered from Markdown to HTML. + +@returns {template.HTML} + +@examples + + ```go + fmt.Println("Hello world!") + ``` + + ```go {linenos=true file="layouts/index.html" copy=true} + fmt.Println("Hello world!") + ``` +*/}} +{{/* prettier-ignore-end */}} + +{{- $copy := false }} +{{- $file := or .Attributes.file "" }} +{{- $details := false }} +{{- $open := "" }} +{{- $summary := or .Attributes.summary "Details" | .Page.RenderString }} +{{- $ext := strings.TrimPrefix "." (path.Ext $file) }} +{{- $lang := or .Type $ext "text" }} +{{- if in (slice "html" "gotmpl") $lang }} + {{- $lang = "go-html-template" }} +{{- end }} +{{- if eq $lang "md" }} + {{- $lang = "text" }} +{{- end }} + +{{- with .Attributes.copy }} + {{- if in (slice true "true" 1) . }} + {{- $copy = true }} + {{- else if in (slice false "false" 0) . }} + {{- $copy = false }} + {{- end }} +{{- end }} + +{{- with .Attributes.details }} + {{- if in (slice true "true" 1) . }} + {{- $details = true }} + {{- else if in (slice false "false" 0) . }} + {{- $details = false }} + {{- end }} +{{- end }} + +{{- with .Attributes.open }} + {{- if in (slice true "true" 1) . }} + {{- $open = "open" }} + {{- else if in (slice false "false" 0) . }} + {{- $open = "" }} + {{- end }} +{{- end }} + +{{- if $details }} +
+ {{ $summary }} +{{- end }} + +
+ {{- $fileSelectClass := "select-none" }} + {{- if $copy }} + {{- $fileSelectClass = "select-text" }} + + + + {{- end }} + {{- with $file }} +
+ {{ . }} +
+ {{- end }} + +
+ {{- transform.Highlight (strings.TrimSpace .Inner) $lang .Options }} +
+
+ +{{- if $details }} +
+{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-link.html b/docs/layouts/_markup/render-link.html similarity index 60% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-link.html rename to docs/layouts/_markup/render-link.html index 9b6cad114..70011220e 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/_default/_markup/render-link.html +++ b/docs/layouts/_markup/render-link.html @@ -1,7 +1,8 @@ -{{- /* Last modified: 2024-04-26T13:54:00-07:00 */}} +{{/* prettier-ignore-start */ -}} +{{- /* Last modified: 2025-01-19T14:44:56-08:00 */}} {{- /* -Copyright 2023 Veriphor LLC +Copyright 2025 Veriphor LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of @@ -75,13 +76,13 @@ either of these shortcodes in conjunction with this render hook. @context {string} Title The link title. @returns {template.html} -*/}} - +*/ -}} +{{/* prettier-ignore-end */ -}} {{- /* Initialize. */}} {{- $renderHookName := "link" }} {{- /* Verify minimum required version. */}} -{{- $minHugoVersion := "0.120.0" }} +{{- $minHugoVersion := "0.141.0" }} {{- if lt hugo.Version $minHugoVersion }} {{- errorf "The %q render hook requires Hugo v%s or later." $renderHookName $minHugoVersion }} {{- end }} @@ -98,12 +99,7 @@ either of these shortcodes in conjunction with this render hook. {{- end }} {{- /* Determine content path for warning and error messages. */}} -{{- $contentPath := "" }} -{{- with .Page.File }} - {{- $contentPath = .Path }} -{{- else }} - {{- $contentPath = .Path }} -{{- end }} +{{- $contentPath := .Page.String }} {{- /* Parse destination. */}} {{- $u := urls.Parse .Destination }} @@ -113,7 +109,16 @@ either of these shortcodes in conjunction with this render hook. {{- /* Set attributes for anchor element. */}} {{- $attrs := dict "href" $u.String }} -{{- if $u.IsAbs }} +{{- if eq $u.String "g" }} + {{- /* Destination is a glossary term. */}} + {{- $ctx := dict + "contentPath" $contentPath + "errorLevel" $errorLevel + "renderHookName" $renderHookName + "text" .Text + }} + {{- $attrs = partial "inline/h-rh-l/get-glossary-link-attributes.html" $ctx }} +{{- else if $u.IsAbs }} {{- /* Destination is a remote resource. */}} {{- $attrs = merge $attrs (dict "rel" "external") }} {{- else }} @@ -136,29 +141,23 @@ either of these shortcodes in conjunction with this render hook. {{- $href = printf "%s#%s" $href . }} {{- end }} {{- $attrs = dict "href" $href }} + {{- else with $.PageInner.Resources.Get $u.Path }} + {{- /* Destination is a page resource; drop query and fragment. */}} + {{- $attrs = dict "href" .RelPermalink }} + {{- else with (and (ne $.Page.BundleType "leaf") ($.Page.CurrentSection.Resources.Get $u.Path)) }} + {{- /* Destination is a section resource, and current page is not a leaf bundle. */}} + {{- $attrs = dict "href" .RelPermalink }} + {{- else with resources.Get $u.Path }} + {{- /* Destination is a global resource; drop query and fragment. */}} + {{- $attrs = dict "href" .RelPermalink }} {{- else }} - {{- with $.PageInner.Resources.Get $u.Path }} - {{- /* Destination is a page resource; drop query and fragment. */}} - {{- $attrs = dict "href" .RelPermalink }} - {{- else }} - {{- with (and (ne $.Page.BundleType "leaf") ($.Page.CurrentSection.Resources.Get $u.Path)) }} - {{- /* Destination is a section resource, and current page is not a leaf bundle. */}} - {{- $attrs = dict "href" .RelPermalink }} - {{- else }} - {{- with resources.Get $u.Path }} - {{- /* Destination is a global resource; drop query and fragment. */}} - {{- $attrs = dict "href" .RelPermalink }} - {{- else }} - {{- if eq $errorLevel "warning" }} - {{- warnf $msg }} - {{- if and $highlightBrokenLinks hugo.IsDevelopment }} - {{- $attrs = merge $attrs (dict "class" "broken") }} - {{- end }} - {{- else if eq $errorLevel "error" }} - {{- errorf $msg }} - {{- end }} - {{- end }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- if and $highlightBrokenLinks hugo.IsDevelopment }} + {{- $attrs = merge $attrs (dict "class" "broken") }} {{- end }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} {{- end }} {{- end }} {{- else }} @@ -185,26 +184,27 @@ either of these shortcodes in conjunction with this render hook. {{- end }} {{- end }} {{- end }} -{{- $attrs = merge $attrs (dict "title" (.Title | transform.HTMLEscape)) }} {{- /* Render anchor element. */ -}} {{ .Text | safeHTML }} + >{{ .Text }} -{{- define "partials/inline/h-rh-l/validate-fragment.html" }} +{{- define "_partials/inline/h-rh-l/validate-fragment.html" }} {{- /* - Validates the fragment portion of a link destination. + Validates the fragment portion of a link destination. - @context {string} contentPath The page containing the link. - @context {string} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error. - @context {page} page The page corresponding to the link destination - @context {struct} parsedURL The link destination parsed by urls.Parse. - @context {string} renderHookName The name of the render hook. + @context {string} contentPath The page containing the link. + @context {string} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error. + @context {page} page The page corresponding to the link destination + @context {struct} parsedURL The link destination parsed by urls.Parse. + @context {string} renderHookName The name of the render hook. */}} {{- /* Initialize. */}} @@ -246,5 +246,75 @@ either of these shortcodes in conjunction with this render hook. {{- end }} {{- end }} {{- end }} +{{- end }} +{{- define "_partials/inline/h-rh-l/get-glossary-link-attributes.html" }} + {{- /* + Returns the anchor element attributes for a link to the given glossary term. + + It first checks for the existence of a glossary page for the given term. If + no page is found, it then checks for a glossary page for the singular form of + the term. If neither page exists it throws a warning or error dependent on + the errorLevel setting + + The returned href attribute does not point to the glossary term page. + Instead, via its fragment, it points to an entry on the glossary page. + + @context {string} contentPath The page containing the link. + @context {string} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error. + @context {string} renderHookName The name of the render hook. + @context {string} text The link text. + */}} + + {{- /* Get context.. */}} + {{- $contentPath := .contentPath }} + {{- $errorLevel := .errorLevel }} + {{- $renderHookName := .renderHookName }} + {{- $text := .text | transform.Plainify | strings.ToLower }} + + {{- /* Initialize. */}} + {{- $glossaryPath := "/quick-reference/glossary" }} + {{- $termGiven := $text }} + {{- $termActual := "" }} + {{- $termSingular := inflect.Singularize $termGiven }} + + {{- /* Verify that the glossary page exists. */}} + {{- $glossaryPage := site.GetPage $glossaryPath }} + {{- if not $glossaryPage }} + {{- errorf "The %q render hook was unable to find %s: see %s" $renderHookName $glossaryPath $contentPath }} + {{- end }} + + {{- /* There's a better way to handle this, but it works for now. */}} + {{- $cheating := dict + "chaining" "chain" + "localize" "localization" + "localized" "localization" + "paginating" "paginate" + "walking" "walk" + "ci/cd" "cicd" + }} + + {{- /* Verify that a glossary term page exists for the given term. */}} + {{- if site.GetPage (urls.JoinPath $glossaryPath ($termGiven | urlize)) }} + {{- $termActual = $termGiven }} + {{- else if site.GetPage (urls.JoinPath $glossaryPath ($termSingular | urlize)) }} + {{- $termActual = $termSingular }} + {{- else }} + {{- $termToTest := index $cheating $termGiven }} + {{- if site.GetPage (urls.JoinPath $glossaryPath ($termToTest | urlize)) }} + {{- $termActual = $termToTest }} + {{- end }} + {{- end }} + + {{- if not $termActual }} + {{- errorf "The %q render hook was unable to find a glossary page for either the singular or plural form of the term %q: see %s" $renderHookName $termGiven $contentPath }} + {{- end }} + + {{- /* Create the href attribute. */}} + {{- $href := "" }} + {{- if $termActual }} + {{- $href = fmt.Printf "%s#%s" $glossaryPage.RelPermalink (anchorize $termActual) }} + {{- end }} + + {{- return (dict "href" $href) }} {{- end -}} diff --git a/docs/layouts/_markup/render-passthrough.html b/docs/layouts/_markup/render-passthrough.html new file mode 100644 index 000000000..0ed001133 --- /dev/null +++ b/docs/layouts/_markup/render-passthrough.html @@ -0,0 +1,9 @@ +{{- $opts := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") }} +{{- with try (transform.ToMath .Inner $opts) }} + {{- with .Err }} + {{ errorf "Unable to render mathematical markup to HTML using the transform.ToMath function. The KaTeX display engine threw the following error: %s: see %s." . $.Position }} + {{- else }} + {{- .Value }} + {{- $.Page.Store.Set "hasMath" true }} + {{- end }} +{{- end -}} diff --git a/docs/layouts/_markup/render-table.html b/docs/layouts/_markup/render-table.html new file mode 100644 index 000000000..7f3a88601 --- /dev/null +++ b/docs/layouts/_markup/render-table.html @@ -0,0 +1,31 @@ +
+ + + {{- range .THead }} + + {{- range . }} + + {{- end }} + + {{- end }} + + + {{- range .TBody }} + + {{- range . }} + + {{- end }} + + {{- end }} + +
+ {{- .Text -}} +
+ {{- .Text -}} +
+
diff --git a/docs/layouts/_partials/docs/functions-aliases.html b/docs/layouts/_partials/docs/functions-aliases.html new file mode 100644 index 000000000..b3a5a7607 --- /dev/null +++ b/docs/layouts/_partials/docs/functions-aliases.html @@ -0,0 +1,12 @@ +{{- with .Params.functions_and_methods.aliases }} + {{- $label := "Alias" }} + {{- if gt (len .) 1 }} + {{- $label = "Aliases" }} + {{- end }} +

{{ $label }}

+ {{- range . }} +
+ {{- . -}} +
+ {{- end }} +{{- end -}} diff --git a/docs/layouts/_partials/docs/functions-return-type.html b/docs/layouts/_partials/docs/functions-return-type.html new file mode 100644 index 000000000..75c97f8d9 --- /dev/null +++ b/docs/layouts/_partials/docs/functions-return-type.html @@ -0,0 +1,6 @@ +{{- with .Params.functions_and_methods.returnType }} +

Returns

+
+ {{- . -}} +
+{{- end -}} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html b/docs/layouts/_partials/docs/functions-signatures.html similarity index 54% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html rename to docs/layouts/_partials/docs/functions-signatures.html index dce160227..6fb61df8e 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/docs/functions-signatures.html +++ b/docs/layouts/_partials/docs/functions-signatures.html @@ -1,12 +1,12 @@ -{{- with .Params.action.signatures }} -

Syntax

+{{- with .Params.functions_and_methods.signatures }} +

Syntax

{{- range . }} {{- $signature := . }} {{- if $.Params.function.returnType }} {{- $signature = printf "%s ⟼ %s" . $.Params.function.returnType }} {{- end }} -
+    
{{- $signature -}} -
+ {{- end }} {{- end -}} diff --git a/docs/layouts/_partials/helpers/debug/list-item-metadata.html b/docs/layouts/_partials/helpers/debug/list-item-metadata.html new file mode 100644 index 000000000..d027484b5 --- /dev/null +++ b/docs/layouts/_partials/helpers/debug/list-item-metadata.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + +
weight: + {{ .Weight }} +
keywords: + {{ delimit (or .Keywords "") ", " }} +
categories: + {{ delimit (or .Params.categories "") ", " }} +
diff --git a/docs/layouts/_partials/helpers/funcs/color-from-string.html b/docs/layouts/_partials/helpers/funcs/color-from-string.html new file mode 100644 index 000000000..cd599530b --- /dev/null +++ b/docs/layouts/_partials/helpers/funcs/color-from-string.html @@ -0,0 +1,25 @@ +{{ $colors := slice "slate" "green" "cyan" "blue" }} +{{ with .single }} + {{ $colors = slice . }} +{{ end }} + +{{ $shades := slice 300 400 500 }} +{{ if not .dark }} + {{ $shades = slice 700 800 }} +{{ end }} +{{ $hash := (hash.FNV32a .text) }} +{{ $i := mod $hash (len $colors) }} +{{ $j := mod $hash (len $shades) }} +{{ $color := index $colors $i }} +{{ $shade1 := index $shades $j }} +{{ $shade2 := 0 }} +{{ $shade3 := 0 }} +{{ if gt $shade1 500 }} + {{ $shade2 = math.Min (sub $shade1 500) 100 | int }} + {{ $shade3 = sub $shade1 100 }} +{{ else }} + {{ $shade2 = math.Max (add $shade1 500) 700 | int }} + {{ $shade3 = add $shade1 200 }} +{{ end }} +{{ $res := dict "color" $color "shade1" $shade1 "shade2" $shade2 "shade3" $shade3 }} +{{ return $res }} diff --git a/docs/layouts/_partials/helpers/funcs/get-github-info.html b/docs/layouts/_partials/helpers/funcs/get-github-info.html new file mode 100644 index 000000000..7e2dc89fa --- /dev/null +++ b/docs/layouts/_partials/helpers/funcs/get-github-info.html @@ -0,0 +1,28 @@ +{{ $url := "https://api.github.com/repos/gohugoio/hugo" }} +{{ $cacheKey := print $url (now.Format "2006-01-02") }} +{{ $headers := dict }} +{{ with os.Getenv "HUGO_GH_TOKEN" }} + {{ $headers = dict "Authorization" (printf "Bearer %s" .) }} +{{ end }} +{{ $opts := dict "headers" $headers "key" $cacheKey }} +{{ $githubRepoInfo := dict }} +{{ with try (resources.GetRemote $url $opts) }} + {{ with .Err }} + {{ warnf "Failed to get GitHub repo info: %s" . }} + {{ else with (.Value | transform.Unmarshal) }} + {{ $githubRepoInfo = dict + "html_url" .html_url + "stargazers_url" .stargazers_url + "watchers_count" .watchers_count + "stargazers_count" .stargazers_count + "forks_count" .forks_count + "contributors_url" .contributors_url + "releases_url" .releases_url + "forks_count" .forks_count + }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} + {{ end }} +{{ end }} + +{{ return $githubRepoInfo }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html b/docs/layouts/_partials/helpers/funcs/get-remote-data.html similarity index 71% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html rename to docs/layouts/_partials/helpers/funcs/get-remote-data.html index 69ac41da4..ed7043421 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/utilities/get-remote-data.html +++ b/docs/layouts/_partials/helpers/funcs/get-remote-data.html @@ -1,3 +1,4 @@ +{{/* prettier-ignore-start */ -}} {{/* Parses the serialized data from the given URL and returns a map or an array. @@ -8,16 +9,16 @@ Supports CSV, JSON, TOML, YAML, and XML. @example {{ partial "get-remote-data.html" "https://example.org/foo.json" }} */}} - +{{/* prettier-ignore-end */ -}} {{ $url := . }} {{ $data := dict }} -{{ with resources.GetRemote $url }} +{{ with try (resources.GetRemote $url) }} {{ with .Err }} {{ errorf "%s" . }} - {{ else }} + {{ else with .Value }} {{ $data = .Content | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} {{ end }} -{{ else }} - {{ errorf "Unable to get remote resource %q" $url }} {{ end }} {{ return $data }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html b/docs/layouts/_partials/helpers/gtag.html similarity index 80% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html rename to docs/layouts/_partials/helpers/gtag.html index 7c6a9ade5..59bf36ba2 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/partials/gtag.html +++ b/docs/layouts/_partials/helpers/gtag.html @@ -1,11 +1,13 @@ {{ with site.Config.Services.GoogleAnalytics.ID }} - + +{{ else }} + {{ with $r | fingerprint }} + + {{ end }} +{{ end }} diff --git a/docs/layouts/_partials/helpers/picture.html b/docs/layouts/_partials/helpers/picture.html new file mode 100644 index 000000000..4dc16c002 --- /dev/null +++ b/docs/layouts/_partials/helpers/picture.html @@ -0,0 +1,27 @@ +{{ $image := .image }} +{{ $width := .width | default 1000 }} +{{ $width1x := div $width 2 }} +{{ $imageWebp := $image.Resize (printf "%dx webp" $width) }} +{{ $image1x := $image.Resize (printf "%dx" $width1x) }} +{{ $image1xWebp := $image.Resize (printf "%dx webp" $width1x) }} +{{ $class := .class | default "h-64 tablet:h-96 lg:h-full w-full object-cover lg:absolute" }} +{{ $loading := .loading | default "eager" }} + + + + + + + diff --git a/docs/layouts/_partials/helpers/validation/validate-keywords.html b/docs/layouts/_partials/helpers/validation/validate-keywords.html new file mode 100644 index 000000000..3447ec4ef --- /dev/null +++ b/docs/layouts/_partials/helpers/validation/validate-keywords.html @@ -0,0 +1,22 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +We use the front matter keywords field to determine related content. To ensure +consistency, during site build we validate each keyword against the entries in +data/keywords.yaml. + +As of March 5, 2025, this feature is experimental, pending usability +assessment. We anticipate that the number of additions to data/keywords.yaml +will decrease over time, though the initial implementation will require some +effort. +*/}} +{{/* prettier-ignore-end */ -}} +{{- $t := debug.Timer "validateKeywords" }} +{{- $allowedKeywords := collections.Apply site.Data.keywords "strings.ToLower" "." }} +{{- range $p := site.Pages }} + {{- range .Params.keywords }} + {{- if not (in $allowedKeywords (lower .)) }} + {{- warnf "The word or phrase %q is not in the keywords data file. See %s." . $p.Page.String }} + {{- end }} + {{- end }} +{{- end }} +{{- $t.Stop }} diff --git a/docs/layouts/_partials/layouts/blocks/alert.html b/docs/layouts/_partials/layouts/blocks/alert.html new file mode 100644 index 000000000..45f0044d9 --- /dev/null +++ b/docs/layouts/_partials/layouts/blocks/alert.html @@ -0,0 +1,25 @@ +{{- $title := .title | default "" }} +{{- $color := .color | default "yellow" }} +{{- $icon := .icon | default "exclamation-triangle" }} +{{- $text := .text | default "" }} +{{- $class := .class | default "mt-6 mb-8" }} +
+
+
+ + + +
+
+ {{- with $title }} +

+ {{ . }} +

+ {{- end }} +
+ {{ $text }} +
+
+
+
diff --git a/docs/layouts/_partials/layouts/blocks/modal.html b/docs/layouts/_partials/layouts/blocks/modal.html new file mode 100644 index 000000000..7d825c06e --- /dev/null +++ b/docs/layouts/_partials/layouts/blocks/modal.html @@ -0,0 +1,30 @@ +
+
+ {{ .modal_button }} +
+ +
diff --git a/docs/layouts/_partials/layouts/breadcrumbs.html b/docs/layouts/_partials/layouts/breadcrumbs.html new file mode 100644 index 000000000..69bcf7bd5 --- /dev/null +++ b/docs/layouts/_partials/layouts/breadcrumbs.html @@ -0,0 +1,44 @@ +{{ $documentation := site.GetPage "/documentation" }} + + + + +{{ define "breadcrumbs-arrow" }} + + + +{{ end }} diff --git a/docs/layouts/_partials/layouts/date.html b/docs/layouts/_partials/layouts/date.html new file mode 100644 index 000000000..2ec1450a5 --- /dev/null +++ b/docs/layouts/_partials/layouts/date.html @@ -0,0 +1,5 @@ +{{ $humanDate := time.Format "January 2, 2006" . }} +{{ $machineDate := time.Format "2006-01-02T15:04:05-07:00" . }} + diff --git a/docs/layouts/_partials/layouts/docsheader.html b/docs/layouts/_partials/layouts/docsheader.html new file mode 100644 index 000000000..7e8e950f3 --- /dev/null +++ b/docs/layouts/_partials/layouts/docsheader.html @@ -0,0 +1,9 @@ +
+ {{ partial "layouts/breadcrumbs.html" . }} + {{ if and .IsPage (not (eq .Layout "list")) }} +

+ {{ .Title }} +

+ {{ end }} +
diff --git a/docs/layouts/_partials/layouts/explorer.html b/docs/layouts/_partials/layouts/explorer.html new file mode 100644 index 000000000..bb6f8e96a --- /dev/null +++ b/docs/layouts/_partials/layouts/explorer.html @@ -0,0 +1,47 @@ +{{/* This is currently not in use, but kept in case I change my mind. */}} + + +{{ define "docs-explorer-section" }} + {{ $p := .p }} + {{ $level := .level }} + {{ $pleft := $level }} + {{ if gt $level 0 }} + {{ $pleft = add $level 1 }} + {{ end }} + {{ $pl := printf "pl-%d" $pleft }} + {{ $pages := $p.Sections }} + + {{ range $pages }} + {{ $hasChildren := gt (len .Pages) 0 }} + {{ $class := cond (eq $level 0) "text-primary hover:text-primary/70" "text-gray-900 dark:text-gray-400 hover:dark:text-gray-300" }} +
  • + + {{ .LinkTitle }} + + {{ if $hasChildren }} +
      + {{ template "docs-explorer-section" (dict "p" . "level" (add $level 1)) }} +
    + {{ end }} +
  • + {{ end }} + +{{ end }} diff --git a/docs/layouts/_partials/layouts/footer.html b/docs/layouts/_partials/layouts/footer.html new file mode 100644 index 000000000..1b17e44e4 --- /dev/null +++ b/docs/layouts/_partials/layouts/footer.html @@ -0,0 +1,73 @@ +
    +
    +
    + {{/* Column 1 */}} +
    +
    + By the + Hugo Authors
    +
    + + Hugo Logo + + +
    + + {{/* Sponsors */}} +
    + {{ partial "layouts/home/sponsors.html" (dict + "ctx" . + "gtag" "footer" + + ) + }} +
    +
    +
    +

    + The Hugo logos are copyright © Steve Francia 2013–{{ now.Year }}. The + Hugo Gopher is based on an original work by Renée French. +

    +
    +
    +
    diff --git a/docs/layouts/_partials/layouts/head/head-js.html b/docs/layouts/_partials/layouts/head/head-js.html new file mode 100644 index 000000000..d83efcd0f --- /dev/null +++ b/docs/layouts/_partials/layouts/head/head-js.html @@ -0,0 +1,11 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +{{ $opts := dict "minify" true }} +{{ with resources.Get "js/head-early.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "async" "")) }} +{{ end }} +{{ with resources.Get "js/main.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} +{{ with resources.Get "js/turbo.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} diff --git a/docs/layouts/_partials/layouts/head/head.html b/docs/layouts/_partials/layouts/head/head.html new file mode 100644 index 000000000..bb27f6a24 --- /dev/null +++ b/docs/layouts/_partials/layouts/head/head.html @@ -0,0 +1,49 @@ + +{{ hugo.Generator }} + +{{ if hugo.IsProduction }} + +{{ else }} + +{{ end }} + + + {{ with .Title }}{{ . }} |{{ end }} + {{ .Site.Title }} + + + + + + + + + + + +{{ range .AlternativeOutputFormats -}} + +{{ end -}} + + + + + + +{{ partial "opengraph/opengraph.html" . }} +{{- template "_internal/schema.html" . -}} +{{- template "_internal/twitter_cards.html" . -}} + +{{ if hugo.IsProduction }} + {{ partial "helpers/gtag.html" . }} +{{ end }} diff --git a/docs/layouts/_partials/layouts/header/githubstars.html b/docs/layouts/_partials/layouts/header/githubstars.html new file mode 100644 index 000000000..75db5682a --- /dev/null +++ b/docs/layouts/_partials/layouts/header/githubstars.html @@ -0,0 +1,16 @@ +{{ with partialCached "helpers/funcs/get-github-info.html" . "-" }} + + + + + + + + {{ printf "%0.1fk" (div .stargazers_count 1000) }} + + +{{ end }} diff --git a/docs/layouts/_partials/layouts/header/header.html b/docs/layouts/_partials/layouts/header/header.html new file mode 100644 index 000000000..0d2e720d7 --- /dev/null +++ b/docs/layouts/_partials/layouts/header/header.html @@ -0,0 +1,44 @@ +
    +
    + {{ with site.Home }} + HUGO + {{ end }} +
    +
    + {{ range .Site.Menus.global }} + {{ .Name }} + {{ end }} + +
    + +
    + {{/* Search. */}} + {{ partial "layouts/search/input.html" . }} +
    +
    + {{/* QR code. */}} + {{ partial "layouts/header/qr.html" . }} + {{/* Theme selector. */}} + {{ partial "layouts/header/theme.html" . }} + + {{/* Social. */}} + +
    +
    diff --git a/docs/layouts/_partials/layouts/header/qr.html b/docs/layouts/_partials/layouts/header/qr.html new file mode 100644 index 000000000..fea64f625 --- /dev/null +++ b/docs/layouts/_partials/layouts/header/qr.html @@ -0,0 +1,25 @@ +{{ $t := debug.Timer "qr" }} +{{ $qr := partial "_inline/qr" (dict + "page" $ + "img_class" "w-10 bg-white view-transition-qr" ) +}} +{{ $qrBig := partial "_inline/qr" (dict "page" $ "img_class" "w-64 p-4") }} +{{ $t.Stop }} + + +{{ define "_partials/_inline/qr" }} + {{ $img_class := .img_class | default "w-10" }} + {{ with images.QR $.page.Permalink (dict "targetDir" "images/qr") }} + + QR code linking to {{ $.page.Permalink }} + {{ end }} +{{ end }} diff --git a/docs/layouts/_partials/layouts/header/theme.html b/docs/layouts/_partials/layouts/header/theme.html new file mode 100644 index 000000000..e0b356d1d --- /dev/null +++ b/docs/layouts/_partials/layouts/header/theme.html @@ -0,0 +1,35 @@ +
    + +
    diff --git a/docs/layouts/_partials/layouts/home/features.html b/docs/layouts/_partials/layouts/home/features.html new file mode 100644 index 000000000..527c98cb1 --- /dev/null +++ b/docs/layouts/_partials/layouts/home/features.html @@ -0,0 +1,56 @@ +{{/* icons source: https://heroicons.com/ */}} +{{ $dataTOML := ` + [[features]] + heading = "Optimized for speed" + copy = "Written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a large site in seconds, often less." + icon = """ + + + """ + [[features]] + heading = "Flexible framework" + copy = "With its multilingual support, and powerful taxonomy system, Hugo is widely used to create documentation sites, landing pages, corporate, government, nonprofit, education, news, event, and project sites." + icon = """ + + + """ + [[features]] + heading = "Fast assets pipeline" + copy = "Image processing (convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data), JavaScript bundling (tree shake, code splitting), Sass processing, great TailwindCSS support." + icon = """ + + + """ + [[features]] + heading = "Embedded web server" + copy = "Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. " + icon = """ + + + """ + ` +}} +{{ $data := $dataTOML | transform.Unmarshal }} +
    +
    +
    + {{ range $data.features }} +
    +
    +
    + {{ .icon | safeHTML }} +
    + {{ .heading }} +
    +
    + {{ .copy }} +
    +
    + {{ end }} + +
    +
    +
    diff --git a/docs/layouts/_partials/layouts/home/opensource.html b/docs/layouts/_partials/layouts/home/opensource.html new file mode 100644 index 000000000..153c0f4ff --- /dev/null +++ b/docs/layouts/_partials/layouts/home/opensource.html @@ -0,0 +1,111 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +
    +
    +
    +
    +

    + Open source +

    +

    + Hugo is open source and free to use. It is distributed under the + Apache 2.0 License. +

    +
    +
    +
    + + + + Popular. +
    +
    + Hugo has + {{ $githubInfo.stargazers_count | lang.FormatNumber 0 }} + stars on GitHub as of {{ now.Format "January 2, 2006" }}. + Join the crowd and hit the + Star button. +
    +
    +
    +
    + + + + Active. +
    +
    + Hugo has a large and active community. If you have questions or + need help, you can ask in the + Hugo forums. +
    +
    +
    +
    + + + + Frequent releases. +
    +
    + Hugo has a fast + release + cycle. The project is actively maintained and new features are + added regularly. +
    +
    +
    +
    +
    + {{ partial "helpers/picture.html" (dict + "image" (resources.Get "images/hugo-github-screenshot.png") + "alt" "Hugo GitHub Repository" + "width" 640 + "class" "w-full max-w-[38rem] ring-1 shadow-xl dark:shadow-gray-500 ring-gray-400/10") + }} +
    +
    diff --git a/docs/layouts/_partials/layouts/home/sponsors.html b/docs/layouts/_partials/layouts/home/sponsors.html new file mode 100644 index 000000000..1f391e1ec --- /dev/null +++ b/docs/layouts/_partials/layouts/home/sponsors.html @@ -0,0 +1,44 @@ +{{ $gtag := .gtag | default "unknown" }} +{{ $gtag := .gtag | default "unknown" }} +{{ $isFooter := (eq $gtag "footer") }} +{{ $utmSource := cond $isFooter "hugofooter" "hugohome" }} +{{ $containerClass := .containerClass | default "mx-auto max-w-7xl px-6 lg:px-8" }} +{{ with .ctx.Site.Data.sponsors }} +
    +

    Hugo Sponsors

    +
    + {{ range .banners }} +
    + {{ $query_params := .query_params | default "" }} + {{ $url := .link }} + {{ if not .no_query_params }} + {{ $url = printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" (.utm_medium | default "banner") "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }} + {{ end }} + {{ $logo := resources.Get .logo }} + {{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }} + + + +
    + {{ end }} +
    +
    +{{ end }} diff --git a/docs/layouts/_partials/layouts/hooks/body-end.html b/docs/layouts/_partials/layouts/hooks/body-end.html new file mode 100644 index 000000000..9ffd93ba8 --- /dev/null +++ b/docs/layouts/_partials/layouts/hooks/body-end.html @@ -0,0 +1,3 @@ +{{- if .IsHome }} + {{- partial "helpers/validation/validate-keywords.html" }} +{{- end }} diff --git a/docs/layouts/_partials/layouts/hooks/body-main-start.html b/docs/layouts/_partials/layouts/hooks/body-main-start.html new file mode 100644 index 000000000..b28dd21c8 --- /dev/null +++ b/docs/layouts/_partials/layouts/hooks/body-main-start.html @@ -0,0 +1,8 @@ +{{ if or .IsSection .IsPage }} + +{{end }} diff --git a/docs/layouts/_partials/layouts/hooks/body-start.html b/docs/layouts/_partials/layouts/hooks/body-start.html new file mode 100644 index 000000000..3430bd846 --- /dev/null +++ b/docs/layouts/_partials/layouts/hooks/body-start.html @@ -0,0 +1,3 @@ +{{ with resources.Get "js/body-start.js" | js.Build (dict "minify" true) }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "" "")) }} +{{ end }} diff --git a/docs/layouts/_partials/layouts/icons.html b/docs/layouts/_partials/layouts/icons.html new file mode 100644 index 000000000..b05adef00 --- /dev/null +++ b/docs/layouts/_partials/layouts/icons.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{{/* https://heroicons.com/mini exclamation-circle */}} + + + + +{{/* https://heroicons.com/mini exclamation-triangle */}} + + + + +{{/* https://heroicons.com/mini information-circle */}} + + + + +{{/* https://heroicons.com/mini light-bulb */}} + + + diff --git a/docs/layouts/_partials/layouts/in-this-section.html b/docs/layouts/_partials/layouts/in-this-section.html new file mode 100644 index 000000000..28e5ed7eb --- /dev/null +++ b/docs/layouts/_partials/layouts/in-this-section.html @@ -0,0 +1,34 @@ +{{- with .CurrentSection.RegularPages }} + {{ $hasTocOrRelated := or ($.Store.Get "hasToc") ($.Store.Get "hasRelated") }} +
    +

    + In this section +

    + + +
    +{{- end }} diff --git a/docs/layouts/_partials/layouts/page-edit.html b/docs/layouts/_partials/layouts/page-edit.html new file mode 100644 index 000000000..6a976d11a --- /dev/null +++ b/docs/layouts/_partials/layouts/page-edit.html @@ -0,0 +1,26 @@ +
    +
    + +
    + Last updated: + {{ .Lastmod.Format "January 2, 2006" }}{{ with .GitInfo }} + : + {{ .Subject }} ({{ .AbbreviatedHash }}) + {{ end }} +
    + + {{ with .File }} + {{ if not .IsContentAdapter }} + {{ $href := printf "%sedit/master/content/%s/%s" site.Params.ghrepo $.Lang .Path }} + + Improve this page + + {{ end }} + {{ end }} +
    diff --git a/docs/layouts/_partials/layouts/related.html b/docs/layouts/_partials/layouts/related.html new file mode 100644 index 000000000..0245ba771 --- /dev/null +++ b/docs/layouts/_partials/layouts/related.html @@ -0,0 +1,22 @@ +{{- $heading := "See also" }} +{{- $related := site.Pages.Related . }} +{{- $related = $related | complement .CurrentSection.Pages | first 7 }} + +{{- with $related }} + {{ $.Store.Set "hasRelated" true }} +

    + {{ $heading }} +

    + +{{- end }} diff --git a/docs/layouts/_partials/layouts/search/algolialogo.html b/docs/layouts/_partials/layouts/search/algolialogo.html new file mode 100644 index 000000000..a7fc6c0ae --- /dev/null +++ b/docs/layouts/_partials/layouts/search/algolialogo.html @@ -0,0 +1,45 @@ +
    + Search by + + + + + + + + + + +
    diff --git a/docs/layouts/_partials/layouts/search/button.html b/docs/layouts/_partials/layouts/search/button.html new file mode 100644 index 000000000..07c1f7335 --- /dev/null +++ b/docs/layouts/_partials/layouts/search/button.html @@ -0,0 +1,22 @@ +{{ $textColor := "text-gray-300" }} +{{ $fillColor := "fill-slate-400 dark:fill-slate-500" }} +{{ if .standalone }} + {{ $textColor = "text-gray-800 dark:text-gray-300 " }} + {{ $fillColor = "fill-slate-500 dark:fill-slate-400" }} +{{ end }} + + + diff --git a/docs/layouts/_partials/layouts/search/input.html b/docs/layouts/_partials/layouts/search/input.html new file mode 100644 index 000000000..5f5ff07b9 --- /dev/null +++ b/docs/layouts/_partials/layouts/search/input.html @@ -0,0 +1,4 @@ +
    + {{ partial "layouts/search/button.html" (dict "page" . "standalone" false) }} + {{ partial "layouts/search/results.html" . }} +
    diff --git a/docs/layouts/_partials/layouts/search/results.html b/docs/layouts/_partials/layouts/search/results.html new file mode 100644 index 000000000..cd9b88dc0 --- /dev/null +++ b/docs/layouts/_partials/layouts/search/results.html @@ -0,0 +1,90 @@ + diff --git a/docs/layouts/_partials/layouts/templates.html b/docs/layouts/_partials/layouts/templates.html new file mode 100644 index 000000000..72b71a3d9 --- /dev/null +++ b/docs/layouts/_partials/layouts/templates.html @@ -0,0 +1,7 @@ + diff --git a/docs/layouts/_partials/layouts/toc.html b/docs/layouts/_partials/layouts/toc.html new file mode 100644 index 000000000..774bc15c7 --- /dev/null +++ b/docs/layouts/_partials/layouts/toc.html @@ -0,0 +1,46 @@ +{{ with .Fragments }} + {{ with .Headings }} +
    +

    + On this page +

    + +
    + {{ end }} +{{ end }} + +{{ define "render-toc-level" }} + {{ range .h }} + {{ if and .ID (and (ge .Level 2) (le .Level 4)) }} + {{ $indentation := "ml-0" }} + {{ if eq .Level 3 }} + {{ $indentation = "ml-2 lg:ml-3" }} + {{ else if eq .Level 4 }} + {{ $indentation = "ml-4 lg:ml-6" }} + {{ end }} + {{ $.p.Store.Set "hasToc" true }} +
  • + + {{ .Title | safeHTML }} + +
  • + {{ end }} + {{ with .Headings }} +
      + {{ template "render-toc-level" (dict "h" . "p" $.p) }} +
    + {{ end }} + {{ end }} +{{ end }} diff --git a/docs/layouts/_partials/opengraph/get-featured-image.html b/docs/layouts/_partials/opengraph/get-featured-image.html new file mode 100644 index 000000000..50ee2a44d --- /dev/null +++ b/docs/layouts/_partials/opengraph/get-featured-image.html @@ -0,0 +1,26 @@ +{{ $images := $.Resources.ByType "image" }} +{{ $featured := $images.GetMatch "*feature*" }} +{{ if not $featured }} + {{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }} +{{ end }} +{{ if not $featured }} + {{ $featured = resources.Get "/opengraph/gohugoio-card-base-1.png" }} + {{ $size := 80 }} + {{ $title := $.LinkTitle }} + {{ if gt (len $title) 20 }} + {{ $size = 70 }} + {{ end }} + + {{ $text := $title }} + {{ $textOptions := dict + "color" "#FFF" + "size" $size + "lineSpacing" 10 + "x" 65 "y" 80 + "font" (resources.Get "/opengraph/mulish-black.ttf") + }} + + {{ $featured = $featured | images.Filter (images.Text $text $textOptions) }} +{{ end }} + +{{ return $featured }} diff --git a/docs/layouts/_partials/opengraph/opengraph.html b/docs/layouts/_partials/opengraph/opengraph.html new file mode 100644 index 000000000..e32e07298 --- /dev/null +++ b/docs/layouts/_partials/opengraph/opengraph.html @@ -0,0 +1,84 @@ + + + + + +{{- with $.Params.images -}} + {{- range first 6 . }} + + {{ end -}} +{{- else -}} + {{- $featured := partial "opengraph/get-featured-image.html" . }} + {{- with $featured -}} + + {{- else -}} + {{- with $.Site.Params.images }} + + {{ end -}} + {{- end -}} +{{- end -}} + +{{- if .IsPage }} + {{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} + + {{ with .PublishDate }} + + {{ end }} + {{ with .Lastmod }} + + {{ end }} +{{- end -}} + +{{- with .Params.audio }}{{ end }} +{{- with .Params.locale }} + +{{ end }} +{{- with .Site.Params.title }} + +{{ end }} +{{- with .Params.videos }} + {{- range . }} + + {{ end }} + +{{ end }} + +{{- /* If it is part of a series, link to related articles */}} +{{- $permalink := .Permalink }} +{{- $siteSeries := .Site.Taxonomies.series }} +{{ with .Params.series }} + {{- range $name := . }} + {{- $series := index $siteSeries ($name | urlize) }} + {{- range $page := first 6 $series.Pages }} + {{- if ne $page.Permalink $permalink }} + + {{ end }} + {{- end }} + {{ end }} + +{{ end }} + +{{- /* Facebook Page Admin ID for Domain Insights */}} +{{- with site.Params.social.facebook_admin }} + +{{ end }} diff --git a/docs/layouts/_shortcodes/chroma-lexers.html b/docs/layouts/_shortcodes/chroma-lexers.html new file mode 100644 index 000000000..fd7130501 --- /dev/null +++ b/docs/layouts/_shortcodes/chroma-lexers.html @@ -0,0 +1,28 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders an HTML template of Chroma lexers and their aliases. + +@example {{< chroma-lexers >}} +*/ -}} +{{/* prettier-ignore-end */ -}} +
    + + + + + + + {{- range site.Data.docs.chroma.lexers }} + + + + + {{- end }} + +
    LanguageIdentifiers
    {{ .Name }} + {{- range $k, $_ := .Aliases }} + {{- if $k }},{{ end }} + {{ . }} + {{- end }} +
    +
    diff --git a/docs/layouts/_shortcodes/code-toggle.html b/docs/layouts/_shortcodes/code-toggle.html new file mode 100644 index 000000000..a22c378be --- /dev/null +++ b/docs/layouts/_shortcodes/code-toggle.html @@ -0,0 +1,120 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders syntax-highlighted configuration data in JSON, TOML, and YAML formats. + +@param {string} [config] The section of site.Data.docs.config to render. +@param {bool} [copy=false] Whether to display a copy-to-clipboard button. +@param {string} [dataKey] The section of site.Data.docs to render. +@param {string} [file] The file name to display above the rendered code. +@param {bool} [fm=false] Whether to render the code as front matter. +@param {bool} [skipHeader=false] Whether to omit top level key(s) when rendering a section of site.Data.docs.config. + +@example {{< code-toggle file=hugo config=build />}} + +@example {{< code-toggle file=content/example.md fm="true" }} + title='Example' + draft='false + {{< /code-toggle }} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- /* Initialize. */}} +{{- $config := "" }} +{{- $copy := false }} +{{- $dataKey := "" }} +{{- $file := "" }} +{{- $fm := false }} +{{- $skipHeader := false }} + +{{- /* Get parameters. */}} +{{- $config = .Get "config" }} +{{- $dataKey = .Get "dataKey" }} +{{- $file = .Get "file" }} +{{- if in (slice "false" false 0) (.Get "copy") }} + {{- $copy = false }} +{{- else if in (slice "true" true 1) (.Get "copy") }} + {{- $copy = true }} +{{- end }} +{{- if in (slice "false" false 0) (.Get "fm") }} + {{- $fm = false }} +{{- else if in (slice "true" true 1) (.Get "fm") }} + {{- $fm = true }} +{{- end }} +{{- if in (slice "false" false 0) (.Get "skipHeader") }} + {{- $skipHeader = false }} +{{- else if in (slice "true" true 1) (.Get "skipHeader") }} + {{- $skipHeader = true }} +{{- end }} + +{{- /* Define constants. */}} +{{- $delimiters := dict "toml" "+++" "yaml" "---" }} +{{- $langs := slice "yaml" "toml" "json" }} +{{- $placeHolder := "#-hugo-placeholder-#" }} + +{{- /* Render. */}} +{{- $code := "" }} +{{- if $config }} + {{- $file = $file | default "hugo" }} + {{- $sections := (split $config ".") }} + {{- $configSection := index $.Site.Data.docs.config $sections }} + {{- $code = dict $sections $configSection }} + {{- if $skipHeader }} + {{- $code = $configSection }} + {{- end }} +{{- else if $dataKey }} + {{- $file = $file | default $dataKey }} + {{- $sections := (split $dataKey ".") }} + {{- $code = index $.Site.Data.docs $sections }} +{{- else }} + {{- $code = $.Inner }} +{{- end }} + + +
    + {{- if $copy }} + + + + {{- end }} + + {{- if $code }} + {{- range $i, $lang := $langs }} +
    + {{- $hCode := $code | transform.Remarshal . }} + {{- if and $fm (in (slice "toml" "yaml") .) }} + {{- $hCode = printf "%s\n%s\n%s" $placeHolder $hCode $placeHolder }} + {{- end }} + {{- $hCode = $hCode | replaceRE `\n+` "\n" }} + {{- highlight $hCode . "" | replaceRE $placeHolder (index $delimiters .) | safeHTML }} +
    + {{- end }} + {{- end }} +
    diff --git a/docs/layouts/_shortcodes/datatable-filtered.html b/docs/layouts/_shortcodes/datatable-filtered.html new file mode 100644 index 000000000..de8b0cf55 --- /dev/null +++ b/docs/layouts/_shortcodes/datatable-filtered.html @@ -0,0 +1,47 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $filter := split (index .Params 2) " " }} +{{ $filter1 := index $filter 0 }} +{{ $filter2 := index $filter 1 }} +{{ $filter3 := index $filter 2 }} + +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 3 .Params }} +{{ $list := where $list $filter1 $filter2 $filter3 }} + + +
    + + + + {{ range $fields }} + + {{ end }} + + + + {{ range $list }} + + {{ range $k, $v := . }} + {{ $.Scratch.Set $k $v }} + {{ end }} + {{ range $k, $v := $fields }} + + {{ end }} + + {{ end }} + +
    {{ . }}
    + {{ $tdContent := $.Scratch.Get . }} + {{ if eq $k 3 }} + {{ printf "%v" $tdContent | + strings.ReplaceRE `\[` "
    1. " | + strings.ReplaceRE `\s` "
    2. " | + strings.ReplaceRE `\]` "
    " | + safeHTML + }} + {{ else }} + {{ $tdContent }} + {{ end }} +
    +
    diff --git a/docs/layouts/_shortcodes/datatable.html b/docs/layouts/_shortcodes/datatable.html new file mode 100644 index 000000000..f135d841c --- /dev/null +++ b/docs/layouts/_shortcodes/datatable.html @@ -0,0 +1,39 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 2 .Params }} + + +
    + + + + {{ range $fields }} + {{ $s := . }} + {{ if eq $s "_key" }} + {{ $s = "type" }} + {{ end }} + + {{ end }} + + + + {{ range $k1, $v1 := $list }} + + {{ range $k2, $v2 := . }} + {{ $.Scratch.Set $k2 $v2 }} + {{ end }} + {{ range $fields }} + {{ $s := "" }} + {{ if eq . "_key" }} + {{ $s = $k1 }} + {{ else }} + {{ $s = $.Scratch.Get . }} + {{ end }} + + {{ end }} + + {{ end }} + +
    {{ $s }}
    {{ $s }}
    +
    diff --git a/docs/layouts/_shortcodes/deprecated-in.html b/docs/layouts/_shortcodes/deprecated-in.html new file mode 100644 index 000000000..ce2ba389e --- /dev/null +++ b/docs/layouts/_shortcodes/deprecated-in.html @@ -0,0 +1,29 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders a callout indicating the version in which a feature was deprecated. + +Include descriptive text between the opening and closing tags, or omit the +descriptive text and call the shortcode with a self-closing tag. + +@param {string} 0 The semantic version string, with or without a leading v. + +@example {{< deprecated-in 0.144.0 />}} + +@example {{< deprecated-in 0.144.0 >}} + Some descriptive text here. + {{< /deprecated-in >}} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- with $version := .Get 0 | strings.TrimLeft "vV" }} + {{- $href := printf "https://github.com/gohugoio/hugo/releases/tag/v%s" $version }} + {{- $inner := strings.TrimSpace $.Inner }} + {{- $text := printf "Deprecated in [v%s](%s)\n\n%s" $version $href $inner | $.Page.RenderString (dict "display" "block") }} + {{- partial "layouts/blocks/alert.html" (dict + "color" "orange" + "icon" "exclamation" + "text" $text + ) + }} +{{- else }} + {{- errorf "The %q shortcode requires a single positional parameter indicating version. See %s" .Name .Position }} +{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/eturl.html b/docs/layouts/_shortcodes/eturl.html similarity index 52% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/eturl.html rename to docs/layouts/_shortcodes/eturl.html index c0cf30aec..a0237dbe0 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/eturl.html +++ b/docs/layouts/_shortcodes/eturl.html @@ -1,3 +1,4 @@ +{{/* prettier-ignore-start */ -}} {{- /* Renders an absolute URL to the source code for an embedded template. @@ -6,31 +7,20 @@ embedded_templates.toml file in the data directory. @param {string} filename The embedded template's file name, excluding extension. -@returns template.HTML - @example {{% et robots.txt %}} @example {{% et filename=robots.txt %}} -*/}} - -{{- /* Get parameters. */}} -{{- $filename := "" -}} -{{- if .IsNamedParams -}} - {{- $filename = .Get "filename" -}} -{{- else -}} - {{- $filename = .Get 0 -}} -{{- end -}} - -{{- /* Render. */}} -{{- with $filename -}} - {{- with site.Data.embedded_template_urls -}} - {{- with index . $filename -}} - {{- urls.JoinPath site.Data.embedded_template_urls.base_url . -}} - {{- else -}} - {{- errorf "The %q shortcode was unable to find a URL for the embedded template named %q. Check the name. See %s" $.Name $filename $.Position -}} - {{- end -}} - {{- else -}} - {{- errorf "The %q shortcode was unable to find the embedded_template_urls data file in the site's data directory. See %s" $.Name $.Position -}} - {{- end -}} -{{- else -}} - {{- errorf "The %q shortcodes requires a named or positional parameter, the file name of the embedded template, excluding its extension. See %s" .Name .Position -}} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- with $filename := or (.Get "filename") (.Get 0) }} + {{- with site.Data.embedded_template_urls }} + {{- with index . $filename }} + {{- urls.JoinPath site.Data.embedded_template_urls.base_url . }} + {{- else }} + {{- errorf "The %q shortcode was unable to find a URL for the embedded template named %q. Check the name. See %s" $.Name $filename $.Position }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the embedded_template_urls data file in the site's data directory. See %s" $.Name $.Position }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcodes requires a named or positional parameter, the file name of the embedded template, excluding its extension. See %s" .Name .Position }} {{- end -}} diff --git a/docs/layouts/_shortcodes/glossary-term.html b/docs/layouts/_shortcodes/glossary-term.html new file mode 100644 index 000000000..2a45dc8cb --- /dev/null +++ b/docs/layouts/_shortcodes/glossary-term.html @@ -0,0 +1,18 @@ +{{- /* +Renders the definition of the given glossary term. + +@param {string} (.Get 0) The glossary term. + +@example {{% glossary-term float %}} +@example {{% glossary-term "floating point" %}} +*/ -}} +{{- with .Get 0 }} + {{- $path := printf "/quick-reference/glossary/%s" (urlize .) }} + {{- with site.GetPage $path }} +{{ .RenderShortcodes }}{{/* Do not indent. */}} + {{- else }} + {{- errorf "The glossary term (%s) shortcode was unable to find %s: see %s" $.Name $path $.Position }} + {{- end }} +{{- else }} + {{- errorf "The glossary term (%s) shortcode requires one positional parameter: see %s" $.Name $.Position }} +{{- end -}} diff --git a/docs/layouts/_shortcodes/glossary.html b/docs/layouts/_shortcodes/glossary.html new file mode 100644 index 000000000..7331d5c9f --- /dev/null +++ b/docs/layouts/_shortcodes/glossary.html @@ -0,0 +1,54 @@ +{{- /* +Renders the glossary of terms. + +When you call this shortcode using the {{% %}} notation, the glossary terms are +Markdown headings (level 6) which means they are members of .Fragments. This +allows the link render hook to verify links to glossary terms. + +Yes, the terms themselves are pages, but we don't want to link to the pages, at +least not right now. Instead, we want to link to the ids rendered by this +shortcode. + +@example {{% glossary %}} +*/ -}} +{{- $path := "/quick-reference/glossary" }} +{{- with site.GetPage $path }} + + {{- /* Build and render alphabetical index. */}} + {{- $m := dict }} + {{- range $p := .Pages.ByTitle }} + {{- $k := substr .Title 0 1 | strings.ToUpper }} + {{- if index $m $k }} + {{- continue }} + {{- end }} + {{- $anchor := path.BaseName .Path | anchorize }} + {{- $m = merge $m (dict $k $anchor) }} + {{- end }} + {{- range $k, $v := $m }} +[{{ $k }}](#{{ $v }}) {{/* Do not indent. */}} + {{- end }} + + {{/* Render glossary terms. */}} + {{- range $p := .Pages.ByTitle }} +{{ .Title }}{{/* Do not indent. */}} +: {{ .RawContent | strings.TrimSpace | safeHTML }}{{/* Do not indent. */}} + {{ with .Params.reference }} + {{- $destination := "" }} + {{- with $u := urls.Parse . }} + {{- if $u.IsAbs }} + {{- $destination = $u.String }} + {{- else }} + {{- with site.GetPage $u.Path }} + {{- $destination = .RelPermalink }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the reference link %s: see %s" $.Name . $p.String }} + {{- end }} + {{- end }} + {{- end }} +: See [details]({{ $destination }}).{{/* Do not indent. */}} + {{- end }} + {{ end }} + +{{- else }} + {{- errorf "The %q shortcode was unable to get %s: see %s" .Name $path .Position}} +{{- end }} diff --git a/docs/layouts/_shortcodes/hl.html b/docs/layouts/_shortcodes/hl.html new file mode 100644 index 000000000..3fafcb5e8 --- /dev/null +++ b/docs/layouts/_shortcodes/hl.html @@ -0,0 +1,14 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Returns syntax-highlighted code from the given text. + +This is useful as a terse way to highlight inline code snippets. Calling the +highlight shortcode for inline snippets is verbose. + +@example This is {{< hl python >}}inline{{< /hl >}} code. +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- $code := .Inner | strings.TrimSpace }} +{{- $lang := or (.Get 0) "go" }} +{{- $opts := dict "hl_inline" true "noClasses" true }} +{{- transform.Highlight $code $lang $opts }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html b/docs/layouts/_shortcodes/img.html similarity index 79% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html rename to docs/layouts/_shortcodes/img.html index dd8c60e18..e49afc57f 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/img.html +++ b/docs/layouts/_shortcodes/img.html @@ -1,90 +1,85 @@ +{{/* prettier-ignore-start */ -}} {{- /* Renders the given image using the given filter, if any. +When using the text filter, provide the arguments in this order: + +0. The text +1. The horizontal offset, in pixels, relative to the left of the image (default 20) +2. The vertical offset, in pixels, relative to the top of the image (default 20) +3. The font size in pixels (default 64) +4. The line height (default 1.2) +5. The font color (default #ffffff) + +When using the padding filter, provide all arguments in this order: + +0. Padding top +1. Padding right +2. Padding bottom +3. Padding right +4. Canvas color + @param {string} src The path to the image which must be a remote, page, or global resource. @param {string} [filter] The filter to apply to the image (case-insensitive). @param {string} [filterArgs] A comma-delimited list of arguments to pass to the filter. @param {bool} [example=false] If true, renders a before/after example. @param {int} [exampleWidth=384] Image width, in pixels, when rendering a before/after example. -@returns {template.HTML} +@example {{< img src="zion-national-park.jpg" >}} +@example {{< img src="zion-national-park.jpg" alt="Zion National Park" >}} -@examples +@example {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + >}} - {{< img src="zion-national-park.jpg" >}} +@example {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="process" + filterArgs="resize 400x webp" + >}} - {{< img src="zion-national-park.jpg" alt="Zion National Park" >}} +@example {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="colorize" + filterArgs="180,50,20" + >}} - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - >}} +@example {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + example=true + >}} - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="process" - filterArgs="resize 400x webp" - >}} +@example {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + example=true + exampleWidth=400 + >}} - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="colorize" - filterArgs="180,50,20" - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - example=true - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - example=true - exampleWidth=400 - >}} - - When using the text filter, provide the arguments in this order: - - 0. The text - 1. The horizontal offset, in pixels, relative to the left of the image (default 20) - 2. The vertical offset, in pixels, relative to the top of the image (default 20) - 3. The font size in pixels (default 64) - 4. The line height (default 1.2) - 5. The font color (default #ffffff) - - {{< img - src="images/examples/zion-national-park.jpg" - alt="Zion National Park" - filter="Text" - filterArgs="Zion National Park,25,250,56" - example=true - >}} - - When using the padding filter, provide all arguments in this order: - - 0. Padding top - 1. Padding right - 2. Padding bottom - 3. Padding right - 4. Canvas color - - {{< img - src="images/examples/zion-national-park.jpg" - alt="Zion National Park" - filter="Padding" - filterArgs="20,50,20,50,#0705" - example=true - >}} - -*/}} +@example {{< img + src="images/examples/zion-national-park.jpg" + alt="Zion National Park" + filter="Text" + filterArgs="Zion National Park,25,250,56" + example=true + >}} +@example {{< img + src="images/examples/zion-national-park.jpg" + alt="Zion National Park" + filter="Padding" + filterArgs="20,50,20,50,#0705" + example=true + >}} +*/ -}} +{{/* prettier-ignore-end */ -}} {{- /* Initialize. */}} {{- $alt := "" }} {{- $src := "" }} @@ -100,12 +95,12 @@ Renders the given image using the given filter, if any. "fontSize" 64 "lineHeight" 1.2 "fontColor" "#ffffff" - "fontPath" "https://github.com/google/fonts/raw/main/ofl/lato/Lato-Regular.ttf" + "fontPath" "https://github.com/google/fonts/raw/refs/heads/main/ofl/lato/Lato-Regular.ttf" }} {{- /* Get and validate parameters. */}} {{- with .Get "alt" }} - {{- $alt = .}} + {{- $alt = . }} {{- end }} {{- with .Get "src" }} @@ -120,8 +115,8 @@ Renders the given image using the given filter, if any. {{- $validFilters := slice "autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither" - "gamma" "gaussianblur" "grayscale" "hue" "invert" "none" "opacity" "overlay" - "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" + "gamma" "gaussianblur" "grayscale" "hue" "invert" "mask" "none" "opacity" + "overlay" "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" "unsharpmask" }} @@ -138,7 +133,7 @@ Renders the given image using the given filter, if any. {{- if in (slice "false" false 0) (.Get "example") }} {{- $example = false }} -{{- else if in (slice "true" true 1) (.Get "example")}} +{{- else if in (slice "true" true 1) (.Get "example") }} {{- $example = true }} {{- end }} @@ -229,6 +224,12 @@ Renders the given image using the given filter, if any. {{- $ctx = merge $ctx (dict "argsRequired" 0) }} {{- template "validate-arg-count" $ctx }} {{- $f = images.Invert }} +{{- else if eq $filter "mask" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }} + {{- $maskImage := partial "inline/get-resource.html" $ctx }} + {{- $f = images.Mask $maskImage }} {{- else if eq $filter "opacity" }} {{- $ctx = merge $ctx (dict "argsRequired" 1) }} {{- template "validate-arg-count" $ctx }} @@ -322,13 +323,22 @@ Renders the given image using the given filter, if any. {{- end }} {{- /* Render. */}} +{{- $class := "border-1 border-gray-300 dark:border-gray-500" }} {{- if $example }}

    Original

    - {{ $alt }} + {{ $alt }}

    Processed

    - {{ $alt }} + {{ $alt }} {{- else -}} - {{ $alt }} + {{ $alt }} {{- end }} {{- define "validate-arg-count" }} @@ -349,20 +359,20 @@ Renders the given image using the given filter, if any. {{- end }} {{- end }} -{{- define "partials/inline/get-resource.html" }} +{{- define "_partials/inline/get-resource.html" }} {{- $r := "" }} {{- $u := urls.Parse .src }} {{- $msg := "The %q shortcode was unable to resolve %s. See %s" }} {{- if $u.IsAbs }} - {{- with resources.GetRemote $u.String }} + {{- with try (resources.GetRemote $u.String) }} {{- with .Err }} {{- errorf "%s" . }} - {{- else }} + {{- else with .Value }} {{- /* This is a remote resource. */}} {{- $r = . }} + {{- else }} + {{- errorf $msg $.name $u.String $.position }} {{- end }} - {{- else }} - {{- errorf $msg $.name $u.String $.position }} {{- end }} {{- else }} {{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }} @@ -377,5 +387,5 @@ Renders the given image using the given filter, if any. {{- end }} {{- end }} {{- end }} - {{- return $r}} + {{- return $r }} {{- end -}} diff --git a/docs/layouts/_shortcodes/imgproc.html b/docs/layouts/_shortcodes/imgproc.html new file mode 100644 index 000000000..fee48525a --- /dev/null +++ b/docs/layouts/_shortcodes/imgproc.html @@ -0,0 +1,39 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders the given image using the given process specification. + +@param {string} path The path to the image, either a page resource or a global resource. +@param {string} spec The image processing specification. +@param {string} alt The alt attribute of the img element. + +@example {{< imgproc path="sunset.jpg" spec="resize 300x" alt="A sunset" >}} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- with $.Get "path" }} + {{- with $i := or ($.Page.Resources.Get .) (resources.Get .) }} + {{- with $spec := $.Get "spec" }} + {{- with $i.Process . }} +
    + {{ $.Get `alt` }} +
    + {{- with $.Inner }} + {{ . }} + {{- else }} + {{ $spec }} + {{- end }} +
    +
    + {{- end }} + {{- else }} + {{- errorf "The %q shortcode requires a 'spec' argument containing the image processing specification. See %s" $.Name $.Position }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a 'path' argument indicating the image path. The image must be a page resource or a global resource. See %s" $.Name $.Position }} +{{- end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html b/docs/layouts/_shortcodes/include.html similarity index 82% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html rename to docs/layouts/_shortcodes/include.html index 9ad6e4ecb..81b0c1d8f 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/shortcodes/include.html +++ b/docs/layouts/_shortcodes/include.html @@ -1,16 +1,16 @@ +{{/* prettier-ignore-start */ -}} {{- /* Renders the page using the RenderShortcode method on the Page object. You must call this shortcode using the {{% %}} notation. @param {string} (positional parameter 0) The path to the page, relative to the content directory. -@returns template.HTML @example {{% include "functions/_common/glob-patterns" %}} -*/}} - +*/ -}} +{{/* prettier-ignore-end */ -}} {{- with .Get 0 }} - {{- with site.GetPage . }} + {{- with or ($.Page.GetPage .) (site.GetPage .) }} {{- .RenderShortcodes }} {{- else }} {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} diff --git a/docs/layouts/_shortcodes/list-pages-in-section.html b/docs/layouts/_shortcodes/list-pages-in-section.html new file mode 100644 index 000000000..f6dfe7275 --- /dev/null +++ b/docs/layouts/_shortcodes/list-pages-in-section.html @@ -0,0 +1,69 @@ +{{- /* +Renders a description list of the pages in the given section. + +Render a subset of the pages in the section by specifying a predefined filter, +and whether to include those pages. + +Filters are defined in the data directory, in the file named page_filters. Each +filter is an array of paths to a file, relative to the root of the content +directory. Hugo will throw an error if the specified filter does not exist, or +if any of the pages in the filter do not exist. + +@param {string} path The path to the section. +@param {string} [filter=""] The name of filter list. +@param {string} [filterType=""] The type of filter, either include or exclude. +@param {string} [titlePrefix=""] The string to prepend to the link title. + +@example {{< list-pages-in-section path=/methods/resources >}} +@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude >}} +@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude titlePrefix=foo >}} +*/}} + +{{/* Initialize. */}} +{{ $filter := or "" (.Get "filter" | lower) }} +{{ $filterType := or (.Get "filterType") "none" | lower }} +{{ $filteredPages := slice }} +{{ $titlePrefix := or (.Get "titlePrefix") "" }} + +{{/* Build slice of filtered pages. */}} +{{ with $filter }} + {{ with index site.Data.page_filters . }} + {{ range . }} + {{ with site.GetPage . }} + {{ $filteredPages = $filteredPages | append . }} + {{ else }} + {{ errorf "The %q shortcode was unable to find %q as specified in the page_filters data file. See %s" $.Name . $.Position }} + {{ end }} + {{ end }} + {{ else }} + {{ errorf "The %q shortcode was unable to find the %q filter in the page_filters data file. See %s" $.Name . $.Position }} + {{ end }} +{{ end }} + +{{/* Render. */}} +{{ with $sectionPath := .Get "path" }} + {{ with site.GetPage . }} + {{ with .RegularPages }} + {{ range $page := .ByTitle }} + {{ if or + (and (eq $filterType "include") (in $filteredPages $page)) + (and (eq $filterType "exclude") (not (in $filteredPages $page))) + (eq $filterType "none") + }} + {{ $linkTitle := .LinkTitle }} + {{ with $titlePrefix }} + {{ $linkTitle = printf "%s%s" . $linkTitle }} + {{ end }} +[{{ $linkTitle }}]({{ $page.RelPermalink }}){{/* Do not indent. */}} +: {{ $page.Description }}{{/* Do not indent. */}} + {{ end }} + {{ end }} + {{ else }} + {{ warnf "The %q shortcode found no pages in the %q section. See %s" $.Name $sectionPath $.Position }} + {{ end }} + {{ else }} + {{ errorf "The %q shortcode was unable to find %q. See %s" $.Name $sectionPath $.Position }} + {{ end }} +{{ else }} + {{ errorf "The %q shortcode requires a 'path' parameter indicating the path to the section. See %s" $.Name $.Position }} +{{ end }} diff --git a/docs/layouts/_shortcodes/module-mounts-note.html b/docs/layouts/_shortcodes/module-mounts-note.html new file mode 100644 index 000000000..ba89abcbf --- /dev/null +++ b/docs/layouts/_shortcodes/module-mounts-note.html @@ -0,0 +1,2 @@ +For a more flexible approach to configuring this directory, consult the section +on [module mounts](/configuration/module/#mounts). diff --git a/docs/layouts/_shortcodes/new-in.html b/docs/layouts/_shortcodes/new-in.html new file mode 100644 index 000000000..955d0a710 --- /dev/null +++ b/docs/layouts/_shortcodes/new-in.html @@ -0,0 +1,64 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders a callout or badge indicating the version in which a feature was added. + +To render a callout, include descriptive text between the opening and closing +tags. To render a badge,omit the descriptive text and call the shortcode with a +self-closing tag. + +When comparing the current version to the specified version, the "new in" +button will be hidden if any of the following conditions is true: + +- The major version difference exceeds the majorVersionDiffThreshold +- The minor version difference exceeds the minorVersionDiffThreshold + +@param {string} 0 The semantic version string, with or without a leading v. + +@example {{< new-in 0.100.0 />}} + +@example {{{< new-in 0.100.0 >}} + Some descriptive text here. + {{< /new-in >}} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- $majorVersionDiffThreshold := 0 }} +{{- $minorVersionDiffThreshold := 30 }} +{{- $displayExpirationWarning := true }} + +{{- with $version := .Get 0 | strings.TrimLeft "vV" }} + {{- $majorVersionDiff := sub (index (split hugo.Version ".") 0 | int) (index (split $version ".") 0 | int) }} + {{- $minorVersionDiff := sub (index (split hugo.Version ".") 1 | int) (index (split $version ".") 1 | int) }} + {{- if or (gt $majorVersionDiff $majorVersionDiffThreshold) (gt $minorVersionDiff $minorVersionDiffThreshold) }} + {{- if $displayExpirationWarning }} + {{- warnf "This call to the %q shortcode should be removed: %s. The button is now hidden because the specified version (%s) is older than the display threshold." $.Name $.Position $version }} + {{- end }} + {{- else }} + {{- $href := printf "https://github.com/gohugoio/hugo/releases/tag/v%s" $version }} + {{- with $.Inner }} + {{- $inner := strings.TrimSpace . }} + {{- $text := printf "New in [v%s](%s)\n\n%s" $version $href $inner | $.Page.RenderString (dict "display" "block") }} + {{ partial "layouts/blocks/alert.html" (dict + "color" "green" + "icon" "exclamation" + "text" $text + ) + }} + {{- else }} + + + + + + New in + v{{ $version }} + + + {{- end }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a single positional parameter indicating version. See %s" .Name .Position }} +{{- end }} diff --git a/docs/layouts/_shortcodes/per-lang-config-keys.html b/docs/layouts/_shortcodes/per-lang-config-keys.html new file mode 100644 index 000000000..31d7daf6a --- /dev/null +++ b/docs/layouts/_shortcodes/per-lang-config-keys.html @@ -0,0 +1,71 @@ +{{/* prettier-ignore-start */ -}} +{{- /* +Renders a responsive grid of the configuration keys that can be defined +separately for each language. +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- $siteConfigKeys := slice + (dict "baseURL" "/configuration/all/#baseurl") + (dict "buildDrafts" "/configuration/all/#builddrafts") + (dict "buildExpired" "/configuration/all/#buildexpired") + (dict "buildFuture" "/configuration/all/#buildfuture") + (dict "canonifyURLs" "/configuration/all/#canonifyurls") + (dict "capitalizeListTitles" "/configuration/all/#capitalizelisttitles") + (dict "contentDir" "/configuration/all/#contentdir") + (dict "copyright" "/configuration/all/#copyright") + (dict "disableAliases" "/configuration/all/#disablealiases") + (dict "disableHugoGeneratorInject" "/configuration/all/#disablehugogeneratorinject") + (dict "disableKinds" "/configuration/all/#disablekinds") + (dict "disableLiveReload" "/configuration/all/#disablelivereload") + (dict "disablePathToLower" "/configuration/all/#disablepathtolower") + (dict "enableEmoji " "/configuration/all/#enableemoji") + (dict "frontmatter" "/configuration/front-matter/") + (dict "hasCJKLanguage" "/configuration/all/#hascjklanguage") + (dict "languageCode" "/configuration/all/#languagecode") + (dict "mainSections" "/configuration/all/#mainsections") + (dict "markup" "/configuration/markup/") + (dict "mediaTypes" "/configuration/media-types/") + (dict "menus" "/configuration/menus/") + (dict "outputFormats" "/configuration/output-formats") + (dict "outputs" "/configuration/outputs/") + (dict "page" "/configuration/page/") + (dict "pagination" "/configuration/pagination/") + (dict "params" "/configuration/params/") + (dict "permalinks" "/configuration/permalinks/") + (dict "pluralizeListTitles" "/configuration/all/#pluralizelisttitles") + (dict "privacy" "/configuration/privacy/") + (dict "refLinksErrorLevel" "/configuration/all/#reflinkserrorlevel") + (dict "refLinksNotFoundURL" "/configuration/all/#reflinksnotfoundurl") + (dict "related" "/configuration/related-content/") + (dict "relativeURLs" "/configuration/all/#relativeurls") + (dict "removePathAccents" "/configuration/all/#removepathaccents") + (dict "renderSegments" "/configuration/all/#rendersegments") + (dict "sectionPagesMenu" "/configuration/all/#sectionpagesmenu") + (dict "security" "/configuration/security/") + (dict "services" "/configuration/services/") + (dict "sitemap" "/configuration/sitemap/") + (dict "staticDir" "/configuration/all/#staticdir") + (dict "summaryLength" "/configuration/all/#summarylength") + (dict "taxonomies" "/configuration/taxonomies/") + (dict "timeZone" "/configuration/all/#timezone") + (dict "title" "/configuration/all/#title") + (dict "titleCaseStyle" "/configuration/all/#titlecasestyle") +}} + +{{- $a := len $siteConfigKeys }} +{{- $b := math.Ceil (div $a 2.) }} +{{- $c := math.Ceil (div $a 3.) }} + + +
    + {{- range $siteConfigKeys }} + {{ range $k, $v := . }} + {{ $u := urls.Parse $v }} + {{ if not (site.GetPage $u.Path) }} + {{ errorf "The %q shorcode was unable to find %s. See %s." $.Name $u.Path $.Position }} + {{ end }} + {{ $k }} + {{ end }} + {{- end }} +
    diff --git a/docs/layouts/_shortcodes/quick-reference.html b/docs/layouts/_shortcodes/quick-reference.html new file mode 100644 index 000000000..0ac544036 --- /dev/null +++ b/docs/layouts/_shortcodes/quick-reference.html @@ -0,0 +1,30 @@ +{{- /* +Renders the child sections of the given top-level section, listing each child's +immediate descendants. + +@param {string} section The top-level section to render. + +@example {{% quick-reference section="/functions" %}} +*/ -}} +{{ $section := "" }} +{{ with .Get "section" }} + {{ $section = . }} +{{ else }} + {{ errorf "The %q shortcode requires a 'section' parameter. See %s" .Name .Position }} +{{ end }} + +{{ with site.GetPage $section }} + {{ range .Sections }} +## {{ .LinkTitle }}{{/* Do not indent. */}} +{{ .Description }}{{/* Do not indent. */}} + {{ .Content }} + {{ with .Pages }} + {{ range . }} +[{{ .LinkTitle }}]({{ .RelPermalink }}){{/* Do not indent. */}} +: {{ .Description }}{{/* Do not indent. */}} + {{ end }} + {{ end }} + {{ end }} +{{ else }} + {{ errorf "The %q shortcodes was unable to find the %q section. See %s" .Name $section .Position }} +{{ end }} diff --git a/docs/layouts/_shortcodes/root-configuration-keys.html b/docs/layouts/_shortcodes/root-configuration-keys.html new file mode 100644 index 000000000..46a6e074f --- /dev/null +++ b/docs/layouts/_shortcodes/root-configuration-keys.html @@ -0,0 +1,45 @@ +{{/* prettier-ignore-start */ -}} +{{/* +Renders a comma-separated list of links to the root key configuration pages. + +@example {{< root-configuration-keys >}} +*/ -}} +{{/* prettier-ignore-end */ -}} +{{- /* Create scratch map of key:filename. */}} +{{- $s := newScratch }} +{{- range $k, $v := site.Data.docs.config }} + {{- if or (reflect.IsMap .) (reflect.IsSlice .) }} + {{- $s.Set $k ($k | humanize | anchorize) }} + {{- end }} +{{- end }} + +{{/* Deprecated. */}} +{{- $s.Delete "author" }} + +{{/* Use mounts instead. */}} +{{- $s.Delete "staticDir" }} +{{- $s.Delete "ignoreFiles" }} + +{{/* This key is "HTTPCache" not "httpCache". */}} +{{- $s.Set "HTTPCache" "http-cache" }} + +{{/* This key is "frontmatter" not "frontMatter" */}} +{{- $s.Set "frontmatter" "front-matter" }} + +{{/* The page title is "Related content" not "related". */}} +{{- $s.Set "related" "related-content" }} + +{{/* It can be configured as bool or map; we want to show map. */}} +{{- $s.Set "uglyURLs" "ugly-urls" }} + +{{- $links := slice }} +{{- range $k, $v := $s.Values }} + {{- $path := printf "/configuration/%s" $v }} + {{- with site.GetPage $path }} + {{- $links = $links | append (printf "%s" .RelPermalink $k) }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the page %s. See %s." $.Name $path $.Position }} + {{- end }} +{{- end }} + +{{- delimit $links ", " ", and " | safeHTML -}} diff --git a/docs/layouts/_shortcodes/syntax-highlighting-styles.html b/docs/layouts/_shortcodes/syntax-highlighting-styles.html new file mode 100644 index 000000000..297849cef --- /dev/null +++ b/docs/layouts/_shortcodes/syntax-highlighting-styles.html @@ -0,0 +1,70 @@ +{{- /* +Renders a gallery a Chroma syntax highlighting styles. + +@example {{% syntax-highlighting-styles %}} +*/ -}} +{{- $examples := slice }} + +{{- /* Example: css */}} +{{- $example := dict "lang" "css" "code" ` +body { + font-size: 16px; /* comment */ +} +`}} +{{- $examples = $examples | append $example }} + +{{- /* Example: html */}} +{{- $example = dict "lang" "html" "code" ` +Example +`}} +{{- $examples = $examples | append $example }} + +{{- /* Example: go-html-template */}} +{{- $example = dict "lang" "go-html-template" "code" ` +{{ with $.Page.Params.content }} + {{ . | $.Page.RenderString }} {{/* comment */}} +{{ end }} +`}} +{{- $examples = $examples | append $example }} + +{{- /* Example: javascript */}} +{{- $example = dict "lang" "javascript" "code" ` +if ([1,"one",2,"two"].includes(value)){ + console.log("Number is either 1 or 2."); // comment +} +`}} +{{- $examples := $examples | append $example }} + +{{- /* Example: markdown */}} +{{- $example = dict "lang" "markdown" "code" ` +{{< figure src="kitten.jpg" >}} +[example](https://example.org "An example") +`}} +{{- $examples := $examples | append $example }} + +{{- /* Example: toml */}} +{{- $example = dict "lang" "toml" "code" ` +[params] +bool = true # comment +string = 'foo' +`}} +{{- $examples := $examples | append $example }} + +{{- /* Render */}} +{{- with site.Data.docs.chroma.styles }} + {{- range $style := . }} + +### {{ $style }} {class="!mt-7 !mb-6"}{{/* Do not indent. */}} + + {{- range $examples }} + +{{ .lang }}{{/* Do not indent. */}} +{class="text-sm !-mt-3 !-mb-5"}{{/* Do not indent. */}} + +```{{ .lang }} {noClasses=true style="{{ $style }}"}{{/* Do not indent. */}} +{{- .code | safeHTML -}}{{/* Do not indent. */}} +```{{/* Do not indent. */}} + + {{- end }} + {{- end }} +{{- end }} diff --git a/docs/layouts/baseof.html b/docs/layouts/baseof.html new file mode 100644 index 000000000..4c14a6b6d --- /dev/null +++ b/docs/layouts/baseof.html @@ -0,0 +1,74 @@ + + + + + + {{ .Title }} + + + + {{ partial "layouts/head/head-js.html" . }} + {{ with (templates.Defer (dict "key" "global")) }} + {{ $t := debug.Timer "tailwindcss" }} + {{ with resources.Get "css/styles.css" }} + {{ $opts := dict + "inlineImports" true + "minify" (not hugo.IsDevelopment) + }} + {{ with . | css.TailwindCSS $opts }} + {{ partial "helpers/linkcss.html" (dict "r" .) }} + {{ end }} + {{ end }} + {{ $t.Stop }} + {{ end }} + {{ $noop := .WordCount }} + {{ if .Page.Store.Get "hasMath" }} + + {{ end }} + {{ partial "layouts/head/head.html" . }} + + + {{ partial "layouts/hooks/body-start.html" . }} + {{/* Layout. */}} + {{ block "header" . }} + {{ partial "layouts/header/header.html" . }} + {{ end }} + {{ block "subheader" . }} + {{ end }} + {{ block "hero" . }} + {{ end }} +
    +
    + {{ partial "layouts/hooks/body-main-start.html" . }} + {{ block "main" . }}{{ end }} +
    + {{ block "rightsidebar" . }} + + {{ end }} +
    + {{/* Common icons. */}} + {{ partial "layouts/icons.html" . }} + {{/* Common templates. */}} + {{ partial "layouts/templates.html" . }} + {{/* Footer. */}} + {{ block "footer" . }} + {{ partial "layouts/footer.html" . }} + {{ end }} + {{ partial "layouts/hooks/body-end.html" . }} + + diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.headers b/docs/layouts/home.headers similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.headers rename to docs/layouts/home.headers diff --git a/docs/layouts/home.html b/docs/layouts/home.html new file mode 100644 index 000000000..392f66cd8 --- /dev/null +++ b/docs/layouts/home.html @@ -0,0 +1,52 @@ +{{ define "main" }} +
    + {{ partial "layouts/home/opensource.html" . }} +
    + {{ partial "layouts/home/sponsors.html" (dict "ctx" . "gtag" "home" ) }} +
    + {{ partial "layouts/home/features.html" . }} +
    +{{ end }} + +{{ define "hero" }} +
    +
    +
    + Hugo Logo +

    + The world’s fastest framework for building websites +

    +
    + Hugo is one of the most popular open-source static site generators. + With its amazing speed and flexibility, Hugo makes building websites + fun again. +
    +
    + {{ with site.GetPage "/getting-started" }} + {{ .LinkTitle }} + {{ end }} +
    + {{ partial "layouts/search/button.html" (dict "page" . "standalone" true) }} +
    +
    +
    +
    +
    +{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} + +{{ define "leftsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.redir b/docs/layouts/home.redir similarity index 57% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.redir rename to docs/layouts/home.redir index 2dfd2bc0f..bb72f96e5 100644 --- a/docs/_vendor/github.com/gohugoio/gohugoioTheme/layouts/index.redir +++ b/docs/layouts/home.redir @@ -1,6 +1,6 @@ # Netlify redirects. See https://www.netlify.com/docs/redirects/ -{{ range $p := .Site.Pages -}} +{{ range $p := .Site.Pages -}} {{ range .Aliases }} -{{ . | printf "%-35s" }} {{ $p.RelPermalink -}} +{{ . | printf "%-35s" }} {{ $p.RelPermalink -}} {{ end -}} {{- end -}} diff --git a/docs/layouts/list.html b/docs/layouts/list.html new file mode 100644 index 000000000..b049b6da9 --- /dev/null +++ b/docs/layouts/list.html @@ -0,0 +1,69 @@ +{{ define "main" }} + {{ $pages := "" }} + {{ if .IsPage }} + {{/* We currently have a slightly odd content structure with no top level /docs section. */}} + {{ $pages = .CurrentSection.Pages }} + {{ else }} + {{ $pages = .Pages }} + {{ if eq .Section "news" }} + {{ $pages = $pages.ByPublishDate.Reverse }} + {{ end }} + {{ end }} + + +
    + {{ partial "layouts/docsheader.html" . }} + +
    +{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/docs/layouts/list.rss.xml b/docs/layouts/list.rss.xml new file mode 100644 index 000000000..90fa22148 --- /dev/null +++ b/docs/layouts/list.rss.xml @@ -0,0 +1,33 @@ +{{- printf "" | safeHTML }} + + + Hugo News + Recent news about Hugo, a static site generator written in Go, optimized for speed and designed for flexibility. + {{ .Permalink }} + Hugo {{ hugo.Version }} + {{ or site.Language.LanguageCode site.Language.Lang }} + {{- with site.Copyright }} + {{ . }} + {{- end }} + {{- with .OutputFormats.Get "rss" }} + {{ printf "" .Permalink .MediaType | safeHTML }} + {{- end }} + {{- $limit := cond (gt site.Config.Services.RSS.Limit 0) site.Config.Services.RSS.Limit 999 }} + {{- $pages := "" }} + {{- with site.GetPage "/news" }} + {{- $pages = .Pages.ByPublishDate.Reverse | first $limit }} + {{- else }} + {{- errorf "The list.rss.xml layout was unable to find the 'news' page." }} + {{- end }} + {{ (index $pages 0).PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + {{- range $pages }} + + {{ .Title }} + {{ or .Params.permalink .Permalink }} + {{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + {{ or .Params.permalink .Permalink }} + {{ .Summary | transform.XMLEscape | safeHTML }} + + {{- end }} + + diff --git a/docs/layouts/shortcodes/img.html b/docs/layouts/shortcodes/img.html deleted file mode 100644 index 34476b3d8..000000000 --- a/docs/layouts/shortcodes/img.html +++ /dev/null @@ -1,381 +0,0 @@ -{{- /* -Renders the given image using the given filter, if any. - -@param {string} src The path to the image which must be a remote, page, or global resource. -@param {string} [filter] The filter to apply to the image (case-insensitive). -@param {string} [filterArgs] A comma-delimited list of arguments to pass to the filter. -@param {bool} [example=false] If true, renders a before/after example. -@param {int} [exampleWidth=384] Image width, in pixels, when rendering a before/after example. - -@returns {template.HTML} - -@examples - - {{< img src="zion-national-park.jpg" >}} - - {{< img src="zion-national-park.jpg" alt="Zion National Park" >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="process" - filterArgs="resize 400x webp" - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="colorize" - filterArgs="180,50,20" - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - example=true - >}} - - {{< img - src="zion-national-park.jpg" - alt="Zion National Park" - filter="grayscale" - example=true - exampleWidth=400 - >}} - - When using the text filter, provide the arguments in this order: - - 0. The text - 1. The horizontal offset, in pixels, relative to the left of the image (default 20) - 2. The vertical offset, in pixels, relative to the top of the image (default 20) - 3. The font size in pixels (default 64) - 4. The line height (default 1.2) - 5. The font color (default #ffffff) - - {{< img - src="images/examples/zion-national-park.jpg" - alt="Zion National Park" - filter="Text" - filterArgs="Zion National Park,25,250,56" - example=true - >}} - - When using the padding filter, provide all arguments in this order: - - 0. Padding top - 1. Padding right - 2. Padding bottom - 3. Padding right - 4. Canvas color - - {{< img - src="images/examples/zion-national-park.jpg" - alt="Zion National Park" - filter="Padding" - filterArgs="20,50,20,50,#0705" - example=true - >}} - -*/}} - -{{- /* Initialize. */}} -{{- $alt := "" }} -{{- $src := "" }} -{{- $filter := "" }} -{{- $filterArgs := slice }} -{{- $example := false }} -{{- $exampleWidth := 384 }} - -{{- /* Default values to use with the text filter. */}} -{{ $textFilterOpts := dict - "xOffset" 20 - "yOffset" 20 - "fontSize" 64 - "lineHeight" 1.2 - "fontColor" "#ffffff" - "fontPath" "https://github.com/google/fonts/raw/main/ofl/lato/Lato-Regular.ttf" -}} - -{{- /* Get and validate parameters. */}} -{{- with .Get "alt" }} - {{- $alt = .}} -{{- end }} - -{{- with .Get "src" }} - {{- $src = . }} -{{- else }} - {{- errorf "The %q shortcode requires a file parameter. See %s" .Name .Position }} -{{- end }} - -{{- with .Get "filter" }} - {{- $filter = . | lower }} -{{- end }} - -{{- $validFilters := slice - "autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither" - "gamma" "gaussianblur" "grayscale" "hue" "invert" "none" "opacity" "overlay" - "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" - "unsharpmask" -}} - -{{- with $filter }} - {{- if not (in $validFilters .) }} - {{- errorf "The filter passed to the %q shortcode is invalid. The filter must be one of %s. See %s" $.Name (delimit $validFilters ", " ", or ") $.Position }} - {{- end }} -{{- end }} - -{{- with .Get "filterArgs" }} - {{- $filterArgs = split . "," }} - {{- $filterArgs = apply $filterArgs "trim" "." " " }} -{{- end }} - -{{- if in (slice "false" false 0) (.Get "example") }} - {{- $example = false }} -{{- else if in (slice "true" true 1) (.Get "example")}} - {{- $example = true }} -{{- end }} - -{{- with .Get "exampleWidth" }} - {{- $exampleWidth = . | int }} -{{- end }} - -{{- /* Get image. */}} -{{- $ctx := dict "page" .Page "src" $src "name" .Name "position" .Position }} -{{- $i := partial "inline/get-resource.html" $ctx }} - -{{- /* Resize if rendering before/after examples. */}} -{{- if $example }} - {{- $i = $i.Resize (printf "%dx" $exampleWidth) }} -{{- end }} - -{{- /* Create filter. */}} -{{- $f := "" }} -{{- $ctx := dict "filter" $filter "args" $filterArgs "name" .Name "position" .Position }} -{{- if eq $filter "autoorient" }} - {{- $ctx = merge $ctx (dict "argsRequired" 0) }} - {{- template "validate-arg-count" $ctx }} - {{- $f = images.AutoOrient }} -{{- else if eq $filter "brightness" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Brightness (index $filterArgs 0) }} -{{- else if eq $filter "colorbalance" }} - {{- $ctx = merge $ctx (dict "argsRequired" 3) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "percentage red" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "percentage green" "argValue" (index $filterArgs 1) "min" -100 "max" 500) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "percentage blue" "argValue" (index $filterArgs 2) "min" -100 "max" 500) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.ColorBalance (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} -{{- else if eq $filter "colorize" }} - {{- $ctx = merge $ctx (dict "argsRequired" 3) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "hue" "argValue" (index $filterArgs 0) "min" 0 "max" 360) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "saturation" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 2) "min" 0 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Colorize (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} -{{- else if eq $filter "contrast" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Contrast (index $filterArgs 0) }} -{{- else if eq $filter "dither" }} - {{- $f = images.Dither }} -{{- else if eq $filter "gamma" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "gamma" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Gamma (index $filterArgs 0) }} -{{- else if eq $filter "gaussianblur" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.GaussianBlur (index $filterArgs 0) }} -{{- else if eq $filter "grayscale" }} - {{- $ctx = merge $ctx (dict "argsRequired" 0) }} - {{- template "validate-arg-count" $ctx }} - {{- $f = images.Grayscale }} -{{- else if eq $filter "hue" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "shift" "argValue" (index $filterArgs 0) "min" -180 "max" 180) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Hue (index $filterArgs 0) }} -{{- else if eq $filter "invert" }} - {{- $ctx = merge $ctx (dict "argsRequired" 0) }} - {{- template "validate-arg-count" $ctx }} - {{- $f = images.Invert }} -{{- else if eq $filter "opacity" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "opacity" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Opacity (index $filterArgs 0) }} -{{- else if eq $filter "overlay" }} - {{- $ctx = merge $ctx (dict "argsRequired" 3) }} - {{- template "validate-arg-count" $ctx }} - {{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }} - {{- $overlayImg := partial "inline/get-resource.html" $ctx }} - {{- $f = images.Overlay $overlayImg (index $filterArgs 1 | float ) (index $filterArgs 2 | float) }} -{{- else if eq $filter "padding" }} - {{- $ctx = merge $ctx (dict "argsRequired" 5) }} - {{- template "validate-arg-count" $ctx }} - {{- $f = images.Padding - (index $filterArgs 0 | int) - (index $filterArgs 1 | int) - (index $filterArgs 2 | int) - (index $filterArgs 3 | int) - (index $filterArgs 4) - }} -{{- else if eq $filter "pixelate" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "size" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Pixelate (index $filterArgs 0) }} -{{- else if eq $filter "process" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $f = images.Process (index $filterArgs 0) }} -{{- else if eq $filter "saturation" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Saturation (index $filterArgs 0) }} -{{- else if eq $filter "sepia" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Sepia (index $filterArgs 0) }} -{{- else if eq $filter "sigmoid" }} - {{- $ctx = merge $ctx (dict "argsRequired" 2) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "midpoint" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "factor" "argValue" (index $filterArgs 1) "min" -10 "max" 10) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.Sigmoid (index $filterArgs 0) (index $filterArgs 1) }} -{{- else if eq $filter "text" }} - {{- $ctx = merge $ctx (dict "argsRequired" 1) }} - {{- template "validate-arg-count" $ctx }} - {{- $ctx := dict "src" $textFilterOpts.fontPath "name" .Name "position" .Position }} - {{- $font := or (partial "inline/get-resource.html" $ctx) }} - {{- $fontSize := or (index $filterArgs 3 | int) $textFilterOpts.fontSize }} - {{- $lineHeight := math.Max (or (index $filterArgs 4 | float) $textFilterOpts.lineHeight) 1 }} - {{- $opts := dict - "x" (or (index $filterArgs 1 | int) $textFilterOpts.xOffset) - "y" (or (index $filterArgs 2 | int) $textFilterOpts.yOffset) - "size" $fontSize - "linespacing" (mul (sub $lineHeight 1) $fontSize) - "color" (or (index $filterArgs 5) $textFilterOpts.fontColor) - "font" $font - }} - {{- $f = images.Text (index $filterArgs 0) $opts }} -{{- else if eq $filter "unsharpmask" }} - {{- $ctx = merge $ctx (dict "argsRequired" 3) }} - {{- template "validate-arg-count" $ctx }} - {{- $filterArgs = apply $filterArgs "float" "." }} - {{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 500) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "amount" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }} - {{- template "validate-arg-value" $ctx }} - {{- $ctx = merge $ctx (dict "argName" "threshold" "argValue" (index $filterArgs 2) "min" 0 "max" 1) }} - {{- template "validate-arg-value" $ctx }} - {{- $f = images.UnsharpMask (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} -{{- end }} - -{{- /* Apply filter. */}} -{{- $fi := $i }} -{{- with $f }} - {{- $fi = $i.Filter . }} -{{- end }} - -{{- /* Render. */}} -{{- if $example }} -

    Original

    - {{ $alt }} -

    Processed

    - {{ $alt }} -{{- else -}} - {{ $alt }} -{{- end }} - -{{- define "validate-arg-count" }} - {{- $msg := "When using the %q filter, the %q shortcode requires an args parameter with %d %s. See %s" }} - {{- if lt (len .args) .argsRequired }} - {{- $text := "values" }} - {{- if eq 1 .argsRequired }} - {{- $text = "value" }} - {{- end }} - {{- errorf $msg .filter .name .argsRequired $text .position }} - {{- end }} -{{- end }} - -{{- define "validate-arg-value" }} - {{- $msg := "The %q argument passed to the %q shortcode is invalid. Expected a value in the range [%v,%v], but received %v. See %s" }} - {{- if or (lt .argValue .min) (gt .argValue .max) }} - {{- errorf $msg .argName .name .min .max .argValue .position }} - {{- end }} -{{- end }} - -{{- define "partials/inline/get-resource.html" }} - {{- $r := "" }} - {{- $u := urls.Parse .src }} - {{- $msg := "The %q shortcode was unable to resolve %s. See %s" }} - {{- if $u.IsAbs }} - {{- with resources.GetRemote $u.String }} - {{- with .Err }} - {{- errorf "%s" }} - {{- else }} - {{- /* This is a remote resource. */}} - {{- $r = . }} - {{- end }} - {{- else }} - {{- errorf $msg $.name $u.String $.position }} - {{- end }} - {{- else }} - {{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }} - {{- /* This is a page resource. */}} - {{- $r = . }} - {{- else }} - {{- with resources.Get $u.Path }} - {{- /* This is a global resource. */}} - {{- $r = . }} - {{- else }} - {{- errorf $msg $.name $u.Path $.position }} - {{- end }} - {{- end }} - {{- end }} - {{- return $r}} -{{- end -}} diff --git a/docs/layouts/single.html b/docs/layouts/single.html new file mode 100644 index 000000000..2e9e4f379 --- /dev/null +++ b/docs/layouts/single.html @@ -0,0 +1,80 @@ +{{ define "main" }} + {{ $ttop := debug.Timer "single" }} +
    + {{ partial "layouts/docsheader.html" . }} +
    + {{ with .Params.description }} +
    + {{ . | markdownify }} +
    + {{ end }} + {{ if .Params.show_publish_date }} + {{ with .PublishDate }} +

    + {{ partial "layouts/date.html" . }} +

    + {{ end }} + {{ end }} + {{ $t := debug.Timer "single.categories" }} + {{ $categories := .GetTerms "categories" }} + {{ with $categories }} +
    + {{ range . }} + {{ $text := .LinkTitle }} + {{ $class := "" }} + {{ range (slice true false ) }} + {{ $color := partial "helpers/funcs/color-from-string.html" (dict "text" $text "dark" . "--single" "green" ) }} + + {{ $prefix := "" }} + {{ if . }} + {{ $prefix = "dark:" }} + {{ end }} + {{ $class = printf "%sbg-%s-%d %stext-%s-%d border %sborder-%s-%d" + $prefix $color.color $color.shade1 + $prefix $color.color $color.shade2 + $prefix $color.color $color.shade3 + }} + {{ end }} + + + + {{ .LinkTitle }} + + {{ end }} +
    + {{ end }} + {{ $t.Stop }} + + {{ if .Params.functions_and_methods.signatures }} +
    + {{- partial "docs/functions-signatures.html" . -}} + {{- partial "docs/functions-return-type.html" . -}} + {{- partial "docs/functions-aliases.html" . -}} +
    + {{ end }} + {{ $t := debug.Timer "single.content" }} + {{ .Content }} + {{ $t.Stop }} + {{ $t := debug.Timer "single.page-edit" }} + {{ partial "layouts/page-edit.html" . }} + {{ $t.Stop }} +
    +
    + {{ $ttop.Stop }} +{{ end }} + +{{ define "rightsidebar_content" }} + {{/* in-this-section.html depends on these being reneredc first. */}} + {{ $related := partial "layouts/related.html" . }} + {{ $toc := partial "layouts/toc.html" . }} + {{ if not .Params.hide_in_this_section }} + {{ partial "layouts/in-this-section.html" . }} + {{ end }} + {{ $related }} + {{ if $.Store.Get "hasToc" }} + {{ $toc }} + {{ end }} +{{ end }} diff --git a/docs/netlify.toml b/docs/netlify.toml index 2780fe747..c24a32a60 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -3,7 +3,7 @@ command = "hugo --gc --minify" [build.environment] - HUGO_VERSION = "0.134.0" + HUGO_VERSION = "0.146.7" [context.production.environment] HUGO_ENV = "production" @@ -16,7 +16,7 @@ HUGO_ENV = "production" [context.deploy-preview] - command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL" + command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL --enableGitInfo" [context.branch-deploy] command = "hugo --gc --minify -b $DEPLOY_PRIME_URL" @@ -24,7 +24,32 @@ [context.next.environment] HUGO_ENABLEGITINFO = "true" -[[redirects]] - from = "/npmjs/*" - to = "/npmjs/" - status = 200 +[[headers]] + for = "/*.jpg" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.png" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.css" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.js" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.ttf" + + [headers.values] + Cache-Control = "public, max-age=31536000" diff --git a/docs/package.hugo.json b/docs/package.hugo.json new file mode 100644 index 000000000..24ffc7ff5 --- /dev/null +++ b/docs/package.hugo.json @@ -0,0 +1,25 @@ +{ + "name": "hugoDocs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "", + "devDependencies": { + "@awmottaz/prettier-plugin-void-html": "~1.8.0", + "@tailwindcss/cli": "~4.1.0", + "@tailwindcss/typography": "~0.5.16", + "prettier": "~3.5.3", + "prettier-plugin-go-template": "~0.0.15", + "tailwindcss": "~4.1.0" + }, + "dependencies": { + "@alpinejs/focus": "~3.14.9", + "@alpinejs/persist": "~3.14.9", + "@hotwired/turbo": "~8.0.13", + "alpinejs": "~3.14.9" + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..24ffc7ff5 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,25 @@ +{ + "name": "hugoDocs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "", + "devDependencies": { + "@awmottaz/prettier-plugin-void-html": "~1.8.0", + "@tailwindcss/cli": "~4.1.0", + "@tailwindcss/typography": "~0.5.16", + "prettier": "~3.5.3", + "prettier-plugin-go-template": "~0.0.15", + "tailwindcss": "~4.1.0" + }, + "dependencies": { + "@alpinejs/focus": "~3.14.9", + "@alpinejs/persist": "~3.14.9", + "@hotwired/turbo": "~8.0.13", + "alpinejs": "~3.14.9" + } +} diff --git a/docs/pull-theme.sh b/docs/pull-theme.sh deleted file mode 100755 index 828b6cfb4..000000000 --- a/docs/pull-theme.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -git subtree pull --prefix=themes/gohugoioTheme/ git@github.com:gohugoio/gohugoioTheme.git master --squash - diff --git a/docs/src/css/_chroma.css b/docs/src/css/_chroma.css deleted file mode 100644 index 1ad06604b..000000000 --- a/docs/src/css/_chroma.css +++ /dev/null @@ -1,43 +0,0 @@ -/* Background */ .chroma { background-color: #f0f0f0 } -/* Error */ .chroma .ss4 { } -/* LineHighlight */ .chroma .hl { background-color: #ffffcc; display: block; width: 100% } -/* LineNumbers */ .chroma .ln { ; margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } -/* Keyword */ .chroma .s3e8 { color: #007020; font-weight: bold } -/* KeywordPseudo */ .chroma .s3ec { color: #007020 } -/* KeywordType */ .chroma .s3ee { color: #902000 } -/* NameAttribute */ .chroma .s7d1 { color: #4070a0 } -/* NameBuiltin */ .chroma .s7d2 { color: #007020 } -/* NameClass */ .chroma .s7d4 { color: #0e84b5; font-weight: bold } -/* NameConstant */ .chroma .s7d5 { color: #60add5 } -/* NameDecorator */ .chroma .s7d6 { color: #555555; font-weight: bold } -/* NameEntity */ .chroma .s7d7 { color: #d55537; font-weight: bold } -/* NameException */ .chroma .s7d8 { color: #007020 } -/* NameFunction */ .chroma .s7d9 { color: #06287e } -/* NameLabel */ .chroma .s7dc { color: #002070; font-weight: bold } -/* NameNamespace */ .chroma .s7dd { color: #0e84b5; font-weight: bold } -/* NameTag */ .chroma .s7e2 { color: #062873; font-weight: bold } -/* NameVariable */ .chroma .s7e3 { color: #bb60d5 } -/* LiteralString */ .chroma .sc1c { color: #4070a0 } -/* LiteralStringDoc */ .chroma .sc23 { color: #4070a0; font-style: italic } -/* LiteralStringEscape */ .chroma .sc25 { color: #4070a0; font-weight: bold } -/* LiteralStringInterpol */ .chroma .sc27 { color: #70a0d0; font-style: italic } -/* LiteralStringOther */ .chroma .sc29 { color: #c65d09 } -/* LiteralStringRegex */ .chroma .sc2a { color: #235388 } -/* LiteralStringSymbol */ .chroma .sc2c { color: #517918 } -/* LiteralNumber */ .chroma .sc80 { color: #40a070 } -/* Operator */ .chroma .sfa0 { color: #666666 } -/* OperatorWord */ .chroma .sfa1 { color: #007020; font-weight: bold } -/* Comment */ .chroma .s1770 { color: #60a0b0; font-style: italic } -/* CommentSpecial */ .chroma .s1774 { color: #60a0b0; background-color: #fff0f0 } -/* CommentPreproc */ .chroma .s17d4 { color: #007020 } -/* GenericDeleted */ .chroma .s1b59 { color: #a00000 } -/* GenericEmph */ .chroma .s1b5a { font-style: italic } -/* GenericError */ .chroma .s1b5b { color: #ff0000 } -/* GenericHeading */ .chroma .s1b5c { color: #000080; font-weight: bold } -/* GenericInserted */ .chroma .s1b5d { color: #00a000 } -/* GenericOutput */ .chroma .s1b5e { color: #888888 } -/* GenericPrompt */ .chroma .s1b5f { color: #c65d09; font-weight: bold } -/* GenericStrong */ .chroma .s1b60 { font-weight: bold } -/* GenericSubheading */ .chroma .s1b61 { color: #800080; font-weight: bold } -/* GenericTraceback */ .chroma .s1b62 { color: #0044dd } -/* TextWhitespace */ .chroma .s1f41 { color: #bbbbbb } diff --git a/docs/src/package-lock.json b/docs/src/package-lock.json deleted file mode 100644 index 48e341a09..000000000 --- a/docs/src/package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lockfileVersion": 1 -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-144x144.png b/docs/static/android-chrome-144x144.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-144x144.png rename to docs/static/android-chrome-144x144.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-192x192.png b/docs/static/android-chrome-192x192.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-192x192.png rename to docs/static/android-chrome-192x192.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-256x256.png b/docs/static/android-chrome-256x256.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-256x256.png rename to docs/static/android-chrome-256x256.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-36x36.png b/docs/static/android-chrome-36x36.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-36x36.png rename to docs/static/android-chrome-36x36.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-48x48.png b/docs/static/android-chrome-48x48.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-48x48.png rename to docs/static/android-chrome-48x48.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-72x72.png b/docs/static/android-chrome-72x72.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-72x72.png rename to docs/static/android-chrome-72x72.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-96x96.png b/docs/static/android-chrome-96x96.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/android-chrome-96x96.png rename to docs/static/android-chrome-96x96.png diff --git a/docs/static/apple-touch-icon.png b/docs/static/apple-touch-icon.png index 50e23ce1d..ecf1fc020 100644 Binary files a/docs/static/apple-touch-icon.png and b/docs/static/apple-touch-icon.png differ diff --git a/docs/static/css/hugofont.css b/docs/static/css/hugofont.css deleted file mode 100644 index 09d6ce070..000000000 --- a/docs/static/css/hugofont.css +++ /dev/null @@ -1,184 +0,0 @@ -@font-face { - font-family: 'hugo'; - src:url('../fonts/hugo.eot'); - src:url('../fonts/hugo.eot?#iefix') format('embedded-opentype'), - url('../fonts/hugo.woff') format('woff'), - url('../fonts/hugo.ttf') format('truetype'), - url('../fonts/hugo.svg#hugo') format('svg'); - font-weight: normal; - font-style: normal; -} - -[class^="icon-"], [class*=" icon-"] { - font-family: 'hugo'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.icon-home:before { - content: "\21"; -} -.icon-html5:before { - content: "\23"; -} -.icon-css3:before { - content: "\24"; -} -.icon-console:before { - content: "\25"; -} -.icon-link:before { - content: "\26"; -} -.icon-fire:before { - content: "\28"; -} -.icon-check-alt:before { - content: "\29"; -} -.icon-hugo_serif:before { - content: "\e600"; -} -.icon-x-altx-alt:before { - content: "\2a"; -} -.icon-circlestar:before { - content: "\2b"; -} -.icon-file-css:before { - content: "\2c"; -} -.icon-radio-checked:before { - content: "\2e"; -} -.icon-quote:before { - content: "\44"; -} -.icon-airplane2:before { - content: "\45"; -} -.icon-heart:before { - content: "\46"; -} -.icon-rocket:before { - content: "\47"; -} -.icon-house:before { - content: "\48"; -} -.icon-arrow-right:before { - content: "\e001"; -} -.icon-arrow-left:before { - content: "\e002"; -} -.icon-flow-branch:before { - content: "\e004"; -} -.icon-pen:before { - content: "\e005"; -} -.icon-idea:before { - content: "\3b"; -} -.icon-gears:before { - content: "\3c"; -} -.icon-talking:before { - content: "\3d"; -} -.icon-tag:before { - content: "\3e"; -} -.icon-rocket2:before { - content: "\3f"; -} -.icon-octocat:before { - content: "\41"; -} -.icon-announce:before { - content: "\42"; -} -.icon-edit:before { - content: "\43"; -} -.icon-power-cord:before { - content: "\50"; -} -.icon-apple:before { - content: "\51"; -} -.icon-windows8:before { - content: "\52"; -} -.icon-tux:before { - content: "\53"; -} -.icon-file-xml:before { - content: "\54"; -} -.icon-fork:before { - content: "\55"; -} -.icon-arrow-down:before { - content: "\56"; -} -.icon-pacman:before { - content: "\e000"; -} -.icon-embed:before { - content: "\2f"; -} -.icon-code:before { - content: "\30"; -} -.icon-cc:before { - content: "\31"; -} -.icon-cc-by:before { - content: "\32"; -} -.icon-cc-nc:before { - content: "\33"; -} -.icon-beaker-alt:before { - content: "\39"; -} -.icon-w3c:before { - content: "\3a"; -} -.icon-bolt:before { - content: "\49"; -} -.icon-flow-tree:before { - content: "\4a"; -} -.icon-twitter:before { - content: "\4b"; -} -.icon-beaker:before { - content: "\4c"; -} -.icon-images:before { - content: "\4d"; -} -.icon-bubbles:before { - content: "\4e"; -} -.icon-meter2:before { - content: "\4f"; -} -.icon-hugo_sans:before { - content: "\68"; -} -.icon-spf13:before { - content: "\27"; -} diff --git a/docs/static/css/style.css b/docs/static/css/style.css deleted file mode 100644 index 312c247c9..000000000 --- a/docs/static/css/style.css +++ /dev/null @@ -1,684 +0,0 @@ -/* Import fonts */ -@import url(//fonts.googleapis.com/css?family=Lato:300,400,700,900,300italic,400italic,700italic,900italic); - -/* ****************************** - For the github btn -****************************** */ - -.github-btn { - font-size: 11px; -} -.github-btn, -.github-btn .btn { - font-weight: bold; -} -.github-btn .btn-default { - text-shadow: 0 1px 0 #fff; - background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e0e0e0)); - background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e0e0e0, 100%); - background-image: -moz-linear-gradient(top, #ffffff 0%, #e0e0e0 100%); - background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%); - background-repeat: repeat-x; - border-color: #dbdbdb; - border-color: #ccc; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); -} - -.github-btn .btn-default:hover, .github-btn .btn-default:focus { - background-color: #e0e0e0; - background-position: 0 -15px; - color: #333; - border-color: #adadad; -} - -.nav-github { - width: 325px; -} - .nav-github > span { - padding-right: 0.5em; - } - - .icon-github { - display: inline-block; - font-family: FontAwesome; - font-style: normal; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - .github-watchers .icon-github:before{ - content: "\f005"; - } - - .github-forks .icon-github:before{ - content: "\f126"; - } - -.gh-count{ - padding: 2px 5px 3px 4px; - color: #555; - text-decoration: none; - text-shadow:0 1px 0 #fff; - white-space:nowrap; - cursor:pointer; - border-radius:3px; - position:relative; - display:none; - margin-left:4px; - background-color:#fafafa; - border:1px solid #d4d4d4; -} - -.gh-count:hover,.gh-count:focus{color:#4183c4;text-decoration: none;} -.gh-count:before,.gh-count:after{content:' ';position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid} -.gh-count:before{top:50%;left:-3px;margin-top:-4px;border-width:4px 4px 4px 0;border-right-color:#fafafa} -.gh-count:after{top:50%;left:-4px;z-index:-1;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#d4d4d4} - -thead { - font-weight: bold; -} - -table { - width: 100%; -} - - -h1, h2, h3 { - margin-top: .8em; - margin-bottom: .7em; -} - -pre code { - font-size: 15px !important; - font-family: Menlo, Consolas, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', Monaco, 'Droid Sans Mono', monospace; -} - -body { - color: #353b44; - background: #edece4; - font-family: 'Lato', sans-serif; - padding: 0px !important; - margin: 0px !important; - font-size: 16px !important; - font-weight: 400; -} - -h2,h3,h4,h5{ - font-weight: 700; -} - - -h1[id]:before, h2[id]:before, h3[id]:before, h4[id]:before, h5[id]:before { - display: block; - content: " "; - margin-top: -75px; - height: 75px; - visibility: hidden; -} - -label{ - font-weight: 400; -} - -.sidebar-menu .fa { - width: 30px; - text-align: center; -} - -a, a:hover, a:focus { - text-decoration: none; - outline: none; - outline: 0; -} - -img { - max-width: 100%; - height: auto; -} - -.panel-body a { - line-height: 1.1; - display: inline-block; -} -.panel-body a:after { - display: block; - content: ""; - height: 1px; - width: 0%; - background-color: #ff4088; - -webkit-transition: width 0.5s ease; - -moz-transition: width 0.5s ease; - -ms-transition: width 0.5s ease; - transition: width 0.5s ease; -} - -.panel-body a:hover:after, .panel-body a:focus:after { - width: 100%; -} - -input:focus, textarea:focus { outline: none; } -*:focus {outline: none;} -::selection { - background: #ff4088; - color: #fff; -} -::-moz-selection { - background: #ff4088; - color: #fff; -} - -#container { - width: 100%; - height: 100%; -} - -/*sidebar navigation*/ - -#sidebar { - width: 214px; - height: 100%; - position: fixed; - background: #ffffff; - overflow-y: auto; -} - - -ul.sidebar-menu , ul.sidebar-menu li ul.sub{ - margin: -2px 0 0; - padding: 0; -} - -ul.sidebar-menu { - margin-top: 60px; -} - -#sidebar > ul > li > ul.sub { - display: none; -} - -#sidebar > ul > li.active > ul.sub, #sidebar > ul > li > ul.sub > li > a { - display: block; -} - -ul.sidebar-menu li ul.sub li{ - background: #eeeeee; - margin-bottom: 0; - margin-left: 0; - margin-right: 0; -} - -ul.sidebar-menu li ul.sub li:last-child{ - border-radius: 0 0 4px 4px; - -webkit-border-radius: 0 0 4px 4px; -} - -ul.sidebar-menu li ul.sub li a { - font-size: 12px; - padding: 0 0 0 32px; - line-height: 35px; - height: 35px; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - color: #656C73; - font-size: 14px; -} - -ul.sidebar-menu li ul.sub li a:hover, ul.sidebar-menu li ul.sub li.active a { - color: #ff4088; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - display: block; -} - -ul.sidebar-menu li{ - line-height: 20px !important; -} - -ul.sidebar-menu li.sub-menu{ - line-height: 15px; - font-size: 16px; -} - -ul.sidebar-menu li a span{ - display: inline-block; -} - -ul.sidebar-menu li a{ - color: #72767D; - text-decoration: none; - display: block; - padding: 10px 0 10px 10px; - font-size: 16px; - font-weight: 400; - outline: none; - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; - border-right: 1px solid #D7D7D7; - border-bottom: 1px solid #D7D7D7; - white-space: nowrap; -} - -ul.sidebar-menu li.active a, ul.sidebar-menu li a:hover, ul.sidebar-menu li a:focus { - background: #eeeeee; - color: #ff4088; - display: block; - /*border-radius: 4px; - -webkit-border-radius: 4px;*/ - -webkit-transition: all 0.3s ease; - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -ms-transition: all 0.3s ease; - transition: all 0.3s ease; -} -ul.sidebar-menu li a:hover, ul.sidebar-menu li a:focus { - border-bottom: 1px solid #ff4088; -} -/*ul.sidebar-menu li.active a,*/ ul.sidebar-menu .sub-menu li.active a{ - border-bottom: 1px solid #ff4088; -} - -ul.sidebar-menu li a i { - font-size: 18px; - padding-right: 6px; - /*color: #ff4088;*/ -} - -ul.sidebar-menu li a:hover i, ul.sidebar-menu li a:focus i { - color: #ff4088; -} - -ul.sidebar-menu li.active a i { - color: #ff4088; -} - - -#sidebar ul > li > a .menu-arrow { - float: right; - margin-right: 8px; - margin-top: 6px; -} - -@-moz-document url-prefix() { - #sidebar ul > li > a .menu-arrow { - float: right; - margin-right: 8px; - margin-top: -16px; - } -} - -#main-content { - margin-left: 200px; - line-height: 1.8; - font-size: 18px; -} - -.header { - min-height: 60px; - padding: 0 10px; -} -.header { - position: fixed; - left: 0; - right: 0; - z-index: 1002; - text-align:center; -} - - -.black-bg { - background: rgba(20,20,20,0.9); - border-bottom: 1px solid #f1f2f7; -} - -.wrapper { - display: inline-block; - margin-top: 60px; - padding: 0px 15px 15px 0px; - width: 100%; -} - -a.logo { - font-size: 22px; - font-weight: 400; - color: #8E8E93; - float: left; - margin-top: 10px; - text-transform: uppercase; -} - -a.logo:hover, a.logo:focus { - text-decoration: none; - outline: none; -} - -h1.top-menu { - margin-top: -5px; -} -.title-row { - margin-top: 15px; - margin-left: 16px; - color: #EEE; -} -.notification-row { - float: right; - margin-top: 15px; - margin-left: 65px; -} - - -.top-nav { - margin-top: 15px; -} - -/*--sidebar toggle---*/ - -.toggle-nav { - float: left; - padding-right: 5px; - margin-top: 20px; - cursor: pointer; - color: gray; -} - -.toggle-nav .icon-reorder { - cursor: pointer; - display: inline-block; - font-size: 20px; -} - - -@-webkit-keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -@-ms-keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -@keyframes square { - 0% { background-position: 0 0; } - 25% { background-position: 100% 0; } - 50% { background-position: 100% 100%; } - 75% { background-position: 0 100%; } - 100% { background-position: 0 0; } -} - -.navigation { - position: absolute; - top: 0; - bottom: 0; - margin: 0; - max-width: 150px; - min-width: 90px; - width:100%; - min-height:1200px; - cursor:pointer; - display: flex; - justify-content: center; - align-content: center; - flex-direction: column; - font-size: 6em; - color: rgba(0,0,0,0.5); - text-align: center; - -webkit-transition: all 350ms ease; - transition: all 350ms ease; -} - -.navigation.next { - right:0; -} - - -.navigation:hover { - background-color: rgba(0,0,0,0.1); -} - -/* Google Custom Search box */ - -input.gsc-input, -.gsc-input-box, -.gsc-input-box-hover, -.gsc-input-box-focus, -.gsc-search-button, -.gsc-inline-block { - box-sizing: content-box; - line-height: normal; -} - -.gsc-control-cse { - padding: 0.1em 0 0.5em 1em !important; - width: 16em !important; - float: right; -} - -input.gsc-search-button-v2 { - padding: 6px 12px !important; -} - -.gsc-search-box-tools .gsc-search-box .gsc-input { - padding-right: 1px !important; -} - -/* Styled keypress from Wikipedia */ - -kbd { - border: 1px solid #aaa; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - border-radius: 0.2em; - -moz-box-shadow: 0.1em 0.2em 0.2em #ddd; - -webkit-box-shadow: 0.1em 0.2em 0.2em #ddd; - box-shadow: 0.1em 0.2em 0.2em #ddd; - background-color: #f9f9f9; - background-image: -moz-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: -o-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: -webkit-linear-gradient(top, #eee, #f9f9f9, #eee); - background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee); - padding: 0.1em 0.3em; - font-family: inherit; - font-size: 0.85em; -} - -/* For definitions of variables */ - -dl { - margin: 1em; - border-bottom: 1px solid #ccc; -} - -dt { - float: left; - clear: left; - width: 9.5em; - margin: 0.125em; - padding: 2px 4px; -} - -dd { - padding: 0.2em 0 0.2em 10em; - border-top: 1px solid #ccc; -} - -/* Prevent linebreak right after an icon */ -#main-content .fa { - display: inline; -} - -/* Logo for FreeBSD until Font Awesome adds it, see https://github.com/FortAwesome/Font-Awesome/issues/1116 */ -i.freebsd-19px:before { - content: url(/img/freebsd-19px.svg); - vertical-align: -7%; -} - -/* Responsive videos */ -.video-container { - position: relative; - padding-bottom: 56.25%; /* 16:9 */ - padding-top: 30px; - height: 0; - overflow: hidden; - margin: 20px 0; -} - -.video-container iframe, -.video-container object, -.video-container embed { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -/* Google custom search */ -.cse { - margin-top: 20px; - padding-right: 20px; -} - - -/* Table of contents */ - -.toc ul { list-style: none; margin: 0; padding: 0 5px; } -.toc ul li { display: inline; } -#TableOfContents > ul > li > ul > li > ul li { margin-right: 8px; } -#TableOfContents > ul > li > ul > li > a, #TableOfContents > ul > li > a { font-weight: bold; background-color: #eeeeee; padding: 0 10px; margin: 0 2px; } -#TableOfContents > ul > li > ul > li > a { font-style: italic; } -.toc.compact ul > li > ul > li > ul { display: none; } - -#toc { - position:fixed; - background-color: rgba(0, 0, 0, 0.1); - padding: 10px 50px 10px 20px; -} - -.showcase-container { - display: inline-block; - position: relative; - width: 100%; -} - -.showcase-container img { - border: 1px solid #555; -} - -.showcase-container h4 { - margin-top: 0; - margin-bottom: 0; -} -.dummy { - padding-top: 90%; /* Making rows line up even if img proportions off */ -} - -.thumbnail { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -@media(max-width:1200px) { - .toc { - display: none; - } -} - - -/* Footer panel */ -.footer-panel { - width: 100%; - border-top:1px #efefef solid; - line-height: 30px; - padding: 25px 0px 15px; - margin-top: 15px; - background: #f9f9f9; - display: inline-block; - float: left; -} - -.footer-panel p { - padding-left: 20px; - padding-right: 20px; - font-size: medium; - font-style: italic; -} - - -/* Search form */ -#search-input { - width: 100%; - border: 1px solid #B3B3B3; - border-radius: 3px; - padding: 5px; -} - -#search-input:focus { - border-color: #F04A9C; -} - -/* Search result wrapper */ -.algolia-autocomplete { - width: 100%; -} - -/* List of search results */ -.aa-dropdown-menu { - box-sizing: border-box; - width: 100%; - background-color: #FFFFFF; - border: 1px solid #B3B3B3; - padding: 0; - font-size: 16px; - margin: 4 0 4 0; -} - -/* Highlight terms in search result headers */ -.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight { - background-color: #F04A9C; -} - -/* Highlight terms in search result body */ -.algolia-docsearch-suggestion--highlight { - color: #F04A9C; - font-weight: 900; -} - -/* Currently selected search result */ -.aa-cursor .algolia-docsearch-suggestion--content { - color: inherit; -} - -.aa-cursor .algolia-docsearch-suggestion { - background: #EFEFEF; - color: #353B44; -} - -.algolia-docsearch-suggestion { - font-size: 16px; - color: #9AA2AB; -} - -.algolia-docsearch-suggestion--category-header, -.algolia-docsearch-suggestion--subcategory-column { - display: none !important; -} diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-16x16.png b/docs/static/favicon-16x16.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-16x16.png rename to docs/static/favicon-16x16.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-32x32.png b/docs/static/favicon-32x32.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/favicon-32x32.png rename to docs/static/favicon-32x32.png diff --git a/docs/static/favicon.ico b/docs/static/favicon.ico index 36693330b..dc007a99e 100644 Binary files a/docs/static/favicon.ico and b/docs/static/favicon.ico differ diff --git a/docs/static/fonts/Mulish-Italic-VariableFont_wght.ttf b/docs/static/fonts/Mulish-Italic-VariableFont_wght.ttf new file mode 100644 index 000000000..e5425c75e Binary files /dev/null and b/docs/static/fonts/Mulish-Italic-VariableFont_wght.ttf differ diff --git a/docs/static/fonts/Mulish-VariableFont_wght.ttf b/docs/static/fonts/Mulish-VariableFont_wght.ttf new file mode 100644 index 000000000..410f7aa63 Binary files /dev/null and b/docs/static/fonts/Mulish-VariableFont_wght.ttf differ diff --git a/docs/static/fonts/hugo.eot b/docs/static/fonts/hugo.eot deleted file mode 100644 index b92f00f93..000000000 Binary files a/docs/static/fonts/hugo.eot and /dev/null differ diff --git a/docs/static/fonts/hugo.svg b/docs/static/fonts/hugo.svg deleted file mode 100644 index 7913f7c1f..000000000 --- a/docs/static/fonts/hugo.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/static/fonts/hugo.ttf b/docs/static/fonts/hugo.ttf deleted file mode 100644 index 962914d33..000000000 Binary files a/docs/static/fonts/hugo.ttf and /dev/null differ diff --git a/docs/static/fonts/hugo.woff b/docs/static/fonts/hugo.woff deleted file mode 100644 index 4693fbe7f..000000000 Binary files a/docs/static/fonts/hugo.woff and /dev/null differ diff --git a/docs/static/images/blog/hugo-26-poster.png b/docs/static/images/blog/hugo-26-poster.png deleted file mode 100644 index 827f1f7bb..000000000 Binary files a/docs/static/images/blog/hugo-26-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-27-poster.png b/docs/static/images/blog/hugo-27-poster.png deleted file mode 100644 index 69efa36bc..000000000 Binary files a/docs/static/images/blog/hugo-27-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-28-poster.png b/docs/static/images/blog/hugo-28-poster.png deleted file mode 100644 index ae3d6ac16..000000000 Binary files a/docs/static/images/blog/hugo-28-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-29-poster.png b/docs/static/images/blog/hugo-29-poster.png deleted file mode 100644 index dbe2d434f..000000000 Binary files a/docs/static/images/blog/hugo-29-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-30-poster.png b/docs/static/images/blog/hugo-30-poster.png deleted file mode 100644 index 214369e89..000000000 Binary files a/docs/static/images/blog/hugo-30-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-31-poster.png b/docs/static/images/blog/hugo-31-poster.png deleted file mode 100644 index e11e53aa7..000000000 Binary files a/docs/static/images/blog/hugo-31-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-32-poster.png b/docs/static/images/blog/hugo-32-poster.png deleted file mode 100644 index f915247ad..000000000 Binary files a/docs/static/images/blog/hugo-32-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-bug-poster.png b/docs/static/images/blog/hugo-bug-poster.png deleted file mode 100644 index cd236682d..000000000 Binary files a/docs/static/images/blog/hugo-bug-poster.png and /dev/null differ diff --git a/docs/static/images/blog/hugo-http2-push.png b/docs/static/images/blog/hugo-http2-push.png deleted file mode 100644 index 1ddfd4653..000000000 Binary files a/docs/static/images/blog/hugo-http2-push.png and /dev/null differ diff --git a/docs/static/images/blog/sunset.jpg b/docs/static/images/blog/sunset.jpg deleted file mode 100644 index 4dbcc0836..000000000 Binary files a/docs/static/images/blog/sunset.jpg and /dev/null differ diff --git a/docs/static/images/contribute/development/accept-cla.png b/docs/static/images/contribute/development/accept-cla.png deleted file mode 100644 index 272de935e..000000000 Binary files a/docs/static/images/contribute/development/accept-cla.png and /dev/null differ diff --git a/docs/static/images/contribute/development/copy-remote-url.png b/docs/static/images/contribute/development/copy-remote-url.png deleted file mode 100644 index a97a8f48f..000000000 Binary files a/docs/static/images/contribute/development/copy-remote-url.png and /dev/null differ diff --git a/docs/static/images/contribute/development/forking-a-repository.png b/docs/static/images/contribute/development/forking-a-repository.png deleted file mode 100644 index b2566b841..000000000 Binary files a/docs/static/images/contribute/development/forking-a-repository.png and /dev/null differ diff --git a/docs/static/images/contribute/development/open-pull-request.png b/docs/static/images/contribute/development/open-pull-request.png deleted file mode 100644 index 3f8328964..000000000 Binary files a/docs/static/images/contribute/development/open-pull-request.png and /dev/null differ diff --git a/docs/static/images/gohugoio-card-1.png b/docs/static/images/gohugoio-card-1.png deleted file mode 100644 index 09953aed9..000000000 Binary files a/docs/static/images/gohugoio-card-1.png and /dev/null differ diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/gopher-hero.svg b/docs/static/images/gopher-hero.svg similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/gopher-hero.svg rename to docs/static/images/gopher-hero.svg diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/gopher-side_color.svg b/docs/static/images/gopher-side_color.svg similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/gopher-side_color.svg rename to docs/static/images/gopher-side_color.svg diff --git a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png b/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png deleted file mode 100644 index 1ec752428..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif b/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif deleted file mode 100644 index 6c57cf3b2..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png b/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png deleted file mode 100644 index 3b17e2b01..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-azure/basic-app-details.png b/docs/static/images/hosting-and-deployment/hosting-on-azure/basic-app-details.png deleted file mode 100644 index 6cd39424e..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-azure/basic-app-details.png and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-azure/create-in-portal.png b/docs/static/images/hosting-and-deployment/hosting-on-azure/create-in-portal.png deleted file mode 100644 index 07dffbae6..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-azure/create-in-portal.png and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-blog-post.png b/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-blog-post.png deleted file mode 100644 index b78f6fd15..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-blog-post.png and /dev/null differ diff --git a/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-create-repo.png b/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-create-repo.png deleted file mode 100644 index e97f13465..000000000 Binary files a/docs/static/images/hosting-and-deployment/hosting-on-bitbucket/bitbucket-create-repo.png and /dev/null differ diff --git a/docs/static/images/hugo-content-bundles.png b/docs/static/images/hugo-content-bundles.png deleted file mode 100644 index 501e671e2..000000000 Binary files a/docs/static/images/hugo-content-bundles.png and /dev/null differ diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/hugo-logo-wide.svg b/docs/static/images/hugo-logo-wide.svg similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/images/hugo-logo-wide.svg rename to docs/static/images/hugo-logo-wide.svg diff --git a/docs/static/images/icon-custom-outputs.svg b/docs/static/images/icon-custom-outputs.svg deleted file mode 100644 index ccf581f31..000000000 --- a/docs/static/images/icon-custom-outputs.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/manifest.json b/docs/static/manifest.json similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/manifest.json rename to docs/static/manifest.json diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-144x144.png b/docs/static/mstile-144x144.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-144x144.png rename to docs/static/mstile-144x144.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-150x150.png b/docs/static/mstile-150x150.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-150x150.png rename to docs/static/mstile-150x150.png diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-310x310.png b/docs/static/mstile-310x310.png similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/mstile-310x310.png rename to docs/static/mstile-310x310.png diff --git a/docs/static/npmjs/index.html b/docs/static/npmjs/index.html deleted file mode 100644 index 88dd510af..000000000 --- a/docs/static/npmjs/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/_vendor/github.com/gohugoio/gohugoioTheme/static/safari-pinned-tab.svg b/docs/static/safari-pinned-tab.svg similarity index 100% rename from docs/_vendor/github.com/gohugoio/gohugoioTheme/static/safari-pinned-tab.svg rename to docs/static/safari-pinned-tab.svg diff --git a/docs/static/shared/branding/hugo-tall.png b/docs/static/shared/branding/hugo-tall.png deleted file mode 100644 index 001ce5eb3..000000000 Binary files a/docs/static/shared/branding/hugo-tall.png and /dev/null differ diff --git a/docs/static/shared/branding/made-with-hugo-dark.png b/docs/static/shared/branding/made-with-hugo-dark.png deleted file mode 100644 index c6cadf283..000000000 Binary files a/docs/static/shared/branding/made-with-hugo-dark.png and /dev/null differ diff --git a/docs/static/shared/branding/made-with-hugo-long-dark.png b/docs/static/shared/branding/made-with-hugo-long-dark.png deleted file mode 100644 index 1e49995fb..000000000 Binary files a/docs/static/shared/branding/made-with-hugo-long-dark.png and /dev/null differ diff --git a/docs/static/shared/branding/made-with-hugo-long.png b/docs/static/shared/branding/made-with-hugo-long.png deleted file mode 100644 index c5df534cf..000000000 Binary files a/docs/static/shared/branding/made-with-hugo-long.png and /dev/null differ diff --git a/docs/static/shared/branding/made-with-hugo.png b/docs/static/shared/branding/made-with-hugo.png deleted file mode 100644 index 52dfd19e5..000000000 Binary files a/docs/static/shared/branding/made-with-hugo.png and /dev/null differ diff --git a/docs/static/shared/branding/powered-by-hugo-dark.png b/docs/static/shared/branding/powered-by-hugo-dark.png deleted file mode 100644 index a8e2ebc80..000000000 Binary files a/docs/static/shared/branding/powered-by-hugo-dark.png and /dev/null differ diff --git a/docs/static/shared/branding/powered-by-hugo-long-dark.png b/docs/static/shared/branding/powered-by-hugo-long-dark.png deleted file mode 100644 index 1b760b1bf..000000000 Binary files a/docs/static/shared/branding/powered-by-hugo-long-dark.png and /dev/null differ diff --git a/docs/static/shared/branding/powered-by-hugo-long.png b/docs/static/shared/branding/powered-by-hugo-long.png deleted file mode 100644 index 37131359d..000000000 Binary files a/docs/static/shared/branding/powered-by-hugo-long.png and /dev/null differ diff --git a/docs/static/shared/branding/powered-by-hugo.png b/docs/static/shared/branding/powered-by-hugo.png deleted file mode 100644 index 27ff099d5..000000000 Binary files a/docs/static/shared/branding/powered-by-hugo.png and /dev/null differ diff --git a/docs/tailwind.config.js b/docs/tailwind.config.js new file mode 100644 index 000000000..9d3c29050 --- /dev/null +++ b/docs/tailwind.config.js @@ -0,0 +1 @@ +/* Empty for now. */ diff --git a/go.mod b/go.mod index 392119e17..1fa93cb5c 100644 --- a/go.mod +++ b/go.mod @@ -2,53 +2,51 @@ module github.com/gohugoio/hugo require ( github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 - github.com/alecthomas/chroma/v2 v2.14.0 + github.com/alecthomas/chroma/v2 v2.18.0 github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c - github.com/aws/aws-sdk-go-v2 v1.30.3 - github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4 + github.com/aws/aws-sdk-go-v2 v1.36.4 + github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10 github.com/bep/clocks v0.5.0 github.com/bep/debounce v1.2.0 github.com/bep/gitmap v1.6.0 github.com/bep/goat v0.5.0 - github.com/bep/godartsass v1.2.0 - github.com/bep/godartsass/v2 v2.1.0 + github.com/bep/godartsass/v2 v2.5.0 github.com/bep/golibsass v1.2.0 + github.com/bep/goportabletext v0.1.0 github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 - github.com/bep/imagemeta v0.8.1 - github.com/bep/lazycache v0.4.0 + github.com/bep/imagemeta v0.12.0 + github.com/bep/lazycache v0.8.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 - github.com/bep/overlayfs v0.9.2 - github.com/bep/simplecobra v0.4.0 + github.com/bep/overlayfs v0.10.0 + github.com/bep/simplecobra v0.6.0 github.com/bep/tmc v0.5.1 github.com/cespare/xxhash/v2 v2.3.0 github.com/clbanning/mxj/v2 v2.7.0 - github.com/cli/safeexec v1.0.1 github.com/disintegration/gift v1.2.1 github.com/dustin/go-humanize v1.0.1 - github.com/evanw/esbuild v0.24.0 - github.com/fatih/color v1.17.0 + github.com/evanw/esbuild v0.25.5 + github.com/fatih/color v1.18.0 github.com/fortytw2/leaktest v1.3.0 github.com/frankban/quicktest v1.14.6 - github.com/fsnotify/fsnotify v1.7.0 - github.com/getkin/kin-openapi v0.123.0 + github.com/fsnotify/fsnotify v1.9.0 + github.com/getkin/kin-openapi v0.132.0 github.com/ghodss/yaml v1.0.0 github.com/gobuffalo/flect v1.0.3 github.com/gobwas/glob v0.2.3 github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e - github.com/gohugoio/hashstructure v0.1.0 + github.com/gohugoio/hashstructure v0.5.0 github.com/gohugoio/httpcache v0.7.0 - github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0 - github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0 + github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0 + github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 github.com/gohugoio/locales v0.14.0 github.com/gohugoio/localescompressed v1.0.1 github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/gorilla/websocket v1.5.3 - github.com/hairyhenderson/go-codeowners v0.6.0 + github.com/hairyhenderson/go-codeowners v0.7.0 github.com/jdkato/prose v1.2.1 - github.com/kylelemons/godebug v1.1.0 github.com/kyokomi/emoji/v2 v2.2.13 github.com/magefile/mage v1.15.0 github.com/makeworld-the-better-one/dither/v2 v2.4.0 @@ -56,42 +54,46 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c github.com/muesli/smartcrop v0.3.0 - github.com/niklasfasching/go-org v1.7.0 - github.com/olekukonko/tablewriter v0.0.5 + github.com/niklasfasching/go-org v1.8.0 + github.com/olekukonko/tablewriter v1.0.7 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 - github.com/pelletier/go-toml/v2 v2.2.3 - github.com/rogpeppe/go-internal v1.13.1 - github.com/sanity-io/litter v1.5.5 - github.com/spf13/afero v1.11.0 - github.com/spf13/cast v1.7.0 - github.com/spf13/cobra v1.8.1 + github.com/pelletier/go-toml/v2 v2.2.4 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c + github.com/rogpeppe/go-internal v1.14.1 + github.com/sanity-io/litter v1.5.8 + github.com/spf13/afero v1.14.0 + github.com/spf13/cast v1.9.2 + github.com/spf13/cobra v1.9.1 github.com/spf13/fsync v0.10.1 - github.com/spf13/pflag v1.0.5 - github.com/tdewolff/minify/v2 v2.20.37 - github.com/tdewolff/parse/v2 v2.7.15 - github.com/tetratelabs/wazero v1.8.1 - github.com/yuin/goldmark v1.7.4 - github.com/yuin/goldmark-emoji v1.0.4 + github.com/spf13/pflag v1.0.6 + github.com/tdewolff/minify/v2 v2.23.8 + github.com/tdewolff/parse/v2 v2.8.1 + github.com/tetratelabs/wazero v1.9.0 + github.com/yuin/goldmark v1.7.12 + github.com/yuin/goldmark-emoji v1.0.6 go.uber.org/automaxprocs v1.5.3 - gocloud.dev v0.39.0 + gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 - golang.org/x/image v0.21.0 - golang.org/x/mod v0.21.0 - golang.org/x/net v0.30.0 - golang.org/x/sync v0.8.0 - golang.org/x/text v0.19.0 - golang.org/x/tools v0.26.0 - google.golang.org/api v0.191.0 + golang.org/x/image v0.28.0 + golang.org/x/mod v0.25.0 + golang.org/x/net v0.41.0 + golang.org/x/sync v0.15.0 + golang.org/x/text v0.26.0 + golang.org/x/tools v0.33.0 + google.golang.org/api v0.237.0 gopkg.in/yaml.v2 v2.4.0 + rsc.io/qr v0.2.0 ) require ( - cloud.google.com/go v0.115.0 // indirect - cloud.google.com/go/auth v0.8.1 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect - cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/iam v1.1.13 // indirect - cloud.google.com/go/storage v1.43.0 // indirect + cel.dev/expr v0.23.0 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/storage v1.50.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect @@ -99,14 +101,17 @@ require ( github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect @@ -117,56 +122,71 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.3 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect - github.com/dlclark/regexp2 v1.11.0 // indirect + github.com/aws/smithy-go v1.22.2 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/swag v0.22.8 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/s2a-go v0.1.8 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/google/wire v0.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.14.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/yaml v0.2.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 // indirect + github.com/olekukonko/ll v0.0.8 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect + github.com/zeebo/errs v1.4.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/time v0.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect - google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/grpc v1.73.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v1.0.0 // indirect software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect ) -go 1.22.6 +go 1.23.0 diff --git a/go.sum b/go.sum index 6f3e61e89..4328b73b9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss= +cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -17,26 +19,30 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo= -cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= -cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= +cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= -cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= -cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= -cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= +cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -47,8 +53,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= -cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= +cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= @@ -71,18 +79,26 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= -github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= -github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0 h1:nNMpRpnkWDAaqcpxMJvxa/Ud98gjbYwayJY4/9bdjiU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= +github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M= github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E= +github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= @@ -93,16 +109,16 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4 h1:I/sQ9uGOs72/483obb2SPoa9ZEsYGbel6jcTTwD/0zU= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.38.4/go.mod h1:P6ByphKl2oNQZlv4WsCaLSmRncKEcOnbitYLtJPfqZI= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10 h1:fdLh7eMf5mxtggx2nG0+cFkaiRK+ULCOPK3qq8eTje4= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10/go.mod h1:uBca+/1aH5v/RYWXqyymLrsbmx1vU9bBxeurlC627Gc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= @@ -119,8 +135,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrA github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo= @@ -129,28 +145,28 @@ github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= -github.com/bep/godartsass v1.2.0 h1:E2VvQrxAHAFwbjyOIExAMmogTItSKodoKuijNrGm5yU= -github.com/bep/godartsass v1.2.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8= -github.com/bep/godartsass/v2 v2.1.0 h1:fq5Y1xYf4diu4tXABiekZUCA+5l/dmNjGKCeQwdy+s0= -github.com/bep/godartsass/v2 v2.1.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo= +github.com/bep/godartsass/v2 v2.5.0 h1:tKRvwVdyjCIr48qgtLa4gHEdtRkPF8H1OeEhJAEv7xg= +github.com/bep/godartsass/v2 v2.5.0/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= +github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw= +github.com/bep/goportabletext v0.1.0/go.mod h1:6lzSTsSue75bbcyvVc0zqd1CdApuT+xkZQ6Re5DzZFg= github.com/bep/gowebp v0.3.0 h1:MhmMrcf88pUY7/PsEhMgEP0T6fDUnRTMpN8OclDrbrY= github.com/bep/gowebp v0.3.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI= github.com/bep/helpers v0.5.0 h1:rneezhnG7GzLFlsEWO/EnleaBRuluBDGFimalO6Y50o= github.com/bep/helpers v0.5.0/go.mod h1:dSqCzIvHbzsk5YOesp1M7sKAq5xUcvANsRoKdawxH4Q= -github.com/bep/imagemeta v0.8.1 h1:tjZLPRftjxU7PTI87o5e5WKOFQ4S9S0engiP1OTpJTI= -github.com/bep/imagemeta v0.8.1/go.mod h1:5piPAq5Qomh07m/dPPCLN3mDJyFusvUG7VwdRD/vX0s= -github.com/bep/lazycache v0.4.0 h1:X8yVyWNVupPd4e1jV7efi3zb7ZV/qcjKQgIQ5aPbkYI= -github.com/bep/lazycache v0.4.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= +github.com/bep/imagemeta v0.12.0 h1:ARf+igs5B7pf079LrqRnwzQ/wEB8Q9v4NSDRZO1/F5k= +github.com/bep/imagemeta v0.12.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= +github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8= +github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA= github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY= -github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs= -github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40= -github.com/bep/simplecobra v0.4.0 h1:ufX/6WcOtEVJdCd7hsztTWURlZkOaWYOD+zCqrM8qUE= -github.com/bep/simplecobra v0.4.0/go.mod h1:evSM6iQqRwqpV7W4H4DlYFfe9mZ0x6Hj5GEOnIV7dI4= +github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE= +github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro= +github.com/bep/simplecobra v0.6.0 h1:PpY/0PvYp6jt4OC/9SGoNPi6HzvpYzu8IPogVV6Xk90= +github.com/bep/simplecobra v0.6.0/go.mod h1:q0ecBAefJZYpzgkbPbQ901hzA98g3ZvCZWZRhzNtB5o= github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg= @@ -163,15 +179,14 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= -github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -179,8 +194,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -188,11 +203,19 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanw/esbuild v0.24.0 h1:GZ78naTLp7FKr+K7eNuM/SLs5maeiHYRPsTg6kmdsSE= -github.com/evanw/esbuild v0.24.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/evanw/esbuild v0.25.5 h1:E+JpeY5S/1LFmnX1vtuZqUKT7qDVcfXdhzMhM3uIKFs= +github.com/evanw/esbuild v0.25.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -200,27 +223,30 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= -github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8= -github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= -github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= @@ -229,22 +255,22 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= -github.com/gohugoio/hashstructure v0.1.0 h1:kBSTMLMyTXbrJVAxaKI+wv30MMJJxn9Q8kfQtJaZ400= -github.com/gohugoio/hashstructure v0.1.0/go.mod h1:8ohPTAfQLTs2WdzB6k9etmQYclDUeNsIHGPAFejbsEA= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= -github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0 h1:MNdY6hYCTQEekY0oAfsxWZU1CDt6iH+tMLgyMJQh/sg= -github.com/gohugoio/hugo-goldmark-extensions/extras v0.2.0/go.mod h1:oBdBVuiZ0fv9xd8xflUgt53QxW5jOCb1S+xntcN4SKo= -github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0 h1:7PY5PIJ2mck7v6R52yCFvvYHvsPMEbulgRviw3I9lP4= -github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.0/go.mod h1:r8g5S7bHfdj0+9ShBog864ufCsVODKQZNjYYY8OnJpM= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0 h1:gj49kTR5Z4Hnm0ZaQrgPVazL3DUkppw+x6XhHCmh+Wk= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0/go.mod h1:IMMj7xiUbLt1YNJ6m7AM4cnsX4cFnnfkleO/lBHGzUg= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 h1:nUzXfRTszLliZuN0JTKeunXTRaiFX6ksaWP0puLLYAY= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1/go.mod h1:Wy8ThAA8p2/w1DY05vEzq6EIeI2mzDjvHsu7ULBVwog= github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc= github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4= github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo= github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0= github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 h1:sgew0XCnZwnzpWxTt3V8LLiCO7OQi3C6dycaE67wfkU= github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95/go.mod h1:bOlVlCa1/RajcHpXkrUXPSHB/Re1UnlXxD1Qp8SKOd8= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -287,10 +313,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= @@ -312,25 +337,25 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= +github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hairyhenderson/go-codeowners v0.6.0 h1:cRCtmNf9Ni1GIeiAAlHX5IEEB2gr61813Kx5JmXxAAk= -github.com/hairyhenderson/go-codeowners v0.6.0/go.mod h1:RFWbGcjlXhRKNezt7AQHmJucY0alk4osN0+RKOsIAa8= +github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= +github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -341,8 +366,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -358,7 +381,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -382,8 +404,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= @@ -394,14 +416,22 @@ github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78Rwc github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= -github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= -github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY= +github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 h1:r3FaAI0NZK3hSmtTDrBVREhKULp8oUeqLT5Eyl2mSPo= +github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.8 h1:sbGZ1Fx4QxJXEqL/6IG8GEFnYojUSQ45dJVwN2FH2fc= +github.com/olekukonko/ll v0.0.8/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= +github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -410,35 +440,40 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= -github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg= +github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= +github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= +github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE= github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -450,17 +485,16 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tdewolff/minify/v2 v2.20.37 h1:Q97cx4STXCh1dlWDlNHZniE8BJ2EBL0+2b0n92BJQhw= -github.com/tdewolff/minify/v2 v2.20.37/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU= -github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw= -github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA= -github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo= -github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= -github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550= -github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.23.8 h1:tvjHzRer46kwOfpdCBCWsDblCw3QtnLJRd61pTVkyZ8= +github.com/tdewolff/minify/v2 v2.23.8/go.mod h1:VW3ISUd3gDOZuQ/jwZr4sCzsuX+Qvsx87FDMjk6Rvno= +github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po= +github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= +github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -469,11 +503,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90= -github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= +github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -482,22 +517,30 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= -gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= +gocloud.dev v0.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng= +gocloud.dev v0.40.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -509,8 +552,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -526,8 +569,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= -golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= +golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= +golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -554,8 +597,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -595,8 +638,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -606,8 +649,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -623,8 +666,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -672,8 +715,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -692,13 +735,13 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -751,8 +794,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -778,8 +821,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.191.0 h1:cJcF09Z+4HAB2t5qTQM1ZtfL/PemsLFkcFG67qq2afk= -google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= +google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc= +google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -823,12 +866,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 h1:CT2Thj5AuPV9phrYMtzX11k+XkzMGfRAet42PmoTATM= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988/go.mod h1:7uvplUBj4RjHAxIZ//98LzOvrQ04JBkaixRmCMI29hc= -google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988 h1:+/tmTy5zAieooKIXfzDm9KiA3Bv6JBwriRN9LY+yayk= -google.golang.org/genproto/googleapis/api v0.0.0-20240812133136-8ffd90a71988/go.mod h1:4+X6GvPs+25wZKbQq9qyAXrwIRExv7w0Ea6MgZLZiDM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 h1:V71AcdLZr2p8dC9dbOIMCpqi4EmRl8wUwnJzXXLmbmc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= +google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= +google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= +google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -845,8 +888,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -857,8 +900,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -872,7 +915,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -885,6 +927,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= diff --git a/helpers/content.go b/helpers/content.go index 9d74a3d31..6edcf88d4 100644 --- a/helpers/content.go +++ b/helpers/content.go @@ -109,10 +109,7 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) { startOfTOC := bytes.Index(content, first) - peekEnd := len(content) - if peekEnd > 70+startOfTOC { - peekEnd = 70 + startOfTOC - } + peekEnd := min(len(content), 70+startOfTOC) if startOfTOC < 0 { return stripEmptyNav(content), toc diff --git a/helpers/docshelper.go b/helpers/docshelper.go index 35d07d366..5edbef118 100644 --- a/helpers/docshelper.go +++ b/helpers/docshelper.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/alecthomas/chroma/v2/lexers" + "github.com/alecthomas/chroma/v2/styles" "github.com/gohugoio/hugo/docshelper" ) @@ -30,7 +31,10 @@ func init() { } - return docshelper.DocProvider{"chroma": map[string]any{"lexers": chromaLexers}} + return docshelper.DocProvider{"chroma": map[string]any{ + "lexers": chromaLexers, + "styles": styles.Names(), + }} } docshelper.AddDocProviderFunc(docsProvider) diff --git a/helpers/emoji.go b/helpers/emoji.go index c103a5479..aa5540dca 100644 --- a/helpers/emoji.go +++ b/helpers/emoji.go @@ -43,11 +43,7 @@ func Emojify(source []byte) []byte { j := start + k - upper := j + emojiMaxSize - - if upper > len(source) { - upper = len(source) - } + upper := min(j+emojiMaxSize, len(source)) endEmoji := bytes.Index(source[j+1:upper], emojiDelim) nextWordDelim := bytes.Index(source[j:upper], emojiWordDelim) diff --git a/helpers/general.go b/helpers/general.go index 11cc185a8..76275a6b9 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -63,7 +63,7 @@ func UniqueStrings(s []string) []string { unique := make([]string, 0, len(s)) for i, val := range s { var seen bool - for j := 0; j < i; j++ { + for j := range i { if s[j] == val { seen = true break @@ -83,7 +83,7 @@ func UniqueStringsReuse(s []string) []string { for i, val := range s { var seen bool - for j := 0; j < i; j++ { + for j := range i { if s[j] == val { seen = true break diff --git a/helpers/processing_stats.go b/helpers/processing_stats.go index 540060aa2..42376b46d 100644 --- a/helpers/processing_stats.go +++ b/helpers/processing_stats.go @@ -19,6 +19,8 @@ import ( "sync/atomic" "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/renderer" + "github.com/olekukonko/tablewriter/tw" ) // ProcessingStats represents statistics about a site build. @@ -66,30 +68,13 @@ func (s *ProcessingStats) Add(counter *uint64, amount int) { atomic.AddUint64(counter, uint64(amount)) } -// Table writes a table-formatted representation of the stats in a -// ProcessingStats instance to w. -func (s *ProcessingStats) Table(w io.Writer) { - titleVals := s.toVals() - data := make([][]string, len(titleVals)) - for i, tv := range titleVals { - data[i] = []string{tv.name, strconv.Itoa(int(tv.val))} - } - - table := tablewriter.NewWriter(w) - - table.AppendBulk(data) - table.SetHeader([]string{"", s.Name}) - table.SetBorder(false) - table.Render() -} - // ProcessingStatsTable writes a table-formatted representation of stats to w. func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) { names := make([]string, len(stats)+1) var data [][]string - for i := 0; i < len(stats); i++ { + for i := range stats { stat := stats[i] names[i+1] = stat.Name @@ -106,13 +91,26 @@ func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) { data[j] = append(data[j], strconv.Itoa(int(tv.val))) } } - } - table := tablewriter.NewWriter(w) + table := tablewriter.NewTable( + w, + tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{ + Borders: tw.BorderNone, + Symbols: tw.NewSymbols(tw.StyleLight), + Settings: tw.Settings{ + Separators: tw.Separators{BetweenRows: tw.Off}, + Lines: tw.Lines{ShowFooterLine: tw.On}, + }, + })), + tablewriter.WithConfig( + tablewriter.Config{ + MaxWidth: 70, + Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight, PerColumn: []tw.Align{tw.AlignLeft}}}, + }), + ) - table.AppendBulk(data) - table.SetHeader(names) - table.SetBorder(false) + table.Bulk(data) + table.Header(names) table.Render() } diff --git a/helpers/url_test.go b/helpers/url_test.go index ce1b24487..7f625035c 100644 --- a/helpers/url_test.go +++ b/helpers/url_test.go @@ -101,10 +101,10 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, v := config.New() if multilingual { v.Set("languages", map[string]any{ - "fr": map[string]interface{}{ + "fr": map[string]any{ "weight": 20, }, - "en": map[string]interface{}{ + "en": map[string]any{ "weight": 10, }, }) @@ -112,7 +112,7 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool, } else { v.Set("defaultContentLanguage", lang) v.Set("languages", map[string]any{ - lang: map[string]interface{}{ + lang: map[string]any{ "weight": 10, }, }) @@ -167,10 +167,10 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool, v := config.New() if multilingual { v.Set("languages", map[string]any{ - "fr": map[string]interface{}{ + "fr": map[string]any{ "weight": 20, }, - "en": map[string]interface{}{ + "en": map[string]any{ "weight": 10, }, }) @@ -178,7 +178,7 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool, } else { v.Set("defaultContentLanguage", lang) v.Set("languages", map[string]any{ - lang: map[string]interface{}{ + lang: map[string]any{ "weight": 10, }, }) diff --git a/htesting/hqt/checkers.go b/htesting/hqt/checkers.go index bf2cb9428..b06185756 100644 --- a/htesting/hqt/checkers.go +++ b/htesting/hqt/checkers.go @@ -96,6 +96,32 @@ func normalizeString(s string) string { return strings.Join(lines, "\n") } +// IsAllElementsEqual asserts that all elements in the slice are equal. +var IsAllElementsEqual qt.Checker = &sliceAllElementsEqualChecker{ + argNames: []string{"got"}, +} + +type sliceAllElementsEqualChecker struct { + argNames +} + +func (c *sliceAllElementsEqualChecker) Check(got any, args []any, note func(key string, value any)) (err error) { + gotSlice := reflect.ValueOf(got) + numElements := gotSlice.Len() + if numElements < 2 { + return nil + } + first := gotSlice.Index(0).Interface() + // Check that the others are equal to the first. + for i := 1; i < numElements; i++ { + if diff := cmp.Diff(first, gotSlice.Index(i).Interface()); diff != "" { + return fmt.Errorf("element %d is not equal to the first element:\n%s", i, diff) + } + } + + return nil +} + // DeepAllowUnexported creates an option to allow compare of unexported types // in the given list of types. // see https://github.com/google/go-cmp/issues/40#issuecomment-328615283 @@ -125,7 +151,7 @@ func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { structTypes(v.Elem(), m) } case reflect.Slice, reflect.Array: - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { structTypes(v.Index(i), m) } case reflect.Map: @@ -134,7 +160,7 @@ func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { } case reflect.Struct: m[v.Type()] = struct{}{} - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { structTypes(v.Field(i), m) } } diff --git a/htesting/test_helpers.go b/htesting/test_helpers.go index ff14de58d..2043b1a4b 100644 --- a/htesting/test_helpers.go +++ b/htesting/test_helpers.go @@ -110,6 +110,11 @@ func IsCI() bool { return (os.Getenv("CI") != "" || os.Getenv("CI_LOCAL") != "") && os.Getenv("CIRCLE_BRANCH") == "" } +// IsRealCI reports whether we're running in a CI server, but not in a local CI setup. +func IsRealCI() bool { + return IsCI() && os.Getenv("CI_LOCAL") == "" +} + // IsGitHubAction reports whether we're running in a GitHub Action. func IsGitHubAction() bool { return os.Getenv("GITHUB_ACTION") != "" diff --git a/hugofs/fileinfo.go b/hugofs/fileinfo.go index 60d2a38df..5e6a87acc 100644 --- a/hugofs/fileinfo.go +++ b/hugofs/fileinfo.go @@ -93,7 +93,7 @@ func (m *FileMeta) Merge(from *FileMeta) { dstv := reflect.Indirect(reflect.ValueOf(m)) srcv := reflect.Indirect(reflect.ValueOf(from)) - for i := 0; i < dstv.NumField(); i++ { + for i := range dstv.NumField() { v := dstv.Field(i) if !v.CanSet() { continue diff --git a/hugofs/fs.go b/hugofs/fs.go index fab0d3886..aecf72a7f 100644 --- a/hugofs/fs.go +++ b/hugofs/fs.go @@ -214,7 +214,7 @@ func WalkFilesystems(fs afero.Fs, fn WalkFn) bool { } } } else if cfs, ok := fs.(overlayfs.FilesystemIterator); ok { - for i := 0; i < cfs.NumFilesystems(); i++ { + for i := range cfs.NumFilesystems() { if WalkFilesystems(cfs.Filesystem(i), fn) { return true } diff --git a/hugofs/glob/glob.go b/hugofs/glob/glob.go index 42aa1fa3b..a4e5c49e8 100644 --- a/hugofs/glob/glob.go +++ b/hugofs/glob/glob.go @@ -166,7 +166,7 @@ func FilterGlobParts(a []string) []string { // HasGlobChar returns whether s contains any glob wildcards. func HasGlobChar(s string) bool { - for i := 0; i < len(s); i++ { + for i := range len(s) { if syntax.Special(s[i]) { return true } diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go index 2ecd88e9e..388493174 100644 --- a/hugofs/rootmapping_fs.go +++ b/hugofs/rootmapping_fs.go @@ -246,11 +246,11 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) { return nil, nil } - fss := make([]FileMetaInfo, len(roots)) - for i, r := range roots { + fss := make([]FileMetaInfo, 0, len(roots)) + for _, r := range roots { if r.fiSingleFile != nil { // A single file mount. - fss[i] = r.fiSingleFile + fss = append(fss, r.fiSingleFile) continue } bfs := NewBasePathFs(fs.Fs, r.To) @@ -261,9 +261,9 @@ func (fs *RootMappingFs) Mounts(base string) ([]FileMetaInfo, error) { fs = decorateDirs(fs, r.Meta) fi, err := fs.Stat("") if err != nil { - return nil, fmt.Errorf("RootMappingFs.Dirs: %w", err) + continue } - fss[i] = fi.(FileMetaInfo) + fss = append(fss, fi.(FileMetaInfo)) } return fss, nil @@ -311,12 +311,13 @@ func (fs *RootMappingFs) Open(name string) (afero.File, error) { // Stat returns the os.FileInfo structure describing a given file. If there is // an error, it will be of type *os.PathError. +// If multiple roots are found, the last one will be used. func (fs *RootMappingFs) Stat(name string) (os.FileInfo, error) { fis, err := fs.doStat(name) if err != nil { return nil, err } - return fis[0], nil + return fis[len(fis)-1], nil } type ComponentPath struct { diff --git a/hugofs/walk_test.go b/hugofs/walk_test.go index 7366d008d..03b808533 100644 --- a/hugofs/walk_test.go +++ b/hugofs/walk_test.go @@ -91,7 +91,7 @@ func TestWalkRootMappingFs(t *testing.T) { p := para.New(4) r, _ := p.Start(context.Background()) - for i := 0; i < 8; i++ { + for range 8 { r.Run(func() error { _, err := collectPaths(bfs, "") if err != nil { @@ -153,7 +153,7 @@ func BenchmarkWalk(b *testing.B) { fs := NewBaseFileDecorator(afero.NewMemMapFs()) writeFiles := func(dir string, numfiles int) { - for i := 0; i < numfiles; i++ { + for i := range numfiles { filename := filepath.Join(dir, fmt.Sprintf("file%d.txt", i)) c.Assert(afero.WriteFile(fs, filename, []byte("content"), 0o777), qt.IsNil) } diff --git a/hugolib/404_test.go b/hugolib/404_test.go index fef020905..78cf0e70c 100644 --- a/hugolib/404_test.go +++ b/hugolib/404_test.go @@ -14,6 +14,7 @@ package hugolib import ( + "fmt" "testing" ) @@ -82,3 +83,33 @@ Page not found Base: Page not found`) } + +func Test404EditTemplate(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "http://example.com/" +disableLiveReload = true +[internal] +fastRenderMode = true +-- layouts/_default/baseof.html -- +Base: {{ block "main" . }}{{ end }} +-- layouts/404.html -- +{{ define "main" }} +Not found. +{{ end }} + + ` + + b := TestRunning(t, files) + + b.AssertFileContent("public/404.html", `Not found.`) + + b.EditFiles("layouts/404.html", `Not found. Updated.`).Build() + + fmt.Println("Rebuilding") + b.BuildPartial("/does-not-exist") + + b.AssertFileContent("public/404.html", `Not found. Updated.`) +} diff --git a/hugolib/alias.go b/hugolib/alias.go index 08d57a8bc..7b252c613 100644 --- a/hugolib/alias.go +++ b/hugolib/alias.go @@ -29,16 +29,17 @@ import ( "github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/tpl" + "github.com/gohugoio/hugo/tpl/tplimpl" ) type aliasHandler struct { - t tpl.TemplateHandler + ts *tplimpl.TemplateStore log loggers.Logger allowRoot bool } -func newAliasHandler(t tpl.TemplateHandler, l loggers.Logger, allowRoot bool) aliasHandler { - return aliasHandler{t, l, allowRoot} +func newAliasHandler(ts *tplimpl.TemplateStore, l loggers.Logger, allowRoot bool) aliasHandler { + return aliasHandler{ts, l, allowRoot} } type aliasPage struct { @@ -47,16 +48,25 @@ type aliasPage struct { } func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, error) { - var templ tpl.Template - var found bool + var templateDesc tplimpl.TemplateDescriptor + var base string = "" + if ps, ok := p.(*pageState); ok { + base, templateDesc = ps.GetInternalTemplateBasePathAndDescriptor() + } + templateDesc.LayoutFromUser = "" + templateDesc.Kind = "" + templateDesc.OutputFormat = output.AliasHTMLFormat.Name + templateDesc.MediaType = output.AliasHTMLFormat.MediaType.Type - templ, found = a.t.Lookup("alias.html") - if !found { - // TODO(bep) consolidate - templ, found = a.t.Lookup("_internal/alias.html") - if !found { - return nil, errors.New("no alias template found") - } + q := tplimpl.TemplateQuery{ + Path: base, + Category: tplimpl.CategoryLayout, + Desc: templateDesc, + } + + t := a.ts.LookupPagesLayout(q) + if t == nil { + return nil, errors.New("no alias template found") } data := aliasPage{ @@ -67,7 +77,7 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, err ctx := tpl.Context.Page.Set(context.Background(), p) buffer := new(bytes.Buffer) - err := a.t.ExecuteWithContext(ctx, templ, buffer, data) + err := a.ts.ExecuteWithContext(ctx, t, buffer, data) if err != nil { return nil, err } @@ -79,7 +89,7 @@ func (s *Site) writeDestAlias(path, permalink string, outputFormat output.Format } func (s *Site) publishDestAlias(allowRoot bool, path, permalink string, outputFormat output.Format, p page.Page) (err error) { - handler := newAliasHandler(s.Tmpl(), s.Log, allowRoot) + handler := newAliasHandler(s.GetTemplateStore(), s.Log, allowRoot) targetPath, err := handler.targetPathAlias(path) if err != nil { diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go index c017050c6..1ba7bb16c 100644 --- a/hugolib/alias_test.go +++ b/hugolib/alias_test.go @@ -107,13 +107,26 @@ func TestAliasMultipleOutputFormats(t *testing.T) { func TestAliasTemplate(t *testing.T) { t.Parallel() - b := newTestSitesBuilder(t) - b.WithSimpleConfigFile().WithContent("page.md", pageWithAlias).WithTemplatesAdded("alias.html", aliasTemplate) + files := ` +-- hugo.toml -- +baseURL = "http://example.com" +-- layouts/single.html -- +Single. +-- layouts/home.html -- +Home. +-- layouts/alias.html -- +ALIASTEMPLATE +-- content/page.md -- +--- +title: "Page" +aliases: ["/foo/bar/"] +--- +` - b.CreateSites().Build(BuildCfg{}) + b := Test(t, files) // the real page - b.AssertFileContent("public/page/index.html", "For some moments the old man") + b.AssertFileContent("public/page/index.html", "Single.") // the alias redirector b.AssertFileContent("public/foo/bar/index.html", "ALIASTEMPLATE") } diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index cbceaaed5..3e3e471b7 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -841,3 +841,127 @@ title: p1 b.AssertFileExists("public/s1/index.html", false) b.AssertFileExists("public/s1/p1/index.html", false) } + +// Issue 12594. +func TestCascadeOrder(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','sitemap','taxonomy','term', 'home'] +-- content/_index.md -- +--- +title: Home +cascade: +- _target: + path: "**" + params: + background: yosemite.jpg +- _target: + params: + background: goldenbridge.jpg +--- +-- content/p1.md -- +--- +title: p1 +--- +-- layouts/_default/single.html -- +Background: {{ .Params.background }}| +-- layouts/_default/list.html -- +{{ .Title }}| + ` + + for range 10 { + b := Test(t, files) + b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg") + } +} + +// Issue #12465. +func TestCascadeOverlap(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','sitemap','taxonomy','term'] +-- layouts/_default/list.html -- +{{ .Title }} +-- layouts/_default/single.html -- +{{ .Title }} +-- content/s/_index.md -- +--- +title: s +cascade: + build: + render: never +--- +-- content/s/p1.md -- +--- +title: p1 +--- +-- content/sx/_index.md -- +--- +title: sx +--- +-- content/sx/p2.md -- +--- +title: p2 +--- +` + + b := Test(t, files) + + b.AssertFileExists("public/s/index.html", false) + b.AssertFileExists("public/s/p1/index.html", false) + + b.AssertFileExists("public/sx/index.html", true) // failing + b.AssertFileExists("public/sx/p2/index.html", true) // failing +} + +func TestCascadeGotmplIssue13743(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +[cascade.params] +foo = 'bar' +[cascade.target] +path = '/p1' +-- content/_content.gotmpl -- +{{ .AddPage (dict "title" "p1" "path" "p1") }} +-- layouts/all.html -- +{{ .Title }}|{{ .Params.foo }} +` + + b := Test(t, files) + + b.AssertFileContent("public/p1/index.html", "p1|bar") // actual content is "p1|" +} + +func TestCascadeWarnOverrideIssue13806(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +[[cascade]] +[cascade.params] +searchable = true +[cascade.target] +kind = 'page' +-- content/something.md -- +--- +title: Something +params: + searchable: false +--- +-- layouts/all.html -- +All. + +` + + b := Test(t, files, TestOptWarn()) + + b.AssertLogContains("! WARN") +} diff --git a/hugolib/collections_test.go b/hugolib/collections_test.go index fff57337f..f62d4c604 100644 --- a/hugolib/collections_test.go +++ b/hugolib/collections_test.go @@ -39,6 +39,8 @@ title: "Page" `) b.CreateSites().Build(BuildCfg{}) + // b.H.TemplateStore.PrintDebug("", tplimpl.CategoryLayout, os.Stdout) + c.Assert(len(b.H.Sites), qt.Equals, 1) c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2) @@ -71,9 +73,9 @@ tags_weight: %d {{ $pageGroups := slice $cool $blue }} {{ $weighted := slice $wp1 $wp2 }} -{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }} -{{ printf "pageGroups:%d:%T:%v/%v" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0) (index (index $pageGroups 1).Pages 0)}} -{{ printf "weightedPages:%d::%T:%v" (len $weighted) $weighted $weighted | safeHTML }} +{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }} +{{ printf "pageGroups:%d:%T:%s|%s" (len $pageGroups) $pageGroups (index (index $pageGroups 0).Pages 0).Path (index (index $pageGroups 1).Pages 0).Path}} +{{ printf "weightedPages:%d:%T" (len $weighted) $weighted | safeHTML }} `) b.CreateSites().Build(BuildCfg{}) @@ -82,9 +84,9 @@ tags_weight: %d c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2) b.AssertFileContent("public/index.html", - "pages:2:page.Pages:Page(/page1)/Page(/page2)", - "pageGroups:2:page.PagesGroup:Page(/page1)/Page(/page2)", - `weightedPages:2::page.WeightedPages:[WeightedPage(10,"Page") WeightedPage(20,"Page")]`) + "pages:2:page.Pages:/page1|/page2", + "pageGroups:2:page.PagesGroup:/page1|/page2", + `weightedPages:2:page.WeightedPages`) } func TestUnionFunc(t *testing.T) { @@ -189,7 +191,7 @@ tags_weight: %d {{ $appendStrings := slice "a" "b" | append "c" "d" "e" }} {{ $appendStringsSlice := slice "a" "b" "c" | append (slice "c" "d") }} -{{ printf "pages:%d:%T:%v/%v" (len $pages) $pages (index $pages 0) (index $pages 1) }} +{{ printf "pages:%d:%T:%s|%s" (len $pages) $pages (index $pages 0).Path (index $pages 1).Path }} {{ printf "appendPages:%d:%T:%v/%v" (len $appendPages) $appendPages (index $appendPages 0).Kind (index $appendPages 8).Kind }} {{ printf "appendStrings:%T:%v" $appendStrings $appendStrings }} {{ printf "appendStringsSlice:%T:%v" $appendStringsSlice $appendStringsSlice }} @@ -207,7 +209,7 @@ tags_weight: %d c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 2) b.AssertFileContent("public/index.html", - "pages:2:page.Pages:Page(/page2)/Page(/page1)", + "pages:2:page.Pages:/page2|/page1", "appendPages:9:page.Pages:home/page", "appendStrings:[]string:[a b c d e]", "appendStringsSlice:[]string:[a b c c d]", diff --git a/hugolib/config_test.go b/hugolib/config_test.go index c9db0d2f0..635283836 100644 --- a/hugolib/config_test.go +++ b/hugolib/config_test.go @@ -475,7 +475,7 @@ name = "menu-theme" }) }) - // Issue #8724 + // Issue #8724 ##13643 for _, mergeStrategy := range []string{"none", "shallow"} { c.Run(fmt.Sprintf("Merge with sitemap config in theme, mergestrategy %s", mergeStrategy), func(c *qt.C) { smapConfigTempl := `[sitemap] @@ -495,7 +495,7 @@ name = "menu-theme" b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "", Disable: false, Priority: -1, Filename: "sitemap.xml"}) b.AssertFileContent("public/sitemap.xml", "schemas/sitemap") } else { - b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: -1, Filename: "sitemap.xml"}) + b.Assert(got.Sitemap, qt.DeepEquals, config.SitemapConfig{ChangeFreq: "monthly", Disable: false, Priority: 0.5, Filename: "sitemap.xml"}) b.AssertFileContent("public/sitemap.xml", "monthly") } }) @@ -793,7 +793,7 @@ Single. files := strings.ReplaceAll(filesTemplate, "WEIGHT_EN", "2") files = strings.ReplaceAll(files, "WEIGHT_SV", "1") - for i := 0; i < 20; i++ { + for range 20 { cfg := config.New() b, err := NewIntegrationTestBuilder( IntegrationTestConfig{ @@ -1339,6 +1339,29 @@ Home. b.Assert(len(b.H.Sites), qt.Equals, 1) } +func TestDisableDefaultLanguageRedirect(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +defaultContentLanguageInSubdir = true +disableDefaultLanguageRedirect = true +[languages] +[languages.en] +title = "English Title" +[languages.sv] +title = "Swedish Title" +-- layouts/index.html -- +Home. + + +` + b := Test(t, files) + + b.Assert(len(b.H.Sites), qt.Equals, 2) + b.AssertFileExists("public/index.html", false) +} + func TestLoadConfigYamlEnvVar(t *testing.T) { defaultEnv := []string{`HUGO_OUTPUTS=home: ['json']`} @@ -1383,7 +1406,7 @@ home = ["html"] "home": {"html"}, "page": {"html"}, "rss": {"rss"}, - "section": nil, + "section": {}, "taxonomy": {"html", "rss"}, "term": {"html", "rss"}, }) diff --git a/hugolib/content_factory.go b/hugolib/content_factory.go index e6b0fb506..36cb29e63 100644 --- a/hugolib/content_factory.go +++ b/hugolib/content_factory.go @@ -72,12 +72,12 @@ func (f ContentFactory) ApplyArchetypeTemplate(w io.Writer, p page.Page, archety templateSource = f.shortcodeReplacerPre.Replace(templateSource) - templ, err := ps.s.TextTmpl().Parse("archetype.md", string(templateSource)) + templ, err := ps.s.TemplateStore.TextParse("archetype.md", templateSource) if err != nil { return fmt.Errorf("failed to parse archetype template: %s: %w", err, err) } - result, err := executeToString(context.Background(), ps.s.Tmpl(), templ, d) + result, err := executeToString(context.Background(), ps.s.GetTemplateStore(), templ, d) if err != nil { return fmt.Errorf("failed to execute archetype template: %s: %w", err, err) } @@ -114,7 +114,7 @@ func (f ContentFactory) CreateContentPlaceHolder(filename string, force bool) (s // the paths correct. placeholder := `--- title: "Content Placeholder" -_build: +build: render: never list: never publishResources: false diff --git a/hugolib/content_map.go b/hugolib/content_map.go index 55c96c9a0..596a8f7f9 100644 --- a/hugolib/content_map.go +++ b/hugolib/content_map.go @@ -264,8 +264,8 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo, buildConfig *BuildCfg) (pageCoun meta := fi.Meta() pi := meta.PathInfo - switch pi.BundleType() { - case paths.PathTypeFile, paths.PathTypeContentResource: + switch pi.Type() { + case paths.TypeFile, paths.TypeContentResource: m.s.Log.Trace(logg.StringFunc( func() string { return fmt.Sprintf("insert resource: %q", fi.Meta().Filename) @@ -275,7 +275,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo, buildConfig *BuildCfg) (pageCoun addErr = err return } - case paths.PathTypeContentData: + case paths.TypeContentData: pc, rc, err := m.addPagesFromGoTmplFi(fi, buildConfig) pageCount += pc resourceCount += rc @@ -349,15 +349,14 @@ func (m *pageMap) addPagesFromGoTmplFi(fi hugofs.FileMetaInfo, buildConfig *Buil DepsFromSite: func(s page.Site) pagesfromdata.PagesFromTemplateDeps { ss := s.(*Site) return pagesfromdata.PagesFromTemplateDeps{ - TmplFinder: ss.TextTmpl(), - TmplExec: ss.Tmpl(), + TemplateStore: ss.GetTemplateStore(), } }, DependencyManager: s.Conf.NewIdentityManager("pagesfromdata"), Watching: s.Conf.Watching(), HandlePage: func(pt *pagesfromdata.PagesFromTemplate, pc *pagemeta.PageConfig) error { s := pt.Site.(*Site) - if err := pc.Compile(pt.GoTmplFi.Meta().PathInfo.Base(), true, "", s.Log, s.conf.MediaTypes.Config); err != nil { + if err := pc.CompileForPagesFromDataPre(pt.GoTmplFi.Meta().PathInfo.Base(), m.s.Log, s.conf.MediaTypes.Config); err != nil { return err } diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index c3f06a592..47637d3e8 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -21,7 +21,6 @@ import ( "sort" "strconv" "strings" - "sync" "sync/atomic" "time" @@ -109,6 +108,11 @@ type pageMap struct { cfg contentMapConfig } +// Invoked on rebuilds. +func (m *pageMap) Reset() { + m.pageReverseIndex.Reset() +} + // pageTrees holds pages and resources in a tree structure for all sites/languages. // Each site gets its own tree set via the Shape method. type pageTrees struct { @@ -175,7 +179,7 @@ func (t *pageTrees) collectAndMarkStaleIdentities(p *paths.Path) []identity.Iden if p.Component() == files.ComponentFolderContent { // It may also be a bundled content resource. - key := p.ForBundleType(paths.PathTypeContentResource).Base() + key := p.ForType(paths.TypeContentResource).Base() tree = t.treeResources nCount = 0 tree.ForEeachInDimension(key, doctree.DimensionLanguage.Index(), @@ -504,8 +508,23 @@ func (m *pageMap) forEachResourceInPage( // A page key points to the logical path of a page, which when sourced from the filesystem // may represent a directory (bundles) or a single content file (e.g. p1.md). // So, to avoid any overlapping ambiguity, we start looking from the owning directory. - ownerKey, _ := m.treePages.LongestPrefixAll(path.Dir(resourceKey)) - if ownerKey != keyPage { + s := resourceKey + + for { + s = path.Dir(s) + ownerKey, found := m.treePages.LongestPrefixAll(s) + if !found { + return true, nil + } + if ownerKey == keyPage { + break + } + + if s != ownerKey && strings.HasPrefix(s, ownerKey) { + // Keep looking + continue + } + // Stop walking downwards, someone else owns this resource. rw.SkipPrefix(ownerKey + "/") return false, nil @@ -919,61 +938,58 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) * s: s, } - m.pageReverseIndex = &contentTreeReverseIndex{ - initFn: func(rm map[any]contentNodeI) { - add := func(k string, n contentNodeI) { - existing, found := rm[k] - if found && existing != ambiguousContentNode { - rm[k] = ambiguousContentNode - } else if !found { - rm[k] = n + m.pageReverseIndex = newContentTreeTreverseIndex(func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) { + add := func(k string, n contentNodeI) { + existing, found := get(k) + if found && existing != ambiguousContentNode { + set(k, ambiguousContentNode) + } else if !found { + set(k, n) + } + } + + w := &doctree.NodeShiftTreeWalker[contentNodeI]{ + Tree: m.treePages, + LockType: doctree.LockTypeRead, + Handle: func(s string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { + p := n.(*pageState) + if p.PathInfo() != nil { + add(p.PathInfo().BaseNameNoIdentifier(), p) } - } + return false, nil + }, + } - w := &doctree.NodeShiftTreeWalker[contentNodeI]{ - Tree: m.treePages, - LockType: doctree.LockTypeRead, - Handle: func(s string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { - p := n.(*pageState) - if p.PathInfo() != nil { - add(p.PathInfo().BaseNameNoIdentifier(), p) - } - return false, nil - }, - } - - if err := w.Walk(context.Background()); err != nil { - panic(err) - } - }, - contentTreeReverseIndexMap: &contentTreeReverseIndexMap{}, - } + if err := w.Walk(context.Background()); err != nil { + panic(err) + } + }) return m } -type contentTreeReverseIndex struct { - initFn func(rm map[any]contentNodeI) - *contentTreeReverseIndexMap -} - -func (c *contentTreeReverseIndex) Reset() { - c.contentTreeReverseIndexMap = &contentTreeReverseIndexMap{ - m: make(map[any]contentNodeI), +func newContentTreeTreverseIndex(init func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI))) *contentTreeReverseIndex { + return &contentTreeReverseIndex{ + initFn: init, + mm: maps.NewCache[any, contentNodeI](), } } -func (c *contentTreeReverseIndex) Get(key any) contentNodeI { - c.init.Do(func() { - c.m = make(map[any]contentNodeI) - c.initFn(c.contentTreeReverseIndexMap.m) - }) - return c.m[key] +type contentTreeReverseIndex struct { + initFn func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) + mm *maps.Cache[any, contentNodeI] } -type contentTreeReverseIndexMap struct { - init sync.Once - m map[any]contentNodeI +func (c *contentTreeReverseIndex) Reset() { + c.mm.Reset() +} + +func (c *contentTreeReverseIndex) Get(key any) contentNodeI { + v, _ := c.mm.InitAndGet(key, func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) error { + c.initFn(get, set) + return nil + }) + return v } type sitePagesAssembler struct { @@ -1106,6 +1122,9 @@ func (h *HugoSites) resolveAndClearStateForIdentities( l logg.LevelLogger, cachebuster func(s string) bool, changes []identity.Identity, ) error { + // Drain the cache eviction stack to start fresh. + evictedStart := h.Deps.MemCache.DrainEvictedIdentities() + h.Log.Debug().Log(logg.StringFunc( func() string { var sb strings.Builder @@ -1146,17 +1165,32 @@ func (h *HugoSites) resolveAndClearStateForIdentities( } // The order matters here: - // 1. Handle the cache busters first, as those may produce identities for the page reset step. + // 1. Then GC the cache, which may produce changes. // 2. Then reset the page outputs, which may mark some resources as stale. - // 3. Then GC the cache. - if cachebuster != nil { - if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { - ll := l.WithField("substep", "gc dynacache cachebuster") - h.dynacacheGCCacheBuster(cachebuster) - return ll, nil - }); err != nil { - return err + if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { + ll := l.WithField("substep", "gc dynacache") + + predicate := func(k any, v any) bool { + if cachebuster != nil { + if s, ok := k.(string); ok { + return cachebuster(s) + } + } + return false } + + h.MemCache.ClearOnRebuild(predicate, changes...) + h.Log.Trace(logg.StringFunc(func() string { + var sb strings.Builder + sb.WriteString("dynacache keys:\n") + for _, key := range h.MemCache.Keys(nil) { + sb.WriteString(fmt.Sprintf(" %s\n", key)) + } + return sb.String() + })) + return ll, nil + }); err != nil { + return err } // Drain the cache eviction stack. @@ -1165,6 +1199,27 @@ func (h *HugoSites) resolveAndClearStateForIdentities( for _, c := range evicted { changes = append(changes, c.Identity) } + + if len(evictedStart) > 0 { + // In low memory situations and/or very big sites, there can be a lot of unrelated evicted items, + // but there's a chance that some of them are related to the changes we are about to process, + // so check. + depsFinder := identity.NewFinder(identity.FinderConfig{}) + var addends []identity.Identity + for _, ev := range evictedStart { + for _, id := range changes { + if cachebuster != nil && cachebuster(ev.Key.(string)) { + addends = append(addends, ev.Identity) + break + } + if r := depsFinder.Contains(id, ev.Identity, -1); r > 0 { + addends = append(addends, ev.Identity) + break + } + } + } + changes = append(changes, addends...) + } } else { // Mass eviction, we might as well invalidate everything. changes = []identity.Identity{identity.GenghisKhan} @@ -1221,23 +1276,6 @@ func (h *HugoSites) resolveAndClearStateForIdentities( return err } - if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { - ll := l.WithField("substep", "gc dynacache") - - h.MemCache.ClearOnRebuild(changes...) - h.Log.Trace(logg.StringFunc(func() string { - var sb strings.Builder - sb.WriteString("dynacache keys:\n") - for _, key := range h.MemCache.Keys(nil) { - sb.WriteString(fmt.Sprintf(" %s\n", key)) - } - return sb.String() - })) - return ll, nil - }); err != nil { - return err - } - return nil } @@ -1265,14 +1303,13 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context, checkedCounter atomic.Int64 ) - resetPo := func(po *pageOutput, r identity.FinderResult) { - if po.pco != nil { + resetPo := func(po *pageOutput, rebuildContent bool, r identity.FinderResult) { + if rebuildContent && po.pco != nil { po.pco.Reset() // Will invalidate content cache. } po.renderState = 0 - po.p.resourcesPublishInit = &sync.Once{} - if r == identity.FinderFoundOneOfMany { + if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatus404HTMLFormat.Name { // Will force a re-render even in fast render mode. po.renderOnce = false } @@ -1284,19 +1321,21 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context, } // This can be a relativeley expensive operations, so we do it in parallel. - g := rungroup.Run[*pageState](ctx, rungroup.Config[*pageState]{ + g := rungroup.Run(ctx, rungroup.Config[*pageState]{ NumWorkers: h.numWorkers, Handle: func(ctx context.Context, p *pageState) error { if !p.isRenderedAny() { // This needs no reset, so no need to check it. return nil } + // First check the top level dependency manager. for _, id := range changes { checkedCounter.Add(1) if r := depsFinder.Contains(id, p.dependencyManager, 2); r > identity.FinderFoundOneOfManyRepetition { for _, po := range p.pageOutputs { - resetPo(po, r) + // Note that p.dependencyManager is used when rendering content, so reset that. + resetPo(po, true, r) } // Done. return nil @@ -1308,10 +1347,12 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context, if !po.isRendered() { continue } + for _, id := range changes { checkedCounter.Add(1) if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition { - resetPo(po, r) + // Note that dependencyManagerOutput is not used when rendering content, so don't reset that. + resetPo(po, false, r) continue OUTPUTS } } @@ -1369,7 +1410,7 @@ func (sa *sitePagesAssembler) applyAggregates() error { } // Handle cascades first to get any default dates set. - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] if keyPage == "" { // Home page gets it's cascade from the site config. cascade = sa.conf.Cascade.Config @@ -1379,9 +1420,9 @@ func (sa *sitePagesAssembler) applyAggregates() error { pw.WalkContext.Data().Insert(keyPage, cascade) } } else { - _, data := pw.WalkContext.Data().LongestPrefix(keyPage) + _, data := pw.WalkContext.Data().LongestPrefix(paths.Dir(keyPage)) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) } } @@ -1463,11 +1504,11 @@ func (sa *sitePagesAssembler) applyAggregates() error { pageResource := rs.r.(*pageState) relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo) pageResource.m.resourcePath = relPath - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // Apply cascade (if set) to the page. _, data := pw.WalkContext.Data().LongestPrefix(resourceKey) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) } if err := pageResource.setMetaPost(cascade); err != nil { return false, err @@ -1531,10 +1572,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error { const eventName = "dates" if p.Kind() == kinds.KindTerm { - var cascade map[page.PageMatcher]maps.Params + var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] _, data := pw.WalkContext.Data().LongestPrefix(s) if data != nil { - cascade = data.(map[page.PageMatcher]maps.Params) + cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) } if err := p.setMetaPost(cascade); err != nil { return false, err @@ -1593,12 +1634,18 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error { } func (sa *sitePagesAssembler) assembleTermsAndTranslations() error { + if sa.pageMap.cfg.taxonomyTermDisabled { + return nil + } + var ( pages = sa.pageMap.treePages entries = sa.pageMap.treeTaxonomyEntries views = sa.pageMap.cfg.taxonomyConfig.views ) + rebuild := sa.s.h.isRebuild() + lockType := doctree.LockTypeWrite w := &doctree.NodeShiftTreeWalker[contentNodeI]{ Tree: pages, @@ -1610,10 +1657,6 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error { return false, nil } - if sa.pageMap.cfg.taxonomyTermDisabled { - return false, nil - } - for _, viewName := range views { vals := types.ToStringSlicePreserveString(getParam(ps, viewName.plural, false)) if vals == nil { @@ -1635,6 +1678,14 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error { pi := sa.Site.Conf.PathParser().Parse(files.ComponentFolderContent, viewTermKey+"/_index.md") term := pages.Get(pi.Base()) if term == nil { + if rebuild { + // A new tag was added in server mode. + taxonomy := pages.Get(viewName.pluralTreeKey) + if taxonomy != nil { + sa.assembleChanges.Add(taxonomy.GetIdentity()) + } + } + m := &pageMeta{ term: v, singular: viewName.singular, @@ -1642,7 +1693,9 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error { pathInfo: pi, pageMetaParams: &pageMetaParams{ pageConfig: &pagemeta.PageConfig{ - Kind: kinds.KindTerm, + PageConfigEarly: pagemeta.PageConfigEarly{ + Kind: kinds.KindTerm, + }, }, }, } @@ -1672,6 +1725,7 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error { }) } } + return false, nil }, } @@ -1751,6 +1805,11 @@ func (sa *sitePagesAssembler) assembleResources() error { mt = rs.rc.ContentMediaType } + var filename string + if rs.fi != nil { + filename = rs.fi.Meta().Filename + } + rd := resources.ResourceSourceDescriptor{ OpenReadSeekCloser: rs.opener, Path: rs.path, @@ -1759,6 +1818,7 @@ func (sa *sitePagesAssembler) assembleResources() error { TargetBasePaths: targetBasePaths, BasePathRelPermalink: targetPaths.SubResourceBaseLink, BasePathTargetPath: baseTarget, + SourceFilenameOrPath: filename, NameNormalized: relPath, NameOriginal: relPathOriginal, MediaType: mt, @@ -1896,7 +1956,9 @@ func (sa *sitePagesAssembler) addStandalonePages() error { pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix), pageMetaParams: &pageMetaParams{ pageConfig: &pagemeta.PageConfig{ - Kind: kind, + PageConfigEarly: pagemeta.PageConfigEarly{ + Kind: kind, + }, }, }, standaloneOutputFormat: f, @@ -1907,7 +1969,7 @@ func (sa *sitePagesAssembler) addStandalonePages() error { tree.InsertIntoValuesDimension(key, p) } - addStandalone("/404", kinds.KindStatus404, output.HTTPStatusHTMLFormat) + addStandalone("/404", kinds.KindStatus404, output.HTTPStatus404HTMLFormat) if s.conf.EnableRobotsTXT { if m.i == 0 || s.Conf.IsMultihost() { @@ -2022,7 +2084,9 @@ func (sa *sitePagesAssembler) addMissingRootSections() error { pathInfo: p, pageMetaParams: &pageMetaParams{ pageConfig: &pagemeta.PageConfig{ - Kind: kinds.KindHome, + PageConfigEarly: pagemeta.PageConfigEarly{ + Kind: kinds.KindHome, + }, }, }, } @@ -2055,7 +2119,9 @@ func (sa *sitePagesAssembler) addMissingTaxonomies() error { pathInfo: sa.Conf.PathParser().Parse(files.ComponentFolderContent, key+"/_index.md"), pageMetaParams: &pageMetaParams{ pageConfig: &pagemeta.PageConfig{ - Kind: kinds.KindTaxonomy, + PageConfigEarly: pagemeta.PageConfigEarly{ + Kind: kinds.KindTaxonomy, + }, }, }, singular: viewName.singular, diff --git a/hugolib/content_map_test.go b/hugolib/content_map_test.go index bf9920071..f72862150 100644 --- a/hugolib/content_map_test.go +++ b/hugolib/content_map_test.go @@ -17,9 +17,11 @@ import ( "fmt" "path/filepath" "strings" + "sync" "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/identity" ) func TestContentMapSite(t *testing.T) { @@ -240,8 +242,13 @@ Data en } func TestBundleMultipleContentPageWithSamePath(t *testing.T) { + t.Parallel() + files := ` -- hugo.toml -- +printPathWarnings = true +-- layouts/all.html -- +All. -- content/bundle/index.md -- --- title: "Bundle md" @@ -271,14 +278,18 @@ Bundle: {{ $bundle.Title }}|{{ $bundle.Params.foo }}|{{ $bundle.File.Filename }} P1: {{ $p1.Title }}|{{ $p1.Params.foo }}|{{ $p1.File.Filename }}| ` - b := Test(t, files) + for range 3 { + b := Test(t, files, TestOptWarn()) - // There's multiple content files sharing the same logical path and language. - // This is a little arbitrary, but we have to pick one and prefer the Markdown version. - b.AssertFileContent("public/index.html", - filepath.FromSlash("Bundle: Bundle md|md|/content/bundle/index.md|"), - filepath.FromSlash("P1: P1 md|md|/content/p1.md|"), - ) + b.AssertLogContains("WARN Duplicate content path: \"/p1\"") + + // There's multiple content files sharing the same logical path and language. + // This is a little arbitrary, but we have to pick one and prefer the Markdown version. + b.AssertFileContent("public/index.html", + filepath.FromSlash("Bundle: Bundle md|md|/content/bundle/index.md|"), + filepath.FromSlash("P1: P1 md|md|/content/p1.md|"), + ) + } } // Issue #11944 @@ -321,7 +332,7 @@ R: {{ with $r }}{{ .Content }}{{ end }}|Len: {{ len $bundle.Resources }}|$ ` - for i := 0; i < 3; i++ { + for range 3 { b := Test(t, files) b.AssertFileContent("public/index.html", "R: Data 1.txt|", "Len: 1|") } @@ -359,6 +370,35 @@ p1-foo.txt b.AssertFileExists("public/s1/p1/index.html", true) } +// Issue 13228. +func TestBranchResourceOverlap(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- content/_index.md -- +--- +title: home +--- +-- content/s1/_index.md -- +--- +title: s1 +--- +-- content/s1x/a.txt -- +a.txt +-- layouts/index.html -- +Home. +{{ range .Resources.Match "**" }} + {{ .Name }}| +{{ end }} +` + + b := Test(t, files) + + b.AssertFileContent("public/index.html", "s1x/a.txt|") +} + func TestSitemapOverrideFilename(t *testing.T) { t.Parallel() @@ -396,3 +436,126 @@ irrelevant "https://example.org/en/sitemap.xml", ) } + +func TestContentTreeReverseIndex(t *testing.T) { + t.Parallel() + + c := qt.New(t) + + pageReverseIndex := newContentTreeTreverseIndex( + func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) { + for i := range 10 { + key := fmt.Sprint(i) + set(key, &testContentNode{key: key}) + } + }, + ) + + for i := range 10 { + key := fmt.Sprint(i) + v := pageReverseIndex.Get(key) + c.Assert(v, qt.Not(qt.IsNil)) + c.Assert(v.Path(), qt.Equals, key) + } +} + +// Issue 13019. +func TestContentTreeReverseIndexPara(t *testing.T) { + t.Parallel() + + var wg sync.WaitGroup + + for range 10 { + pageReverseIndex := newContentTreeTreverseIndex( + func(get func(key any) (contentNodeI, bool), set func(key any, val contentNodeI)) { + for i := range 10 { + key := fmt.Sprint(i) + set(key, &testContentNode{key: key}) + } + }, + ) + + for j := range 10 { + wg.Add(1) + go func(i int) { + defer wg.Done() + pageReverseIndex.Get(fmt.Sprint(i)) + }(j) + } + } +} + +type testContentNode struct { + key string +} + +func (n *testContentNode) GetIdentity() identity.Identity { + return identity.StringIdentity(n.key) +} + +func (n *testContentNode) ForEeachIdentity(cb func(id identity.Identity) bool) bool { + panic("not supported") +} + +func (n *testContentNode) Path() string { + return n.key +} + +func (n *testContentNode) isContentNodeBranch() bool { + return false +} + +func (n *testContentNode) resetBuildState() { +} + +func (n *testContentNode) MarkStale() { +} + +// Issue 12274. +func TestHTMLNotContent(t *testing.T) { + filesTemplate := ` +-- hugo.toml.temp -- +[contentTypes] +[contentTypes."text/markdown"] +# Empty for now. +-- hugo.yaml.temp -- +contentTypes: + text/markdown: {} +-- hugo.json.temp -- +{ + "contentTypes": { + "text/markdown": {} + } +} +-- content/p1/index.md -- +--- +title: p1 +--- +-- content/p1/a.html -- +

    a

    +-- content/p1/b.html -- +

    b

    +-- content/p1/c.html -- +

    c

    +-- layouts/_default/single.html -- +Path: {{ .Path }}|{{.Kind }} +|{{ (.Resources.Get "a.html").RelPermalink -}} +|{{ (.Resources.Get "b.html").RelPermalink -}} +|{{ (.Resources.Get "c.html").Publish }} +` + + for _, format := range []string{"toml", "yaml", "json"} { + format := format + t.Run(format, func(t *testing.T) { + t.Parallel() + + files := strings.Replace(filesTemplate, format+".temp", format, 1) + b := Test(t, files) + + b.AssertFileContent("public/p1/index.html", "|/p1/a.html|/p1/b.html|") + b.AssertFileContent("public/p1/a.html", "

    a

    ") + b.AssertFileContent("public/p1/b.html", "

    b

    ") + b.AssertFileContent("public/p1/c.html", "

    c

    ") + }) + } +} diff --git a/hugolib/content_render_hooks_test.go b/hugolib/content_render_hooks_test.go index 593d01da8..c4e15a5c6 100644 --- a/hugolib/content_render_hooks_test.go +++ b/hugolib/content_render_hooks_test.go @@ -17,6 +17,8 @@ import ( "fmt" "strings" "testing" + + qt "github.com/frankban/quicktest" ) func TestRenderHooksRSS(t *testing.T) { @@ -79,6 +81,64 @@ xml-heading: Heading in p2| `) } +// Issue 13242. +func TestRenderHooksRSSOnly(t *testing.T) { + files := ` +-- hugo.toml -- +baseURL = "https://example.org" +disableKinds = ["taxonomy", "term"] +-- layouts/index.html -- +{{ $p := site.GetPage "p1.md" }} +{{ $p2 := site.GetPage "p2.md" }} +P1: {{ $p.Content }} +P2: {{ $p2.Content }} +-- layouts/index.xml -- +{{ $p2 := site.GetPage "p2.md" }} +{{ $p3 := site.GetPage "p3.md" }} +P2: {{ $p2.Content }} +P3: {{ $p3.Content }} +-- layouts/_default/_markup/render-link.rss.xml -- +xml-link: {{ .Destination | safeURL }}| +-- layouts/_default/_markup/render-heading.rss.xml -- +xml-heading: {{ .Text }}| +-- content/p1.md -- +--- +title: "p1" +--- +P1. [I'm an inline-style link](https://www.gohugo.io) + +# Heading in p1 + +-- content/p2.md -- +--- +title: "p2" +--- +P2. [I'm an inline-style link](https://www.bep.is) + +# Heading in p2 + +-- content/p3.md -- +--- +title: "p3" +outputs: ["rss"] +--- +P3. [I'm an inline-style link](https://www.example.org) +` + b := Test(t, files) + + b.AssertFileContent("public/index.html", ` +P1:

    P1. I’m an inline-style link

    +

    Heading in p1

    +

    Heading in p2

    +`) + + b.AssertFileContent("public/index.xml", ` +P2:

    P2. xml-link: https://www.bep.is|

    +P3:

    P3. xml-link: https://www.example.org|

    +xml-heading: Heading in p2| +`) +} + // https://github.com/gohugoio/hugo/issues/6629 func TestRenderLinkWithMarkupInText(t *testing.T) { b := newTestSitesBuilder(t) @@ -90,7 +150,7 @@ baseURL="https://example.org" [markup.goldmark] [markup.goldmark.renderer] unsafe = true - + `) b.WithTemplates("index.html", ` @@ -223,16 +283,16 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== -- layouts/_default/single.html -- {{ .Title }}|{{ .Content }}|$ - + ` t.Run("Default multilingual", func(t *testing.T) { b := Test(t, files) b.AssertFileContent("public/nn/p1/index.html", - "p1|

    P2

    ", "\"Pixel\"") + "p1|

    P2

    ", "\"Pixel\"") b.AssertFileContent("public/en/p1/index.html", - "p1 en|

    P2

    ", "\"Pixel\"") + "p1 en|

    P2

    ", "\"Pixel\"") }) t.Run("Disabled", func(t *testing.T) { @@ -246,9 +306,10 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA func TestRenderHooksDefaultEscape(t *testing.T) { files := ` -- hugo.toml -- -[markup.goldmark.renderHooks] +[markup.goldmark.extensions.typographer] +disable = true [markup.goldmark.renderHooks.image] - enableDefault = ENABLE +enableDefault = ENABLE [markup.goldmark.renderHooks.link] enableDefault = ENABLE [markup.goldmark.parser] @@ -256,6 +317,7 @@ wrapStandAloneImageWithinParagraph = false [markup.goldmark.parser.attribute] block = true title = true + -- content/_index.md -- --- title: "Home" @@ -279,7 +341,7 @@ Image: ![alt-"<>&](/destination-"<> 'title-"<>&') if enabled { b.AssertFileContent("public/index.html", "Link: text-"<>&", - "img alt=\"alt-"<>&\" src=\"/destination-%22%3C%3E\" title=\"title-"<>&\">", + "img src=\"/destination-%22%3C%3E\" alt=\"alt-"<>&\" title=\"title-"<>&\">", "><script>", ) } else { @@ -291,3 +353,119 @@ Image: ![alt-"<>&](/destination-"<> 'title-"<>&') }) } } + +// Issue 13410. +func TestRenderHooksMultilineTitlePlainText(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +First line. +Second line. +---------------- +-- layouts/_default/_markup/render-heading.html -- +Plain text: {{ .PlainText }}|Text: {{ .Text }}| +-- layouts/_default/single.html -- +Content: {{ .Content}}| +} +` + b := Test(t, files) + + b.AssertFileContent("public/p1/index.html", + "Content: Plain text: First line.\nSecond line.|", + "|Text: First line.\nSecond line.||\n", + ) +} + +func TestContentOutputReuseRenderHooksAndShortcodesHTMLOnly(t *testing.T) { + files := ` +-- hugo.toml -- +-- layouts/index.html -- +HTML: {{ .Title }}|{{ .Content }}| +-- layouts/index.xml -- +XML: {{ .Title }}|{{ .Content }}| +-- layouts/_markup/render-heading.html -- +Render heading. +-- layouts/shortcodes/myshortcode.html -- +My shortcode. +-- content/_index.md -- +--- +title: "Home" +--- +## Heading + +{{< myshortcode >}} +` + b := Test(t, files) + + s := b.H.Sites[0] + b.Assert(s.home.pageOutputTemplateVariationsState.Load(), qt.Equals, uint32(1)) + b.AssertFileContent("public/index.html", "HTML: Home|Render heading.\nMy shortcode.\n|") + b.AssertFileContent("public/index.xml", "XML: Home|Render heading.\nMy shortcode.\n|") +} + +func TestContentOutputNoReuseRenderHooksInBothHTMLAnXML(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term"] +-- layouts/index.html -- +HTML: {{ .Title }}|{{ .Content }}| +-- layouts/index.xml -- +XML: {{ .Title }}|{{ .Content }}| +-- layouts/_markup/render-heading.html -- +Render heading. +-- layouts/_markup/render-heading.xml -- +Render heading XML. +-- content/_index.md -- +--- +title: "Home" +--- +## Heading + + +` + b := Test(t, files) + + s := b.H.Sites[0] + b.Assert(s.home.pageOutputTemplateVariationsState.Load() > 1, qt.IsTrue) + b.AssertFileContentExact("public/index.xml", "XML: Home|Render heading XML.|") + b.AssertFileContentExact("public/index.html", "HTML: Home|Render heading.|") +} + +func TestContentOutputNoReuseShortcodesInBothHTMLAnXML(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term"] +-- layouts/index.html -- +HTML: {{ .Title }}|{{ .Content }}| +-- layouts/index.xml -- +XML: {{ .Title }}|{{ .Content }}| +-- layouts/_markup/render-heading.html -- +Render heading. + +-- layouts/shortcodes/myshortcode.html -- +My shortcode HTML. +-- layouts/shortcodes/myshortcode.xml -- +My shortcode XML. +-- content/_index.md -- +--- +title: "Home" +--- +## Heading + +{{< myshortcode >}} + + +` + b := Test(t, files) + + b.AssertFileContentExact("public/index.xml", "My shortcode XML.") + b.AssertFileContentExact("public/index.html", "My shortcode HTML.") + s := b.H.Sites[0] + b.Assert(s.home.pageOutputTemplateVariationsState.Load() > 1, qt.IsTrue) +} diff --git a/hugolib/disableKinds_test.go b/hugolib/disableKinds_test.go index 8262f562a..4dc476fc9 100644 --- a/hugolib/disableKinds_test.go +++ b/hugolib/disableKinds_test.go @@ -44,14 +44,14 @@ tags: ["mytag"] `, "sect/no-list.md", ` --- title: No List -_build: +build: list: false --- `, "sect/no-render.md", ` --- title: No List -_build: +build: render: false --- `, @@ -59,14 +59,14 @@ _build: --- title: No Render Link aliases: ["/link-alias"] -_build: +build: render: link --- `, "sect/no-publishresources/index.md", ` --- title: No Publish Resources -_build: +build: publishResources: false --- @@ -81,7 +81,7 @@ headless: true --- title: Headless Local Lists cascade: - _build: + build: render: false list: local publishResources: false @@ -365,7 +365,7 @@ Data1: {{ $data1.RelPermalink }} `) b.WithContent("section/bundle-false/index.md", `---\ntitle: BundleFalse -_build: +build: publishResources: false ---`, "section/bundle-false/data1.json", "Some data1", @@ -388,7 +388,7 @@ func TestNoRenderAndNoPublishResources(t *testing.T) { noRenderPage := ` --- title: %s -_build: +build: render: false publishResources: false --- diff --git a/hugolib/doctree/nodeshiftree_test.go b/hugolib/doctree/nodeshiftree_test.go index ac89037ac..0f4fb6f68 100644 --- a/hugolib/doctree/nodeshiftree_test.go +++ b/hugolib/doctree/nodeshiftree_test.go @@ -193,7 +193,7 @@ func TestTreePara(t *testing.T) { }, ) - for i := 0; i < 8; i++ { + for i := range 8 { i := i r.Run(func() error { a := &testValue{ID: "/a"} @@ -289,7 +289,7 @@ func BenchmarkTreeInsert(b *testing.B) { }, ) - for i := 0; i < numElements; i++ { + for i := range numElements { lang := rand.Intn(2) tree.InsertIntoValuesDimension(fmt.Sprintf("/%d", i), &testValue{ID: fmt.Sprintf("/%d", i), Lang: lang, Weight: i, NoCopy: true}) } @@ -323,7 +323,7 @@ func BenchmarkWalk(b *testing.B) { }, ) - for i := 0; i < numElements; i++ { + for i := range numElements { lang := rand.Intn(2) tree.InsertIntoValuesDimension(fmt.Sprintf("/%d", i), &testValue{ID: fmt.Sprintf("/%d", i), Lang: lang, Weight: i, NoCopy: true}) } @@ -355,8 +355,8 @@ func BenchmarkWalk(b *testing.B) { base := createTree() b.ResetTimer() for i := 0; i < b.N; i++ { - for d1 := 0; d1 < 1; d1++ { - for d2 := 0; d2 < 2; d2++ { + for d1 := range 1 { + for d2 := range 2 { tree := base.Shape(d1, d2) w := &doctree.NodeShiftTreeWalker[*testValue]{ Tree: tree, diff --git a/hugolib/doctree/nodeshifttree.go b/hugolib/doctree/nodeshifttree.go index 36382c2d7..298b24d1b 100644 --- a/hugolib/doctree/nodeshifttree.go +++ b/hugolib/doctree/nodeshifttree.go @@ -59,7 +59,7 @@ type ( ) // NodeShiftTree is the root of a tree that can be shaped using the Shape method. -// Note that multipled shapes of the same tree is meant to be used concurrently, +// Note that multiplied shapes of the same tree is meant to be used concurrently, // so use the applicable locking when needed. type NodeShiftTree[T any] struct { tree *radix.Tree @@ -363,7 +363,7 @@ func (r *NodeShiftTreeWalker[T]) Walk(ctx context.Context) error { main := r.Tree var err error - fnMain := func(s string, v interface{}) bool { + fnMain := func(s string, v any) bool { if r.ShouldSkip(s) { return false } diff --git a/hugolib/doctree/simpletree.go b/hugolib/doctree/simpletree.go index 2193c08f6..b79ef3f2f 100644 --- a/hugolib/doctree/simpletree.go +++ b/hugolib/doctree/simpletree.go @@ -14,35 +14,46 @@ package doctree import ( + "iter" "sync" radix "github.com/armon/go-radix" ) -// Tree is a radix tree that holds T. +// Tree is a non thread safe radix tree that holds T. type Tree[T any] interface { + TreeCommon[T] + WalkPrefix(s string, f func(s string, v T) (bool, error)) error + WalkPath(s string, f func(s string, v T) (bool, error)) error + All() iter.Seq2[string, T] +} + +// TreeThreadSafe is a thread safe radix tree that holds T. +type TreeThreadSafe[T any] interface { + TreeCommon[T] + WalkPrefix(lockType LockType, s string, f func(s string, v T) (bool, error)) error + WalkPath(lockType LockType, s string, f func(s string, v T) (bool, error)) error + All(lockType LockType) iter.Seq2[string, T] +} + +type TreeCommon[T any] interface { Get(s string) T LongestPrefix(s string) (string, T) Insert(s string, v T) T - WalkPrefix(lockType LockType, s string, f func(s string, v T) (bool, error)) error } -// NewSimpleTree creates a new SimpleTree. -func NewSimpleTree[T comparable]() *SimpleTree[T] { +func NewSimpleTree[T any]() *SimpleTree[T] { return &SimpleTree[T]{tree: radix.New()} } -// SimpleTree is a thread safe radix tree that holds T. -type SimpleTree[T comparable] struct { - mu sync.RWMutex +// SimpleTree is a radix tree that holds T. +// This tree is not thread safe. +type SimpleTree[T any] struct { tree *radix.Tree zero T } func (tree *SimpleTree[T]) Get(s string) T { - tree.mu.RLock() - defer tree.mu.RUnlock() - if v, ok := tree.tree.Get(s); ok { return v.(T) } @@ -50,9 +61,6 @@ func (tree *SimpleTree[T]) Get(s string) T { } func (tree *SimpleTree[T]) LongestPrefix(s string) (string, T) { - tree.mu.RLock() - defer tree.mu.RUnlock() - if s, v, ok := tree.tree.LongestPrefix(s); ok { return s, v.(T) } @@ -60,17 +68,121 @@ func (tree *SimpleTree[T]) LongestPrefix(s string) (string, T) { } func (tree *SimpleTree[T]) Insert(s string, v T) T { + tree.tree.Insert(s, v) + return v +} + +func (tree *SimpleTree[T]) Walk(f func(s string, v T) (bool, error)) error { + var err error + tree.tree.Walk(func(s string, v any) bool { + var b bool + b, err = f(s, v.(T)) + if err != nil { + return true + } + return b + }) + return err +} + +func (tree *SimpleTree[T]) WalkPrefix(s string, f func(s string, v T) (bool, error)) error { + var err error + tree.tree.WalkPrefix(s, func(s string, v any) bool { + var b bool + b, err = f(s, v.(T)) + if err != nil { + return true + } + return b + }) + + return err +} + +func (tree *SimpleTree[T]) WalkPath(s string, f func(s string, v T) (bool, error)) error { + var err error + tree.tree.WalkPath(s, func(s string, v any) bool { + var b bool + b, err = f(s, v.(T)) + if err != nil { + return true + } + return b + }) + return err +} + +func (tree *SimpleTree[T]) All() iter.Seq2[string, T] { + return func(yield func(s string, v T) bool) { + tree.tree.Walk(func(s string, v any) bool { + return !yield(s, v.(T)) + }) + } +} + +// NewSimpleThreadSafeTree creates a new SimpleTree. +func NewSimpleThreadSafeTree[T any]() *SimpleThreadSafeTree[T] { + return &SimpleThreadSafeTree[T]{tree: radix.New(), mu: new(sync.RWMutex)} +} + +// SimpleThreadSafeTree is a thread safe radix tree that holds T. +type SimpleThreadSafeTree[T any] struct { + mu *sync.RWMutex + noLock bool + tree *radix.Tree + zero T +} + +var noopFunc = func() {} + +func (tree *SimpleThreadSafeTree[T]) readLock() func() { + if tree.noLock { + return noopFunc + } + tree.mu.RLock() + return tree.mu.RUnlock +} + +func (tree *SimpleThreadSafeTree[T]) writeLock() func() { + if tree.noLock { + return noopFunc + } tree.mu.Lock() - defer tree.mu.Unlock() + return tree.mu.Unlock +} + +func (tree *SimpleThreadSafeTree[T]) Get(s string) T { + unlock := tree.readLock() + defer unlock() + + if v, ok := tree.tree.Get(s); ok { + return v.(T) + } + return tree.zero +} + +func (tree *SimpleThreadSafeTree[T]) LongestPrefix(s string) (string, T) { + unlock := tree.readLock() + defer unlock() + + if s, v, ok := tree.tree.LongestPrefix(s); ok { + return s, v.(T) + } + return "", tree.zero +} + +func (tree *SimpleThreadSafeTree[T]) Insert(s string, v T) T { + unlock := tree.writeLock() + defer unlock() tree.tree.Insert(s, v) return v } -func (tree *SimpleTree[T]) Lock(lockType LockType) func() { +func (tree *SimpleThreadSafeTree[T]) Lock(lockType LockType) func() { switch lockType { case LockTypeNone: - return func() {} + return noopFunc case LockTypeRead: tree.mu.RLock() return tree.mu.RUnlock @@ -78,10 +190,16 @@ func (tree *SimpleTree[T]) Lock(lockType LockType) func() { tree.mu.Lock() return tree.mu.Unlock } - return func() {} + return noopFunc } -func (tree *SimpleTree[T]) WalkPrefix(lockType LockType, s string, f func(s string, v T) (bool, error)) error { +func (tree SimpleThreadSafeTree[T]) LockTree(lockType LockType) (TreeThreadSafe[T], func()) { + unlock := tree.Lock(lockType) + tree.noLock = true + return &tree, unlock // create a copy of tree with the noLock flag set to true. +} + +func (tree *SimpleThreadSafeTree[T]) WalkPrefix(lockType LockType, s string, f func(s string, v T) (bool, error)) error { commit := tree.Lock(lockType) defer commit() var err error @@ -96,3 +214,31 @@ func (tree *SimpleTree[T]) WalkPrefix(lockType LockType, s string, f func(s stri return err } + +func (tree *SimpleThreadSafeTree[T]) WalkPath(lockType LockType, s string, f func(s string, v T) (bool, error)) error { + commit := tree.Lock(lockType) + defer commit() + var err error + tree.tree.WalkPath(s, func(s string, v any) bool { + var b bool + b, err = f(s, v.(T)) + if err != nil { + return true + } + return b + }) + + return err +} + +func (tree *SimpleThreadSafeTree[T]) All(lockType LockType) iter.Seq2[string, T] { + commit := tree.Lock(lockType) + defer commit() + return func(yield func(s string, v T) bool) { + tree.tree.Walk(func(s string, v any) bool { + return !yield(s, v.(T)) + }) + } +} + +// iter.Seq[*TemplWithBaseApplied] diff --git a/hugolib/doctree/support.go b/hugolib/doctree/support.go index adcc3b06c..f1b713b31 100644 --- a/hugolib/doctree/support.go +++ b/hugolib/doctree/support.go @@ -17,8 +17,6 @@ import ( "fmt" "strings" "sync" - - radix "github.com/armon/go-radix" ) var _ MutableTrees = MutableTrees{} @@ -60,11 +58,9 @@ func (ctx *WalkContext[T]) AddPostHook(handler func() error) { ctx.HooksPost = append(ctx.HooksPost, handler) } -func (ctx *WalkContext[T]) Data() *SimpleTree[any] { +func (ctx *WalkContext[T]) Data() *SimpleThreadSafeTree[any] { ctx.dataInit.Do(func() { - ctx.data = &SimpleTree[any]{ - tree: radix.New(), - } + ctx.data = NewSimpleThreadSafeTree[any]() }) return ctx.data } @@ -191,7 +187,7 @@ func (t MutableTrees) CanLock() bool { // WalkContext is passed to the Walk callback. type WalkContext[T any] struct { - data *SimpleTree[any] + data *SimpleThreadSafeTree[any] dataInit sync.Once eventHandlers eventHandlers[T] events []*Event[T] diff --git a/hugolib/doctree/treeshifttree.go b/hugolib/doctree/treeshifttree.go index cd13b9f61..8f958d828 100644 --- a/hugolib/doctree/treeshifttree.go +++ b/hugolib/doctree/treeshifttree.go @@ -13,7 +13,9 @@ package doctree -var _ Tree[string] = (*TreeShiftTree[string])(nil) +import "iter" + +var _ TreeThreadSafe[string] = (*TreeShiftTree[string])(nil) type TreeShiftTree[T comparable] struct { // This tree is shiftable in one dimension. @@ -26,16 +28,16 @@ type TreeShiftTree[T comparable] struct { zero T // Will be of length equal to the length of the dimension. - trees []*SimpleTree[T] + trees []*SimpleThreadSafeTree[T] } func NewTreeShiftTree[T comparable](d, length int) *TreeShiftTree[T] { if length <= 0 { panic("length must be > 0") } - trees := make([]*SimpleTree[T], length) - for i := 0; i < length; i++ { - trees[i] = NewSimpleTree[T]() + trees := make([]*SimpleThreadSafeTree[T], length) + for i := range length { + trees[i] = NewSimpleThreadSafeTree[T]() } return &TreeShiftTree[T]{d: d, trees: trees} } @@ -91,6 +93,14 @@ func (t *TreeShiftTree[T]) WalkPrefixRaw(lockType LockType, s string, f func(s s return nil } +func (t *TreeShiftTree[T]) WalkPath(lockType LockType, s string, f func(s string, v T) (bool, error)) error { + return t.trees[t.v].WalkPath(lockType, s, f) +} + +func (t *TreeShiftTree[T]) All(lockType LockType) iter.Seq2[string, T] { + return t.trees[t.v].All(lockType) +} + func (t *TreeShiftTree[T]) LenRaw() int { var count int for _, tt := range t.trees { diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go deleted file mode 100644 index 198c31ae8..000000000 --- a/hugolib/embedded_shortcodes_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package hugolib - -import ( - "testing" - - "github.com/gohugoio/hugo/htesting" -) - -func TestEmbeddedShortcodes(t *testing.T) { - if !htesting.IsCI() { - t.Skip("skip on non-CI for now") - } - - t.Run("with theme", func(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -baseURL = "https://example.com" -disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "section"] -ignoreErrors = ["error-missing-instagram-accesstoken"] -[params] -foo = "bar" --- content/_index.md -- ---- -title: "Home" ---- - -## Figure - -{{< figure src="image.png" >}} - -## Gist - -{{< gist spf13 7896402 >}} - -## Highlight - -{{< highlight go >}} -package main -{{< /highlight >}} - -## Instagram - -{{< instagram BWNjjyYFxVx >}} - -## Tweet - -{{< tweet user="1626985695280603138" id="877500564405444608" >}} - -## Vimeo - -{{< vimeo 20097015 >}} - -## YouTube - -{{< youtube 0RKpf3rK57I >}} - -## Param - -Foo: {{< param foo >}} - --- layouts/index.html -- -Content: {{ .Content }}| -` - b := Test(t, files) - - b.AssertFileContent("public/index.html", ` -
    -https://gist.github.com/spf13/7896402.js -main -https://t.co/X94FmYDEZJ -https://www.youtube.com/embed/0RKpf3rK57I -Foo: bar - - - -`) - }) -} diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go index cb7846cd1..b32b8796f 100644 --- a/hugolib/filesystems/basefs.go +++ b/hugolib/filesystems/basefs.go @@ -397,7 +397,7 @@ func (d *SourceFilesystem) mounts() []hugofs.FileMetaInfo { }) // Filter out any mounts not belonging to this filesystem. - // TODO(bep) I think this is superflous. + // TODO(bep) I think this is superfluous. n := 0 for _, mm := range m { if mm.Meta().Component == d.Name { @@ -634,7 +634,7 @@ func (b *sourceFilesystemsBuilder) createMainOverlayFs(p *paths.Paths) (*filesys mounts := make([]mountsDescriptor, len(mods)) - for i := 0; i < len(mods); i++ { + for i := range mods { mod := mods[i] dir := mod.Dir() diff --git a/hugolib/filesystems/basefs_test.go b/hugolib/filesystems/basefs_test.go index e39709386..abe06ac4a 100644 --- a/hugolib/filesystems/basefs_test.go +++ b/hugolib/filesystems/basefs_test.go @@ -57,14 +57,14 @@ func TestNewBaseFs(t *testing.T) { filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme)) filenameOverlap := filepath.Join(base, "f3.txt") afs.Mkdir(base, 0o755) - content := []byte(fmt.Sprintf("content:%s:%s", theme, dir)) + content := fmt.Appendf(nil, "content:%s:%s", theme, dir) afero.WriteFile(afs, filenameTheme, content, 0o755) afero.WriteFile(afs, filenameOverlap, content, 0o755) } // Write some files to the root of the theme base := filepath.Join(workingDir, "themes", theme) - afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0o755) - afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0o755) + afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), fmt.Appendf(nil, "content:%s", theme), 0o755) + afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), fmt.Appendf(nil, "content:%s", theme), 0o755) } afero.WriteFile(afs, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0o755) @@ -220,6 +220,18 @@ target = 'content' source = 'content2' target = 'content/c2' [[module.mounts]] +source = 'content3' +target = 'content/watchdisabled' +disableWatch = true +[[module.mounts]] +source = 'content4' +target = 'content/excludedsome' +excludeFiles = 'p1.md' +[[module.mounts]] +source = 'content5' +target = 'content/excludedall' +excludeFiles = '/**' +[[module.mounts]] source = "hugo_stats.json" target = "assets/watching/hugo_stats.json" -- hugo_stats.json -- @@ -230,12 +242,27 @@ foo -- themes/t1/layouts/_default/single.html -- {{ .Content }} -- themes/t1/static/f1.txt -- +-- content3/p1.md -- +-- content4/p1.md -- +-- content4/p2.md -- +-- content5/p3.md -- +-- content5/p4.md -- ` b := hugolib.Test(t, files) bfs := b.H.BaseFs - watchFilenames := bfs.WatchFilenames() - // []string{"/hugo_stats.json", "/content", "/content2", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"} - b.Assert(watchFilenames, qt.HasLen, 6) + watchFilenames := toSlashes(bfs.WatchFilenames()) + + // content3 has disableWatch = true + // content5 has excludeFiles = '/**' + b.Assert(watchFilenames, qt.DeepEquals, []string{"/hugo_stats.json", "/content", "/content2", "/content4", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"}) +} + +func toSlashes(in []string) []string { + out := make([]string, len(in)) + for i, s := range in { + out[i] = filepath.ToSlash(s) + } + return out } func TestNoSymlinks(t *testing.T) { @@ -656,8 +683,8 @@ func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val strin workingDir := v.GetString("workingDir") v.Set(key, val) fs.Mkdir(val, 0o755) - for i := 0; i < num; i++ { + for i := range num { filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1)) - afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0o755) + afero.WriteFile(fs, filename, fmt.Appendf(nil, "content:%s:%d", key, i+1), 0o755) } } diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 659a772f2..0b68af2ec 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -85,7 +85,8 @@ type HugoSites struct { pageTrees *pageTrees - postRenderInit sync.Once + printUnusedTemplatesInit sync.Once + printPathWarningsInit sync.Once // File change events with filename stored in this map will be skipped. skipRebuildForFilenamesMu sync.Mutex @@ -111,6 +112,10 @@ func (h *HugoSites) ShouldSkipFileChangeEvent(ev fsnotify.Event) bool { return h.skipRebuildForFilenames[ev.Name] } +func (h *HugoSites) Close() error { + return h.Deps.Close() +} + func (h *HugoSites) isRebuild() bool { return h.buildCounter.Load() > 0 } @@ -129,7 +134,6 @@ func (h *HugoSites) resolveSite(lang string) *Site { return nil } -// Only used in tests. type buildCounters struct { contentRenderCounter atomic.Uint64 pageRenderCounter atomic.Uint64 @@ -179,9 +183,6 @@ type hugoSitesInit struct { // Loads the data from all of the /data folders. data *lazy.Init - // Performs late initialization (before render) of the templates. - layouts *lazy.Init - // Loads the Git info and CODEOWNERS for all the pages if enabled. gitInfo *lazy.Init } @@ -309,7 +310,7 @@ func (h *HugoSites) NumLogErrors() int { func (h *HugoSites) PrintProcessingStats(w io.Writer) { stats := make([]*helpers.ProcessingStats, len(h.Sites)) - for i := 0; i < len(h.Sites); i++ { + for i := range h.Sites { stats[i] = h.Sites[i].PathSpec.ProcessingStats } helpers.ProcessingStatsTable(w, stats...) @@ -415,8 +416,8 @@ type BuildCfg struct { // Set in server mode when the last build failed for some reason. ErrRecovery bool - // Recently visited URLs. This is used for partial re-rendering. - RecentlyVisited *types.EvictingStringQueue + // Recently visited or touched URLs. This is used for partial re-rendering. + RecentlyTouched *types.EvictingQueue[string] // Can be set to build only with a sub set of the content source. ContentInclusionFilter *glob.FilenameFilter @@ -428,7 +429,7 @@ type BuildCfg struct { } // shouldRender returns whether this output format should be rendered or not. -func (cfg *BuildCfg) shouldRender(p *pageState) bool { +func (cfg *BuildCfg) shouldRender(infol logg.LevelLogger, p *pageState) bool { if p.skipRender() { return false } @@ -456,18 +457,20 @@ func (cfg *BuildCfg) shouldRender(p *pageState) bool { return false } - if p.outputFormat().IsHTML { - // This is fast render mode and the output format is HTML, - // rerender if this page is one of the recently visited. - return cfg.RecentlyVisited.Contains(p.RelPermalink()) + if relURL := p.getRelURL(); relURL != "" { + if cfg.RecentlyTouched.Contains(relURL) { + infol.Logf("render recently touched URL %q (%s)", relURL, p.outputFormat().Name) + return true + } } // In fast render mode, we want to avoid re-rendering the sitemaps etc. and // other big listings whenever we e.g. change a content file, - // but we want partial renders of the recently visited pages to also include + // but we want partial renders of the recently touched pages to also include // alternative formats of the same HTML page (e.g. RSS, JSON). for _, po := range p.pageOutputs { - if po.render && po.f.IsHTML && cfg.RecentlyVisited.Contains(po.RelPermalink()) { + if po.render && po.f.IsHTML && cfg.RecentlyTouched.Contains(po.getRelURL()) { + infol.Logf("render recently touched URL %q, %s version of %s", po.getRelURL(), po.f.Name, p.outputFormat().Name) return true } } @@ -553,7 +556,6 @@ func (h *HugoSites) handleDataFile(r *source.File) error { higherPrecedentData := current[r.BaseFileName()] switch data.(type) { - case nil: case map[string]any: switch higherPrecedentData.(type) { diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 65ce946e9..ce8ddd143 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -170,20 +170,26 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error { h.SendError(fmt.Errorf("render: %w", err)) } - if err := h.postRenderOnce(); err != nil { - h.SendError(fmt.Errorf("postRenderOnce: %w", err)) - } - // Make sure to write any build stats to disk first so it's available // to the post processors. if err := h.writeBuildStats(); err != nil { return err } + // We need to do this before render deferred. + if err := h.printPathWarningsOnce(); err != nil { + h.SendError(fmt.Errorf("printPathWarnings: %w", err)) + } + if err := h.renderDeferred(infol); err != nil { h.SendError(fmt.Errorf("renderDeferred: %w", err)) } + // This needs to be done after the deferred rendering to get complete template usage coverage. + if err := h.printUnusedTemplatesOnce(); err != nil { + h.SendError(fmt.Errorf("printPathWarnings: %w", err)) + } + if err := h.postProcess(infol); err != nil { h.SendError(fmt.Errorf("postProcess: %w", err)) } @@ -250,10 +256,6 @@ func (h *HugoSites) process(ctx context.Context, l logg.LevelLogger, config *Bui l = l.WithField("step", "process") defer loggers.TimeTrackf(l, time.Now(), nil, "") - if _, err := h.init.layouts.Do(ctx); err != nil { - return err - } - if len(events) > 0 { // This is a rebuild triggered from file events. return h.processPartialFileEvents(ctx, l, config, init, events) @@ -322,6 +324,14 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil } } + // Handle new terms from assemblePagesStep2. + changes = bcfg.WhatChanged.Drain() + if len(changes) > 0 { + if err := h.resolveAndClearStateForIdentities(ctx, l, nil, changes); err != nil { + return err + } + } + h.renderFormats = output.Formats{} for _, s := range h.Sites { s.s.initRenderFormats() @@ -345,7 +355,23 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error { loggers.TimeTrackf(l, start, h.buildCounters.loggFields(), "") }() - siteRenderContext := &siteRenderContext{cfg: config, multihost: h.Configs.IsMultihost} + siteRenderContext := &siteRenderContext{cfg: config, infol: l, multihost: h.Configs.IsMultihost} + + renderErr := func(err error) error { + if err == nil { + return nil + } + // In Hugo 0.141.0 we replaced the special error handling for resources.GetRemote + // with the more general try. + if strings.Contains(err.Error(), "can't evaluate field Err in type") { + if strings.Contains(err.Error(), "resource.Resource") { + return fmt.Errorf("%s: Resource.Err was removed in Hugo v0.141.0 and replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/", err) + } else if strings.Contains(err.Error(), "template.HTML") { + return fmt.Errorf("%s: the return type of transform.ToMath was changed in Hugo v0.141.0 and the error handling replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/", err) + } + } + return err + } i := 0 for _, s := range h.Sites { @@ -394,7 +420,7 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error { } } else { if err := s.render(siteRenderContext); err != nil { - return err + return renderErr(err) } } loggers.TimeTrackf(ll, start, nil, "") @@ -476,17 +502,17 @@ func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error { defer deferred.Mu.Unlock() if !deferred.Executed { - tmpl := s.Deps.Tmpl() - templ, found := tmpl.Lookup(deferred.TemplateName) - if !found { - panic(fmt.Sprintf("template %q not found", deferred.TemplateName)) + tmpl := s.Deps.GetTemplateStore() + ti := s.TemplateStore.LookupByPath(deferred.TemplatePath) + if ti == nil { + panic(fmt.Sprintf("template %q not found", deferred.TemplatePath)) } if err := func() error { buf := bufferpool.GetBuffer() defer bufferpool.PutBuffer(buf) - err = tmpl.ExecuteWithContext(deferred.Ctx, templ, buf, deferred.Data) + err = tmpl.ExecuteWithContext(deferred.Ctx, ti, buf, deferred.Data) if err != nil { return err } @@ -500,6 +526,7 @@ func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error { } content = append(content[:low], append([]byte(deferred.Result), content[high:]...)...) + forward = len(deferred.Result) changed = true return nil @@ -524,16 +551,17 @@ func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error { }, }) - de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) { + de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) bool { g.Enqueue(filename) + return true }) return g.Wait() } -// / postRenderOnce runs some post processing that only needs to be done once, e.g. printing of unused templates. -func (h *HugoSites) postRenderOnce() error { - h.postRenderInit.Do(func() { +// printPathWarningsOnce prints path warnings if enabled. +func (h *HugoSites) printPathWarningsOnce() error { + h.printPathWarningsInit.Do(func() { conf := h.Configs.Base if conf.PrintPathWarnings { // We need to do this before any post processing, as that may write to the same files twice @@ -548,11 +576,22 @@ func (h *HugoSites) postRenderOnce() error { return false }) } + }) + return nil +} +// / printUnusedTemplatesOnce prints unused templates if enabled. +func (h *HugoSites) printUnusedTemplatesOnce() error { + h.printUnusedTemplatesInit.Do(func() { + conf := h.Configs.Base if conf.PrintUnusedTemplates { - unusedTemplates := h.Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates() + unusedTemplates := h.GetTemplateStore().UnusedTemplates() for _, unusedTemplate := range unusedTemplates { - h.Log.Warnf("Template %s is unused, source file %s", unusedTemplate.Name(), unusedTemplate.Filename()) + if unusedTemplate.Fi != nil { + h.Log.Warnf("Template %s is unused, source %q", unusedTemplate.PathInfo.Path(), unusedTemplate.Fi.Meta().Filename) + } else { + h.Log.Warnf("Template %s is unused", unusedTemplate.PathInfo.Path()) + } } } }) @@ -741,15 +780,15 @@ type pathChange struct { // The path to the changed file. p *paths.Path - // If true, this is a delete operation (a delete or a rename). - delete bool + // If true, this is a structural change (e.g. a delete or a rename). + structural bool // If true, this is a directory. isDir bool } func (p pathChange) isStructuralChange() bool { - return p.delete || p.isDir + return p.structural || p.isDir } func (h *HugoSites) processPartialRebuildChanges(ctx context.Context, l logg.LevelLogger, config *BuildCfg) error { @@ -813,6 +852,11 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo addedContentPaths []*paths.Path ) + var ( + addedOrChangedContent []pathChange + changes []identity.Identity + ) + for _, ev := range eventInfos { cpss := h.BaseFs.ResolvePaths(ev.Name) pss := make([]*paths.Path, len(cpss)) @@ -839,6 +883,13 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo if err == nil && g != nil { cacheBusters = append(cacheBusters, g) } + + if ev.added { + changes = append(changes, identity.StructuralChangeAdd) + } + if ev.removed { + changes = append(changes, identity.StructuralChangeRemove) + } } if ev.removed { @@ -850,11 +901,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } - var ( - addedOrChangedContent []pathChange - changes []identity.Identity - ) - // Find the most specific identity possible. handleChange := func(pathInfo *paths.Path, delete, isDir bool) { switch pathInfo.Component() { @@ -888,12 +934,12 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo needsPagesAssemble = true - if config.RecentlyVisited != nil { + if config.RecentlyTouched != nil { // Fast render mode. Adding them to the visited queue // avoids rerendering them on navigation. for _, id := range changes { if p, ok := id.(page.Page); ok { - config.RecentlyVisited.Add(p.RelPermalink()) + config.RecentlyTouched.Add(p.RelPermalink()) } } } @@ -915,12 +961,12 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } - addedOrChangedContent = append(addedOrChangedContent, pathChange{p: pathInfo, delete: delete, isDir: isDir}) + addedOrChangedContent = append(addedOrChangedContent, pathChange{p: pathInfo, structural: delete, isDir: isDir}) case files.ComponentFolderLayouts: tmplChanged = true templatePath := pathInfo.Unnormalized().TrimLeadingSlash().PathNoLang() - if !h.Tmpl().HasTemplate(templatePath) { + if !h.GetTemplateStore().HasTemplate(templatePath) { tmplAdded = true } @@ -940,8 +986,9 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } else { logger.Println("Template changed", pathInfo.Path()) - if templ, found := h.Tmpl().GetIdentity(templatePath); found { - changes = append(changes, templ) + id := h.GetTemplateStore().GetIdentity(pathInfo.Path()) + if id != nil { + changes = append(changes, id) } else { changes = append(changes, pathInfo) } @@ -1036,11 +1083,20 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo handleChange(id, false, true) } + for _, id := range changes { + if id == identity.GenghisKhan { + for i, cp := range addedOrChangedContent { + cp.structural = true + addedOrChangedContent[i] = cp + } + break + } + } + resourceFiles := h.fileEventsContentPaths(addedOrChangedContent) changed := &WhatChanged{ needsPagesAssembly: needsPagesAssemble, - identitySet: make(identity.Identities), } changed.Add(changes...) @@ -1062,17 +1118,39 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo } } + changes2 := changed.Changes() + h.Deps.OnChangeListeners.Notify(changes2...) + if err := h.resolveAndClearStateForIdentities(ctx, l, cacheBusterOr, changed.Drain()); err != nil { return err } - if tmplChanged || i18nChanged { - // TODO(bep) we should split this, but currently the loading of i18n and layout files are tied together. See #12048. - h.init.layouts.Reset() + if tmplChanged { if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { - // TODO(bep) this could probably be optimized to somehow - // only load the changed templates and its dependencies, but that is non-trivial. + depsFinder := identity.NewFinder(identity.FinderConfig{}) ll := l.WithField("substep", "rebuild templates") + s := h.Sites[0] + if err := s.Deps.TemplateStore.RefreshFiles(func(fi hugofs.FileMetaInfo) bool { + pi := fi.Meta().PathInfo + for _, id := range changes2 { + if depsFinder.Contains(pi, id, -1) > 0 { + return true + } + } + return false + }); err != nil { + return ll, err + } + + return ll, nil + }); err != nil { + return err + } + } + + if i18nChanged { + if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) { + ll := l.WithField("substep", "rebuild i18n") var prototype *deps.Deps for i, s := range h.Sites { if err := s.Deps.Compile(prototype); err != nil { @@ -1141,10 +1219,6 @@ func (s *Site) handleContentAdapterChanges(bi pagesfromdata.BuildInfo, buildConf } func (h *HugoSites) processContentAdaptersOnRebuild(ctx context.Context, buildConfig *BuildCfg) error { - // Make sure the layouts are initialized. - if _, err := h.init.layouts.Do(context.Background()); err != nil { - return err - } g := rungroup.Run[*pagesfromdata.PagesFromTemplate](ctx, rungroup.Config[*pagesfromdata.PagesFromTemplate]{ NumWorkers: h.numWorkers, Handle: func(ctx context.Context, p *pagesfromdata.PagesFromTemplate) error { diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index 71afe6767..1298d7f4f 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -554,8 +554,6 @@ toc line 3 toc line 4 - - ` t.Run("base template", func(t *testing.T) { @@ -569,7 +567,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/baseof.html:4:6"`)) + b.Assert(err.Error(), qt.Contains, `baseof.html:4:6`) }) t.Run("index template", func(t *testing.T) { @@ -583,7 +581,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:3:7"`)) + b.Assert(err.Error(), qt.Contains, `index.html:3:7"`) }) t.Run("partial from define", func(t *testing.T) { @@ -597,8 +595,7 @@ toc line 4 ).BuildE() b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`render of "home" failed: "/layouts/index.html:7:8": execute of template failed`)) - b.Assert(err.Error(), qt.Contains, `execute of template failed: template: partials/toc.html:2:8: executing "partials/toc.html"`) + b.Assert(err.Error(), qt.Contains, `toc.html:2:8"`) }) } diff --git a/hugolib/hugo_sites_multihost_test.go b/hugolib/hugo_sites_multihost_test.go index 39504202b..37f7ab927 100644 --- a/hugolib/hugo_sites_multihost_test.go +++ b/hugolib/hugo_sites_multihost_test.go @@ -205,9 +205,9 @@ title: mybundle-en b.AssertFileExists("public/de/mybundle/pixel.png", true) b.AssertFileExists("public/en/mybundle/pixel.png", true) - b.AssertFileExists("public/de/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/de/mybundle/pixel_hu_58204cbc58507d74.png", true) // failing test below - b.AssertFileExists("public/en/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/en/mybundle/pixel_hu_58204cbc58507d74.png", true) } func TestMultihostResourceOneBaseURLWithSuPath(t *testing.T) { diff --git a/hugolib/hugo_smoke_test.go b/hugolib/hugo_smoke_test.go index c43ba7293..09d57bbff 100644 --- a/hugolib/hugo_smoke_test.go +++ b/hugolib/hugo_smoke_test.go @@ -76,12 +76,13 @@ Single: {{ .Title }}|{{ .RelPermalink}}|{{ range .OutputFormats }}{{ .Name }}: { ` - b := Test(t, files) - - b.AssertFileContent("public/index.html", `List: |/|html: /|rss: /index.xml|$`) - b.AssertFileContent("public/index.xml", `List xml: |/|html: /|rss: /index.xml|$`) - b.AssertFileContent("public/p1/index.html", `Single: Page|/p1/|html: /p1/|$`) - b.AssertFileExists("public/p1/index.xml", false) + for i := 0; i < 2; i++ { + b := Test(t, files) + b.AssertFileContent("public/index.html", `List: |/|html: /|rss: /index.xml|$`) + b.AssertFileContent("public/index.xml", `List xml: |/|html: /|rss: /index.xml|$`) + b.AssertFileContent("public/p1/index.html", `Single: Page|/p1/|html: /p1/|$`) + b.AssertFileExists("public/p1/index.xml", false) + } } func TestSmoke(t *testing.T) { diff --git a/hugolib/image_test.go b/hugolib/image_test.go index 7dcd9fc26..09a5b841e 100644 --- a/hugolib/image_test.go +++ b/hugolib/image_test.go @@ -72,20 +72,20 @@ SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Ex b.Build(BuildCfg{}) - b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") - b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") - b.AssertImage(200, 200, "public/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "public/bundle/sunset_hu_77061c65c31d2244.jpg") // Check the file cache - b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu_77061c65c31d2244.jpg") - b.AssertFileContent("resources/_gen/images/bundle/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/bundle/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") - b.AssertFileContent("resources/_gen/images/images/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/images/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") b.AssertNoDuplicateWrites() diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index b45defb42..f28407fa1 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -2,6 +2,7 @@ package hugolib import ( "bytes" + "context" "encoding/base64" "errors" "fmt" @@ -24,6 +25,7 @@ import ( "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/config/security" @@ -32,6 +34,7 @@ import ( "github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/hugofs" "github.com/spf13/afero" + "github.com/spf13/cast" "golang.org/x/text/unicode/norm" "golang.org/x/tools/txtar" ) @@ -67,6 +70,13 @@ func TestOptDebug() TestOpt { } } +// TestOptInfo will enable info logging in integration tests. +func TestOptInfo() TestOpt { + return func(c *IntegrationTestConfig) { + c.LogLevel = logg.LevelInfo + } +} + // TestOptWarn will enable warn logging in integration tests. func TestOptWarn() TestOpt { return func(c *IntegrationTestConfig) { @@ -74,6 +84,13 @@ func TestOptWarn() TestOpt { } } +// TestOptOsFs will enable the real file system in integration tests. +func TestOptOsFs() TestOpt { + return func(c *IntegrationTestConfig) { + c.NeedsOsFS = true + } +} + // TestOptWithNFDOnDarwin will normalize the Unicode filenames to NFD on Darwin. func TestOptWithNFDOnDarwin() TestOpt { return func(c *IntegrationTestConfig) { @@ -81,6 +98,19 @@ func TestOptWithNFDOnDarwin() TestOpt { } } +// TestOptWithOSFs enables the real file system. +func TestOptWithOSFs() TestOpt { + return func(c *IntegrationTestConfig) { + c.NeedsOsFS = true + } +} + +func TestOptWithPrintAndKeepTempDir(b bool) TestOpt { + return func(c *IntegrationTestConfig) { + c.PrintAndKeepTempDir = b + } +} + // TestOptWithWorkingDir allows setting any config optiona as a function al option. func TestOptWithConfig(fn func(c *IntegrationTestConfig)) TestOpt { return func(c *IntegrationTestConfig) { @@ -189,19 +219,31 @@ type IntegrationTestBuilder struct { type lockingBuffer struct { sync.Mutex - bytes.Buffer + buf bytes.Buffer +} + +func (b *lockingBuffer) String() string { + b.Lock() + defer b.Unlock() + return b.buf.String() +} + +func (b *lockingBuffer) Reset() { + b.Lock() + defer b.Unlock() + b.buf.Reset() } func (b *lockingBuffer) ReadFrom(r io.Reader) (n int64, err error) { b.Lock() - n, err = b.Buffer.ReadFrom(r) + n, err = b.buf.ReadFrom(r) b.Unlock() return } func (b *lockingBuffer) Write(p []byte) (n int, err error) { b.Lock() - n, err = b.Buffer.Write(p) + n, err = b.buf.Write(p) b.Unlock() return } @@ -221,7 +263,7 @@ func (s *IntegrationTestBuilder) AssertLogContains(els ...string) { } } -// AssertLogNotContains asserts that the last build log does matches the given regular expressions. +// AssertLogMatches asserts that the last build log matches the given regular expressions. // The regular expressions can be negated with a "! " prefix. func (s *IntegrationTestBuilder) AssertLogMatches(expression string) { s.Helper() @@ -246,11 +288,6 @@ func (s *IntegrationTestBuilder) AssertBuildCountGitInfo(count int) { s.Assert(s.H.init.gitInfo.InitCount(), qt.Equals, count) } -func (s *IntegrationTestBuilder) AssertBuildCountLayouts(count int) { - s.Helper() - s.Assert(s.H.init.layouts.InitCount(), qt.Equals, count) -} - func (s *IntegrationTestBuilder) AssertFileCount(dirname string, expected int) { s.Helper() fs := s.fs.WorkingDirReadOnly @@ -280,8 +317,9 @@ func (s *IntegrationTestBuilder) negate(match string) (string, bool) { func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...string) { s.Helper() content := strings.TrimSpace(s.FileContent(filename)) + for _, m := range matches { - cm := qt.Commentf("File: %s Match %s", filename, m) + cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content) lines := strings.Split(m, "\n") for _, match := range lines { match = strings.TrimSpace(match) @@ -299,11 +337,28 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s } } +func (s *IntegrationTestBuilder) AssertFileContentEquals(filename string, match string) { + s.Helper() + content := s.FileContent(filename) + s.Assert(content, qt.Equals, match, qt.Commentf(match)) +} + func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches ...string) { s.Helper() content := s.FileContent(filename) for _, m := range matches { - s.Assert(content, qt.Contains, m, qt.Commentf(m)) + cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content) + s.Assert(content, qt.Contains, m, cm) + } +} + +func (s *IntegrationTestBuilder) AssertNoRenderShortcodesArtifacts() { + s.Helper() + for _, p := range s.H.Pages() { + content, err := p.Content(context.Background()) + s.Assert(err, qt.IsNil) + comment := qt.Commentf("Page: %s\n%s", p.Path(), content) + s.Assert(strings.Contains(cast.ToString(content), "__hugo_ctx"), qt.IsFalse, comment) } } @@ -430,6 +485,33 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder { return s } +func (s *IntegrationTestBuilder) BuildPartial(urls ...string) *IntegrationTestBuilder { + if _, err := s.BuildPartialE(urls...); err != nil { + s.Fatal(err) + } + return s +} + +func (s *IntegrationTestBuilder) BuildPartialE(urls ...string) (*IntegrationTestBuilder, error) { + if s.buildCount == 0 { + panic("BuildPartial can only be used after a full build") + } + if !s.Cfg.Running { + panic("BuildPartial can only be used in server mode") + } + visited := types.NewEvictingQueue[string](len(urls)) + for _, url := range urls { + visited.Add(url) + } + buildCfg := BuildCfg{RecentlyTouched: visited, PartialReRender: true} + return s, s.build(buildCfg) +} + +func (s *IntegrationTestBuilder) Close() { + s.Helper() + s.Assert(s.H.Close(), qt.IsNil) +} + func (s *IntegrationTestBuilder) LogString() string { return s.lastBuildLog } @@ -619,8 +701,8 @@ func (s *IntegrationTestBuilder) initBuilder() error { logger := loggers.New( loggers.Options{ - Stdout: w, - Stderr: w, + StdOut: w, + StdErr: w, Level: s.Cfg.LogLevel, DistinctLevel: logg.LevelWarn, }, @@ -644,7 +726,7 @@ func (s *IntegrationTestBuilder) initBuilder() error { s.Assert(err, qt.IsNil) - depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: logger.Level(), LogOut: logger.Out()} + depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: logger.Level(), StdErr: logger.StdErr()} sites, err := NewHugoSites(depsCfg) if err != nil { initErr = err @@ -665,7 +747,7 @@ func (s *IntegrationTestBuilder) initBuilder() error { sc := security.DefaultConfig sc.Exec.Allow, err = security.NewWhitelist("npm") s.Assert(err, qt.IsNil) - ex := hexec.New(sc, s.Cfg.WorkingDir) + ex := hexec.New(sc, s.Cfg.WorkingDir, loggers.NewDefault()) command, err := ex.New("npm", "install") s.Assert(err, qt.IsNil) s.Assert(command.Run(), qt.IsNil) @@ -706,10 +788,6 @@ func (s *IntegrationTestBuilder) build(cfg BuildCfg) error { s.counters = &buildCounters{} cfg.testCounters = s.counters - if s.buildCount > 0 && (len(changeEvents) == 0) { - return nil - } - s.buildCount++ err := s.H.Build(cfg, changeEvents...) @@ -840,6 +918,11 @@ type IntegrationTestConfig struct { // The files to use on txtar format, see // https://pkg.go.dev/golang.org/x/exp/cmd/txtar + // There are some contentions used in this test setup. + // - §§§ can be used to wrap code fences. + // - §§ can be used to wrap multiline strings. + // - filenames prefixed with sourcefilename: will be read from the file system relative to the current dir. + // - filenames with a .png or .jpg extension will be treated as binary and base64 decoded. TxtarString string // COnfig to use as the base. We will also read the config from the txtar. diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go index 304b4fbf4..3be999c31 100644 --- a/hugolib/menu_test.go +++ b/hugolib/menu_test.go @@ -105,94 +105,6 @@ Menu Main: {{ partial "menu.html" (dict "page" . "menu" "main") }}`, "/sect3/|Sect3s|Sect3s|0|-|-|") } -// related issue #7594 -func TestMenusSort(t *testing.T) { - b := newTestSitesBuilder(t).WithSimpleConfigFile() - - b.WithTemplatesAdded("index.html", ` -{{ range $k, $v := .Site.Menus.main }} -Default1|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -{{ range $k, $v := .Site.Menus.main.ByWeight }} -ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -{{ range $k, $v := (.Site.Menus.main.ByWeight).Reverse }} -Reverse|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -{{ range $k, $v := .Site.Menus.main }} -Default2|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -{{ range $k, $v := .Site.Menus.main.ByWeight }} -ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -{{ range $k, $v := .Site.Menus.main }} -Default3|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }} -`) - - b.WithContent("_index.md", ` ---- -title: Home -menu: - main: - weight: 100 ----`) - - b.WithContent("blog/A.md", ` ---- -title: "A" -menu: - main: - weight: 10 ---- -`) - - b.WithContent("blog/B.md", ` ---- -title: "B" -menu: - main: - weight: 20 ---- -`) - b.WithContent("blog/C.md", ` ---- -title: "C" -menu: - main: - weight: 30 ---- -`) - - b.Build(BuildCfg{}) - - b.AssertFileContent("public/index.html", - `Default1|0|10|A|/blog/a/|Page(/blog/a) - Default1|1|20|B|/blog/b/|Page(/blog/b) - Default1|2|30|C|/blog/c/|Page(/blog/c) - Default1|3|100|Home|/|Page(/) - - ByWeight|0|10|A|/blog/a/|Page(/blog/a) - ByWeight|1|20|B|/blog/b/|Page(/blog/b) - ByWeight|2|30|C|/blog/c/|Page(/blog/c) - ByWeight|3|100|Home|/|Page(/) - - Reverse|0|100|Home|/|Page(/) - Reverse|1|30|C|/blog/c/|Page(/blog/c) - Reverse|2|20|B|/blog/b/|Page(/blog/b) - Reverse|3|10|A|/blog/a/|Page(/blog/a) - - Default2|0|10|A|/blog/a/|Page(/blog/a) - Default2|1|20|B|/blog/b/|Page(/blog/b) - Default2|2|30|C|/blog/c/|Page(/blog/c) - Default2|3|100|Home|/|Page(/) - - ByWeight|0|10|A|/blog/a/|Page(/blog/a) - ByWeight|1|20|B|/blog/b/|Page(/blog/b) - ByWeight|2|30|C|/blog/c/|Page(/blog/c) - ByWeight|3|100|Home|/|Page(/) - - Default3|0|10|A|/blog/a/|Page(/blog/a) - Default3|1|20|B|/blog/b/|Page(/blog/b) - Default3|2|30|C|/blog/c/|Page(/blog/c) - Default3|3|100|Home|/|Page(/)`, - ) -} - func TestMenusFrontMatter(t *testing.T) { b := newTestSitesBuilder(t).WithSimpleConfigFile() @@ -340,9 +252,9 @@ menu: `) - b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|Children: + b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|Children: {{- $children := sort .Children ".Page.Date" "desc" }}{{ range $children }}{{ .Title }}|{{ end }}{{ end }} - + `) b.Build(BuildCfg{}) @@ -360,11 +272,11 @@ func TestMenuParamsEmptyYaml(t *testing.T) { b.WithContent("p1.md", `--- menus: - main: + main: identity: journal weight: 2 params: ---- +--- `) b.Build(BuildCfg{}) } @@ -377,9 +289,9 @@ title = "Contact Us" url = "mailto:noreply@example.com" weight = 300 [menus.main.params] -foo = "foo_config" -key2 = "key2_config" -camelCase = "camelCase_config" +foo = "foo_config" +key2 = "key2_config" +camelCase = "camelCase_config" `) b.WithTemplatesAdded("index.html", ` @@ -431,14 +343,14 @@ weight = 1 pageRef = "/blog/post3" title = "My Post 3" url = "/blog/post3" - + `) commonTempl := ` Main: {{ len .Site.Menus.main }} {{ range .Site.Menus.main }} -{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page }} -{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page }} +{{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page.Path }} +{{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page.Path }} {{ end }} ` @@ -494,34 +406,34 @@ title: "Contact: With No Menu Defined" b.AssertFileContent("public/index.html", ` Main: 5 -Home|HasMenuCurrent: false|Page: Page(/) -Blog|HasMenuCurrent: false|Page: Page(/blog) -My Post 2: With Menu Defined|HasMenuCurrent: false|Page: Page(/blog/post2) -My Post 3|HasMenuCurrent: false|Page: Page(/blog/post3) -Contact Us|HasMenuCurrent: false|Page: Page(/contact) +Home|HasMenuCurrent: false|Page: / +Blog|HasMenuCurrent: false|Page: /blog +My Post 2: With Menu Defined|HasMenuCurrent: false|Page: /blog/post2 +My Post 3|HasMenuCurrent: false|Page: /blog/post3 +Contact Us|HasMenuCurrent: false|Page: /contact `) b.AssertFileContent("public/blog/post1/index.html", ` -Home|HasMenuCurrent: false|Page: Page(/) -Blog|HasMenuCurrent: true|Page: Page(/blog) +Home|HasMenuCurrent: false|Page: / +Blog|HasMenuCurrent: true|Page: /blog `) b.AssertFileContent("public/blog/post2/index.html", ` -Home|HasMenuCurrent: false|Page: Page(/) -Blog|HasMenuCurrent: true|Page: Page(/blog) -Blog|IsMenuCurrent: false|Page: Page(/blog) +Home|HasMenuCurrent: false|Page: / +Blog|HasMenuCurrent: true|Page: /blog +Blog|IsMenuCurrent: false|Page: /blog `) b.AssertFileContent("public/blog/post3/index.html", ` -Home|HasMenuCurrent: false|Page: Page(/) -Blog|HasMenuCurrent: true|Page: Page(/blog) +Home|HasMenuCurrent: false|Page: / +Blog|HasMenuCurrent: true|Page: /blog `) b.AssertFileContent("public/contact/index.html", ` -Contact Us|HasMenuCurrent: false|Page: Page(/contact) -Contact Us|IsMenuCurrent: true|Page: Page(/contact) -Blog|HasMenuCurrent: false|Page: Page(/blog) -Blog|IsMenuCurrent: false|Page: Page(/blog) +Contact Us|HasMenuCurrent: false|Page: /contact +Contact Us|IsMenuCurrent: true|Page: /contact +Blog|HasMenuCurrent: false|Page: /blog +Blog|IsMenuCurrent: false|Page: /blog `) } @@ -607,7 +519,7 @@ Menu Item: {{ $i }}: {{ .Pre }}{{ .Name }}{{ .Post }}|{{ .URL }}| b := Test(t, files) b.AssertFileContent("public/index.html", ` -Menu Item: 0: Home|/| +Menu Item: 0: Home|/| `) } @@ -710,3 +622,79 @@ title: p3 b.AssertFileExists("public/index.html", true) b.AssertFileContent("public/index.html", `p2s1`) } + +// Issue 13161 +func TestMenuNameAndTitleFallback(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','sitemap','taxonomy','term'] +[[menus.main]] +name = 'P1_ME_Name' +title = 'P1_ME_Title' +pageRef = '/p1' +weight = 10 +[[menus.main]] +pageRef = '/p2' +weight = 20 +[[menus.main]] +pageRef = '/p3' +weight = 30 +[[menus.main]] +name = 'S1_ME_Name' +title = 'S1_ME_Title' +pageRef = '/s1' +weight = 40 +[[menus.main]] +pageRef = '/s2' +weight = 50 +[[menus.main]] +pageRef = '/s3' +weight = 60 +-- content/p1.md -- +--- +title: P1_Title +--- +-- content/p2.md -- +--- +title: P2_Title +--- +-- content/p3.md -- +--- +title: P3_Title +linkTitle: P3_LinkTitle +--- +-- content/s1/_index.md -- +--- +title: S1_Title +--- +-- content/s2/_index.md -- +--- +title: S2_Title +--- +-- content/s3/_index.md -- +--- +title: S3_Title +linkTitle: S3_LinkTitle +--- +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/list.html -- +{{ .Content }} +-- layouts/_default/home.html -- +{{- range site.Menus.main }} +URL: {{ .URL }}| Name: {{ .Name }}| Title: {{ .Title }}| PageRef: {{ .PageRef }}| Page.Title: {{ .Page.Title }}| Page.LinkTitle: {{ .Page.LinkTitle }}| +{{- end }} +` + + b := Test(t, files) + b.AssertFileContent("public/index.html", + `URL: /p1/| Name: P1_ME_Name| Title: P1_ME_Title| PageRef: /p1| Page.Title: P1_Title| Page.LinkTitle: P1_Title|`, + `URL: /p2/| Name: P2_Title| Title: P2_Title| PageRef: /p2| Page.Title: P2_Title| Page.LinkTitle: P2_Title|`, + `URL: /p3/| Name: P3_LinkTitle| Title: P3_Title| PageRef: /p3| Page.Title: P3_Title| Page.LinkTitle: P3_LinkTitle|`, + `URL: /s1/| Name: S1_ME_Name| Title: S1_ME_Title| PageRef: /s1| Page.Title: S1_Title| Page.LinkTitle: S1_Title|`, + `URL: /s2/| Name: S2_Title| Title: S2_Title| PageRef: /s2| Page.Title: S2_Title| Page.LinkTitle: S2_Title|`, + `URL: /s3/| Name: S3_LinkTitle| Title: S3_Title| PageRef: /s3| Page.Title: S3_Title| Page.LinkTitle: S3_LinkTitle|`, + ) +} diff --git a/hugolib/page.go b/hugolib/page.go index 7525ab672..bb3835c1e 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -16,8 +16,9 @@ package hugolib import ( "context" "fmt" + "path/filepath" "strconv" - "sync" + "strings" "sync/atomic" "github.com/gohugoio/hugo/hugofs" @@ -26,17 +27,15 @@ import ( "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/output" - "github.com/gohugoio/hugo/output/layouts" "github.com/gohugoio/hugo/related" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/spf13/afero" "github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/tableofcontents" - "github.com/gohugoio/hugo/tpl" - "github.com/gohugoio/hugo/common/herrors" - "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/source" @@ -111,8 +110,15 @@ type pageState struct { *pageCommon resource.Staler - dependencyManager identity.Manager - resourcesPublishInit *sync.Once + dependencyManager identity.Manager +} + +func (p *pageState) incrPageOutputTemplateVariation() { + p.pageOutputTemplateVariationsState.Add(1) +} + +func (p *pageState) canReusePageOutputContent() bool { + return p.pageOutputTemplateVariationsState.Load() == 1 } func (p *pageState) IdentifierBase() string { @@ -142,16 +148,30 @@ func (p *pageState) GetDependencyManagerForScope(scope int) identity.Manager { } } +func (p *pageState) GetDependencyManagerForScopesAll() []identity.Manager { + return []identity.Manager{p.dependencyManager, p.dependencyManagerOutput} +} + func (p *pageState) Key() string { return "page-" + strconv.FormatUint(p.pid, 10) } -func (p *pageState) resetBuildState() { - p.Scratcher = maps.NewScratcher() +// RelatedKeywords implements the related.Document interface needed for fast page searches. +func (p *pageState) RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error) { + v, found, err := page.NamedPageMetaValue(p, cfg.Name) + if err != nil { + return nil, err + } + + if !found { + return nil, nil + } + + return cfg.ToKeywords(v) } -func (p *pageState) reusePageOutputContent() bool { - return p.pageOutputTemplateVariationsState.Load() == 1 +func (p *pageState) resetBuildState() { + // Nothing to do for now. } func (p *pageState) skipRender() bool { @@ -180,10 +200,6 @@ func (p *pageState) isContentNodeBranch() bool { return p.IsNode() } -func (p *pageState) Err() resource.ResourceError { - return nil -} - // Eq returns whether the current page equals the given page. // This is what's invoked when doing `{{ if eq $page $otherPage }}` func (p *pageState) Eq(other any) bool { @@ -358,7 +374,22 @@ func (p *pageState) Site() page.Site { } func (p *pageState) String() string { - return fmt.Sprintf("Page(%s)", p.Path()) + var sb strings.Builder + if p.File() != nil { + // The forward slashes even on Windows is motivated by + // getting stable tests. + // This information is meant for getting positional information in logs, + // so the direction of the slashes should not matter. + sb.WriteString(filepath.ToSlash(p.File().Filename())) + if p.File().IsContentAdapter() { + // Also include the path. + sb.WriteString(":") + sb.WriteString(p.Path()) + } + } else { + sb.WriteString(p.Path()) + } + return sb.String() } // IsTranslated returns whether this content file is translated to @@ -444,49 +475,41 @@ func (ps *pageState) initCommonProviders(pp pagePaths) error { return nil } -func (p *pageState) getLayoutDescriptor() layouts.LayoutDescriptor { - p.layoutDescriptorInit.Do(func() { - var section string - sections := p.SectionsEntries() - - switch p.Kind() { - case kinds.KindSection: - if len(sections) > 0 { - section = sections[0] - } - case kinds.KindTaxonomy, kinds.KindTerm: - - if p.m.singular != "" { - section = p.m.singular - } else if len(sections) > 0 { - section = sections[0] - } - default: - } - - p.layoutDescriptor = layouts.LayoutDescriptor{ - Kind: p.Kind(), - Type: p.Type(), - Lang: p.Language().Lang, - Layout: p.Layout(), - Section: section, - } - }) - - return p.layoutDescriptor +// Exported so it can be used in integration tests. +func (po *pageOutput) GetInternalTemplateBasePathAndDescriptor() (string, tplimpl.TemplateDescriptor) { + p := po.p + f := po.f + base := p.PathInfo().BaseReTyped(p.m.pageConfig.Type) + return base, tplimpl.TemplateDescriptor{ + Kind: p.Kind(), + Lang: p.Language().Lang, + LayoutFromUser: p.Layout(), + OutputFormat: f.Name, + MediaType: f.MediaType.Type, + IsPlainText: f.IsPlainText, + } } -func (p *pageState) resolveTemplate(layouts ...string) (tpl.Template, bool, error) { - f := p.outputFormat() - - d := p.getLayoutDescriptor() +func (p *pageState) resolveTemplate(layouts ...string) (*tplimpl.TemplInfo, bool, error) { + dir, d := p.GetInternalTemplateBasePathAndDescriptor() if len(layouts) > 0 { - d.Layout = layouts[0] - d.LayoutOverride = true + d.LayoutFromUser = layouts[0] + d.LayoutFromUserMustMatch = true } - return p.s.Tmpl().LookupLayout(d, f) + q := tplimpl.TemplateQuery{ + Path: dir, + Category: tplimpl.CategoryLayout, + Desc: d, + } + + tinfo := p.s.TemplateStore.LookupPagesLayout(q) + if tinfo == nil { + return nil, false, nil + } + + return tinfo, true, nil } // Must be run after the site section tree etc. is built and ready. @@ -498,39 +521,35 @@ func (p *pageState) initPage() error { } func (p *pageState) renderResources() error { - var initErr error + for _, r := range p.Resources() { - p.resourcesPublishInit.Do(func() { - for _, r := range p.Resources() { - if _, ok := r.(page.Page); ok { + if _, ok := r.(page.Page); ok { + if p.s.h.buildCounter.Load() == 0 { // Pages gets rendered with the owning page but we count them here. p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Pages) - continue - } - - if _, isWrapper := r.(resource.ResourceWrapper); isWrapper { - // Skip resources that are wrapped. - // These gets published on its own. - continue - } - - src, ok := r.(resource.Source) - if !ok { - initErr = fmt.Errorf("resource %T does not support resource.Source", r) - return - } - - if err := src.Publish(); err != nil { - if !herrors.IsNotExist(err) { - p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err) - } - } else { - p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files) } + continue } - }) - return initErr + if resources.IsPublished(r) { + continue + } + + src, ok := r.(resource.Source) + if !ok { + return fmt.Errorf("resource %T does not support resource.Source", r) + } + + if err := src.Publish(); err != nil { + if !herrors.IsNotExist(err) { + p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err) + } + } else { + p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files) + } + } + + return nil } func (p *pageState) AlternativeOutputFormats() page.OutputFormats { @@ -675,9 +694,9 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error { if isRenderingSite { cp := p.pageOutput.pco - if cp == nil && p.reusePageOutputContent() { + if cp == nil && p.canReusePageOutputContent() { // Look for content to reuse. - for i := 0; i < len(p.pageOutputs); i++ { + for i := range p.pageOutputs { if i == idx { continue } diff --git a/hugolib/page__common.go b/hugolib/page__common.go index d3b0bd112..f6f01bbe2 100644 --- a/hugolib/page__common.go +++ b/hugolib/page__common.go @@ -21,7 +21,6 @@ import ( "github.com/gohugoio/hugo/lazy" "github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/navigation" - "github.com/gohugoio/hugo/output/layouts" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/source" @@ -56,9 +55,7 @@ type pageCommon struct { store *maps.Scratch // All of these represents the common parts of a page.Page - maps.Scratcher navigation.PageMenusProvider - page.AuthorProvider page.AlternativeOutputFormatsProvider page.ChildCareProvider page.FileProvider @@ -70,7 +67,6 @@ type pageCommon struct { page.PageMetaInternalProvider page.Positioner page.RawContentProvider - page.RelatedKeywordsProvider page.RefProvider page.ShortcodeInfoProvider page.SitesProvider @@ -89,9 +85,6 @@ type pageCommon struct { // should look like. targetPathDescriptor page.TargetPathDescriptor - layoutDescriptor layouts.LayoutDescriptor - layoutDescriptorInit sync.Once - // Set if feature enabled and this is in a Git repo. gitInfo source.GitInfo codeowners []string @@ -104,7 +97,7 @@ type pageCommon struct { pageMenus *pageMenus // Internal use - page.InternalDependencies + page.RelatedDocsHandlerProvider contentConverterInit sync.Once contentConverter converter.Converter @@ -113,3 +106,8 @@ type pageCommon struct { func (p *pageCommon) Store() *maps.Scratch { return p.store } + +// See issue 13016. +func (p *pageCommon) Scratch() *maps.Scratch { + return p.Store() +} diff --git a/hugolib/page__content.go b/hugolib/page__content.go index 4ec91f7b5..20abb7884 100644 --- a/hugolib/page__content.go +++ b/hugolib/page__content.go @@ -24,6 +24,8 @@ import ( "strings" "unicode/utf8" + maps0 "maps" + "github.com/bep/logg" "github.com/gohugoio/hugo/common/hcontext" "github.com/gohugoio/hugo/common/herrors" @@ -32,7 +34,6 @@ import ( "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types/hstring" "github.com/gohugoio/hugo/helpers" - "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/markup" "github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/goldmark/hugocontext" @@ -599,7 +600,7 @@ func (c *cachedContentScope) contentRendered(ctx context.Context) (contentSummar return nil, err } if hasShortcodeVariants { - cp.po.p.pageOutputTemplateVariationsState.Add(1) + cp.po.p.incrPageOutputTemplateVariation() } var result contentSummary @@ -666,7 +667,13 @@ func (c *cachedContentScope) mustContentToC(ctx context.Context) contentTableOfC return ct } -var setGetContentCallbackInContext = hcontext.NewContextDispatcher[func(*pageContentOutput, contentTableOfContents)]("contentCallback") +type contextKey uint8 + +const ( + contextKeyContentCallback contextKey = iota +) + +var setGetContentCallbackInContext = hcontext.NewContextDispatcher[func(*pageContentOutput, contentTableOfContents)](contextKeyContentCallback) func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfContents, error) { cp := c.pco @@ -683,10 +690,9 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont if err := cp.initRenderHooks(); err != nil { return nil, err } - f := cp.po.f po := cp.po p := po.p - ct.contentPlaceholders, err = c.shortcodeState.prepareShortcodesForPage(ctx, p, f, false) + ct.contentPlaceholders, err = c.shortcodeState.prepareShortcodesForPage(ctx, po, false) if err != nil { return nil, err } @@ -696,22 +702,18 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont cp.otherOutputs.Set(cp2.po.p.pid, cp2) // Merge content placeholders - for k, v := range ct2.contentPlaceholders { - ct.contentPlaceholders[k] = v - } + maps0.Copy(ct.contentPlaceholders, ct2.contentPlaceholders) if p.s.conf.Internal.Watch { for _, s := range cp2.po.p.m.content.shortcodeState.shortcodes { - for _, templ := range s.templs { - cp.trackDependency(templ.(identity.IdentityProvider)) - } + cp.trackDependency(s.templ) } } // Transfer shortcode names so HasShortcode works for shortcodes from included pages. cp.po.p.m.content.shortcodeState.transferNames(cp2.po.p.m.content.shortcodeState) if cp2.po.p.pageOutputTemplateVariationsState.Load() > 0 { - cp.po.p.pageOutputTemplateVariationsState.Add(1) + cp.po.p.incrPageOutputTemplateVariation() } } @@ -724,22 +726,21 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont } if hasVariants { - p.pageOutputTemplateVariationsState.Add(1) + p.incrPageOutputTemplateVariation() } isHTML := cp.po.p.m.pageConfig.ContentMediaType.IsHTML() if !isHTML { - createAndSetToC := func(tocProvider converter.TableOfContentsProvider) { + createAndSetToC := func(tocProvider converter.TableOfContentsProvider) error { cfg := p.s.ContentSpec.Converters.GetMarkupConfig() ct.tableOfContents = tocProvider.TableOfContents() - ct.tableOfContentsHTML = template.HTML( - ct.tableOfContents.ToHTML( - cfg.TableOfContents.StartLevel, - cfg.TableOfContents.EndLevel, - cfg.TableOfContents.Ordered, - ), + ct.tableOfContentsHTML, err = ct.tableOfContents.ToHTML( + cfg.TableOfContents.StartLevel, + cfg.TableOfContents.EndLevel, + cfg.TableOfContents.Ordered, ) + return err } // If the converter supports doing the parsing separately, we do that. @@ -849,7 +850,7 @@ func (c *cachedContentScope) contentPlain(ctx context.Context) (contentPlainPlai }) if err != nil { if herrors.IsTimeoutError(err) { - err = fmt.Errorf("timed out rendering the page content. You may have a circular loop in a shortcode, or your site may have resources that take longer to build than the `timeout` limit in your Hugo config file: %w", err) + err = fmt.Errorf("timed out rendering the page content. Extend the `timeout` limit in your Hugo config file: %w", err) } return contentPlainPlainWords{}, err } @@ -863,6 +864,10 @@ type cachedContentScope struct { } func (c *cachedContentScope) prepareContext(ctx context.Context) context.Context { + // A regular page's shortcode etc. may be rendered by e.g. the home page, + // so we need to track any changes to this content's page. + ctx = tpl.Context.DependencyManagerScopedProvider.Set(ctx, c.pco.po.p) + // The markup scope is recursive, so if already set to a non zero value, preserve that value. if s := hugo.GetMarkupScope(ctx); s != "" || s == c.scope { return ctx @@ -978,7 +983,7 @@ func (c *cachedContentScope) RenderString(ctx context.Context, args ...any) (tem return "", err } - placeholders, err := s.prepareShortcodesForPage(ctx, pco.po.p, pco.po.f, true) + placeholders, err := s.prepareShortcodesForPage(ctx, pco.po, true) if err != nil { return "", err } @@ -988,7 +993,7 @@ func (c *cachedContentScope) RenderString(ctx context.Context, args ...any) (tem return "", err } if hasVariants { - pco.po.p.pageOutputTemplateVariationsState.Add(1) + pco.po.p.incrPageOutputTemplateVariation() } b, err := pco.renderContentWithConverter(ctx, conv, contentToRender, false) if err != nil { @@ -1026,7 +1031,7 @@ func (c *cachedContentScope) RenderString(ctx context.Context, args ...any) (tem return "", err } if hasShortcodeVariants { - pco.po.p.pageOutputTemplateVariationsState.Add(1) + pco.po.p.incrPageOutputTemplateVariation() } } @@ -1108,7 +1113,7 @@ func (c *cachedContentScope) RenderShortcodes(ctx context.Context) (template.HTM } if hasVariants { - pco.po.p.pageOutputTemplateVariationsState.Add(1) + pco.po.p.incrPageOutputTemplateVariation() } if cb != nil { diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 97a716b43..1af489f18 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -21,16 +21,14 @@ import ( "strings" "time" + "github.com/bep/logg" "github.com/gobuffalo/flect" "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/markup/converter" xmaps "golang.org/x/exp/maps" - "github.com/gohugoio/hugo/related" - "github.com/gohugoio/hugo/source" - "github.com/gohugoio/hugo/common/constants" "github.com/gohugoio/hugo/common/hashing" "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/loggers" @@ -55,7 +53,6 @@ type pageMeta struct { resource.Staler *pageMetaParams - pageMetaFrontMatter // Set for standalone pages, e.g. robotsTXT. standaloneOutputFormat output.Format @@ -73,11 +70,8 @@ type pageMeta struct { // Prepare for a rebuild of the data passed in from front matter. func (m *pageMeta) setMetaPostPrepareRebuild() { - params := xmaps.Clone[map[string]any](m.paramsOriginal) - m.pageMetaParams.pageConfig = &pagemeta.PageConfig{ - Params: params, - } - m.pageMetaFrontMatter = pageMetaFrontMatter{} + params := xmaps.Clone(m.paramsOriginal) + m.pageMetaParams.pageConfig = pagemeta.ClonePageConfigForRebuild(m.pageMetaParams.pageConfig, params) } type pageMetaParams struct { @@ -88,19 +82,18 @@ type pageMetaParams struct { // These are only set in watch mode. datesOriginal pagemeta.Dates - paramsOriginal map[string]any // contains the original params as defined in the front matter. - cascadeOriginal map[page.PageMatcher]maps.Params // contains the original cascade as defined in the front matter. + paramsOriginal map[string]any // contains the original params as defined in the front matter. + cascadeOriginal *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // contains the original cascade as defined in the front matter. } -// From page front matter. -type pageMetaFrontMatter struct { - configuredOutputFormats output.Formats // outputs defined in front matter. -} - -func (m *pageMetaParams) init(preserveOringal bool) { - if preserveOringal { - m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params) - m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.pageConfig.CascadeCompiled) +func (m *pageMetaParams) init(preserveOriginal bool) { + if preserveOriginal { + if m.pageConfig.IsFromContentAdapter { + m.paramsOriginal = xmaps.Clone(m.pageConfig.ContentAdapterData) + } else { + m.paramsOriginal = xmaps.Clone(m.pageConfig.Params) + } + m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone() } } @@ -108,28 +101,11 @@ func (p *pageMeta) Aliases() []string { return p.pageConfig.Aliases } -// Deprecated: Use taxonomies instead. -func (p *pageMeta) Author() page.Author { - hugo.Deprecate(".Page.Author", "Use taxonomies instead.", "v0.98.0") - authors := p.Authors() - - for _, author := range authors { - return author - } - return page.Author{} -} - -// Deprecated: Use taxonomies instead. -func (p *pageMeta) Authors() page.AuthorList { - hugo.Deprecate(".Page.Authors", "Use taxonomies instead.", "v0.112.0") - return nil -} - func (p *pageMeta) BundleType() string { - switch p.pathInfo.BundleType() { - case paths.PathTypeLeaf: + switch p.pathInfo.Type() { + case paths.TypeLeaf: return "leaf" - case paths.PathTypeBranch: + case paths.TypeBranch: return "branch" default: return "" @@ -231,16 +207,6 @@ func (p *pageMeta) PathInfo() *paths.Path { return p.pathInfo } -// RelatedKeywords implements the related.Document interface needed for fast page searches. -func (p *pageMeta) RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error) { - v, err := p.Param(cfg.Name) - if err != nil { - return nil, err - } - - return cfg.ToKeywords(v) -} - func (p *pageMeta) IsSection() bool { return p.Kind() == kinds.KindSection } @@ -286,7 +252,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf // Check for any cascade define on itself. if cv, found := frontmatter["cascade"]; found { var err error - cascade, err := page.DecodeCascade(logger, cv) + cascade, err := page.DecodeCascade(logger, true, cv) if err != nil { return err } @@ -324,31 +290,37 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf return nil } -func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error { +func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) error { ps.m.setMetaPostCount++ var cascadeHashPre uint64 if ps.m.setMetaPostCount > 1 { cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled) - ps.m.pageConfig.CascadeCompiled = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal) + ps.m.pageConfig.CascadeCompiled = ps.m.cascadeOriginal.Clone() } // Apply cascades first so they can be overridden later. if cascade != nil { if ps.m.pageConfig.CascadeCompiled != nil { - for k, v := range cascade { - vv, found := ps.m.pageConfig.CascadeCompiled[k] + cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool { + vv, found := ps.m.pageConfig.CascadeCompiled.Get(k) if !found { - ps.m.pageConfig.CascadeCompiled[k] = v + ps.m.pageConfig.CascadeCompiled.Set(k, v) } else { // Merge - for ck, cv := range v { - if _, found := vv[ck]; !found { - vv[ck] = cv + for ck, cv := range v.Params { + if _, found := vv.Params[ck]; !found { + vv.Params[ck] = cv + } + } + for ck, cv := range v.Fields { + if _, found := vv.Fields[ck]; !found { + vv.Fields[ck] = cv } } } - } + return true + }) cascade = ps.m.pageConfig.CascadeCompiled } else { ps.m.pageConfig.CascadeCompiled = cascade @@ -372,15 +344,33 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error } // Cascade is also applied to itself. - for m, v := range cascade { - if !m.Matches(ps) { - continue + var err error + cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool { + if !k.Matches(ps) { + return true } - for kk, vv := range v { + for kk, vv := range v.Params { if _, found := ps.m.pageConfig.Params[kk]; !found { ps.m.pageConfig.Params[kk] = vv } } + + for kk, vv := range v.Fields { + if ps.m.pageConfig.IsFromContentAdapter { + if _, found := ps.m.pageConfig.ContentAdapterData[kk]; !found { + ps.m.pageConfig.ContentAdapterData[kk] = vv + } + } else { + if _, found := ps.m.pageConfig.Params[kk]; !found { + ps.m.pageConfig.Params[kk] = vv + } + } + } + return true + }) + + if err != nil { + return err } if err := ps.setMetaPostParams(); err != nil { @@ -428,6 +418,12 @@ func (p *pageState) setMetaPostParams() error { PathOrTitle: p.pathOrTitle(), } + if isContentAdapter { + if err := pm.pageConfig.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil { + return err + } + } + // Handle the date separately // TODO(bep) we need to "do more" in this area so this can be split up and // more easily tested without the Page, but the coupling is strong. @@ -444,6 +440,7 @@ func (p *pageState) setMetaPostParams() error { var buildConfig any var isNewBuildKeyword bool if v, ok := pm.pageConfig.Params["_build"]; ok { + hugo.Deprecate("The \"_build\" front matter key", "Use \"build\" instead. See https://gohugo.io/content-management/build-options.", "0.145.0") buildConfig = v } else { buildConfig = pm.pageConfig.Params["build"] @@ -502,6 +499,11 @@ params: continue } + if loki == "path" || loki == "kind" || loki == "lang" { + // See issue 12484. + hugo.DeprecateLevelMin(loki+" in front matter", "", "v0.144.0", logg.LevelWarn) + } + switch loki { case "title": pcfg.Title = cast.ToString(v) @@ -548,16 +550,7 @@ params: for i, s := range o { o[i] = strings.ToLower(s) } - if len(o) > 0 { - // Output formats are explicitly set in front matter, use those. - outFormats, err := p.s.conf.OutputFormats.Config.GetByNames(o...) - if err != nil { - p.s.Log.Errorf("Failed to resolve output formats: %s", err) - } else { - pm.configuredOutputFormats = outFormats - params[loki] = outFormats - } - } + pm.pageConfig.Outputs = o case "draft": draft = new(bool) *draft = cast.ToBool(v) @@ -653,9 +646,6 @@ params: } for k, v := range userParams { - if _, found := params[k]; found { - p.s.Log.Warnidf(constants.WarnFrontMatterParamsOverrides, "Hugo front matter key %q is overridden in params section.", k) - } params[strings.ToLower(k)] = v } @@ -689,7 +679,7 @@ params: return err } - if err := pcfg.Compile("", false, ext, p.s.Log, p.s.conf.MediaTypes.Config); err != nil { + if err := pcfg.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil { return err } @@ -846,8 +836,8 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter. // The output formats this page will be rendered to. func (m *pageMeta) outputFormats() output.Formats { - if len(m.configuredOutputFormats) > 0 { - return m.configuredOutputFormats + if len(m.pageConfig.ConfiguredOutputFormats) > 0 { + return m.pageConfig.ConfiguredOutputFormats } return m.s.conf.C.KindOutputFormats[m.Kind()] } diff --git a/hugolib/page__new.go b/hugolib/page__new.go index b7d9b10f2..80115cc72 100644 --- a/hugolib/page__new.go +++ b/hugolib/page__new.go @@ -15,12 +15,13 @@ package hugolib import ( "fmt" - "sync" + "strings" "sync/atomic" "github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/common/constants" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/paths" @@ -34,6 +35,23 @@ import ( var pageIDCounter atomic.Uint64 func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { + p, pth, err := h.doNewPage(m) + if err != nil { + // Make sure that any partially created page part is marked as stale. + m.MarkStale() + } + + if p != nil && pth != nil && p.IsHome() && pth.IsLeafBundle() { + msg := "Using %s in your content's root directory is usually incorrect for your home page. " + msg += "You should use %s instead. If you don't rename this file, your home page will be " + msg += "treated as a leaf bundle, meaning it won't be able to have any child pages or sections." + h.Log.Warnidf(constants.WarnHomePageIsLeafBundle, msg, pth.PathNoLeadingSlash(), strings.ReplaceAll(pth.PathNoLeadingSlash(), "index", "_index")) + } + + return p, pth, err +} + +func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) { m.Staler = &resources.AtomicStaler{} if m.pageMetaParams == nil { m.pageMetaParams = &pageMetaParams{ @@ -131,6 +149,7 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { } } + var tc viewName // Identify Page Kind. if m.pageConfig.Kind == "" { m.pageConfig.Kind = kinds.KindSection @@ -138,16 +157,13 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { m.pageConfig.Kind = kinds.KindHome } else if m.pathInfo.IsBranchBundle() { // A section, taxonomy or term. - tc := m.s.pageMap.cfg.getTaxonomyConfig(m.Path()) + tc = m.s.pageMap.cfg.getTaxonomyConfig(m.Path()) if !tc.IsZero() { // Either a taxonomy or a term. if tc.pluralTreeKey == m.Path() { m.pageConfig.Kind = kinds.KindTaxonomy - m.singular = tc.singular } else { m.pageConfig.Kind = kinds.KindTerm - m.term = m.pathInfo.Unnormalized().BaseNameNoIdentifier() - m.singular = tc.singular } } } else if m.f != nil { @@ -155,6 +171,19 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { } } + if m.pageConfig.Kind == kinds.KindTerm || m.pageConfig.Kind == kinds.KindTaxonomy { + if tc.IsZero() { + tc = m.s.pageMap.cfg.getTaxonomyConfig(m.Path()) + } + if tc.IsZero() { + return nil, fmt.Errorf("no taxonomy configuration found for %q", m.Path()) + } + m.singular = tc.singular + if m.pageConfig.Kind == kinds.KindTerm { + m.term = paths.TrimLeading(strings.TrimPrefix(m.pathInfo.Unnormalized().Base(), tc.pluralTreeKey)) + } + } + if m.pageConfig.Kind == kinds.KindPage && !m.s.conf.IsKindEnabled(m.pageConfig.Kind) { return nil, nil } @@ -169,13 +198,10 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { pid: pid, pageOutput: nopPageOutput, pageOutputTemplateVariationsState: &atomic.Uint32{}, - resourcesPublishInit: &sync.Once{}, Staler: m, dependencyManager: m.s.Conf.NewIdentityManager(m.Path()), pageCommon: &pageCommon{ FileProvider: m, - AuthorProvider: m, - Scratcher: maps.NewScratcher(), store: maps.NewScratch(), Positioner: page.NopPage, InSectionPositioner: page.NopPage, @@ -183,7 +209,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { ResourceParamsProvider: m, PageMetaProvider: m, PageMetaInternalProvider: m, - RelatedKeywordsProvider: m, OutputFormatsProvider: page.NopPage, ResourceTypeProvider: pageTypesProvider, MediaTypeProvider: pageTypesProvider, @@ -191,11 +216,11 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { ShortcodeInfoProvider: page.NopPage, LanguageProvider: m.s, - InternalDependencies: m.s, - init: lazy.New(), - m: m, - s: m.s, - sWrapped: page.WrapSite(m.s), + RelatedDocsHandlerProvider: m.s, + init: lazy.New(), + m: m, + s: m.s, + sWrapped: page.WrapSite(m.s), }, } @@ -231,10 +256,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) { } return ps, nil }() - // Make sure to evict any cached and now stale data. - if err != nil { - m.MarkStale() - } if ps == nil { return nil, nil, err diff --git a/hugolib/page__paths.go b/hugolib/page__paths.go index 6324b5871..62206cb15 100644 --- a/hugolib/page__paths.go +++ b/hugolib/page__paths.go @@ -71,11 +71,12 @@ func newPagePaths(ps *pageState) (pagePaths, error) { // Use the main format for permalinks, usually HTML. permalinksIndex := 0 if f.Permalinkable { - // Unless it's permalinkable + // Unless it's permalinkable. permalinksIndex = i } targets[f.Name] = targetPathsHolder{ + relURL: relPermalink, paths: paths, OutputFormat: pageOutputFormats[permalinksIndex], } diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index 6ebddbe44..1f7f3411e 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -19,23 +19,21 @@ import ( "errors" "fmt" "html/template" - "strings" "sync" "sync/atomic" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/text" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/spf13/cast" "github.com/gohugoio/hugo/markup/converter/hooks" - "github.com/gohugoio/hugo/markup/highlight/chromalexers" "github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/markup/converter" bp "github.com/gohugoio/hugo/bufferpool" - "github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/resources/page" @@ -120,7 +118,7 @@ func (pco *pageContentOutput) Render(ctx context.Context, layout ...string) (tem } // Make sure to send the *pageState and not the *pageContentOutput to the template. - res, err := executeToString(ctx, pco.po.p.s.Tmpl(), templ, pco.po.p) + res, err := executeToString(ctx, pco.po.p.s.GetTemplateStore(), templ, pco.po.p) if err != nil { return "", pco.po.p.wrapError(fmt.Errorf("failed to execute template %s: %w", templ.Name(), err)) } @@ -274,99 +272,102 @@ func (pco *pageContentOutput) initRenderHooks() error { return r } - layoutDescriptor := pco.po.p.getLayoutDescriptor() - layoutDescriptor.RenderingHook = true - layoutDescriptor.LayoutOverride = false - layoutDescriptor.Layout = "" + // Inherit the descriptor from the page/current output format. + // This allows for fine-grained control of the template used for + // rendering of e.g. links. + base, layoutDescriptor := pco.po.p.GetInternalTemplateBasePathAndDescriptor() switch tp { case hooks.LinkRendererType: - layoutDescriptor.Kind = "render-link" + layoutDescriptor.Variant1 = "link" case hooks.ImageRendererType: - layoutDescriptor.Kind = "render-image" + layoutDescriptor.Variant1 = "image" case hooks.HeadingRendererType: - layoutDescriptor.Kind = "render-heading" + layoutDescriptor.Variant1 = "heading" case hooks.PassthroughRendererType: - layoutDescriptor.Kind = "render-passthrough" + layoutDescriptor.Variant1 = "passthrough" if id != nil { - layoutDescriptor.KindVariants = id.(string) + layoutDescriptor.Variant2 = id.(string) } case hooks.BlockquoteRendererType: - layoutDescriptor.Kind = "render-blockquote" + layoutDescriptor.Variant1 = "blockquote" if id != nil { - layoutDescriptor.KindVariants = id.(string) + layoutDescriptor.Variant2 = id.(string) } case hooks.TableRendererType: - layoutDescriptor.Kind = "render-table" + layoutDescriptor.Variant1 = "table" case hooks.CodeBlockRendererType: - layoutDescriptor.Kind = "render-codeblock" + layoutDescriptor.Variant1 = "codeblock" if id != nil { - lang := id.(string) - lexer := chromalexers.Get(lang) - if lexer != nil { - layoutDescriptor.KindVariants = strings.Join(lexer.Config().Aliases, ",") - } else { - layoutDescriptor.KindVariants = lang - } + layoutDescriptor.Variant2 = id.(string) } } - getHookTemplate := func(f output.Format) (tpl.Template, bool) { - templ, found, err := pco.po.p.s.Tmpl().LookupLayout(layoutDescriptor, f) - if err != nil { - panic(err) - } - if found { - if isitp, ok := templ.(tpl.IsInternalTemplateProvider); ok && isitp.IsInternalTemplate() { - renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks - switch templ.Name() { - case "_default/_markup/render-link.html": - if !renderHookConfig.Link.IsEnableDefault() { - return nil, false - } - case "_default/_markup/render-image.html": - if !renderHookConfig.Image.IsEnableDefault() { - return nil, false - } - } - } - } - return templ, found + renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks + var ignoreInternal bool + switch layoutDescriptor.Variant1 { + case "link": + ignoreInternal = !renderHookConfig.Link.IsEnableDefault() + case "image": + ignoreInternal = !renderHookConfig.Image.IsEnableDefault() } - templ, found1 := getHookTemplate(pco.po.f) - - if !found1 || pco.po.p.reusePageOutputContent() { - // Some hooks may only be available in HTML, and if - // this site is configured to not have HTML output, we need to - // make sure we have a fallback. This should be very rare. - candidates := pco.po.p.s.renderFormats - if pco.po.f.MediaType.FirstSuffix.Suffix != "html" { - if _, found := candidates.GetBySuffix("html"); !found { - candidates = append(candidates, output.HTMLFormat) - } + candidates := pco.po.p.s.renderFormats + var numCandidatesFound int + consider := func(candidate *tplimpl.TemplInfo) bool { + if layoutDescriptor.Variant1 != candidate.D.Variant1 { + return false } - // Check if some of the other output formats would give a different template. - for _, f := range candidates { - if f.Name == pco.po.f.Name { - continue - } - templ2, found2 := getHookTemplate(f) - if found2 { - if !found1 { - templ = templ2 - found1 = true - break - } - - if templ != templ2 { - pco.po.p.pageOutputTemplateVariationsState.Add(1) - break - } - } + if layoutDescriptor.Variant2 != "" && candidate.D.Variant2 != "" && layoutDescriptor.Variant2 != candidate.D.Variant2 { + return false } + + if ignoreInternal && candidate.SubCategory() == tplimpl.SubCategoryEmbedded { + // Don't consider the internal hook templates. + return false + } + + if pco.po.p.pageOutputTemplateVariationsState.Load() > 1 { + return true + } + + if candidate.D.OutputFormat == "" { + numCandidatesFound++ + } else if _, found := candidates.GetByName(candidate.D.OutputFormat); found { + numCandidatesFound++ + } + + return true } + + getHookTemplate := func() (*tplimpl.TemplInfo, bool) { + q := tplimpl.TemplateQuery{ + Path: base, + Category: tplimpl.CategoryMarkup, + Desc: layoutDescriptor, + Consider: consider, + } + + v := pco.po.p.s.TemplateStore.LookupPagesLayout(q) + return v, v != nil + } + + templ, found1 := getHookTemplate() + if found1 && templ == nil { + panic("found1 is true, but templ is nil") + } + + if !found1 && layoutDescriptor.OutputFormat == pco.po.p.s.conf.DefaultOutputFormat { + numCandidatesFound++ + } + + if numCandidatesFound > 1 { + // More than one output format candidate found for this hook temoplate, + // so we cannot reuse the same rendered content. + pco.po.p.incrPageOutputTemplateVariation() + } + if !found1 { if tp == hooks.CodeBlockRendererType { // No user provided template for code blocks, so we use the native Go version -- which is also faster. @@ -378,7 +379,7 @@ func (pco *pageContentOutput) initRenderHooks() error { } r := hookRendererTemplate{ - templateHandler: pco.po.p.s.Tmpl(), + templateHandler: pco.po.p.s.GetTemplateStore(), templ: templ, resolvePosition: resolvePosition, } @@ -463,18 +464,26 @@ type pagePerOutputProviders interface { type targetPather interface { targetPaths() page.TargetPaths + getRelURL() string } type targetPathsHolder struct { - paths page.TargetPaths + // relURL is usually the same as OutputFormat.RelPermalink, but can be different + // for non-permalinkable output formats. These shares RelPermalink with the main (first) output format. + relURL string + paths page.TargetPaths page.OutputFormat } +func (t targetPathsHolder) getRelURL() string { + return t.relURL +} + func (t targetPathsHolder) targetPaths() page.TargetPaths { return t.paths } -func executeToString(ctx context.Context, h tpl.TemplateHandler, templ tpl.Template, data any) (string, error) { +func executeToString(ctx context.Context, h *tplimpl.TemplateStore, templ *tplimpl.TemplInfo, data any) (string, error) { b := bp.GetBuffer() defer bp.PutBuffer(b) if err := h.ExecuteWithContext(ctx, templ, b, data); err != nil { diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 429ab2659..1da67e58f 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1688,6 +1688,32 @@ title: Scratch Me! b.AssertFileContent("public/scratchme/index.html", "C: cv") } +// Issue 13016. +func TestScratchAliasToStore(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "page", "section"] +disableLiveReload = true +-- layouts/index.html -- +{{ .Scratch.Set "a" "b" }} +{{ .Store.Set "c" "d" }} +.Scratch eq .Store: {{ eq .Scratch .Store }} +a: {{ .Store.Get "a" }} +c: {{ .Scratch.Get "c" }} + +` + + b := Test(t, files) + + b.AssertFileContent("public/index.html", + ".Scratch eq .Store: true", + "a: b", + "c: d", + ) +} + func TestPageParam(t *testing.T) { t.Parallel() @@ -1867,3 +1893,110 @@ func TestRenderWithoutArgument(t *testing.T) { b.Assert(err, qt.IsNotNil) } + +// Issue #13021 +func TestAllStores(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "page", "section"] +disableLiveReload = true +-- content/_index.md -- +--- +title: "Home" +--- +{{< s >}} +-- layouts/shortcodes/s.html -- +{{ if not (.Store.Get "Shortcode") }}{{ .Store.Set "Shortcode" (printf "sh-%s" $.Page.Title) }}{{ end }} +Shortcode: {{ .Store.Get "Shortcode" }}| +-- layouts/index.html -- +{{ .Content }} +{{ if not (.Store.Get "Page") }}{{ .Store.Set "Page" (printf "p-%s" $.Title) }}{{ end }} +{{ if not (hugo.Store.Get "Hugo") }}{{ hugo.Store.Set "Hugo" (printf "h-%s" $.Title) }}{{ end }} +{{ if not (site.Store.Get "Site") }}{{ site.Store.Set "Site" (printf "s-%s" $.Title) }}{{ end }} +Page: {{ .Store.Get "Page" }}| +Hugo: {{ hugo.Store.Get "Hugo" }}| +Site: {{ site.Store.Get "Site" }}| +` + + b := TestRunning(t, files) + + b.AssertFileContent("public/index.html", + ` +Shortcode: sh-Home| +Page: p-Home| +Site: s-Home| +Hugo: h-Home| +`, + ) + + b.EditFileReplaceAll("content/_index.md", "Home", "Homer").Build() + + b.AssertFileContent("public/index.html", + ` +Shortcode: sh-Homer| +Page: p-Homer| +Site: s-Home| +Hugo: h-Home| +`, + ) +} + +// See #12484 +func TestPageFrontMatterDeprecatePathKindLang(t *testing.T) { + // This cannot be parallel as it depends on output from the global logger. + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "home", "section"] +-- content/p1.md -- +--- +title: "p1" +kind: "page" +lang: "en" +path: "mypath" +--- +-- layouts/_default/single.html -- +Title: {{ .Title }} +` + b := Test(t, files, TestOptWarn()) + b.AssertFileContent("public/mypath/index.html", "p1") + b.AssertLogContains( + "deprecated: kind in front matter was deprecated", + "deprecated: lang in front matter was deprecated", + "deprecated: path in front matter was deprecated", + ) +} + +// Issue 13538 +func TestHomePageIsLeafBundle(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +defaultContentLanguage = 'de' +defaultContentLanguageInSubdir = true +[languages.de] +weight = 1 +[languages.en] +weight = 2 +-- layouts/all.html -- +{{ .Title }} +-- content/index.de.md -- +--- +title: home de +--- +-- content/index.en.org -- +--- +title: home en +--- +` + + b := Test(t, files, TestOptWarn()) + + b.AssertFileContent("public/de/index.html", "home de") + b.AssertFileContent("public/en/index.html", "home en") + b.AssertLogContains("Using index.de.md in your content's root directory is usually incorrect for your home page. You should use _index.de.md instead.") + b.AssertLogContains("Using index.en.org in your content's root directory is usually incorrect for your home page. You should use _index.en.org instead.") +} diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go index e4da75a72..e5521412b 100644 --- a/hugolib/pagebundler_test.go +++ b/hugolib/pagebundler_test.go @@ -690,7 +690,7 @@ bundle min min key: {{ $jsonMinMin.Key }} `) - for i := 0; i < 3; i++ { + for range 3 { b.Build(BuildCfg{}) diff --git a/hugolib/pagecollections_test.go b/hugolib/pagecollections_test.go index 692ae9ef6..10c973b7e 100644 --- a/hugolib/pagecollections_test.go +++ b/hugolib/pagecollections_test.go @@ -47,8 +47,8 @@ func BenchmarkGetPage(b *testing.B) { b.Fatal(err) } - for i := 0; i < 10; i++ { - for j := 0; j < 100; j++ { + for i := range 10 { + for j := range 100 { writeSource(b, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), "CONTENT") } } @@ -91,8 +91,8 @@ func createGetPageRegularBenchmarkSite(t testing.TB) *Site { return fmt.Sprintf(pageCollectionsPageTemplate, title) } - for i := 0; i < 10; i++ { - for j := 0; j < 100; j++ { + for i := range 10 { + for j := range 100 { content := pc(fmt.Sprintf("Title%d_%d", i, j)) writeSource(c, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), content) } @@ -105,7 +105,7 @@ func TestBenchmarkGetPageRegular(t *testing.T) { c := qt.New(t) s := createGetPageRegularBenchmarkSite(t) - for i := 0; i < 10; i++ { + for i := range 10 { pp := path.Join("/", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", i)) page, _ := s.getPage(nil, pp) c.Assert(page, qt.Not(qt.IsNil), qt.Commentf(pp)) @@ -192,8 +192,8 @@ func TestGetPage(t *testing.T) { return fmt.Sprintf(pageCollectionsPageTemplate, title) } - for i := 0; i < 10; i++ { - for j := 0; j < 10; j++ { + for i := range 10 { + for j := range 10 { content := pc(fmt.Sprintf("Title%d_%d", i, j)) writeSource(t, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", j)), content) } diff --git a/hugolib/pages_capture.go b/hugolib/pages_capture.go index 96c2c0f96..50900e585 100644 --- a/hugolib/pages_capture.go +++ b/hugolib/pages_capture.go @@ -143,13 +143,29 @@ func (c *pagesCollector) Collect() (collectErr error) { s.pageMap.cfg.isRebuild = true } + var hasStructuralChange bool + for _, id := range c.ids { + if id.isStructuralChange() { + hasStructuralChange = true + break + } + } + for _, id := range c.ids { if id.p.IsLeafBundle() { collectErr = c.collectDir( id.p, false, func(fim hugofs.FileMetaInfo) bool { - return true + if hasStructuralChange { + return true + } + fimp := fim.Meta().PathInfo + if fimp == nil { + return true + } + + return fimp.Path() == id.p.Path() }, ) } else if id.p.IsBranchBundle() { @@ -179,7 +195,7 @@ func (c *pagesCollector) Collect() (collectErr error) { return id.p.Dir() == fim.Meta().PathInfo.Dir() } - if fim.Meta().PathInfo.IsLeafBundle() && id.p.BundleType() == paths.PathTypeContentSingle { + if fim.Meta().PathInfo.IsLeafBundle() && id.p.Type() == paths.TypeContentSingle { return id.p.Dir() == fim.Meta().PathInfo.Dir() } @@ -298,7 +314,7 @@ func (c *pagesCollector) collectDirDir(path string, root hugofs.FileMetaInfo, in return nil, filepath.SkipDir } - seen := map[hstrings.Tuple]bool{} + seen := map[hstrings.Strings2]hugofs.FileMetaInfo{} for _, fi := range readdir { if fi.IsDir() { continue @@ -311,11 +327,14 @@ func (c *pagesCollector) collectDirDir(path string, root hugofs.FileMetaInfo, in // These would eventually have been filtered out as duplicates when // inserting them into the document store, // but doing it here will preserve a consistent ordering. - baseLang := hstrings.Tuple{First: pi.Base(), Second: meta.Lang} - if seen[baseLang] { + baseLang := hstrings.Strings2{pi.Base(), meta.Lang} + if fi2, ok := seen[baseLang]; ok { + if c.h.Configs.Base.PrintPathWarnings && !c.h.isRebuild() { + c.logger.Warnf("Duplicate content path: %q file: %q file: %q", pi.Base(), fi2.Meta().Filename, meta.Filename) + } continue } - seen[baseLang] = true + seen[baseLang] = fi if pi == nil { panic(fmt.Sprintf("no path info for %q", meta.Filename)) @@ -358,7 +377,7 @@ func (c *pagesCollector) collectDirDir(path string, root hugofs.FileMetaInfo, in func (c *pagesCollector) handleBundleLeaf(dir, bundle hugofs.FileMetaInfo, inPath string, readdir []hugofs.FileMetaInfo) error { bundlePi := bundle.Meta().PathInfo - seen := map[hstrings.Tuple]bool{} + seen := map[hstrings.Strings2]bool{} walk := func(path string, info hugofs.FileMetaInfo) error { if info.IsDir() { @@ -380,7 +399,7 @@ func (c *pagesCollector) handleBundleLeaf(dir, bundle hugofs.FileMetaInfo, inPat // These would eventually have been filtered out as duplicates when // inserting them into the document store, // but doing it here will preserve a consistent ordering. - baseLang := hstrings.Tuple{First: pi.Base(), Second: info.Meta().Lang} + baseLang := hstrings.Strings2{pi.Base(), info.Meta().Lang} if seen[baseLang] { return nil } diff --git a/hugolib/pages_language_merge_test.go b/hugolib/pages_language_merge_test.go index 3862d7cf0..ba1ed83de 100644 --- a/hugolib/pages_language_merge_test.go +++ b/hugolib/pages_language_merge_test.go @@ -42,7 +42,7 @@ func TestMergeLanguages(t *testing.T) { c.Assert(len(frSite.RegularPages()), qt.Equals, 6) c.Assert(len(nnSite.RegularPages()), qt.Equals, 12) - for i := 0; i < 2; i++ { + for range 2 { mergedNN := nnSite.RegularPages().MergeByLanguage(enSite.RegularPages()) c.Assert(len(mergedNN), qt.Equals, 31) for i := 1; i <= 31; i++ { @@ -163,7 +163,7 @@ date: "2018-02-28" // Add a bundles j := 100 contentPairs = append(contentPairs, []string{"bundle/index.md", fmt.Sprintf(contentTemplate, j, j)}...) - for i := 0; i < 6; i++ { + for i := range 6 { contentPairs = append(contentPairs, []string{fmt.Sprintf("bundle/pb%d.md", i), fmt.Sprintf(contentTemplate, i+j, i+j)}...) } contentPairs = append(contentPairs, []string{"bundle/index.nn.md", fmt.Sprintf(contentTemplate, j, j)}...) diff --git a/hugolib/pagesfromdata/pagesfromgotmpl.go b/hugolib/pagesfromdata/pagesfromgotmpl.go index fd7213bd9..72909a40b 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl.go @@ -29,6 +29,7 @@ import ( "github.com/gohugoio/hugo/resources/page/pagemeta" "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/tpl" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/mitchellh/mapstructure" "github.com/spf13/cast" ) @@ -90,17 +91,20 @@ func (p *pagesFromDataTemplateContext) AddPage(v any) (string, error) { pd := pagemeta.DefaultPageConfig pd.IsFromContentAdapter = true + pd.ContentAdapterData = m - if err := mapstructure.WeakDecode(m, &pd); err != nil { - return "", fmt.Errorf("failed to decode page map: %w", err) + // The rest will be handled after the cascade is calculated and applied. + if err := mapstructure.WeakDecode(pd.ContentAdapterData, &pd.PageConfigEarly); err != nil { + err = fmt.Errorf("failed to decode page map: %w", err) + return "", err } - p.p.buildState.NumPagesAdded++ - if err := pd.Validate(true); err != nil { return "", err } + p.p.buildState.NumPagesAdded++ + return "", p.p.HandlePage(p.p, &pd) } @@ -167,8 +171,7 @@ type PagesFromTemplateOptions struct { } type PagesFromTemplateDeps struct { - TmplFinder tpl.TemplateParseFinder - TmplExec tpl.TemplateExecutor + TemplateStore *tplimpl.TemplateStore } var _ resource.Staler = (*PagesFromTemplate)(nil) @@ -245,10 +248,11 @@ func (b *BuildState) resolveDeletedPaths() { return } var paths []string - b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) { + b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) bool { if _, found := b.sourceInfosCurrent.Get(k); !found { paths = append(paths, k) } + return true }) b.DeletedPaths = paths @@ -269,7 +273,7 @@ type sourceInfo struct { } func (p PagesFromTemplate) CloneForSite(s page.Site) *PagesFromTemplate { - // We deliberately make them share the same DepenencyManager and Store. + // We deliberately make them share the same DependencyManager and Store. p.PagesFromTemplateOptions.Site = s p.PagesFromTemplateDeps = p.PagesFromTemplateOptions.DepsFromSite(s) p.buildState = &BuildState{ @@ -287,6 +291,10 @@ func (p *PagesFromTemplate) GetDependencyManagerForScope(scope int) identity.Man return p.DependencyManager } +func (p *PagesFromTemplate) GetDependencyManagerForScopesAll() []identity.Manager { + return []identity.Manager{p.DependencyManager} +} + func (p *PagesFromTemplate) Execute(ctx context.Context) (BuildInfo, error) { defer func() { p.buildState.PrepareNextBuild() @@ -298,7 +306,7 @@ func (p *PagesFromTemplate) Execute(ctx context.Context) (BuildInfo, error) { } defer f.Close() - tmpl, err := p.TmplFinder.Parse(filepath.ToSlash(p.GoTmplFi.Meta().Filename), helpers.ReaderToString(f)) + tmpl, err := p.TemplateStore.TextParse(filepath.ToSlash(p.GoTmplFi.Meta().Filename), helpers.ReaderToString(f)) if err != nil { return BuildInfo{}, err } @@ -309,7 +317,7 @@ func (p *PagesFromTemplate) Execute(ctx context.Context) (BuildInfo, error) { ctx = tpl.Context.DependencyManagerScopedProvider.Set(ctx, p) - if err := p.TmplExec.ExecuteWithContext(ctx, tmpl, io.Discard, data); err != nil { + if err := p.TemplateStore.ExecuteWithContext(ctx, tmpl, io.Discard, data); err != nil { return BuildInfo{}, err } diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go index a4cf4dcff..db06fb4a4 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Hugo Authors. All rights reserved. +// Copyright 2025 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import ( "github.com/gohugoio/hugo/markup/asciidocext" "github.com/gohugoio/hugo/markup/pandoc" "github.com/gohugoio/hugo/markup/rst" + "github.com/gohugoio/hugo/related" ) const filesPagesFromDataTempleBasic = ` @@ -73,10 +74,11 @@ Pfile Content {{ $title := printf "%s:%s" $pd $pp }} {{ $date := "2023-03-01" | time.AsTime }} {{ $dates := dict "date" $date }} +{{ $keywords := slice "foo" "Bar"}} {{ $contentMarkdown := dict "value" "**Hello World**" "mediaType" "text/markdown" }} {{ $contentMarkdownDefault := dict "value" "**Hello World Default**" }} {{ $contentHTML := dict "value" "Hello World! No **markdown** here." "mediaType" "text/html" }} -{{ $.AddPage (dict "kind" "page" "path" "P1" "title" $title "dates" $dates "content" $contentMarkdown "params" (dict "param1" "param1v" ) ) }} +{{ $.AddPage (dict "kind" "page" "path" "P1" "title" $title "dates" $dates "keywords" $keywords "content" $contentMarkdown "params" (dict "param1" "param1v" ) ) }} {{ $.AddPage (dict "kind" "page" "path" "p2" "title" "p2title" "dates" $dates "content" $contentHTML ) }} {{ $.AddPage (dict "kind" "page" "path" "p3" "title" "p3title" "dates" $dates "content" $contentMarkdownDefault "draft" false ) }} {{ $.AddPage (dict "kind" "page" "path" "p4" "title" "p4title" "dates" $dates "content" $contentMarkdownDefault "draft" $data.draft ) }} @@ -96,7 +98,8 @@ ADD_MORE_PLACEHOLDER func TestPagesFromGoTmplMisc(t *testing.T) { t.Parallel() - b := hugolib.Test(t, filesPagesFromDataTempleBasic) + b := hugolib.Test(t, filesPagesFromDataTempleBasic, hugolib.TestOptWarn()) + b.AssertLogContains("! WARN") b.AssertPublishDir(` docs/p1/mytext.txt docs/p1/sub/mytex2.tx @@ -119,7 +122,7 @@ docs/p1/sub/mymixcasetext2.txt "RelPermalink: /docs/p1/sub/mymixcasetext2.txt|Name: sub/mymixcasetext2.txt|", "RelPermalink: /mydata.yaml|Name: sub/data1.yaml|Title: Sub data|Params: map[]|", "Featured Image: /a/pixel.png|featured.png|", - "Resized Featured Image: /a/pixel_hu16809842526914527184.png|10|", + "Resized Featured Image: /a/pixel_hu_a32b3e361d55df1.png|10|", // Resource from string "RelPermalink: /docs/p1/mytext.txt|Name: textresource|Title: My Text Resource|Params: map[param1:param1v]|", // Dates @@ -329,6 +332,24 @@ func TestPagesFromGoTmplRemoveGoTmpl(t *testing.T) { b.AssertFileContent("public/docs/index.html", "RegularPagesRecursive: pfile:/docs/pfile|$") } +// Issue #13443. +func TestPagesFromGoRelatedKeywords(t *testing.T) { + t.Parallel() + b := hugolib.Test(t, filesPagesFromDataTempleBasic) + + p1 := b.H.Sites[0].RegularPages()[0] + icfg := related.IndexConfig{ + Name: "keywords", + } + k, err := p1.RelatedKeywords(icfg) + b.Assert(err, qt.IsNil) + b.Assert(k, qt.DeepEquals, icfg.StringsToKeywords("foo", "Bar")) + icfg.Name = "title" + k, err = p1.RelatedKeywords(icfg) + b.Assert(err, qt.IsNil) + b.Assert(k, qt.DeepEquals, icfg.StringsToKeywords("p1:p1")) +} + func TestPagesFromGoTmplLanguagePerFile(t *testing.T) { filesTemplate := ` -- hugo.toml -- @@ -362,6 +383,28 @@ Single: {{ .Title }}|{{ .Content }}| } } +func TestPagesFromGoTmplDefaultPageSort(t *testing.T) { + t.Parallel() + files := ` +-- hugo.toml -- +defaultContentLanguage = "en" +-- layouts/index.html -- +{{ range site.RegularPages }}{{ .RelPermalink }}|{{ end}} +-- content/_content.gotmpl -- +{{ $.AddPage (dict "kind" "page" "path" "docs/_p22" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "docs/p12" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "docs/_p12" "title" "A" ) }} +-- content/docs/_content.gotmpl -- +{{ $.AddPage (dict "kind" "page" "path" "_p21" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "p11" "title" "A" ) }} +{{ $.AddPage (dict "kind" "page" "path" "_p11" "title" "A" ) }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "/docs/_p11/|/docs/_p12/|/docs/_p21/|/docs/_p22/|/docs/p11/|/docs/p12/|") +} + func TestPagesFromGoTmplEnableAllLanguages(t *testing.T) { t.Parallel() @@ -653,6 +696,34 @@ Footer: {{ range index site.Menus.footer }}{{ .Name }}|{{ end }}| ) } +// Issue 13384. +func TestPagesFromGoTmplMenusMap(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['rss','section','sitemap','taxonomy','term'] +-- content/_content.gotmpl -- +{{ $menu1 := dict + "parent" "main-page" + "identifier" "id1" +}} +{{ $menu2 := dict + "parent" "main-page" + "identifier" "id2" +}} +{{ $menus := dict "m1" $menu1 "m2" $menu2 }} +{{ .AddPage (dict "path" "p1" "title" "p1" "menus" $menus ) }} + +-- layouts/index.html -- +Menus: {{ range $k, $v := site.Menus }}{{ $k }}|{{ end }} + +` + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", "Menus: m1|m2|") +} + func TestPagesFromGoTmplMore(t *testing.T) { t.Parallel() @@ -678,3 +749,169 @@ summary: {{ .Summary }}|content: {{ .Content}} "

    aaa

    |content:

    aaa

    \n

    bbb

    ", ) } + +// Issue 13063. +func TestPagesFromGoTmplTermIsEmpty(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableKinds = ['section', 'home', 'rss','sitemap'] +printPathWarnings = true +[taxonomies] +tag = "tags" +-- content/mypost.md -- +--- +title: "My Post" +tags: ["mytag"] +--- +-- content/tags/_content.gotmpl -- +{{ .AddPage (dict "path" "mothertag" "title" "My title" "kind" "term") }} +-- +-- layouts/_default/taxonomy.html -- +Terms: {{ range .Data.Terms.ByCount }}{{ .Name }}: {{ .Count }}|{{ end }}§s +-- layouts/_default/single.html -- +Single. +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/tags/index.html", "Terms: mytag: 1|§s") +} + +func TestContentAdapterOutputsIssue13689(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +[outputs] +page = ['html','json'] +-- layouts/page.html -- +html: {{ .Title }} +-- layouts/page.json -- +json: {{ .Title }} +-- content/p1.md -- +--- +title: p1 +--- +-- content/p2.md -- +--- +title: p2 +outputs: + - html +--- +-- content/_content.gotmpl -- +{{ $page := dict "path" "p3" "title" "p3" }} +{{ $.AddPage $page }} + +{{ $page := dict "path" "p4" "title" "p4" "outputs" (slice "html") }} +{{ $.AddPage $page }} +` + + b := hugolib.Test(t, files) + + b.AssertFileExists("public/p1/index.html", true) + b.AssertFileExists("public/p1/index.json", true) + b.AssertFileExists("public/p2/index.html", true) + b.AssertFileExists("public/p2/index.json", false) + b.AssertFileExists("public/p3/index.html", true) + b.AssertFileExists("public/p3/index.json", true) + b.AssertFileExists("public/p4/index.html", true) + b.AssertFileExists("public/p4/index.json", false) // currently returns true +} + +func TestContentAdapterOutputsIssue13692(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','home','sitemap','taxonomy','term'] +[[cascade]] +outputs = ['html','json'] +[cascade.target] +path = '{/s2,/s4}' +-- layouts/section.html -- +html: {{ .Title }} +-- layouts/section.json -- +json: {{ .Title }} +-- content/s1/_index.md -- +--- +title: s1 +--- +-- content/s2/_index.md -- +--- +title: s2 +--- +-- content/_content.gotmpl -- +{{ $page := dict "path" "s3" "title" "s3" "kind" "section" }} +{{ $.AddPage $page }} + +{{ $page := dict "path" "s4" "title" "s4" "kind" "section" }} +{{ $.AddPage $page }} + +{{ $page := dict "path" "s5" "title" "s5" "kind" "section" "outputs" (slice "html") }} + {{ $.AddPage $page }} +` + + b := hugolib.Test(t, files) + + b.AssertFileExists("public/s1/index.html", true) + b.AssertFileExists("public/s1/index.json", false) + b.AssertFileExists("public/s1/index.xml", true) + + b.AssertFileExists("public/s2/index.html", true) + b.AssertFileExists("public/s2/index.json", true) + b.AssertFileExists("public/s2/index.xml", false) + + b.AssertFileExists("public/s3/index.html", true) + b.AssertFileExists("public/s3/index.json", false) + b.AssertFileExists("public/s3/index.xml", true) + + b.AssertFileExists("public/s4/index.html", true) + b.AssertFileExists("public/s4/index.json", true) + b.AssertFileExists("public/s4/index.xml", false) + + b.AssertFileExists("public/s5/index.html", true) + b.AssertFileExists("public/s5/index.json", false) + b.AssertFileExists("public/s5/index.xml", false) +} + +func TestContentAdapterCascadeBasic(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableLiveReload = true +-- content/_index.md -- +--- +cascade: + - title: foo + target: + path: "**" +--- +-- layouts/all.html -- +Title: {{ .Title }}|Content: {{ .Content }}| +-- content/_content.gotmpl -- +{{ $content := dict + "mediaType" "text/markdown" + "value" "The _Hunchback of Notre Dame_ was written by Victor Hugo." +}} + +{{ $page := dict "path" "s1" "kind" "page" }} +{{ $.AddPage $page }} + {{ $page := dict "path" "s2" "kind" "page" "title" "bar" "content" $content }} +{{ $.AddPage $page }} + +` + + b := hugolib.TestRunning(t, files) + + b.AssertFileContent("public/s1/index.html", "Title: foo|") + b.AssertFileContent("public/s2/index.html", "Title: bar|", "Content:

    The Hunchback of Notre Dame was written by Victor Hugo.

    ") + + b.EditFileReplaceAll("content/_index.md", "foo", "baz").Build() + + b.AssertFileContent("public/s1/index.html", "Title: baz|") +} diff --git a/hugolib/paginator_test.go b/hugolib/paginator_test.go index dcee6e38e..2fb87956f 100644 --- a/hugolib/paginator_test.go +++ b/hugolib/paginator_test.go @@ -15,7 +15,6 @@ package hugolib import ( "fmt" - "path/filepath" "testing" qt "github.com/frankban/quicktest" @@ -40,7 +39,7 @@ contentDir = "content/nn" ` b := newTestSitesBuilder(t).WithConfigFile("toml", configFile) var content []string - for i := 0; i < 9; i++ { + for i := range 9 { for _, contentDir := range []string{"content/en", "content/nn"} { content = append(content, fmt.Sprintf(contentDir+"/blog/page%d.md", i), fmt.Sprintf(`--- title: Page %d @@ -102,10 +101,18 @@ URL: {{ $pag.URL }} // Issue 6023 func TestPaginateWithSort(t *testing.T) { - b := newTestSitesBuilder(t).WithSimpleConfigFile() - b.WithTemplatesAdded("index.html", `{{ range (.Paginate (sort .Site.RegularPages ".File.Filename" "desc")).Pages }}|{{ .File.Filename }}{{ end }}`) - b.Build(BuildCfg{}).AssertFileContent("public/index.html", - filepath.FromSlash("|content/sect/doc1.nn.md|content/sect/doc1.nb.md|content/sect/doc1.fr.md|content/sect/doc1.en.md")) + files := ` +-- hugo.toml -- +-- content/a/a.md -- +-- content/z/b.md -- +-- content/x/b.md -- +-- content/x/a.md -- +-- layouts/home.html -- +Paginate: {{ range (.Paginate (sort .Site.RegularPages ".File.Filename" "desc")).Pages }}|{{ .Path }}{{ end }} +` + b := Test(t, files) + + b.AssertFileContent("public/index.html", "Paginate: |/z/b|/x/b|/x/a|/a/a") } // https://github.com/gohugoio/hugo/issues/6797 @@ -118,7 +125,7 @@ cascade: - JSON ---`) - for i := 0; i < 22; i++ { + for i := range 22 { b.WithContent(fmt.Sprintf("p%d.md", i+1), fmt.Sprintf(`--- title: "Page" weight: %d @@ -176,12 +183,12 @@ Paginator: {{ .Paginator }} func TestNilPointerErrorMessage(t *testing.T) { files := ` --- hugo.toml -- +-- hugo.toml -- -- content/p1.md -- -- layouts/_default/single.html -- Home Filename: {{ site.Home.File.Filename }} ` b, err := TestE(t, files) b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, `_default/single.html:1:22: executing "_default/single.html" – File is nil; wrap it in if or with: {{ with site.Home.File }}{{ .Filename }}{{ end }}`) + b.Assert(err.Error(), qt.Contains, `single.html:1:22: executing "single.html" – File is nil; wrap it in if or with: {{ with site.Home.File }}{{ .Filename }}{{ end }}`) } diff --git a/hugolib/params_test.go b/hugolib/params_test.go index cbcc8e540..7f7566024 100644 --- a/hugolib/params_test.go +++ b/hugolib/params_test.go @@ -57,7 +57,7 @@ Summary: {{ .Summary }}| ) } -func TestFrontMatterParamsKindPath(t *testing.T) { +func TestFrontMatterParamsPath(t *testing.T) { t.Parallel() files := ` @@ -72,10 +72,9 @@ date: 2019-08-07 path: "/a/b/c" slug: "s1" --- --- content/mysection.md -- +-- content/mysection/_index.md -- --- title: "My Section" -kind: "section" date: 2022-08-07 path: "/a/b" --- @@ -95,66 +94,6 @@ a/b pages: {{ range $ab.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ end }} ) } -func TestFrontMatterParamsLang(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -baseURL = "https://example.org/" -disableKinds = ["taxonomy", "term"] -defaultContentLanguage = "en" -defaultContentLanguageInSubdir = true -[languages] -[languages.en] -weight = 1 -[languages.nn] -weight = 2 --- content/p1.md -- ---- -title: "P1 nn" -lang: "nn" ---- --- content/p2.md -- ---- -title: "P2" ---- --- layouts/index.html -- -RegularPages: {{ range site.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ end }}$ - -` - - b := Test(t, files) - - b.AssertFileContent("public/en/index.html", - "RegularPages: /p2|/en/p2/|P2|$", - ) - b.AssertFileContent("public/nn/index.html", - "RegularPages: /p1|/nn/p1/|P1 nn|$", - ) -} - -func TestFrontMatterTitleOverrideWarn(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -baseURL = "https://example.org/" -disableKinds = ["taxonomy", "term"] --- content/p1.md -- ---- -title: "My title" -params: - title: "My title from params" ---- - - -` - - b := Test(t, files, TestOptWarn()) - - b.AssertLogContains("ARN Hugo front matter key \"title\" is overridden in params section", "You can suppress this warning") -} - func TestFrontMatterParamsLangNoCascade(t *testing.T) { t.Parallel() diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go index 397dba3f8..60ec873f9 100644 --- a/hugolib/paths/paths.go +++ b/hugolib/paths/paths.go @@ -67,7 +67,7 @@ func New(fs *hugofs.Fs, cfg config.AllProvider) (*Paths, error) { var multihostTargetBasePaths []string if cfg.IsMultihost() && len(cfg.Languages()) > 1 { for _, l := range cfg.Languages() { - multihostTargetBasePaths = append(multihostTargetBasePaths, l.Lang) + multihostTargetBasePaths = append(multihostTargetBasePaths, hpaths.ToSlashPreserveLeading(l.Lang)) } } diff --git a/hugolib/rebuild_test.go b/hugolib/rebuild_test.go index 2219fe812..d4a15fb5b 100644 --- a/hugolib/rebuild_test.go +++ b/hugolib/rebuild_test.go @@ -21,8 +21,13 @@ const rebuildFilesSimple = ` baseURL = "https://example.com" disableKinds = ["term", "taxonomy", "sitemap", "robotstxt", "404"] disableLiveReload = true +[outputFormats] + [outputFormats.rss] + weight = 10 + [outputFormats.html] + weight = 20 [outputs] -home = ["html"] +home = ["rss", "html"] section = ["html"] page = ["html"] -- content/mysection/_index.md -- @@ -46,6 +51,7 @@ My Section Bundle Content Content. title: "My Section" --- -- content/mysection/mysectiontext.txt -- +Content. -- content/_index.md -- --- title: "Home" @@ -58,6 +64,21 @@ Home Text Content. title: "myothersectionpage" --- myothersectionpage Content. +-- content/mythirdsection/mythirdsectionpage.md -- +--- +title: "mythirdsectionpage" +--- +mythirdsectionpage Content. +{{< myshortcodetext >}} +§§§ myothertext +foo +§§§ +-- assets/mytext.txt -- +Assets My Text. +-- assets/myshortcodetext.txt -- +Assets My Shortcode Text. +-- assets/myothertext.txt -- +Assets My Other Text. -- layouts/_default/single.html -- Single: {{ .Title }}|{{ .Content }}$ Resources: {{ range $i, $e := .Resources }}{{ $i }}:{{ .RelPermalink }}|{{ .Content }}|{{ end }}$ @@ -68,9 +89,30 @@ Len Resources: {{ len .Resources }}| Resources: {{ range $i, $e := .Resources }}{{ $i }}:{{ .RelPermalink }}|{{ .Content }}|{{ end }}$ -- layouts/shortcodes/foo.html -- Foo. +-- layouts/shortcodes/myshortcodetext.html -- +{{ warnf "mytext %s" now}} +{{ $r := resources.Get "myshortcodetext.txt" }} +My Shortcode Text: {{ $r.Content }}|{{ $r.Permalink }}| +-- layouts/_default/_markup/render-codeblock-myothertext.html -- +{{ $r := resources.Get "myothertext.txt" }} +My Other Text: {{ $r.Content }}|{{ $r.Permalink }}| ` +func TestRebuildEditLeafBundleHeaderOnly(t *testing.T) { + t.Parallel() + for i := 0; i < 3; i++ { + b := TestRunning(t, rebuildFilesSimple) + b.AssertFileContent("public/mysection/mysectionbundle/index.html", + "My Section Bundle Content Content.") + b.EditFileReplaceAll("content/mysection/mysectionbundle/index.md", "My Section Bundle Content.", "My Section Bundle Content Edited.").Build() + b.AssertFileContent("public/mysection/mysectionbundle/index.html", + "My Section Bundle Content Edited.") + b.AssertRenderCountPage(2) // home (rss) + bundle. + b.AssertRenderCountContent(1) + } +} + func TestRebuildEditTextFileInLeafBundle(t *testing.T) { b := TestRunning(t, rebuildFilesSimple) b.AssertFileContent("public/mysection/mysectionbundle/index.html", @@ -80,7 +122,35 @@ func TestRebuildEditTextFileInLeafBundle(t *testing.T) { b.AssertFileContent("public/mysection/mysectionbundle/index.html", "Text 2 Content Edited") b.AssertRenderCountPage(1) - b.AssertRenderCountContent(1) + b.AssertRenderCountContent(0) +} + +func TestRebuildEditTextFileInShortcode(t *testing.T) { + t.Parallel() + for range 3 { + b := TestRunning(t, rebuildFilesSimple) + b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", + "Text: Assets My Shortcode Text.") + b.EditFileReplaceAll("assets/myshortcodetext.txt", "My Shortcode Text", "My Shortcode Text Edited").Build() + fmt.Println(b.LogString()) + b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", + "Text: Assets My Shortcode Text Edited.") + + } +} + +func TestRebuildEditTextFileInHook(t *testing.T) { + t.Parallel() + for range 3 { + b := TestRunning(t, rebuildFilesSimple) + b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", + "Text: Assets My Other Text.") + b.AssertFileContent("public/myothertext.txt", "Assets My Other Text.") + b.EditFileReplaceAll("assets/myothertext.txt", "My Other Text", "My Other Text Edited").Build() + b.AssertFileContent("public/mythirdsection/mythirdsectionpage/index.html", + "Text: Assets My Other Text Edited.") + + } } func TestRebuiEditUnmarshaledYamlFileInLeafBundle(t *testing.T) { @@ -113,17 +183,17 @@ func TestRebuildEditTextFileInHomeBundle(t *testing.T) { b.AssertFileContent("public/index.html", "Home Content.") b.AssertFileContent("public/index.html", "Home Text Content Edited.") b.AssertRenderCountPage(1) - b.AssertRenderCountContent(1) + b.AssertRenderCountContent(0) } func TestRebuildEditTextFileInBranchBundle(t *testing.T) { b := TestRunning(t, rebuildFilesSimple) - b.AssertFileContent("public/mysection/index.html", "My Section") + b.AssertFileContent("public/mysection/index.html", "My Section", "0:/mysection/mysectiontext.txt|Content.|") b.EditFileReplaceAll("content/mysection/mysectiontext.txt", "Content.", "Content Edited.").Build() - b.AssertFileContent("public/mysection/index.html", "My Section") + b.AssertFileContent("public/mysection/index.html", "My Section", "0:/mysection/mysectiontext.txt|Content Edited.|") b.AssertRenderCountPage(1) - b.AssertRenderCountContent(1) + b.AssertRenderCountContent(0) } func testRebuildBothWatchingAndRunning(t *testing.T, files string, withB func(b *IntegrationTestBuilder)) { @@ -140,8 +210,8 @@ func TestRebuildRenameTextFileInLeafBundle(t *testing.T) { b.RenameFile("content/mysection/mysectionbundle/mysectionbundletext.txt", "content/mysection/mysectionbundle/mysectionbundletext2.txt").Build() b.AssertFileContent("public/mysection/mysectionbundle/index.html", "mysectionbundletext2", "My Section Bundle Text 2 Content.", "Len Resources: 2|") - b.AssertRenderCountPage(5) - b.AssertRenderCountContent(6) + b.AssertRenderCountPage(8) + b.AssertRenderCountContent(9) }) } @@ -161,8 +231,8 @@ func TestRebuilEditContentFileThenAnother(t *testing.T) { b.EditFileReplaceAll("content/myothersection/myothersectionpage.md", "myothersectionpage Content.", "myothersectionpage Content Edited.").Build() b.AssertFileContent("public/myothersection/myothersectionpage/index.html", "myothersectionpage Content Edited") - b.AssertRenderCountPage(1) - b.AssertRenderCountContent(1) + b.AssertRenderCountPage(2) + b.AssertRenderCountContent(2) } func TestRebuildRenameTextFileInBranchBundle(t *testing.T) { @@ -171,7 +241,7 @@ func TestRebuildRenameTextFileInBranchBundle(t *testing.T) { b.RenameFile("content/mysection/mysectiontext.txt", "content/mysection/mysectiontext2.txt").Build() b.AssertFileContent("public/mysection/index.html", "mysectiontext2", "My Section") - b.AssertRenderCountPage(2) + b.AssertRenderCountPage(3) b.AssertRenderCountContent(2) } @@ -181,14 +251,14 @@ func TestRebuildRenameTextFileInHomeBundle(t *testing.T) { b.RenameFile("content/hometext.txt", "content/hometext2.txt").Build() b.AssertFileContent("public/index.html", "hometext2", "Home Text Content.") - b.AssertRenderCountPage(3) + b.AssertRenderCountPage(5) } func TestRebuildRenameDirectoryWithLeafBundle(t *testing.T) { b := TestRunning(t, rebuildFilesSimple) b.RenameDir("content/mysection/mysectionbundle", "content/mysection/mysectionbundlerenamed").Build() b.AssertFileContent("public/mysection/mysectionbundlerenamed/index.html", "My Section Bundle") - b.AssertRenderCountPage(1) + b.AssertRenderCountPage(2) } func TestRebuildRenameDirectoryWithBranchBundle(t *testing.T) { @@ -197,7 +267,7 @@ func TestRebuildRenameDirectoryWithBranchBundle(t *testing.T) { b.AssertFileContent("public/mysectionrenamed/index.html", "My Section") b.AssertFileContent("public/mysectionrenamed/mysectionbundle/index.html", "My Section Bundle") b.AssertFileContent("public/mysectionrenamed/mysectionbundle/mysectionbundletext.txt", "My Section Bundle Text 2 Content.") - b.AssertRenderCountPage(3) + b.AssertRenderCountPage(5) } func TestRebuildRenameDirectoryWithRegularPageUsedInHome(t *testing.T) { @@ -290,13 +360,13 @@ RegularPages: {{ range .Site.RegularPages }}{{ .RelPermalink }}|{{ end }}$ } func TestRebuildRenameDirectoryWithBranchBundleFastRender(t *testing.T) { - recentlyVisited := types.NewEvictingStringQueue(10).Add("/a/b/c/") - b := TestRunning(t, rebuildFilesSimple, func(cfg *IntegrationTestConfig) { cfg.BuildCfg = BuildCfg{RecentlyVisited: recentlyVisited} }) + recentlyVisited := types.NewEvictingQueue[string](10).Add("/a/b/c/") + b := TestRunning(t, rebuildFilesSimple, func(cfg *IntegrationTestConfig) { cfg.BuildCfg = BuildCfg{RecentlyTouched: recentlyVisited} }) b.RenameDir("content/mysection", "content/mysectionrenamed").Build() b.AssertFileContent("public/mysectionrenamed/index.html", "My Section") b.AssertFileContent("public/mysectionrenamed/mysectionbundle/index.html", "My Section Bundle") b.AssertFileContent("public/mysectionrenamed/mysectionbundle/mysectionbundletext.txt", "My Section Bundle Text 2 Content.") - b.AssertRenderCountPage(3) + b.AssertRenderCountPage(5) } func TestRebuilErrorRecovery(t *testing.T) { @@ -417,7 +487,43 @@ Home: {{ .Title }}|{{ .Content }}| }) } -func TestRebuildSingleWithBaseof(t *testing.T) { +func TestRebuildSingle(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +title = "Hugo Site" +baseURL = "https://example.com" +disableKinds = ["term", "taxonomy", "sitemap", "robotstxt", "404"] +disableLiveReload = true +-- content/p1.md -- +--- +title: "P1" +--- +P1 Content. +-- layouts/index.html -- +Home. +-- layouts/single.html -- +Single: {{ .Title }}|{{ .Content }}| +{{ with (templates.Defer (dict "key" "global")) }} +Defer. +{{ end }} +` + b := Test(t, files, TestOptRunning()) + b.AssertFileContent("public/p1/index.html", "Single: P1|", "Defer.") + b.AssertRenderCountPage(3) + b.AssertRenderCountContent(1) + b.EditFileReplaceFunc("layouts/single.html", func(s string) string { + s = strings.Replace(s, "Single", "Single Edited", 1) + s = strings.Replace(s, "Defer.", "Defer Edited.", 1) + return s + }).Build() + b.AssertFileContent("public/p1/index.html", "Single Edited: P1|", "Defer Edited.") + b.AssertRenderCountPage(1) + b.AssertRenderCountContent(0) +} + +func TestRebuildSingleWithBaseofEditSingle(t *testing.T) { t.Parallel() files := ` @@ -431,9 +537,13 @@ disableLiveReload = true title: "P1" --- P1 Content. +[foo](/foo) -- layouts/_default/baseof.html -- Baseof: {{ .Title }}| {{ block "main" . }}default{{ end }} +{{ with (templates.Defer (dict "foo" "bar")) }} +Defer. +{{ end }} -- layouts/index.html -- Home. -- layouts/_default/single.html -- @@ -442,11 +552,81 @@ Single: {{ .Title }}|{{ .Content }}| {{ end }} ` b := Test(t, files, TestOptRunning()) - b.AssertFileContent("public/p1/index.html", "Baseof: P1|\n\nSingle: P1|

    P1 Content.

    \n|") + b.AssertFileContent("public/p1/index.html", "Single: P1|") b.EditFileReplaceFunc("layouts/_default/single.html", func(s string) string { return strings.Replace(s, "Single", "Single Edited", 1) }).Build() - b.AssertFileContent("public/p1/index.html", "Baseof: P1|\n\nSingle Edited: P1|

    P1 Content.

    \n|") + b.AssertFileContent("public/p1/index.html", "Single Edited") +} + +func TestRebuildSingleWithBaseofEditBaseof(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +title = "Hugo Site" +baseURL = "https://example.com" +disableKinds = ["term", "taxonomy"] +disableLiveReload = true +-- content/p1.md -- +--- +title: "P1" +--- +P1 Content. +[foo](/foo) +-- layouts/_default/baseof.html -- +Baseof: {{ .Title }}| +{{ block "main" . }}default{{ end }} +{{ with (templates.Defer (dict "foo" "bar")) }} +Defer. +{{ end }} +-- layouts/index.html -- +Home. +-- layouts/_default/single.html -- +{{ define "main" }} +Single: {{ .Title }}|{{ .Content }}| +{{ end }} +` + b := Test(t, files, TestOptRunning()) + b.AssertFileContent("public/p1/index.html", "Single: P1|") + fmt.Println("===============") + b.EditFileReplaceAll("layouts/_default/baseof.html", "Baseof", "Baseof Edited").Build() + b.AssertFileContent("public/p1/index.html", "Baseof Edited") +} + +func TestRebuildWithDeferEditRenderHook(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +title = "Hugo Site" +baseURL = "https://example.com" +disableKinds = ["term", "taxonomy"] +disableLiveReload = true +-- content/p1.md -- +--- +title: "P1" +--- +P1 Content. +[foo](/foo) +-- layouts/_default/baseof.html -- +Baseof: {{ .Title }}| +{{ block "main" . }}default{{ end }} + {{ with (templates.Defer (dict "foo" "bar")) }} +Defer. +{{ end }} +-- layouts/single.html -- +{{ define "main" }} +Single: {{ .Title }}|{{ .Content }}| +{{ end }} +-- layouts/_default/_markup/render-link.html -- +Render Link. +` + b := Test(t, files, TestOptRunning()) + // Edit render hook. + b.EditFileReplaceAll("layouts/_default/_markup/render-link.html", "Render Link", "Render Link Edited").Build() + + b.AssertFileContent("public/p1/index.html", "Render Link Edited") } func TestRebuildFromString(t *testing.T) { @@ -907,7 +1087,7 @@ Single. {{ partial "head.html" . }}$ RelPermalink: {{ $js.RelPermalink }}| ` - b := TestRunning(t, files) + b := TestRunning(t, files, TestOptOsFs()) b.AssertFileContent("public/p1/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") b.AssertFileContent("public/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") @@ -943,7 +1123,7 @@ Base. {{ partial "common/head.html" . }}$ RelPermalink: {{ $js.RelPermalink }}| ` - b := TestRunning(t, files) + b := TestRunning(t, files, TestOptOsFs()) b.AssertFileContent("public/index.html", "/js/main.712a50b59d0f0dedb4e3606eaa3860b1f1a5305f6c42da30a2985e473ba314eb.js") @@ -1114,6 +1294,49 @@ Content: {{ .Content }} b.AssertFileContent("public/index.html", "Content:

    Home

    ") } +// Issue #13014. +func TestRebuildEditNotPermalinkableCustomOutputFormatTemplateInFastRenderMode(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com/docs/" +disableLiveReload = true +[internal] +fastRenderMode = true +disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404"] +[outputFormats] + [outputFormats.SearchIndex] + baseName = 'Search' + isPlainText = true + mediaType = 'text/plain' + noAlternative = true + permalinkable = false + +[outputs] + home = ['HTML', 'SearchIndex'] +-- content/_index.md -- +--- +title: "Home" +--- +Home. +-- layouts/index.html -- +Home. +-- layouts/_default/index.searchindex.txt -- +Text. {{ .Title }}|{{ .RelPermalink }}| + +` + b := TestRunning(t, files, TestOptInfo()) + + b.AssertFileContent("public/search.txt", "Text.") + + b.EditFileReplaceAll("layouts/_default/index.searchindex.txt", "Text.", "Text Edited.").Build() + + b.BuildPartial("/docs/search.txt") + + b.AssertFileContent("public/search.txt", "Text Edited.") +} + func TestRebuildVariationsAssetsJSImport(t *testing.T) { t.Parallel() files := ` @@ -1239,7 +1462,7 @@ Single. return strings.Replace(s, "red", "blue", 1) }).Build() - b.AssertRenderCountPage(3) + b.AssertRenderCountPage(4) b.AssertFileContent("public/index.html", "Home.", "") } @@ -1435,7 +1658,7 @@ title: "P%d" P%d Content. ` - for i := 0; i < count; i++ { + for i := range count { files += fmt.Sprintf("-- content/mysect/p%d/index.md --\n%s", i, fmt.Sprintf(contentTemplate, i, i)) } @@ -1543,6 +1766,60 @@ MyTemplate: {{ partial "MyTemplate.html" . }}| b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited") } +func TestRebuildEditInlinePartial13723(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +title = "Foo" +-- layouts/baseof.html -- +{{ block "main" . }}Main.{{ end }} +{{ partial "myinlinepartialinbaseof.html" . }}| + {{- define "_partials/myinlinepartialinbaseof.html" }} + My inline partial in baseof. + {{ end }} +-- layouts/_partials/mypartial.html -- +Mypartial. +{{ partial "myinlinepartial.html" . }}| +{{- define "_partials/myinlinepartial.html" }} +Mypartial Inline.|{{ .Title }}| +{{ end }} +-- layouts/_partials/myotherpartial.html -- +Myotherpartial. +{{ partial "myotherinlinepartial.html" . }}| +{{- define "_partials/myotherinlinepartial.html" }} +Myotherpartial Inline.|{{ .Title }}| +{{ return "myotherinlinepartial" }} +{{ end }} +-- layouts/all.html -- +{{ define "main" }} +{{ partial "mypartial.html" . }}| +{{ partial "myotherpartial.html" . }}| + {{ partial "myinlinepartialinall.html" . }}| +{{ end }} + {{- define "_partials/myinlinepartialinall.html" }} + My inline partial in all. + {{ end }} + +` + b := TestRunning(t, files) + b.AssertFileContent("public/index.html", "Mypartial.", "Mypartial Inline.|Foo") + + // Edit inline partial in partial. + b.EditFileReplaceAll("layouts/_partials/mypartial.html", "Mypartial Inline.", "Mypartial Inline Edited.").Build() + b.AssertFileContent("public/index.html", "Mypartial Inline Edited.|Foo") + + // Edit inline partial in baseof. + b.EditFileReplaceAll("layouts/baseof.html", "My inline partial in baseof.", "My inline partial in baseof Edited.").Build() + b.AssertFileContent("public/index.html", "My inline partial in baseof Edited.") + + // Edit inline partial in all. + b.EditFileReplaceAll("layouts/all.html", "My inline partial in all.", "My inline partial in all Edited.").Build() + b.AssertFileContent("public/index.html", "My inline partial in all Edited.") +} + func TestRebuildEditAsciidocContentFile(t *testing.T) { if !asciidocext.Supports() { t.Skip("skip asciidoc") @@ -1642,3 +1919,50 @@ p1-content| b.EditFileReplaceAll("content/p1/index.md", "p1-content", "p1-content-foo").Build() b.AssertFileContent("public/p1/index.html", "p1-content-foo") } + +func TestRebuildEditTagIssue13648(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +-- layouts/all.html -- +All. {{ range .Pages }}{{ .Title }}|{{ end }} +-- content/p1.md -- +--- +title: "P1" +tags: ["tag1"] +--- + +` + b := TestRunning(t, files) + + b.AssertFileContent("public/tags/index.html", "All. Tag1|") + b.EditFileReplaceAll("content/p1.md", "tag1", "tag2").Build() + + // Note that the below is still not correct, as this is effectively a rename, and + // Tag2 should be removed from the list. + // But that is a harder problem to tackle. + b.AssertFileContent("public/tags/index.html", "All. Tag1|Tag2|") +} + +func TestRebuildEditNonReferencedResourceIssue13748(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = "https://example.com" +disableLiveReload = true +-- content/mybundle/index.md -- +-- content/mybundle/resource.txt -- +This is a resource file. +-- layouts/all.html -- +All. +` + b := TestRunning(t, files) + + b.AssertFileContent("public/mybundle/resource.txt", "This is a resource file.") + b.EditFileReplaceAll("content/mybundle/resource.txt", "This is a resource file.", "This is an edited resource file.").Build() + b.AssertFileContent("public/mybundle/resource.txt", "This is an edited resource file.") +} diff --git a/hugolib/rendershortcodes_test.go b/hugolib/rendershortcodes_test.go index 313c80a73..d8b51d3ed 100644 --- a/hugolib/rendershortcodes_test.go +++ b/hugolib/rendershortcodes_test.go @@ -14,6 +14,7 @@ package hugolib import ( + "path/filepath" "strings" "testing" ) @@ -69,6 +70,7 @@ Content: {{ .Content }}| b := Test(t, files) + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Fragments: [p1-h1 p2-h1 p2-h2 p2-h3 p2-withmarkdown p3-h1 p3-h2 p3-withmarkdown]|", "HasShortcode Level 1: true|", @@ -115,6 +117,7 @@ JSON: {{ .Content }} b := Test(t, files) + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Myshort HTML") b.AssertFileContent("public/p1/index.json", "Myshort JSON") } @@ -147,9 +150,11 @@ Myshort Original. {{ .Content }} ` b := TestRunning(t, files) + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Myshort Original.") b.EditFileReplaceAll("layouts/shortcodes/myshort.html", "Original", "Edited").Build() + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Myshort Edited.") } @@ -192,12 +197,14 @@ Myshort Original. }, ).Build() + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Original") b.EditFileReplaceFunc("content/p2.md", func(s string) string { return strings.Replace(s, "Original", "Edited", 1) }) b.Build() + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/p1/index.html", "Edited") } @@ -233,8 +240,10 @@ Myshort Original. ` b := TestRunning(t, files) + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/mysection/index.html", "p1-h1") b.EditFileReplaceAll("content/mysection/_index.md", "p1-h1", "p1-h1 Edited").Build() + b.AssertNoRenderShortcodesArtifacts() b.AssertFileContent("public/mysection/index.html", "p1-h1 Edited") } @@ -314,6 +323,8 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA b := Test(t, files) + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContent("public/markdown/index.html", // Images. "Image: /posts/p1/pixel1.png|\nImage: /posts/p1/pixel2.png|\n|\nImage: /markdown/pixel3.png|

    \n|", @@ -333,3 +344,186 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA b.AssertFileContent("public/html/index.html", "! hugo_ctx") } + +// Issue 12854. +func TestRenderShortcodesWithHTML(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableLiveReload = true +disableKinds = ["home", "taxonomy", "term"] +markup.goldmark.renderer.unsafe = true +-- content/p1.md -- +--- +title: "p1" +--- +{{% include "p2" %}} +-- content/p2.md -- +--- +title: "p2" +--- +Hello world. Some **bold** text. Some Unicode: 神真美好. +-- layouts/shortcodes/include.html -- +{{ with site.GetPage (.Get 0) }} +
    {{ .RenderShortcodes }}
    +{{ end }} +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := TestRunning(t, files, TestOptWarn()) + + b.AssertNoRenderShortcodesArtifacts() + b.AssertLogContains(filepath.ToSlash("WARN .RenderShortcodes detected inside HTML block in \"/content/p1.md\"; this may not be what you intended, see https://gohugo.io/methods/page/rendershortcodes/#limitations\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-rendershortcodes-in-html']")) + b.AssertFileContent("public/p1/index.html", "
    Hello world. Some **bold** text. Some Unicode: 神真美好.\n
    ") + b.EditFileReplaceAll("content/p2.md", "Hello", "Hello Edited").Build() + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContent("public/p1/index.html", "
    Hello Edited world. Some **bold** text. Some Unicode: 神真美好.\n
    ") +} + +func TestRenderShortcodesIncludeMarkdownFileWithoutTrailingNewline(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableLiveReload = true +disableKinds = ["home", "taxonomy", "term"] +markup.goldmark.renderer.unsafe = true +-- content/p1.md -- +--- +title: "p1" +--- +Content p1 id-1000.{{% include "p2" %}}{{% include "p3" %}} + +§§§ go +code_p1 +§§§ +§§§ go +code_p1_2 +§§§ + +§§§ go +code_p1_3 +§§§ +-- content/p2.md -- +--- +title: "p2" +--- +§§§ bash +code_p2 +§§§ +Foo. +-- content/p3.md -- +--- +title: "p3" +--- +§§§ php +code_p3 +§§§ +-- layouts/shortcodes/include.html -- +{{ with site.GetPage (.Get 0) -}} +{{ .RenderShortcodes -}} +{{ end -}} +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +{{ .Inner | safeHTML }} +` + + b := TestRunning(t, files, TestOptWarn()) + + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/p1/index.html", "

    Content p1 id-1000.

    \ncode_p2

    Foo.

    \ncode_p3code_p1code_p1_2code_p1_3") + b.EditFileReplaceAll("content/p1.md", "id-1000.", "id-100.").Build() + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/p1/index.html", "

    Content p1 id-100.

    \ncode_p2

    Foo.

    \ncode_p3code_p1code_p1_2code_p1_3") + b.EditFileReplaceAll("content/p2.md", "code_p2", "codep2").Build() + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/p1/index.html", "

    Content p1 id-100.

    \ncodep2

    Foo.

    \ncode_p3code_p1code_p1_2code_p1_3") + b.EditFileReplaceAll("content/p3.md", "code_p3", "code_p3_edited").Build() + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/p1/index.html", "

    Content p1 id-100.

    \ncodep2

    Foo.

    \ncode_p3_editedcode_p1code_p1_2code_p1_3") +} + +// Issue 13004. +func TestRenderShortcodesIncludeShortRefEdit(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableLiveReload = true +disableKinds = ["home", "taxonomy", "term", "section", "rss", "sitemap", "robotsTXT", "404"] +-- content/first/p1.md -- +--- +title: "p1" +--- +## p1-h1 +{{% include "p2" %}} +-- content/second/p2.md -- +--- +title: "p2" +--- +### p2-h1 + +This is some **markup**. +-- layouts/shortcodes/include.html -- +{{ $p := site.GetPage (.Get 0) -}} +{{ $p.RenderShortcodes -}} +-- layouts/_default/single.html -- +{{ .Content }} +` + b := TestRunning(t, files) + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/first/p1/index.html", "

    p1-h1

    \n

    p2-h1

    \n

    This is some markup.

    \n") + b.EditFileReplaceAll("content/second/p2.md", "p2-h1", "p2-h1-edited").Build() + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/first/p1/index.html", "

    p1-h1

    \n

    p2-h1-edited

    \n

    This is some markup.

    \n") +} + +// Issue 13051. +func TestRenderShortcodesEmptyParagraph(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['section','rss','sitemap','taxonomy','term'] +-- layouts/_default/home.html -- +{{ .Content }} +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/shortcodes/include.html -- + {{ with site.GetPage (.Get 0) }} + {{ .RenderShortcodes }} +{{ end }} +-- content/_index.md -- +--- +title: home +--- + +a + +{{% include "/snippet" %}} + +b + +-- content/snippet.md -- +--- +title: snippet +build: + render: never + list: never +--- + +_emphasized_ + +not emphasized + +` + + b := Test(t, files) + b.AssertNoRenderShortcodesArtifacts() + b.AssertFileContentEquals("public/index.html", + "

    a

    \n

    emphasized

    \n

    not emphasized

    \n

    b

    \n", + ) +} diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 0b17a8db0..00e4c0060 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -67,11 +67,11 @@ FIT: {{ $fit.Name }}|{{ $fit.RelPermalink }}|{{ $fit.Width }} CSS integrity Data first: {{ $cssFingerprinted1.Data.Integrity }} {{ $cssFingerprinted1.RelPermalink }} CSS integrity Data last: {{ $cssFingerprinted2.RelPermalink }} {{ $cssFingerprinted2.Data.Integrity }} -{{ $failedImg := resources.GetRemote "%[1]s/fail.jpg" }} +{{ $failedImg := try (resources.GetRemote "%[1]s/fail.jpg") }} {{ $rimg := resources.GetRemote "%[1]s/sunset.jpg" }} {{ $remotenotfound := resources.GetRemote "%[1]s/notfound.jpg" }} {{ $localnotfound := resources.Get "images/notfound.jpg" }} -{{ $gopherprotocol := resources.GetRemote "gopher://example.org" }} +{{ $gopherprotocol := try (resources.GetRemote "gopher://example.org") }} {{ $rfit := $rimg.Fit "200x200" }} {{ $rfit2 := $rfit.Fit "100x200" }} {{ $rimg = $rimg | fingerprint }} @@ -79,10 +79,10 @@ SUNSET REMOTE: {{ $rimg.Name }}|{{ $rimg.RelPermalink }}|{{ $rimg.Width }}|{{ le FIT REMOTE: {{ $rfit.Name }}|{{ $rfit.RelPermalink }}|{{ $rfit.Width }} REMOTE NOT FOUND: {{ if $remotenotfound }}FAILED{{ else}}OK{{ end }} LOCAL NOT FOUND: {{ if $localnotfound }}FAILED{{ else}}OK{{ end }} -PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ . | safeHTML }}{{ end }} +PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ .Value | safeHTML }}{{ end }} PRINT PROTOCOL ERROR2: {{ with $gopherprotocol }}{{ .Err | safeHTML }}{{ end }} -PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}Err: {{ .Err | safeHTML }}{{ with .Err }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }} -FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}| +PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}{{ with .Err }}Err: {{ . | safeHTML }}{{ with .Cause }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}{{ end }} +FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with .Cause }}{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}{{ end }}{{ end }}| `, ts.URL)) fs := b.Fs.Source @@ -99,23 +99,23 @@ FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with . b.Running() - for i := 0; i < 2; i++ { + for i := range 2 { b.Logf("Test run %d", i) b.Build(BuildCfg{}) b.AssertFileContent("public/index.html", fmt.Sprintf(` SUNSET: /images/sunset.jpg|/images/sunset.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT: /images/sunset.jpg|/images/sunset_hu15210517121918042184.jpg|200 +FIT: /images/sunset.jpg|/images/sunset_hu_f2aae87288f3c13b.jpg|200 CSS integrity Data first: sha256-od9YaHw8nMOL8mUy97Sy8sKwMV3N4hI3aVmZXATxH+8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03+ZmKY/1t2GCOjEEOXj2x2qow94vCc7o= SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200 +FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu_f2aae87288f3c13b.jpg|200 REMOTE NOT FOUND: OK LOCAL NOT FOUND: OK -PRINT PROTOCOL ERROR DETAILS: Err: error calling resources.GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"|| -FAILED REMOTE ERROR DETAILS CONTENT: |failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed } +PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at : error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"| +FAILED REMOTE ERROR DETAILS CONTENT: failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed } |StatusCode: 501|ContentLength: 16|ContentType: text/plain; charset=utf-8| @@ -200,7 +200,7 @@ func BenchmarkResourceChainPostProcess(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() s := newTestSitesBuilder(b) - for i := 0; i < 300; i++ { + for i := range 300 { s.WithContent(fmt.Sprintf("page%d.md", i+1), "---\ntitle: Page\n---") } s.WithTemplates("_default/single.html", `Start. diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go index 0c3c21b90..34c2be393 100644 --- a/hugolib/rss_test.go +++ b/hugolib/rss_test.go @@ -96,3 +96,51 @@ Figure: b.AssertFileContent("public/index.xml", "img src="http://example.com/images/sunset.jpg") } + +// Issue 13332. +func TestRSSCanonifyURLsSubDir(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +baseURL = 'https://example.org/subdir' +disableKinds = ['section','sitemap','taxonomy','term'] +[markup.goldmark.renderHooks.image] +enableDefault = true +[markup.goldmark.renderHooks.link] +enableDefault = true +-- layouts/_default/_markup/render-image.html -- +{{- $u := urls.Parse .Destination -}} +{{- $src := $u.String | relURL -}} + + +{{- /**/ -}} +-- layouts/_default/home.html -- +{{ .Content }}| +-- layouts/_default/single.html -- +{{ .Content }}| +-- layouts/_default/rss.xml -- +{{ with site.GetPage "/s1/p2" }} + {{ .Content | transform.XMLEscape | safeHTML }} +{{ end }} +-- content/s1/p1.md -- +--- +title: p1 +--- +-- content/s1/p2/index.md -- +--- +title: p2 +--- +![alt](a.jpg) + +[p1](/s1/p1) +-- content/s1/p2/a.jpg -- +` + + b := Test(t, files) + + b.AssertFileContent("public/index.xml", "https://example.org/subdir/s1/p1/") + b.AssertFileContent("public/index.xml", + "img src="https://example.org/subdir/a.jpg", + "img srcset="https://example.org/subdir/a.jpg" src="https://example.org/subdir/a.jpg 2x") +} diff --git a/hugolib/segments/segments.go b/hugolib/segments/segments.go index 8f7c18121..941c4ea5c 100644 --- a/hugolib/segments/segments.go +++ b/hugolib/segments/segments.go @@ -44,7 +44,7 @@ func (e excludeInclude) ShouldExcludeCoarse(fields SegmentMatcherFields) bool { } // ShouldExcludeFine returns whether the given fields should be excluded. -// This is used for the finer grained checks, e.g. on invididual pages. +// This is used for the finer grained checks, e.g. on individual pages. func (e excludeInclude) ShouldExcludeFine(fields SegmentMatcherFields) bool { if e.exclude != nil && e.exclude(fields) { return true diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 8a478c9df..56bf1ff9e 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Hugo Authors. All rights reserved. +// Copyright 2025 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import ( "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/gohugoio/hugo/parser/pageparser" "github.com/gohugoio/hugo/resources/page" @@ -36,16 +37,16 @@ import ( "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/text" "github.com/gohugoio/hugo/common/urls" - "github.com/gohugoio/hugo/output" bp "github.com/gohugoio/hugo/bufferpool" "github.com/gohugoio/hugo/tpl" ) var ( - _ urls.RefLinker = (*ShortcodeWithPage)(nil) - _ types.Unwrapper = (*ShortcodeWithPage)(nil) - _ text.Positioner = (*ShortcodeWithPage)(nil) + _ urls.RefLinker = (*ShortcodeWithPage)(nil) + _ types.Unwrapper = (*ShortcodeWithPage)(nil) + _ text.Positioner = (*ShortcodeWithPage)(nil) + _ maps.StoreProvider = (*ShortcodeWithPage)(nil) ) // ShortcodeWithPage is the "." context in a shortcode template. @@ -72,7 +73,7 @@ type ShortcodeWithPage struct { posOffset int pos text.Position - scratch *maps.Scratch + store *maps.Scratch } // InnerDeindent returns the (potentially de-indented) inner content of the shortcode. @@ -124,13 +125,19 @@ func (scp *ShortcodeWithPage) RelRef(args map[string]any) (string, error) { return scp.Page.RelRefFrom(args, scp) } +// Store returns this shortcode's Store. +func (scp *ShortcodeWithPage) Store() *maps.Scratch { + if scp.store == nil { + scp.store = maps.NewScratch() + } + return scp.store +} + // Scratch returns a scratch-pad scoped for this shortcode. This can be used // as a temporary storage for variables, counters etc. +// Deprecated: Use Store instead. Note that from the templates this should be considered a "soft deprecation". func (scp *ShortcodeWithPage) Scratch() *maps.Scratch { - if scp.scratch == nil { - scp.scratch = maps.NewScratch() - } - return scp.scratch + return scp.Store() } // Get is a convenience method to look up shortcode parameters by its key. @@ -198,8 +205,7 @@ type shortcode struct { indentation string // indentation from source. - info tpl.Info // One of the output formats (arbitrary) - templs []tpl.Template // All output formats + templ *tplimpl.TemplInfo // If set, the rendered shortcode is sent as part of the surrounding content // to Goldmark and similar. @@ -223,16 +229,15 @@ func (s shortcode) insertPlaceholder() bool { } func (s shortcode) needsInner() bool { - return s.info != nil && s.info.ParseInfo().IsInner + return s.templ != nil && s.templ.ParseInfo.IsInner } func (s shortcode) configVersion() int { - if s.info == nil { + if s.templ == nil { // Not set for inline shortcodes. return 2 } - - return s.info.ParseInfo().Config.Version + return s.templ.ParseInfo.Config.Version } func (s shortcode) innerString() string { @@ -308,12 +313,12 @@ func prepareShortcode( ctx context.Context, level int, s *Site, - tplVariants tpl.TemplateVariants, sc *shortcode, parent *ShortcodeWithPage, - p *pageState, + po *pageOutput, isRenderString bool, ) (shortcodeRenderer, error) { + p := po.p toParseErr := func(err error) error { source := p.m.content.mustSource() return p.parseError(fmt.Errorf("failed to render shortcode %q: %w", sc.name, err), source, sc.pos) @@ -326,7 +331,7 @@ func prepareShortcode( // parsed and rendered by Goldmark. ctx = tpl.Context.IsInGoldmark.Set(ctx, true) } - r, err := doRenderShortcode(ctx, level, s, tplVariants, sc, parent, p, isRenderString) + r, err := doRenderShortcode(ctx, level, s, sc, parent, po, isRenderString) if err != nil { return nil, false, toParseErr(err) } @@ -345,30 +350,29 @@ func doRenderShortcode( ctx context.Context, level int, s *Site, - tplVariants tpl.TemplateVariants, sc *shortcode, parent *ShortcodeWithPage, - p *pageState, + po *pageOutput, isRenderString bool, ) (shortcodeRenderer, error) { - var tmpl tpl.Template + var tmpl *tplimpl.TemplInfo + p := po.p // Tracks whether this shortcode or any of its children has template variations // in other languages or output formats. We are currently only interested in - // the output formats, so we may get some false positives -- we - // should improve on that. + // the output formats. var hasVariants bool if sc.isInline { if !p.s.ExecHelper.Sec().EnableInlineShortcodes { return zeroShortcode, nil } - templName := path.Join("_inline_shortcode", p.Path(), sc.name) + templatePath := path.Join("_inline_shortcode", p.Path(), sc.name) if sc.isClosing { templStr := sc.innerString() var err error - tmpl, err = s.TextTmpl().Parse(templName, templStr) + tmpl, err = s.TemplateStore.TextParse(templatePath, templStr) if err != nil { if isRenderString { return zeroShortcode, p.wrapError(err) @@ -382,24 +386,47 @@ func doRenderShortcode( } else { // Re-use of shortcode defined earlier in the same page. - var found bool - tmpl, found = s.TextTmpl().Lookup(templName) - if !found { + tmpl = s.TemplateStore.TextLookup(templatePath) + if tmpl == nil { return zeroShortcode, fmt.Errorf("no earlier definition of shortcode %q found", sc.name) } } - tmpl = tpl.AddIdentity(tmpl) } else { - var found, more bool - tmpl, found, more = s.Tmpl().LookupVariant(sc.name, tplVariants) - if !found { - s.Log.Errorf("Unable to locate template for shortcode %q in page %q", sc.name, p.File().Path()) - return zeroShortcode, nil + ofCount := map[string]int{} + include := func(match *tplimpl.TemplInfo) bool { + ofCount[match.D.OutputFormat]++ + return true } - hasVariants = hasVariants || more + base, layoutDescriptor := po.GetInternalTemplateBasePathAndDescriptor() + + // With shortcodes/mymarkdown.md (only), this allows {{% mymarkdown %}} when rendering HTML, + // but will not resolve any template when doing {{< mymarkdown >}}. + layoutDescriptor.AlwaysAllowPlainText = sc.doMarkup + q := tplimpl.TemplateQuery{ + Path: base, + Name: sc.name, + Category: tplimpl.CategoryShortcode, + Desc: layoutDescriptor, + Consider: include, + } + v, err := s.TemplateStore.LookupShortcode(q) + if v == nil { + return zeroShortcode, err + } + tmpl = v + hasVariants = hasVariants || len(ofCount) > 1 + } + + data := &ShortcodeWithPage{ + Ordinal: sc.ordinal, + posOffset: sc.pos, + indentation: sc.indentation, + Params: sc.params, + Page: newPageForShortcode(p), + Parent: parent, + Name: sc.name, } - data := &ShortcodeWithPage{Ordinal: sc.ordinal, posOffset: sc.pos, indentation: sc.indentation, Params: sc.params, Page: newPageForShortcode(p), Parent: parent, Name: sc.name} if sc.params != nil { data.IsNamedParams = reflect.TypeOf(sc.params).Kind() == reflect.Map } @@ -411,7 +438,7 @@ func doRenderShortcode( case string: inner += innerData case *shortcode: - s, err := prepareShortcode(ctx, level+1, s, tplVariants, innerData, data, p, isRenderString) + s, err := prepareShortcode(ctx, level+1, s, innerData, data, po, isRenderString) if err != nil { return zeroShortcode, err } @@ -468,7 +495,7 @@ func doRenderShortcode( } - result, err := renderShortcodeWithPage(ctx, s.Tmpl(), tmpl, data) + result, err := renderShortcodeWithPage(ctx, s.GetTemplateStore(), tmpl, data) if err != nil && sc.isInline { fe := herrors.NewFileErrorFromName(err, p.File().Filename()) @@ -518,16 +545,11 @@ func (s *shortcodeHandler) hasName(name string) bool { return ok } -func (s *shortcodeHandler) prepareShortcodesForPage(ctx context.Context, p *pageState, f output.Format, isRenderString bool) (map[string]shortcodeRenderer, error) { +func (s *shortcodeHandler) prepareShortcodesForPage(ctx context.Context, po *pageOutput, isRenderString bool) (map[string]shortcodeRenderer, error) { rendered := make(map[string]shortcodeRenderer) - tplVariants := tpl.TemplateVariants{ - Language: p.Language().Lang, - OutputFormat: f, - } - for _, v := range s.shortcodes { - s, err := prepareShortcode(ctx, 0, s.s, tplVariants, v, nil, p, isRenderString) + s, err := prepareShortcode(ctx, 0, s.s, v, nil, po, isRenderString) if err != nil { return nil, err } @@ -620,7 +642,7 @@ Loop: // we trust the template on this: // if there's no inner, we're done if !sc.isInline { - if !sc.info.ParseInfo().IsInner { + if !sc.templ.ParseInfo.IsInner { return sc, nil } } @@ -634,7 +656,11 @@ Loop: // return that error, more specific continue } - return nil, fmt.Errorf("%s: shortcode %q does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided", errorPrefix, next.ValStr(source)) + name := sc.name + if name == "" { + name = next.ValStr(source) + } + return nil, fmt.Errorf("%s: shortcode %q does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided", errorPrefix, name) } } if next.IsRightShortcodeDelim() { @@ -652,14 +678,13 @@ Loop: sc.name = currItem.ValStr(source) - // Used to check if the template expects inner content. - templs := s.s.Tmpl().LookupVariants(sc.name) - if templs == nil { + // Used to check if the template expects inner content, + // so just pick one arbitrarily with the same name. + templ := s.s.TemplateStore.LookupShortcodeByName(sc.name) + if templ == nil { return nil, fmt.Errorf("%s: template for shortcode %q not found", errorPrefix, sc.name) } - - sc.info = templs[0].(tpl.Info) - sc.templs = templs + sc.templ = templ case currItem.IsInlineShortcodeName(): sc.name = currItem.ValStr(source) sc.isInline = true @@ -758,7 +783,7 @@ func expandShortcodeTokens( return source, nil } -func renderShortcodeWithPage(ctx context.Context, h tpl.TemplateHandler, tmpl tpl.Template, data *ShortcodeWithPage) (string, error) { +func renderShortcodeWithPage(ctx context.Context, h *tplimpl.TemplateStore, tmpl *tplimpl.TemplInfo, data *ShortcodeWithPage) (string, error) { buffer := bp.GetBuffer() defer bp.PutBuffer(buffer) diff --git a/hugolib/shortcode_page.go b/hugolib/shortcode_page.go index 8030b0285..3d27cc93c 100644 --- a/hugolib/shortcode_page.go +++ b/hugolib/shortcode_page.go @@ -125,3 +125,7 @@ func newPageForRenderHook(p *pageState) page.Page { func (p *pageForRenderHooks) Unwrapv() any { return p.p } + +func (p *pageForRenderHooks) String() string { + return p.p.String() +} diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go index 92812bf66..a1f12e77a 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -33,14 +33,14 @@ func TestExtractShortcodes(t *testing.T) { b := newTestSitesBuilder(t).WithSimpleConfigFile() b.WithTemplates( - "default/single.html", `EMPTY`, - "_internal/shortcodes/tag.html", `tag`, - "_internal/shortcodes/legacytag.html", `{{ $_hugo_config := "{ \"version\": 1 }" }}tag`, - "_internal/shortcodes/sc1.html", `sc1`, - "_internal/shortcodes/sc2.html", `sc2`, - "_internal/shortcodes/inner.html", `{{with .Inner }}{{ . }}{{ end }}`, - "_internal/shortcodes/inner2.html", `{{.Inner}}`, - "_internal/shortcodes/inner3.html", `{{.Inner}}`, + "pages/single.html", `EMPTY`, + "shortcodes/tag.html", `tag`, + "shortcodes/legacytag.html", `{{ $_hugo_config := "{ \"version\": 1 }" }}tag`, + "shortcodes/sc1.html", `sc1`, + "shortcodes/sc2.html", `sc2`, + "shortcodes/inner.html", `{{with .Inner }}{{ . }}{{ end }}`, + "shortcodes/inner2.html", `{{.Inner}}`, + "shortcodes/inner3.html", `{{.Inner}}`, ).WithContent("page.md", `--- title: "Shortcodes Galore!" --- @@ -57,10 +57,9 @@ title: "Shortcodes Galore!" if s == nil { return "" } - var version int - if s.info != nil { - version = s.info.ParseInfo().Config.Version + if s.templ != nil { + version = s.templ.ParseInfo.Config.Version } return strReplacer.Replace(fmt.Sprintf("%s;inline:%t;closing:%t;inner:%v;params:%v;ordinal:%d;markup:%t;version:%d;pos:%d", s.name, s.isInline, s.isClosing, s.inner, s.params, s.ordinal, s.doMarkup, version, s.pos)) @@ -69,7 +68,7 @@ title: "Shortcodes Galore!" regexpCheck := func(re string) func(c *qt.C, shortcode *shortcode, err error) { return func(c *qt.C, shortcode *shortcode, err error) { c.Assert(err, qt.IsNil) - c.Assert(str(shortcode), qt.Matches, ".*"+re+".*") + c.Assert(str(shortcode), qt.Matches, ".*"+re+".*", qt.Commentf("%s", shortcode.name)) } } @@ -831,31 +830,47 @@ title: "Hugo Rocks!" func TestShortcodeNoInner(t *testing.T) { t.Parallel() - b := newTestSitesBuilder(t) - - b.WithContent("mypage.md", `--- + files := ` +-- hugo.toml -- +baseURL = "https://example.org" +disableKinds = ["term", "taxonomy", "home", "section"] +-- content/mypage.md -- +--- title: "No Inner!" --- + {{< noinner >}}{{< /noinner >}} +-- layouts/shortcodes/noinner.html -- +No inner here. +-- layouts/_default/single.html -- +Content: {{ .Content }}| -`).WithTemplatesAdded( - "layouts/shortcodes/noinner.html", `No inner here.`) +` - err := b.BuildE(BuildCfg{}) - b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`"content/mypage.md:4:16": failed to extract shortcode: shortcode "noinner" does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided`)) + b, err := TestE(t, files) + + assert := func() { + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`failed to extract shortcode: shortcode "noinner" does not evaluate .Inner or .InnerDeindent, yet a closing tag was provided`)) + } + + assert() + + b, err = TestE(t, strings.Replace(files, `{{< noinner >}}{{< /noinner >}}`, `{{< noinner />}}`, 1)) + + assert() } func TestShortcodeStableOutputFormatTemplates(t *testing.T) { t.Parallel() - for i := 0; i < 5; i++ { + for range 5 { b := newTestSitesBuilder(t) const numPages = 10 - for i := 0; i < numPages; i++ { + for i := range numPages { b.WithContent(fmt.Sprintf("page%d.md", i), `--- title: "Page" outputs: ["html", "css", "csv", "json"] @@ -872,21 +887,22 @@ outputs: ["html", "css", "csv", "json"] "_default/single.json", "{{ .Content }}", "shortcodes/myshort.html", `Short-HTML`, "shortcodes/myshort.csv", `Short-CSV`, + "shortcodes/myshort.txt", `Short-TXT`, ) b.Build(BuildCfg{}) // helpers.PrintFs(b.Fs.Destination, "public", os.Stdout) - for i := 0; i < numPages; i++ { + for i := range numPages { b.AssertFileContent(fmt.Sprintf("public/page%d/index.html", i), "Short-HTML") b.AssertFileContent(fmt.Sprintf("public/page%d/index.csv", i), "Short-CSV") - b.AssertFileContent(fmt.Sprintf("public/page%d/index.json", i), "Short-HTML") + b.AssertFileContent(fmt.Sprintf("public/page%d/index.json", i), "Short-CSV") } - for i := 0; i < numPages; i++ { - b.AssertFileContent(fmt.Sprintf("public/page%d/styles.css", i), "Short-HTML") + for i := range numPages { + b.AssertFileContent(fmt.Sprintf("public/page%d/styles.css", i), "Short-CSV") } } @@ -902,7 +918,7 @@ func TestShortcodeMarkdownOutputFormat(t *testing.T) { --- title: "p1" --- -{{< foo >}} +{{% foo %}} # The below would have failed using the HTML template parser. -- layouts/shortcodes/foo.md -- §§§ @@ -914,9 +930,7 @@ title: "p1" b := Test(t, files) - b.AssertFileContent("public/p1/index.html", ` -<x") } func TestShortcodePreserveIndentation(t *testing.T) { diff --git a/hugolib/site.go b/hugolib/site.go index 08031390b..acd3b5410 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -17,7 +17,6 @@ import ( "context" "errors" "fmt" - "html/template" "io" "mime" "net/url" @@ -43,11 +42,18 @@ import ( "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/hugolib/doctree" "github.com/gohugoio/hugo/hugolib/pagesfromdata" + "github.com/gohugoio/hugo/internal/js/esbuild" "github.com/gohugoio/hugo/internal/warpc" "github.com/gohugoio/hugo/langs/i18n" "github.com/gohugoio/hugo/modules" "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/tpl/tplimpl" + "github.com/gohugoio/hugo/tpl/tplimplinit" + xmaps "golang.org/x/exp/maps" + + // Loads the template funcs namespaces. + "golang.org/x/text/unicode/norm" "github.com/gohugoio/hugo/common/paths" @@ -95,6 +101,7 @@ type Site struct { language *langs.Language languagei int pageMap *pageMap + store *maps.Scratch // The owning container. h *HugoSites @@ -145,8 +152,11 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { if cfg.Configs.Base.PanicOnWarning { logHookLast = loggers.PanicOnWarningHook } - if cfg.LogOut == nil { - cfg.LogOut = os.Stdout + if cfg.StdOut == nil { + cfg.StdOut = os.Stdout + } + if cfg.StdErr == nil { + cfg.StdErr = os.Stderr } if cfg.LogLevel == 0 { cfg.LogLevel = logg.LevelWarn @@ -156,8 +166,8 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { Level: cfg.LogLevel, DistinctLevel: logg.LevelWarn, // This will drop duplicate log warning and errors. HandlerPost: logHookLast, - Stdout: cfg.LogOut, - Stderr: cfg.LogOut, + StdOut: cfg.StdOut, + StdErr: cfg.StdErr, StoreErrors: conf.Watching(), SuppressStatements: conf.IgnoredLogs(), } @@ -184,8 +194,8 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { BuildState: &deps.BuildState{ OnSignalRebuild: onSignalRebuild, }, + Counters: &deps.Counters{}, MemCache: memCache, - TemplateProvider: tplimpl.DefaultTemplateProvider, TranslationProvider: i18n.NewTranslationProvider(), WasmDispatchers: warpc.AllDispatchers( warpc.Options{ @@ -194,6 +204,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { // Katex is relatively slow. PoolSize: 8, Infof: logger.InfoCommand("wasm").Logf, + Warnf: logger.WarnCommand("wasm").Logf, }, ), } @@ -202,6 +213,12 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { return nil, err } + batcherClient, err := esbuild.NewBatcherClient(firstSiteDeps) + if err != nil { + return nil, err + } + firstSiteDeps.JSBatcherClient = batcherClient + confm := cfg.Configs if err := confm.Validate(logger); err != nil { return nil, err @@ -248,6 +265,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { language: language, languagei: i, frontmatterHandler: frontmatterHandler, + store: maps.NewScratch(), } if i == 0 { @@ -309,7 +327,6 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { return li.Lang < lj.Lang }) - var err error h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites) if err == nil && h == nil { panic("hugo: newHugoSitesNew returned nil error and nil HugoSites") @@ -320,10 +337,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) { numWorkers := config.GetNumWorkerMultiplier() - numWorkersSite := numWorkers - if numWorkersSite > len(sites) { - numWorkersSite = len(sites) - } + numWorkersSite := min(numWorkers, len(sites)) workersSite := para.New(numWorkersSite) h := &HugoSites{ @@ -344,7 +358,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites [] skipRebuildForFilenames: make(map[string]bool), init: &hugoSitesInit{ data: lazy.New(), - layouts: lazy.New(), gitInfo: lazy.New(), }, } @@ -379,6 +392,35 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites [] var prototype *deps.Deps for i, s := range sites { s.h = h + // The template store needs to be initialized after the h container is set on s. + if i == 0 { + templateStore, err := tplimpl.NewStore( + tplimpl.StoreOptions{ + Fs: s.BaseFs.Layouts.Fs, + Log: s.Log, + DefaultContentLanguage: s.Conf.DefaultContentLanguage(), + Watching: s.Conf.Watching(), + PathParser: s.Conf.PathParser(), + Metrics: d.Metrics, + OutputFormats: s.conf.OutputFormats.Config, + MediaTypes: s.conf.MediaTypes.Config, + DefaultOutputFormat: s.conf.DefaultOutputFormat, + TaxonomySingularPlural: s.conf.Taxonomies, + }, tplimpl.SiteOptions{ + Site: s, + TemplateFuncs: tplimplinit.CreateFuncMap(s.Deps), + }) + if err != nil { + return nil, err + } + s.Deps.TemplateStore = templateStore + } else { + s.Deps.TemplateStore = prototype.TemplateStore.WithSiteOpts( + tplimpl.SiteOptions{ + Site: s, + TemplateFuncs: tplimplinit.CreateFuncMap(s.Deps), + }) + } if err := s.Deps.Compile(prototype); err != nil { return nil, err } @@ -400,15 +442,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites [] return nil, nil }) - h.init.layouts.Add(func(context.Context) (any, error) { - for _, s := range h.Sites { - if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil { - return nil, err - } - } - return nil, nil - }) - h.init.gitInfo.Add(func(context.Context) (any, error) { err := h.loadGitInfo() if err != nil { @@ -420,12 +453,6 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites [] return h, nil } -// Deprecated: Use hugo.IsServer instead. -func (s *Site) IsServer() bool { - hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0") - return s.conf.Internal.Running -} - // Returns the server port. func (s *Site) ServerPort() int { return s.conf.C.BaseURL.Port() @@ -440,13 +467,6 @@ func (s *Site) Copyright() string { return s.conf.Copyright } -// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead. -func (s *Site) RSSLink() template.URL { - hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0") - rssOutputFormat := s.home.OutputFormats().Get("rss") - return template.URL(rssOutputFormat.Permalink()) -} - func (s *Site) Config() page.SiteConfig { return page.SiteConfig{ Privacy: s.conf.Privacy, @@ -480,7 +500,10 @@ func (s *Site) MainSections() []string { // Returns a struct with some information about the build. func (s *Site) Hugo() hugo.HugoInfo { - if s.h == nil || s.h.hugoInfo.Environment == "" { + if s.h == nil { + panic("site: hugo: h not initialized") + } + if s.h.hugoInfo.Environment == "" { panic("site: hugo: hugoInfo not initialized") } return s.h.hugoInfo @@ -528,18 +551,6 @@ func (s *Site) Social() map[string]string { return s.conf.Social } -// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead. -func (s *Site) DisqusShortname() string { - hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0") - return s.Config().Services.Disqus.Shortname -} - -// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead. -func (s *Site) GoogleAnalytics() string { - hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0") - return s.Config().Services.GoogleAnalytics.ID -} - func (s *Site) Param(key any) (any, error) { return resource.Param(s, nil, key) } @@ -624,6 +635,10 @@ func (s *Site) AllRegularPages() page.Pages { return s.h.RegularPages() } +func (s *Site) Store() *maps.Scratch { + return s.store +} + func (s *Site) CheckReady() { if s.state != siteStateReady { panic("this method cannot be called before the site is fully initialized") @@ -790,7 +805,7 @@ func (s *Site) initRenderFormats() { Tree: s.pageMap.treePages, Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { if p, ok := n.(*pageState); ok { - for _, f := range p.m.configuredOutputFormats { + for _, f := range p.m.pageConfig.ConfiguredOutputFormats { if !formatSet[f.Name] { formats = append(formats, f) formatSet[f.Name] = true @@ -821,7 +836,7 @@ func (s *Site) initRenderFormats() { s.renderFormats = formats } -func (s *Site) GetRelatedDocsHandler() *page.RelatedDocsHandler { +func (s *Site) GetInternalRelatedDocsHandler() *page.RelatedDocsHandler { return s.relatedDocsHandler } @@ -947,19 +962,24 @@ type WhatChanged struct { mu sync.Mutex needsPagesAssembly bool - identitySet identity.Identities + + ids map[identity.Identity]bool +} + +func (w *WhatChanged) init() { + if w.ids == nil { + w.ids = make(map[identity.Identity]bool) + } } func (w *WhatChanged) Add(ids ...identity.Identity) { w.mu.Lock() defer w.mu.Unlock() - if w.identitySet == nil { - w.identitySet = make(identity.Identities) - } + w.init() for _, id := range ids { - w.identitySet[id] = true + w.ids[id] = true } } @@ -970,20 +990,20 @@ func (w *WhatChanged) Clear() { } func (w *WhatChanged) clear() { - w.identitySet = identity.Identities{} + w.ids = nil } func (w *WhatChanged) Changes() []identity.Identity { - if w == nil || w.identitySet == nil { + if w == nil || w.ids == nil { return nil } - return w.identitySet.AsSlice() + return xmaps.Keys(w.ids) } func (w *WhatChanged) Drain() []identity.Identity { w.mu.Lock() defer w.mu.Unlock() - ids := w.identitySet.AsSlice() + ids := w.Changes() w.clear() return ids } @@ -1245,6 +1265,8 @@ func (s *Site) assembleMenus() error { // If page is still nill, we must make sure that we have a URL that considers baseURL etc. if types.IsNil(me.Page) { me.ConfiguredURL = s.createNodeMenuEntryURL(me.MenuConfig.URL) + } else { + navigation.SetPageValues(me, me.Page) } flat[twoD{name, me.KeyName()}] = me @@ -1361,6 +1383,7 @@ func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string { func (s *Site) resetBuildState(sourceChanged bool) { s.relatedDocsHandler = s.relatedDocsHandler.Clone() s.init.Reset() + s.pageMap.Reset() } func (s *Site) errorCollator(results <-chan error, errs chan<- error) { @@ -1415,7 +1438,7 @@ const ( pageDependencyScopeGlobal ) -func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath string, p *pageState, d any, templ tpl.Template) error { +func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath string, p *pageState, d any, templ *tplimpl.TemplInfo) error { s.h.buildCounters.pageRenderCounter.Add(1) renderBuffer := bp.GetBuffer() defer bp.PutBuffer(renderBuffer) @@ -1474,8 +1497,8 @@ var infoOnMissingLayout = map[string]bool{ // hookRendererTemplate is the canonical implementation of all hooks.ITEMRenderer, // where ITEM is the thing being hooked. type hookRendererTemplate struct { - templateHandler tpl.TemplateHandler - templ tpl.Template + templateHandler *tplimpl.TemplateStore + templ *tplimpl.TemplInfo resolvePosition func(ctx any) text.Position } @@ -1511,7 +1534,7 @@ func (hr hookRendererTemplate) IsDefaultCodeBlockRenderer() bool { return false } -func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, d any, w io.Writer, templ tpl.Template) (err error) { +func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, d any, w io.Writer, templ *tplimpl.TemplInfo) (err error) { if templ == nil { s.logMissingLayout(name, "", "", outputFormat) return nil @@ -1521,8 +1544,12 @@ func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string, panic("nil context") } - if err = s.Tmpl().ExecuteWithContext(ctx, templ, w, d); err != nil { - return fmt.Errorf("render of %q failed: %w", name, err) + if err = s.GetTemplateStore().ExecuteWithContext(ctx, templ, w, d); err != nil { + filename := name + if p, ok := d.(*pageState); ok { + filename = p.String() + } + return fmt.Errorf("render of %q failed: %w", filename, err) } return } @@ -1556,7 +1583,7 @@ func (s *Site) render(ctx *siteRenderContext) (err error) { return err } - if ctx.outIdx == 0 { + if ctx.outIdx == 0 && s.h.buildCounter.Load() == 0 { // Note that even if disableAliases is set, the aliases themselves are // preserved on page. The motivation with this is to be able to generate // 301 redirects in a .htaccess file and similar using a custom output format. diff --git a/hugolib/site_output.go b/hugolib/site_output.go index 2744c0133..3438ea9f7 100644 --- a/hugolib/site_output.go +++ b/hugolib/site_output.go @@ -27,6 +27,7 @@ func createDefaultOutputFormats(allFormats output.Formats) map[string]output.For htmlOut, _ := allFormats.GetByName(output.HTMLFormat.Name) robotsOut, _ := allFormats.GetByName(output.RobotsTxtFormat.Name) sitemapOut, _ := allFormats.GetByName(output.SitemapFormat.Name) + httpStatus404Out, _ := allFormats.GetByName(output.HTTPStatus404HTMLFormat.Name) defaultListTypes := output.Formats{htmlOut} if rssFound { @@ -42,7 +43,7 @@ func createDefaultOutputFormats(allFormats output.Formats) map[string]output.For // Below are for consistency. They are currently not used during rendering. kinds.KindSitemap: {sitemapOut}, kinds.KindRobotsTXT: {robotsOut}, - kinds.KindStatus404: {htmlOut}, + kinds.KindStatus404: {httpStatus404Out}, } // May be disabled @@ -80,7 +81,7 @@ func createSiteOutputFormats(allFormats output.Formats, outputs map[string]any, f, found := allFormats.GetByName(format) if !found { if rssDisabled && strings.EqualFold(format, "RSS") { - // This is legacy behaviour. We used to have both + // This is legacy behavior. We used to have both // a RSS page kind and output format. continue } diff --git a/hugolib/site_output_test.go b/hugolib/site_output_test.go index 37ebce730..caec4c700 100644 --- a/hugolib/site_output_test.go +++ b/hugolib/site_output_test.go @@ -387,7 +387,7 @@ func TestCreateSiteOutputFormats(t *testing.T) { c.Assert(outputs[kinds.KindRSS], deepEqualsOutputFormats, output.Formats{output.RSSFormat}) c.Assert(outputs[kinds.KindSitemap], deepEqualsOutputFormats, output.Formats{output.SitemapFormat}) c.Assert(outputs[kinds.KindRobotsTXT], deepEqualsOutputFormats, output.Formats{output.RobotsTxtFormat}) - c.Assert(outputs[kinds.KindStatus404], deepEqualsOutputFormats, output.Formats{output.HTMLFormat}) + c.Assert(outputs[kinds.KindStatus404], deepEqualsOutputFormats, output.Formats{output.HTTPStatus404HTMLFormat}) }) // Issue #4528 @@ -481,6 +481,7 @@ permalinkable = true [outputFormats.nobase] mediaType = "application/json" permalinkable = true +isPlainText = true ` diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 83f2fce89..6dbb19827 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -20,11 +20,12 @@ import ( "strings" "sync" + "github.com/bep/logg" "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/hugolib/doctree" + "github.com/gohugoio/hugo/tpl/tplimpl" "github.com/gohugoio/hugo/config" - "github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/resources/kinds" "github.com/gohugoio/hugo/resources/page" @@ -33,6 +34,8 @@ import ( type siteRenderContext struct { cfg *BuildCfg + infol logg.LevelLogger + // languageIdx is the zero based index of the site. languageIdx int @@ -54,7 +57,7 @@ func (s siteRenderContext) shouldRenderStandalonePage(kind string) bool { return s.outIdx == 0 } - if kind == kinds.KindStatus404 { + if kind == kinds.KindTemporary || kind == kinds.KindStatus404 { // 1 for all output formats return s.outIdx == 0 } @@ -75,7 +78,7 @@ func (s *Site) renderPages(ctx *siteRenderContext) error { wg := &sync.WaitGroup{} - for i := 0; i < numWorkers; i++ { + for range numWorkers { wg.Add(1) go pageRenderer(ctx, s, pages, results, wg) } @@ -86,7 +89,7 @@ func (s *Site) renderPages(ctx *siteRenderContext) error { Tree: s.pageMap.treePages, Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { if p, ok := n.(*pageState); ok { - if cfg.shouldRender(p) { + if cfg.shouldRender(ctx.infol, p) { select { case <-s.h.Done(): return true, nil @@ -222,7 +225,7 @@ func (s *Site) logMissingLayout(name, layout, kind, outputFormat string) { } // renderPaginator must be run after the owning Page has been rendered. -func (s *Site) renderPaginator(p *pageState, templ tpl.Template) error { +func (s *Site) renderPaginator(p *pageState, templ *tplimpl.TemplInfo) error { paginatePath := s.Conf.Pagination().Path d := p.targetPathDescriptor @@ -334,6 +337,9 @@ func (s *Site) renderAliases() error { // renderMainLanguageRedirect creates a redirect to the main language home, // depending on if it lives in sub folder (e.g. /en) or not. func (s *Site) renderMainLanguageRedirect() error { + if s.conf.DisableDefaultLanguageRedirect { + return nil + } if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultilingual()) { // No need for a redirect return nil diff --git a/hugolib/site_stats_test.go b/hugolib/site_stats_test.go index da09ec368..c045963f3 100644 --- a/hugolib/site_stats_test.go +++ b/hugolib/site_stats_test.go @@ -16,7 +16,6 @@ package hugolib import ( "bytes" "fmt" - "io" "testing" "github.com/gohugoio/hugo/helpers" @@ -69,15 +68,15 @@ aliases: [/Ali%d] "_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}", ) - for i := 0; i < 2; i++ { - for j := 0; j < 2; j++ { + for i := range 2 { + for j := range 2 { pageID := i + j + 1 b.WithContent(fmt.Sprintf("content/sect/p%d.md", pageID), fmt.Sprintf(pageTemplate, pageID, fmt.Sprintf("- tag%d", j), fmt.Sprintf("- category%d", j), pageID)) } } - for i := 0; i < 5; i++ { + for i := range 5 { b.WithContent(fmt.Sprintf("assets/image%d.png", i+1), "image") } @@ -89,14 +88,11 @@ aliases: [/Ali%d] h.Sites[1].PathSpec.ProcessingStats, } - stats[0].Table(io.Discard) - stats[1].Table(io.Discard) - var buff bytes.Buffer helpers.ProcessingStatsTable(&buff, stats...) - c.Assert(buff.String(), qt.Contains, "Pages | 21 | 7") + c.Assert(buff.String(), qt.Contains, "Pages │ 21 │ 7") } func TestSiteLastmod(t *testing.T) { diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 2ee33da24..199c878cd 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -372,14 +372,14 @@ func TestMainSections(t *testing.T) { b := newTestSitesBuilder(c).WithViper(v) - for i := 0; i < 20; i++ { + for i := range 20 { b.WithContent(fmt.Sprintf("page%d.md", i), `--- title: "Page" --- `) } - for i := 0; i < 5; i++ { + for i := range 5 { b.WithContent(fmt.Sprintf("blog/page%d.md", i), `--- title: "Page" tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] @@ -387,7 +387,7 @@ tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] `) } - for i := 0; i < 3; i++ { + for i := range 3 { b.WithContent(fmt.Sprintf("docs/page%d.md", i), `--- title: "Page" --- @@ -615,7 +615,7 @@ var weightedPage5 = `+++ weight = "5" title = "Five" -[_build] +[build] render = "never" +++ Front Matter with Ordered Pages 5` @@ -978,7 +978,6 @@ func TestRefLinking(t *testing.T) { {".", "", true, "/level2/level3/"}, {"./", "", true, "/level2/level3/"}, - // try to confuse parsing {"embedded.dot.md", "", true, "/level2/level3/embedded.dot/"}, // test empty link, as well as fragment only link diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go index 29170118f..091251f80 100644 --- a/hugolib/site_url_test.go +++ b/hugolib/site_url_test.go @@ -97,7 +97,7 @@ Do not go gentle into that good night. writeSource(t, fs, filepath.Join("content", "sect1", "_index.md"), fmt.Sprintf(st, "/ss1/")) writeSource(t, fs, filepath.Join("content", "sect2", "_index.md"), fmt.Sprintf(st, "/ss2/")) - for i := 0; i < 5; i++ { + for i := range 5 { writeSource(t, fs, filepath.Join("content", "sect1", fmt.Sprintf("p%d.md", i+1)), pt) writeSource(t, fs, filepath.Join("content", "sect2", fmt.Sprintf("p%d.md", i+1)), pt) } diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go index 1c2642468..922ecbc12 100644 --- a/hugolib/sitemap_test.go +++ b/hugolib/sitemap_test.go @@ -139,7 +139,7 @@ weight = 1 languageName = "English" [languages.nn] weight = 2 --- layouts/_default/list.xml -- +-- layouts/list.xml -- Site: {{ .Site.Title }}| -- layouts/home -- Home. diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go index 26148dd1b..7aeaa780c 100644 --- a/hugolib/taxonomy_test.go +++ b/hugolib/taxonomy_test.go @@ -76,6 +76,8 @@ func TestTaxonomiesWithAndWithoutContentFile(t *testing.T) { } func doTestTaxonomiesWithAndWithoutContentFile(t *testing.T, uglyURLs bool) { + t.Helper() + siteConfig := ` baseURL = "http://example.com/blog" titleCaseStyle = "firstupper" @@ -314,7 +316,7 @@ func TestTaxonomiesNextGenLoops(t *testing.T) { `) - for i := 0; i < 10; i++ { + for i := range 10 { b.WithContent(fmt.Sprintf("page%d.md", i+1), ` --- Title: "Taxonomy!" diff --git a/hugolib/template_test.go b/hugolib/template_test.go index 055d9593c..a08f83cb8 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -26,6 +26,8 @@ import ( "github.com/gohugoio/hugo/hugofs" ) +// TODO(bep) keep this until we release v0.146.0 as a security against breaking changes, but it's rather messy and mostly duplicate of +// tests in the tplimpl package, so eventually just remove it. func TestTemplateLookupOrder(t *testing.T) { var ( fs *hugofs.Fs @@ -185,6 +187,9 @@ func TestTemplateLookupOrder(t *testing.T) { } { this := this + if this.name != "Variant 1" { + continue + } t.Run(this.name, func(t *testing.T) { // TODO(bep) there are some function vars need to pull down here to enable => t.Parallel() cfg, fs = newTestCfg() @@ -200,7 +205,7 @@ Some content } buildSingleSite(t, deps.DepsCfg{Fs: fs, Configs: configs}, BuildCfg{}) - // helpers.PrintFs(s.BaseFs.Layouts.Fs, "", os.Stdout) + // s.TemplateStore.PrintDebug("", 0, os.Stdout) this.assert(t) }) @@ -250,7 +255,7 @@ Content. Base %d: {{ block "main" . }}FOO{{ end }} ` - for i := 0; i < numPages; i++ { + for i := range numPages { id := i + 1 b.WithContent(fmt.Sprintf("page%d.md", id), fmt.Sprintf(pageTemplate, id, id)) b.WithTemplates(fmt.Sprintf("_default/layout%d.html", id), fmt.Sprintf(singleTemplate, id)) @@ -258,7 +263,7 @@ Base %d: {{ block "main" . }}FOO{{ end }} } b.Build(BuildCfg{}) - for i := 0; i < numPages; i++ { + for i := range numPages { id := i + 1 b.AssertFileContent(fmt.Sprintf("public/page%d/index.html", id), fmt.Sprintf(`Base %d: %d`, id, id)) } @@ -270,11 +275,11 @@ func TestTemplateNoBasePlease(t *testing.T) { b := newTestSitesBuilder(t).WithSimpleConfigFile() b.WithTemplates("_default/list.html", ` - {{ define "main" }} - Bonjour - {{ end }} +{{ define "main" }} + Bonjour +{{ end }} - {{ printf "list" }} +{{ printf "list" }} `) @@ -344,33 +349,36 @@ title: %s b.AssertFileContent("public/p1/index.html", `Single: P1`) }) - t.Run("baseof", func(t *testing.T) { - t.Parallel() - b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig() + { + } +} - b.WithTemplatesAdded( - "index.html", `{{ define "main" }}Main Home En{{ end }}`, - "index.fr.html", `{{ define "main" }}Main Home Fr{{ end }}`, - "baseof.html", `Baseof en: {{ block "main" . }}main block{{ end }}`, - "baseof.fr.html", `Baseof fr: {{ block "main" . }}main block{{ end }}`, - "mysection/baseof.html", `Baseof mysection: {{ block "main" . }}mysection block{{ end }}`, - "_default/single.html", `{{ define "main" }}Main Default Single{{ end }}`, - "_default/list.html", `{{ define "main" }}Main Default List{{ end }}`, - ) +func TestTemplateLookupSitBaseOf(t *testing.T) { + t.Parallel() + b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig() - b.WithContent("mysection/p1.md", `--- + b.WithTemplatesAdded( + "index.html", `{{ define "main" }}Main Home En{{ end }}`, + "index.fr.html", `{{ define "main" }}Main Home Fr{{ end }}`, + "baseof.html", `Baseof en: {{ block "main" . }}main block{{ end }}`, + "baseof.fr.html", `Baseof fr: {{ block "main" . }}main block{{ end }}`, + "mysection/baseof.html", `Baseof mysection: {{ block "main" . }}mysection block{{ end }}`, + "_default/single.html", `{{ define "main" }}Main Default Single{{ end }}`, + "_default/list.html", `{{ define "main" }}Main Default List{{ end }}`, + ) + + b.WithContent("mysection/p1.md", `--- title: My Page --- `) - b.CreateSites().Build(BuildCfg{}) + b.CreateSites().Build(BuildCfg{}) - b.AssertFileContent("public/en/index.html", `Baseof en: Main Home En`) - b.AssertFileContent("public/fr/index.html", `Baseof fr: Main Home Fr`) - b.AssertFileContent("public/en/mysection/index.html", `Baseof mysection: Main Default List`) - b.AssertFileContent("public/en/mysection/p1/index.html", `Baseof mysection: Main Default Single`) - }) + b.AssertFileContent("public/en/index.html", `Baseof en: Main Home En`) + b.AssertFileContent("public/fr/index.html", `Baseof fr: Main Home Fr`) + b.AssertFileContent("public/en/mysection/index.html", `Baseof mysection: Main Default List`) + b.AssertFileContent("public/en/mysection/p1/index.html", `Baseof mysection: Main Default Single`) } func TestTemplateFuncs(t *testing.T) { @@ -707,6 +715,7 @@ a: {{ $a }} b.AssertFileContent("public/index.html", `a: [a b c]`) } +// Legacy behavior for internal templates. func TestOverrideInternalTemplate(t *testing.T) { files := ` -- hugo.toml -- diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go index 08eb21787..2007b658d 100644 --- a/hugolib/testhelpers_test.go +++ b/hugolib/testhelpers_test.go @@ -260,7 +260,7 @@ disable = false respectDoNotTrack = true [privacy.instagram] simple = true -[privacy.twitter] +[privacy.x] enableDNT = true [privacy.vimeo] disable = false @@ -838,7 +838,7 @@ func (s *sitesBuilder) NpmInstall() hexec.Runner { var err error sc.Exec.Allow, err = security.NewWhitelist("npm") s.Assert(err, qt.IsNil) - ex := hexec.New(sc, s.workingDir) + ex := hexec.New(sc, s.workingDir, loggers.NewDefault()) command, err := ex.New("npm", "install") s.Assert(err, qt.IsNil) return command diff --git a/hugoreleaser.env b/hugoreleaser.env index 66d5d0cc3..6da749524 100644 --- a/hugoreleaser.env +++ b/hugoreleaser.env @@ -1,7 +1,43 @@ # Release env. # These will be replaced by script before release. -HUGORELEASER_TAG=v0.136.3 -HUGORELEASER_COMMITISH=bfa2fd683e7f0874c7f7e2198ec407a037dadf14 +HUGORELEASER_TAG=v0.147.9 +HUGORELEASER_COMMITISH=29bdbde19c288d190e889294a862103c6efb70bf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hugoreleaser.toml b/hugoreleaser.toml deleted file mode 100644 index d516bd34b..000000000 --- a/hugoreleaser.toml +++ /dev/null @@ -1,239 +0,0 @@ -project = "hugo" - -# In Hugo v0.103.0 we removed the archive name replacements (e.g. amd64 => 64bit). -# Using standard GOOS/GOARCH values makes it easier for scripts out there, -# but to prevent breakage in Netlify etc. that has adopted to the old names, -# we create aliases for the most common variants. -# According to download numbers from v0.101.0, these are by a good margin the two most popular: -# hugo_extended_0.101.0_Linux-64bit.tar.gz Downloaded 129,016 times -# hugo_0.101.0_Linux-64bit.tar.gz Downloaded 87,846 times -# This replacement will create 2 extra alias archives. -archive_alias_replacements = { "linux-amd64.tar.gz" = "Linux-64bit.tar.gz" } - -[go_settings] - go_proxy = "https://proxy.golang.org" - go_exe = "go" - -[build_settings] - binary = "hugo" - flags = ["-buildmode", "exe"] - env = ["CGO_ENABLED=0"] - ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio" - -[archive_settings] - name_template = "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - extra_files = [ - { source_path = "README.md", target_path = "README.md" }, - { source_path = "LICENSE", target_path = "LICENSE" }, - ] - [archive_settings.type] - format = "tar.gz" - extension = ".tar.gz" - -[release_settings] - name = "${HUGORELEASER_TAG}" - type = "github" - repository = "hugo" - repository_owner = "gohugoio" - draft = true - prerelease = false - - [release_settings.release_notes_settings] - # Use Hugoreleaser's autogenerated release notes. - generate = true - - # Collapse releases with < 10 changes below one title. - short_threshold = 10 - short_title = "What's Changed" - - groups = [ - # Group the changes in the release notes by title. - # You need at least one. - # The groups will be tested in order until a match is found. - # The titles will so be listed in the given order in the release note. - # Any match with ignore=true title will be dropped. - { regexp = "Merge commit|Squashed|releaser:", ignore = true }, - { title = "Note", regexp = "(note|deprecated)", ordinal = 10 }, - { title = "Bug fixes", regexp = "fix", ordinal = 15 }, - { title = "Dependency Updates", regexp = "deps", ordinal = 30 }, - { title = "Build Setup", regexp = "(snap|release|update to)", ordinal = 40 }, - { title = "Documentation", regexp = "(doc|readme)", ordinal = 40 }, - { title = "Improvements", regexp = ".*", ordinal = 20 }, - ] - -[[builds]] - path = "container1/unix/regular" - - [[builds.os]] - goos = "darwin" - [[builds.os.archs]] - goarch = "universal" - [[builds.os]] - goos = "linux" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os.archs]] - goarch = "arm64" - [[builds.os.archs]] - goarch = "arm" - [builds.os.archs.build_settings] - env = ["CGO_ENABLED=0", "GOARM=7"] - - # Unix BSD variants - [[builds.os]] - goos = "dragonfly" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "freebsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "netbsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "openbsd" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os]] - goos = "solaris" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container1/unix/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - env = ["CGO_ENABLED=1"] - - [[builds.os]] - goos = "darwin" - [builds.os.build_settings] - env = ["CGO_ENABLED=1", "CC=o64-clang", "CXX=o64-clang++"] - [[builds.os.archs]] - goarch = "universal" - [[builds.os]] - goos = "linux" - [[builds.os.archs]] - goarch = "amd64" - -[[builds]] - path = "container2/linux/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - - [[builds.os]] - goos = "linux" - [builds.os.build_settings] - env = [ - "CGO_ENABLED=1", - "CC=aarch64-linux-gnu-gcc", - "CXX=aarch64-linux-gnu-g++", - ] - [[builds.os.archs]] - goarch = "arm64" - -[[builds]] - path = "container1/windows/regular" - - [[builds.os]] - goos = "windows" - [builds.os.build_settings] - binary = "hugo.exe" - [[builds.os.archs]] - goarch = "amd64" - [[builds.os.archs]] - goarch = "arm64" - -[[builds]] - path = "container1/windows/extended" - - [builds.build_settings] - flags = ["-buildmode", "exe", "-tags", "extended"] - env = [ - "CGO_ENABLED=1", - "CC=x86_64-w64-mingw32-gcc", - "CXX=x86_64-w64-mingw32-g++", - ] - ldflags = "-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static'" - - [[builds.os]] - goos = "windows" - [builds.os.build_settings] - binary = "hugo.exe" - [[builds.os.archs]] - goarch = "amd64" - -[[archives]] - paths = ["builds/container1/unix/regular/**"] -[[archives]] - paths = ["builds/container1/unix/extended/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - # Only extended builds in container2. - paths = ["builds/container2/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" -[[archives]] - paths = ["builds/**/windows/regular/**"] - [archives.archive_settings.type] - format = "zip" - extension = ".zip" -[[archives]] - paths = ["builds/**/windows/extended/**"] - [archives.archive_settings] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - [archives.archive_settings.type] - format = "zip" - extension = ".zip" -[[archives]] - paths = ["builds/**/regular/linux/{arm64,amd64}"] - [archives.archive_settings] - binary_dir = "/usr/local/bin" - extra_files = [] - [archives.archive_settings.type] - format = "_plugin" - extension = ".deb" - [archives.archive_settings.plugin] - id = "deb" - type = "gorun" - command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@v0.6.1" - [archives.archive_settings.custom_settings] - vendor = "gohugo.io" - homepage = "https://github.com/gohugoio/hugo" - maintainer = "Bjørn Erik Pedersen " - description = "A fast and flexible Static Site Generator written in Go." - license = "Apache-2.0" -[[archives]] - paths = ["builds/**/extended/linux/{arm64,amd64}"] - [archives.archive_settings] - binary_dir = "/usr/local/bin" - extra_files = [] - name_template = "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" - [archives.archive_settings.type] - format = "_plugin" - extension = ".deb" - [archives.archive_settings.plugin] - id = "deb" - type = "gorun" - command = "github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest" - [archives.archive_settings.custom_settings] - vendor = "gohugo.io" - homepage = "https://github.com/gohugoio/hugo" - maintainer = "Bjørn Erik Pedersen " - description = "A fast and flexible Static Site Generator written in Go." - license = "Apache-2.0" - -[[releases]] - paths = ["archives/**"] - path = "r1" - - # The above should allow the following build commands: - # hugoreleaser build -paths "builds/container1/**" - # hugoreleaser build -paths "builds/container2/**" - # hugoreleaser archive - # hugoreleaser release diff --git a/hugoreleaser.yaml b/hugoreleaser.yaml new file mode 100644 index 000000000..368bc898f --- /dev/null +++ b/hugoreleaser.yaml @@ -0,0 +1,272 @@ +project: hugo + +# Common definitions. +definitions: + archive_type_zip: &archive_type_zip + type: + format: zip + extension: .zip + env_extended_linux: &env_extended_linux + - CGO_ENABLED=1 + - CC=aarch64-linux-gnu-gcc + - CXX=aarch64-linux-gnu-g++ + env_extended_windows: &env_extended_windows + - CGO_ENABLED=1 + - CC=x86_64-w64-mingw32-gcc + - CXX=x86_64-w64-mingw32-g++ + env_extended_darwin: &env_extended_darwin + - CGO_ENABLED=1 + - CC=o64-clang + - CXX=o64-clang++ + name_template_extended_withdeploy: &name_template_extended_withdeploy "{{ .Project }}_extended_withdeploy_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + name_template_extended: &name_template_extended "{{ .Project }}_extended_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + archive_deb: &archive_deb + binary_dir: /usr/local/bin + extra_files: [] + type: + format: _plugin + extension: .deb + plugin: + id: deb + type: gorun + command: github.com/gohugoio/hugoreleaser-archive-plugins/deb@latest + custom_settings: + vendor: gohugo.io + homepage: https://github.com/gohugoio/hugo + maintainer: Bjørn Erik Pedersen + description: A fast and flexible Static Site Generator written in Go. + license: Apache-2.0 +archive_alias_replacements: + linux-amd64.tar.gz: Linux-64bit.tar.gz +go_settings: + go_proxy: https://proxy.golang.org + go_exe: go +build_settings: + binary: hugo + flags: + - -buildmode + - exe + env: + - CGO_ENABLED=0 + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio +archive_settings: + name_template: "{{ .Project }}_{{ .Tag | trimPrefix `v` }}_{{ .Goos }}-{{ .Goarch }}" + extra_files: + - source_path: README.md + target_path: README.md + - source_path: LICENSE + target_path: LICENSE + type: + format: tar.gz + extension: .tar.gz +release_settings: + name: ${HUGORELEASER_TAG} + type: github + repository: hugo + repository_owner: gohugoio + draft: true + prerelease: false + release_notes_settings: + generate: true + short_threshold: 10 + short_title: What's Changed + groups: + - regexp: "Merge commit|Squashed|releaser:" + ignore: true + - title: Note + regexp: (note|deprecated) + ordinal: 10 + - title: Bug fixes + regexp: fix + ordinal: 15 + - title: Dependency Updates + regexp: deps + ordinal: 30 + - title: Build Setup + regexp: (snap|release|update to) + ordinal: 40 + - title: Documentation + regexp: (doc|readme) + ordinal: 40 + - title: Improvements + regexp: .* + ordinal: 20 +builds: + - path: container1/unix/regular + os: + - goos: darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - goarch: arm64 + - goarch: arm + build_settings: + env: + - CGO_ENABLED=0 + - GOARM=7 + - goos: dragonfly + archs: + - goarch: amd64 + - goos: freebsd + archs: + - goarch: amd64 + - goos: netbsd + archs: + - goarch: amd64 + - goos: openbsd + archs: + - goarch: amd64 + - goos: solaris + archs: + - goarch: amd64 + - path: container1/unix/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + env: + - CGO_ENABLED=1 + os: + - goos: darwin + build_settings: + env: *env_extended_darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - path: container1/unix/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + env: + - CGO_ENABLED=1 + os: + - goos: darwin + build_settings: + env: *env_extended_darwin + archs: + - goarch: universal + - goos: linux + archs: + - goarch: amd64 + - path: container2/linux/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + os: + - goos: linux + build_settings: + env: *env_extended_linux + archs: + - goarch: arm64 + - path: container2/linux/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + os: + - goos: linux + build_settings: + env: *env_extended_linux + archs: + - goarch: arm64 + - path: container1/windows/regular + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 + - goarch: arm64 + - path: container1/windows/extended + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended + env: *env_extended_windows + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static' + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 + - path: container1/windows/extended-withdeploy + build_settings: + flags: + - -buildmode + - exe + - -tags + - extended,withdeploy + env: *env_extended_windows + ldflags: -s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio -extldflags '-static' + os: + - goos: windows + build_settings: + binary: hugo.exe + archs: + - goarch: amd64 +archives: + - paths: + - builds/container1/unix/regular/** + - paths: + - builds/container1/unix/extended/** + archive_settings: + name_template: *name_template_extended + - paths: + - builds/container1/unix/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + - paths: + - builds/container2/*/extended/** + archive_settings: + name_template: *name_template_extended + - paths: + - builds/container2/*/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + - paths: + - builds/**/windows/regular/** + archive_settings: *archive_type_zip + - paths: + - builds/**/windows/extended/** + archive_settings: + name_template: *name_template_extended + <<: *archive_type_zip + - paths: + - builds/**/windows/extended-withdeploy/** + archive_settings: + name_template: *name_template_extended_withdeploy + <<: *archive_type_zip + - paths: + - builds/**/regular/linux/{arm64,amd64} + archive_settings: *archive_deb + - paths: + - builds/**/extended/linux/{arm64,amd64} + archive_settings: + name_template: *name_template_extended + <<: *archive_deb + - paths: + - builds/**/extended-withdeploy/linux/{arm64,amd64} + archive_settings: + name_template: *name_template_extended_withdeploy + <<: *archive_deb +releases: + - paths: + - archives/** + path: r1 diff --git a/identity/finder.go b/identity/finder.go index 91fac7237..9d9f9d138 100644 --- a/identity/finder.go +++ b/identity/finder.go @@ -27,7 +27,7 @@ func NewFinder(cfg FinderConfig) *Finder { } var searchIDPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &searchID{seen: make(map[Manager]bool)} }, } @@ -45,9 +45,7 @@ func putSearchID(sid *searchID) { sid.dp = nil sid.peq = nil sid.eqer = nil - for k := range sid.seen { - delete(sid.seen, k) - } + clear(sid.seen) searchIDPool.Put(sid) } @@ -122,17 +120,21 @@ func (f *Finder) Contains(id, in Identity, maxDepth int) FinderResult { defer putSearchID(sid) - if r := f.checkOne(sid, in, 0); r > 0 { + r := FinderNotFound + if i := f.checkOne(sid, in, 0); i > r { + r = i + } + if r == FinderFound { return r } m := GetDependencyManager(in) if m != nil { - if r := f.checkManager(sid, m, 0); r > 0 { - return r + if i := f.checkManager(sid, m, 0); i > r { + r = i } } - return FinderNotFound + return r } func (f *Finder) checkMaxDepth(sid *searchID, level int) FinderResult { @@ -279,15 +281,18 @@ func (f *Finder) search(sid *searchID, m Manager, depth int) FinderResult { var r FinderResult m.forEeachIdentity( func(v Identity) bool { - if r > 0 { - panic("should be terminated") + i := f.checkOne(sid, v, depth) + if i > r { + r = i } - r = f.checkOne(sid, v, depth) - if r > 0 { + if r == FinderFound { return true } m := GetDependencyManager(v) - if r = f.checkManager(sid, m, depth+1); r > 0 { + if i := f.checkManager(sid, m, depth+1); i > r { + r = i + } + if r == FinderFound { return true } return false diff --git a/identity/identity.go b/identity/identity.go index d106eb1fc..c78ed0fdd 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -33,6 +33,9 @@ const ( // GenghisKhan is an Identity everyone relates to. GenghisKhan = StringIdentity("__genghiskhan") + + StructuralChangeAdd = StringIdentity("__structural_change_add") + StructuralChangeRemove = StringIdentity("__structural_change_remove") ) var NopManager = new(nopManager) @@ -82,9 +85,8 @@ func FirstIdentity(v any) Identity { var result Identity = Anonymous WalkIdentitiesShallow(v, func(level int, id Identity) bool { result = id - return true + return result != Anonymous }) - return result } @@ -146,6 +148,7 @@ func (d DependencyManagerProviderFunc) GetDependencyManager() Manager { // DependencyManagerScopedProvider provides a manager for dependencies with a given scope. type DependencyManagerScopedProvider interface { GetDependencyManagerForScope(scope int) Manager + GetDependencyManagerForScopesAll() []Manager } // ForEeachIdentityProvider provides a way iterate over identities. @@ -308,11 +311,13 @@ type identityManager struct { func (im *identityManager) AddIdentity(ids ...Identity) { im.mu.Lock() + defer im.mu.Unlock() for _, id := range ids { if id == nil || id == Anonymous { continue } + if _, found := im.ids[id]; !found { if im.onAddIdentity != nil { im.onAddIdentity(id) @@ -320,7 +325,6 @@ func (im *identityManager) AddIdentity(ids ...Identity) { im.ids[id] = true } } - im.mu.Unlock() } func (im *identityManager) AddIdentityForEach(ids ...ForEeachIdentityProvider) { @@ -355,6 +359,10 @@ func (im *identityManager) GetDependencyManagerForScope(int) Manager { return im } +func (im *identityManager) GetDependencyManagerForScopesAll() []Manager { + return []Manager{im} +} + func (im *identityManager) String() string { return fmt.Sprintf("IdentityManager(%s)", im.name) } @@ -501,6 +509,10 @@ func probablyEq(a, b Identity) bool { return true } + if a2, ok := a.(compare.ProbablyEqer); ok && a2.ProbablyEq(b) { + return true + } + if a2, ok := a.(IsProbablyDependentProvider); ok { return a2.IsProbablyDependent(b) } diff --git a/identity/identity_test.go b/identity/identity_test.go index d003caaf0..f9b04aa14 100644 --- a/identity/identity_test.go +++ b/identity/identity_test.go @@ -25,7 +25,7 @@ import ( func BenchmarkIdentityManager(b *testing.B) { createIds := func(num int) []identity.Identity { ids := make([]identity.Identity, num) - for i := 0; i < num; i++ { + for i := range num { name := fmt.Sprintf("id%d", i) ids[i] = &testIdentity{base: name, name: name} } @@ -108,10 +108,10 @@ func BenchmarkIsNotDependent(b *testing.B) { newNestedManager := func(depth, count int) identity.Manager { m1 := identity.NewManager("") - for i := 0; i < depth; i++ { + for range depth { m2 := identity.NewManager("") m1.AddIdentity(m2) - for j := 0; j < count; j++ { + for j := range count { id := fmt.Sprintf("id%d", j) m2.AddIdentity(&testIdentity{id, id, "", ""}) } diff --git a/internal/js/api.go b/internal/js/api.go new file mode 100644 index 000000000..30180dece --- /dev/null +++ b/internal/js/api.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package js + +import ( + "context" + + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/resources/resource" +) + +// BatcherClient is used to do JS batch operations. +type BatcherClient interface { + New(id string) (Batcher, error) + Store() *maps.Cache[string, Batcher] +} + +// BatchPackage holds a group of JavaScript resources. +type BatchPackage interface { + Groups() map[string]resource.Resources +} + +// Batcher is used to build JavaScript packages. +type Batcher interface { + Build(context.Context) (BatchPackage, error) + Config(ctx context.Context) OptionsSetter + Group(ctx context.Context, id string) BatcherGroup +} + +// BatcherGroup is a group of scripts and instances. +type BatcherGroup interface { + Instance(sid, iid string) OptionsSetter + Runner(id string) OptionsSetter + Script(id string) OptionsSetter +} + +// OptionsSetter is used to set options for a batch, script or instance. +type OptionsSetter interface { + SetOptions(map[string]any) string +} diff --git a/internal/js/esbuild/batch-esm-runner.gotmpl b/internal/js/esbuild/batch-esm-runner.gotmpl new file mode 100644 index 000000000..3193b4c30 --- /dev/null +++ b/internal/js/esbuild/batch-esm-runner.gotmpl @@ -0,0 +1,20 @@ +{{ range $i, $e := .Scripts -}} + {{ if eq .Export "*" }} + {{- printf "import %s as Script%d from %q;" .Export $i .Import -}} + {{ else -}} + {{- printf "import { %s as Script%d } from %q;" .Export $i .Import -}} + {{ end -}} +{{ end -}} +{{ range $i, $e := .Runners }} + {{- printf "import { %s as Run%d } from %q;" .Export $i .Import -}} +{{ end -}} +{{ if .Runners -}} + let group = { id: "{{ $.ID }}", scripts: [] } + {{ range $i, $e := .Scripts -}} + group.scripts.push({{ .RunnerJSON $i }}); + {{ end -}} + {{ range $i, $e := .Runners -}} + {{ $id := printf "Run%d" $i }} + {{ $id }}(group); + {{ end -}} +{{ end -}} diff --git a/internal/js/esbuild/batch.go b/internal/js/esbuild/batch.go new file mode 100644 index 000000000..aa50cf2c1 --- /dev/null +++ b/internal/js/esbuild/batch.go @@ -0,0 +1,1444 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package esbuild provides functions for building JavaScript resources. +package esbuild + +import ( + "bytes" + "context" + _ "embed" + "encoding/json" + "fmt" + "io" + "path" + "path/filepath" + "reflect" + "sort" + "strings" + "sync" + "sync/atomic" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/cache/dynacache" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/internal/js" + "github.com/gohugoio/hugo/lazy" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/resources/resource_factories/create" + "github.com/gohugoio/hugo/tpl/tplimpl" + "github.com/mitchellh/mapstructure" + "github.com/spf13/cast" +) + +var _ js.Batcher = (*batcher)(nil) + +const ( + NsBatch = "_hugo-js-batch" + + propsKeyImportContext = "importContext" + propsResoure = "resource" +) + +//go:embed batch-esm-runner.gotmpl +var runnerTemplateStr string + +var _ js.BatchPackage = (*Package)(nil) + +var _ buildToucher = (*optsHolder[scriptOptions])(nil) + +var ( + _ buildToucher = (*scriptGroup)(nil) + _ isBuiltOrTouchedProvider = (*scriptGroup)(nil) +) + +func NewBatcherClient(deps *deps.Deps) (js.BatcherClient, error) { + c := &BatcherClient{ + d: deps, + buildClient: NewBuildClient(deps.BaseFs.Assets, deps.ResourceSpec), + createClient: create.New(deps.ResourceSpec), + batcherStore: maps.NewCache[string, js.Batcher](), + bundlesStore: maps.NewCache[string, js.BatchPackage](), + } + + deps.BuildEndListeners.Add(func(...any) bool { + c.bundlesStore.Reset() + return false + }) + + return c, nil +} + +func (o optionsMap[K, C]) ByKey() optionsGetSetters[K, C] { + var values []optionsGetSetter[K, C] + for _, v := range o { + values = append(values, v) + } + + sort.Slice(values, func(i, j int) bool { + return values[i].Key().String() < values[j].Key().String() + }) + + return values +} + +func (o *opts[K, C]) Compiled() C { + o.h.checkCompileErr() + return o.h.compiled +} + +func (os optionsGetSetters[K, C]) Filter(predicate func(K) bool) optionsGetSetters[K, C] { + var a optionsGetSetters[K, C] + for _, v := range os { + if predicate(v.Key()) { + a = append(a, v) + } + } + return a +} + +func (o *optsHolder[C]) IdentifierBase() string { + return o.optionsID +} + +func (o *opts[K, C]) Key() K { + return o.key +} + +func (o *opts[K, C]) Reset() { + mu := o.once.ResetWithLock() + defer mu.Unlock() + o.h.resetCounter++ +} + +func (o *opts[K, C]) Get(id uint32) js.OptionsSetter { + var b *optsHolder[C] + o.once.Do(func() { + b = o.h + b.setBuilt(id) + }) + return b +} + +func (o *opts[K, C]) GetIdentity() identity.Identity { + return o.h +} + +func (o *optsHolder[C]) SetOptions(m map[string]any) string { + o.optsSetCounter++ + o.optsPrev = o.optsCurr + o.optsCurr = m + o.compiledPrev = o.compiled + o.compiled, o.compileErr = o.compiled.compileOptions(m, o.defaults) + o.checkCompileErr() + return "" +} + +// ValidateBatchID validates the given ID according to some very +func ValidateBatchID(id string, isTopLevel bool) error { + if id == "" { + return fmt.Errorf("id must be set") + } + // No Windows slashes. + if strings.Contains(id, "\\") { + return fmt.Errorf("id must not contain backslashes") + } + + // Allow forward slashes in top level IDs only. + if !isTopLevel && strings.Contains(id, "/") { + return fmt.Errorf("id must not contain forward slashes") + } + + return nil +} + +func newIsBuiltOrTouched() isBuiltOrTouched { + return isBuiltOrTouched{ + built: make(buildIDs), + touched: make(buildIDs), + } +} + +func newOpts[K any, C optionsCompiler[C]](key K, optionsID string, defaults defaultOptionValues) *opts[K, C] { + return &opts[K, C]{ + key: key, + h: &optsHolder[C]{ + optionsID: optionsID, + defaults: defaults, + isBuiltOrTouched: newIsBuiltOrTouched(), + }, + } +} + +// BatcherClient is a client for building JavaScript packages. +type BatcherClient struct { + d *deps.Deps + + once sync.Once + runnerTemplate *tplimpl.TemplInfo + + createClient *create.Client + buildClient *BuildClient + + batcherStore *maps.Cache[string, js.Batcher] + bundlesStore *maps.Cache[string, js.BatchPackage] +} + +// New creates a new Batcher with the given ID. +// This will be typically created once and reused across rebuilds. +func (c *BatcherClient) New(id string) (js.Batcher, error) { + var initErr error + c.once.Do(func() { + // We should fix the initialization order here (or use the Go template package directly), but we need to wait + // for the Hugo templates to be ready. + tmpl, err := c.d.TemplateStore.TextParse("batch-esm-runner", runnerTemplateStr) + if err != nil { + initErr = err + return + } + c.runnerTemplate = tmpl + }) + + if initErr != nil { + return nil, initErr + } + + dependencyManager := c.d.Conf.NewIdentityManager("jsbatch_" + id) + configID := "config_" + id + + b := &batcher{ + id: id, + scriptGroups: make(map[string]*scriptGroup), + dependencyManager: dependencyManager, + client: c, + configOptions: newOpts[scriptID, configOptions]( + scriptID(configID), + configID, + defaultOptionValues{}, + ), + } + + c.d.BuildEndListeners.Add(func(...any) bool { + b.reset() + return false + }) + + idFinder := identity.NewFinder(identity.FinderConfig{}) + + c.d.OnChangeListeners.Add(func(ids ...identity.Identity) bool { + for _, id := range ids { + if r := idFinder.Contains(id, b.dependencyManager, 50); r > 0 { + b.staleVersion.Add(1) + return false + } + + sp, ok := id.(identity.DependencyManagerScopedProvider) + if !ok { + continue + } + idms := sp.GetDependencyManagerForScopesAll() + + for _, g := range b.scriptGroups { + g.forEachIdentity(func(id2 identity.Identity) bool { + bt, ok := id2.(buildToucher) + if !ok { + return false + } + for _, id3 := range idms { + // This handles the removal of the only source for a script group (e.g. all shortcodes in a contnt page). + // Note the very shallow search. + if r := idFinder.Contains(id2, id3, 0); r > 0 { + bt.setTouched(b.buildCount) + return false + } + } + return false + }) + } + } + + return false + }) + + return b, nil +} + +func (c *BatcherClient) Store() *maps.Cache[string, js.Batcher] { + return c.batcherStore +} + +func (c *BatcherClient) buildBatchGroup(ctx context.Context, t *batchGroupTemplateContext) (resource.Resource, string, error) { + var buf bytes.Buffer + + if err := c.d.GetTemplateStore().ExecuteWithContext(ctx, c.runnerTemplate, &buf, t); err != nil { + return nil, "", err + } + + s := paths.AddLeadingSlash(t.keyPath + ".js") + r, err := c.createClient.FromString(s, buf.String()) + if err != nil { + return nil, "", err + } + + return r, s, nil +} + +// Package holds a group of JavaScript resources. +type Package struct { + id string + b *batcher + + groups map[string]resource.Resources +} + +func (p *Package) Groups() map[string]resource.Resources { + return p.groups +} + +type batchGroupTemplateContext struct { + keyPath string + ID string + Runners []scriptRunnerTemplateContext + Scripts []scriptBatchTemplateContext +} + +type batcher struct { + mu sync.Mutex + id string + buildCount uint32 + staleVersion atomic.Uint32 + scriptGroups scriptGroups + + client *BatcherClient + dependencyManager identity.Manager + + configOptions optionsGetSetter[scriptID, configOptions] + + // The last successfully built package. + // If this is non-nil and not stale, we can reuse it (e.g. on server rebuilds) + prevBuild *Package +} + +// Build builds the batch if not already built or if it's stale. +func (b *batcher) Build(ctx context.Context) (js.BatchPackage, error) { + key := dynacache.CleanKey(b.id + ".js") + p, err := b.client.bundlesStore.GetOrCreate(key, func() (js.BatchPackage, error) { + return b.build(ctx) + }) + if err != nil { + return nil, fmt.Errorf("failed to build JS batch %q: %w", b.id, err) + } + return p, nil +} + +func (b *batcher) Config(ctx context.Context) js.OptionsSetter { + return b.configOptions.Get(b.buildCount) +} + +func (b *batcher) Group(ctx context.Context, id string) js.BatcherGroup { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + b.mu.Lock() + defer b.mu.Unlock() + + group, found := b.scriptGroups[id] + if !found { + idm := b.client.d.Conf.NewIdentityManager("jsbatch_" + id) + b.dependencyManager.AddIdentity(idm) + + group = &scriptGroup{ + id: id, b: b, + isBuiltOrTouched: newIsBuiltOrTouched(), + dependencyManager: idm, + scriptsOptions: make(optionsMap[scriptID, scriptOptions]), + instancesOptions: make(optionsMap[instanceID, paramsOptions]), + runnersOptions: make(optionsMap[scriptID, scriptOptions]), + } + b.scriptGroups[id] = group + } + + group.setBuilt(b.buildCount) + + return group +} + +func (b *batcher) isStale() bool { + if b.staleVersion.Load() > 0 { + return true + } + + if b.removeNotSet() { + return true + } + + if b.configOptions.isStale() { + return true + } + + for _, v := range b.scriptGroups { + if v.isStale() { + return true + } + } + + return false +} + +func (b *batcher) build(ctx context.Context) (js.BatchPackage, error) { + b.mu.Lock() + defer b.mu.Unlock() + defer func() { + b.staleVersion.Store(0) + b.buildCount++ + }() + + if b.prevBuild != nil { + if !b.isStale() { + return b.prevBuild, nil + } + } + + p, err := b.doBuild(ctx) + if err != nil { + return nil, err + } + + b.prevBuild = p + + return p, nil +} + +func (b *batcher) doBuild(ctx context.Context) (*Package, error) { + type importContext struct { + name string + resourceGetter resource.ResourceGetter + scriptOptions scriptOptions + dm identity.Manager + } + + state := struct { + importResource *maps.Cache[string, resource.Resource] + resultResource *maps.Cache[string, resource.Resource] + importerImportContext *maps.Cache[string, importContext] + pathGroup *maps.Cache[string, string] + }{ + importResource: maps.NewCache[string, resource.Resource](), + resultResource: maps.NewCache[string, resource.Resource](), + importerImportContext: maps.NewCache[string, importContext](), + pathGroup: maps.NewCache[string, string](), + } + + multihostBasePaths := b.client.d.ResourceSpec.MultihostTargetBasePaths + + // Entry points passed to ESBuid. + var entryPoints []string + addResource := func(group, pth string, r resource.Resource, isResult bool) { + state.pathGroup.Set(paths.TrimExt(pth), group) + state.importResource.Set(pth, r) + if isResult { + state.resultResource.Set(pth, r) + } + entryPoints = append(entryPoints, pth) + } + + for _, g := range b.scriptGroups.Sorted() { + keyPath := g.id + + t := &batchGroupTemplateContext{ + keyPath: keyPath, + ID: g.id, + } + + instances := g.instancesOptions.ByKey() + + for _, vv := range g.scriptsOptions.ByKey() { + keyPath := keyPath + "_" + vv.Key().String() + opts := vv.Compiled() + impPath := path.Join(PrefixHugoVirtual, opts.Dir(), keyPath+opts.Resource.MediaType().FirstSuffix.FullSuffix) + impCtx := opts.ImportContext + + state.importerImportContext.Set(impPath, importContext{ + name: keyPath, + resourceGetter: impCtx, + scriptOptions: opts, + dm: g.dependencyManager, + }) + + bt := scriptBatchTemplateContext{ + opts: vv, + Import: impPath, + Instances: []scriptInstanceBatchTemplateContext{}, + } + state.importResource.Set(bt.Import, vv.Compiled().Resource) + predicate := func(k instanceID) bool { + return k.scriptID == vv.Key() + } + for _, vvv := range instances.Filter(predicate) { + bt.Instances = append(bt.Instances, scriptInstanceBatchTemplateContext{opts: vvv}) + } + + t.Scripts = append(t.Scripts, bt) + } + + for _, vv := range g.runnersOptions.ByKey() { + runnerKeyPath := keyPath + "_" + vv.Key().String() + runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix) + t.Runners = append(t.Runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath}) + addResource(g.id, runnerImpPath, vv.Compiled().Resource, false) + } + + r, s, err := b.client.buildBatchGroup(ctx, t) + if err != nil { + return nil, fmt.Errorf("failed to build JS batch: %w", err) + } + + state.importerImportContext.Set(s, importContext{ + name: s, + resourceGetter: nil, + dm: g.dependencyManager, + }) + + addResource(g.id, s, r, true) + } + + mediaTypes := b.client.d.ResourceSpec.MediaTypes() + + externalOptions := b.configOptions.Compiled().Options + if externalOptions.Format == "" { + externalOptions.Format = "esm" + } + if externalOptions.Format != "esm" { + return nil, fmt.Errorf("only esm format is currently supported") + } + + jsOpts := Options{ + ExternalOptions: externalOptions, + InternalOptions: InternalOptions{ + DependencyManager: b.dependencyManager, + Splitting: true, + ImportOnResolveFunc: func(imp string, args api.OnResolveArgs) string { + var importContextPath string + if args.Kind == api.ResolveEntryPoint { + importContextPath = args.Path + } else { + importContextPath = args.Importer + } + importContext, importContextFound := state.importerImportContext.Get(importContextPath) + + // We want to track the dependencies closest to where they're used. + dm := b.dependencyManager + if importContextFound { + dm = importContext.dm + } + + if r, found := state.importResource.Get(imp); found { + dm.AddIdentity(identity.FirstIdentity(r)) + return imp + } + + if importContext.resourceGetter != nil { + resolved := ResolveResource(imp, importContext.resourceGetter) + if resolved != nil { + resolvePath := resources.InternalResourceTargetPath(resolved) + dm.AddIdentity(identity.FirstIdentity(resolved)) + imp := PrefixHugoVirtual + resolvePath + state.importResource.Set(imp, resolved) + state.importerImportContext.Set(imp, importContext) + return imp + + } + } + return "" + }, + ImportOnLoadFunc: func(args api.OnLoadArgs) string { + imp := args.Path + + if r, found := state.importResource.Get(imp); found { + content, err := r.(resource.ContentProvider).Content(ctx) + if err != nil { + panic(err) + } + return cast.ToString(content) + } + return "" + }, + ImportParamsOnLoadFunc: func(args api.OnLoadArgs) json.RawMessage { + if importContext, found := state.importerImportContext.Get(args.Path); found { + if !importContext.scriptOptions.IsZero() { + return importContext.scriptOptions.Params + } + } + return nil + }, + ErrorMessageResolveFunc: func(args api.Message) *ErrorMessageResolved { + if loc := args.Location; loc != nil { + path := strings.TrimPrefix(loc.File, NsHugoImportResolveFunc+":") + if r, found := state.importResource.Get(path); found { + sourcePath := resources.InternalResourceSourcePathBestEffort(r) + + var contentr hugio.ReadSeekCloser + if cp, ok := r.(hugio.ReadSeekCloserProvider); ok { + contentr, _ = cp.ReadSeekCloser() + } + return &ErrorMessageResolved{ + Content: contentr, + Path: sourcePath, + Message: args.Text, + } + + } + + } + return nil + }, + ResolveSourceMapSource: func(s string) string { + if r, found := state.importResource.Get(s); found { + if ss := resources.InternalResourceSourcePath(r); ss != "" { + return ss + } + return PrefixHugoMemory + s + } + return "" + }, + EntryPoints: entryPoints, + }, + } + + result, err := b.client.buildClient.Build(jsOpts) + if err != nil { + return nil, fmt.Errorf("failed to build JS bundle: %w", err) + } + + groups := make(map[string]resource.Resources) + + createAndAddResource := func(targetPath, group string, o api.OutputFile, mt media.Type) error { + var sourceFilename string + if r, found := state.importResource.Get(targetPath); found { + sourceFilename = resources.InternalResourceSourcePathBestEffort(r) + } + targetPath = path.Join(b.id, targetPath) + + rd := resources.ResourceSourceDescriptor{ + LazyPublish: true, + OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { + return hugio.NewReadSeekerNoOpCloserFromBytes(o.Contents), nil + }, + MediaType: mt, + TargetPath: targetPath, + SourceFilenameOrPath: sourceFilename, + } + r, err := b.client.d.ResourceSpec.NewResource(rd) + if err != nil { + return err + } + + groups[group] = append(groups[group], r) + + return nil + } + + outDir := b.client.d.AbsPublishDir + + createAndAddResources := func(o api.OutputFile) (bool, error) { + p := paths.ToSlashPreserveLeading(strings.TrimPrefix(o.Path, outDir)) + ext := path.Ext(p) + mt, _, found := mediaTypes.GetBySuffix(ext) + if !found { + return false, nil + } + + group, found := state.pathGroup.Get(paths.TrimExt(p)) + + if !found { + return false, nil + } + + if err := createAndAddResource(p, group, o, mt); err != nil { + return false, err + } + + return true, nil + } + + for _, o := range result.OutputFiles { + handled, err := createAndAddResources(o) + if err != nil { + return nil, err + } + + if !handled { + // Copy to destination. + // In a multihost setup, we will have multiple targets. + var targetFilenames []string + if len(multihostBasePaths) > 0 { + for _, base := range multihostBasePaths { + p := strings.TrimPrefix(o.Path, outDir) + targetFilename := filepath.Join(base, b.id, p) + targetFilenames = append(targetFilenames, targetFilename) + } + } else { + p := strings.TrimPrefix(o.Path, outDir) + targetFilename := filepath.Join(b.id, p) + targetFilenames = append(targetFilenames, targetFilename) + } + + fs := b.client.d.BaseFs.PublishFs + + if err := func() error { + fw, err := helpers.OpenFilesForWriting(fs, targetFilenames...) + if err != nil { + return err + } + defer fw.Close() + + fr := bytes.NewReader(o.Contents) + + _, err = io.Copy(fw, fr) + + return err + }(); err != nil { + return nil, fmt.Errorf("failed to copy to %q: %w", targetFilenames, err) + } + } + } + + p := &Package{ + id: path.Join(NsBatch, b.id), + b: b, + groups: groups, + } + + return p, nil +} + +func (b *batcher) removeNotSet() bool { + // We already have the lock. + var removed bool + currentBuildID := b.buildCount + for k, v := range b.scriptGroups { + if !v.isBuilt(currentBuildID) && v.isTouched(currentBuildID) { + // Remove entire group. + removed = true + delete(b.scriptGroups, k) + continue + } + if v.removeTouchedButNotSet() { + removed = true + } + if v.removeNotSet() { + removed = true + } + } + + return removed +} + +func (b *batcher) reset() { + b.mu.Lock() + defer b.mu.Unlock() + b.configOptions.Reset() + for _, v := range b.scriptGroups { + v.Reset() + } +} + +type buildIDs map[uint32]bool + +func (b buildIDs) Has(buildID uint32) bool { + return b[buildID] +} + +func (b buildIDs) Set(buildID uint32) { + b[buildID] = true +} + +type buildToucher interface { + setTouched(buildID uint32) +} + +type configOptions struct { + Options ExternalOptions +} + +func (s configOptions) isStaleCompiled(prev configOptions) bool { + return false +} + +func (s configOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (configOptions, error) { + config, err := DecodeExternalOptions(m) + if err != nil { + return configOptions{}, err + } + + return configOptions{ + Options: config, + }, nil +} + +type defaultOptionValues struct { + defaultExport string +} + +type instanceID struct { + scriptID scriptID + instanceID string +} + +func (i instanceID) String() string { + return i.scriptID.String() + "_" + i.instanceID +} + +type isBuiltOrTouched struct { + built buildIDs + touched buildIDs +} + +func (i isBuiltOrTouched) setBuilt(id uint32) { + i.built.Set(id) +} + +func (i isBuiltOrTouched) isBuilt(id uint32) bool { + return i.built.Has(id) +} + +func (i isBuiltOrTouched) setTouched(id uint32) { + i.touched.Set(id) +} + +func (i isBuiltOrTouched) isTouched(id uint32) bool { + return i.touched.Has(id) +} + +type isBuiltOrTouchedProvider interface { + isBuilt(uint32) bool + isTouched(uint32) bool +} + +type key interface { + comparable + fmt.Stringer +} + +type optionsCompiler[C any] interface { + isStaleCompiled(C) bool + compileOptions(map[string]any, defaultOptionValues) (C, error) +} + +type optionsGetSetter[K, C any] interface { + isBuiltOrTouchedProvider + identity.IdentityProvider + // resource.StaleInfo + + Compiled() C + Key() K + Reset() + + Get(uint32) js.OptionsSetter + isStale() bool + currPrev() (map[string]any, map[string]any) +} + +type optionsGetSetters[K key, C any] []optionsGetSetter[K, C] + +type optionsMap[K key, C any] map[K]optionsGetSetter[K, C] + +type opts[K any, C optionsCompiler[C]] struct { + key K + h *optsHolder[C] + once lazy.OnceMore +} + +type optsHolder[C optionsCompiler[C]] struct { + optionsID string + + defaults defaultOptionValues + + // Keep track of one generation so we can detect changes. + // Note that most of this tracking is performed on the options/map level. + compiled C + compiledPrev C + compileErr error + + resetCounter uint32 + optsSetCounter uint32 + optsCurr map[string]any + optsPrev map[string]any + + isBuiltOrTouched +} + +type paramsOptions struct { + Params json.RawMessage +} + +func (s paramsOptions) isStaleCompiled(prev paramsOptions) bool { + return false +} + +func (s paramsOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (paramsOptions, error) { + v := struct { + Params map[string]any + }{} + + if err := mapstructure.WeakDecode(m, &v); err != nil { + return paramsOptions{}, err + } + + paramsJSON, err := json.Marshal(v.Params) + if err != nil { + return paramsOptions{}, err + } + + return paramsOptions{ + Params: paramsJSON, + }, nil +} + +type scriptBatchTemplateContext struct { + opts optionsGetSetter[scriptID, scriptOptions] + Import string + Instances []scriptInstanceBatchTemplateContext +} + +func (s *scriptBatchTemplateContext) Export() string { + return s.opts.Compiled().Export +} + +func (c scriptBatchTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + Instances []scriptInstanceBatchTemplateContext `json:"instances"` + }{ + ID: c.opts.Key().String(), + Instances: c.Instances, + }) +} + +func (b scriptBatchTemplateContext) RunnerJSON(i int) string { + script := fmt.Sprintf("Script%d", i) + + v := struct { + ID string `json:"id"` + + // Read-only live JavaScript binding. + Binding string `json:"binding"` + Instances []scriptInstanceBatchTemplateContext `json:"instances"` + }{ + b.opts.Key().String(), + script, + b.Instances, + } + + bb, err := json.Marshal(v) + if err != nil { + panic(err) + } + s := string(bb) + + // Remove the quotes to make it a valid JS object. + s = strings.ReplaceAll(s, fmt.Sprintf("%q", script), script) + + return s +} + +type scriptGroup struct { + mu sync.Mutex + id string + b *batcher + isBuiltOrTouched + dependencyManager identity.Manager + + scriptsOptions optionsMap[scriptID, scriptOptions] + instancesOptions optionsMap[instanceID, paramsOptions] + runnersOptions optionsMap[scriptID, scriptOptions] +} + +// For internal use only. +func (b *scriptGroup) GetDependencyManager() identity.Manager { + return b.dependencyManager +} + +// For internal use only. +func (b *scriptGroup) IdentifierBase() string { + return b.id +} + +func (s *scriptGroup) Instance(sid, id string) js.OptionsSetter { + if err := ValidateBatchID(sid, false); err != nil { + panic(err) + } + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + + iid := instanceID{scriptID: scriptID(sid), instanceID: id} + if v, found := s.instancesOptions[iid]; found { + return v.Get(s.b.buildCount) + } + + fullID := "instance_" + s.key() + "_" + iid.String() + + s.instancesOptions[iid] = newOpts[instanceID, paramsOptions]( + iid, + fullID, + defaultOptionValues{}, + ) + + return s.instancesOptions[iid].Get(s.b.buildCount) +} + +func (g *scriptGroup) Reset() { + for _, v := range g.scriptsOptions { + v.Reset() + } + for _, v := range g.instancesOptions { + v.Reset() + } + for _, v := range g.runnersOptions { + v.Reset() + } +} + +func (s *scriptGroup) Runner(id string) js.OptionsSetter { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + sid := scriptID(id) + if v, found := s.runnersOptions[sid]; found { + return v.Get(s.b.buildCount) + } + + runnerIdentity := "runner_" + s.key() + "_" + id + + // A typical signature for a runner would be: + // export default function Run(scripts) {} + // The user can override the default export in the templates. + + s.runnersOptions[sid] = newOpts[scriptID, scriptOptions]( + sid, + runnerIdentity, + defaultOptionValues{ + defaultExport: "default", + }, + ) + + return s.runnersOptions[sid].Get(s.b.buildCount) +} + +func (s *scriptGroup) Script(id string) js.OptionsSetter { + if err := ValidateBatchID(id, false); err != nil { + panic(err) + } + + s.mu.Lock() + defer s.mu.Unlock() + sid := scriptID(id) + if v, found := s.scriptsOptions[sid]; found { + return v.Get(s.b.buildCount) + } + + scriptIdentity := "script_" + s.key() + "_" + id + + s.scriptsOptions[sid] = newOpts[scriptID, scriptOptions]( + sid, + scriptIdentity, + defaultOptionValues{ + defaultExport: "*", + }, + ) + + return s.scriptsOptions[sid].Get(s.b.buildCount) +} + +func (s *scriptGroup) isStale() bool { + for _, v := range s.scriptsOptions { + if v.isStale() { + return true + } + } + + for _, v := range s.instancesOptions { + if v.isStale() { + return true + } + } + + for _, v := range s.runnersOptions { + if v.isStale() { + return true + } + } + + return false +} + +func (v *scriptGroup) forEachIdentity( + f func(id identity.Identity) bool, +) bool { + if f(v) { + return true + } + for _, vv := range v.instancesOptions { + if f(vv.GetIdentity()) { + return true + } + } + + for _, vv := range v.scriptsOptions { + if f(vv.GetIdentity()) { + return true + } + } + + for _, vv := range v.runnersOptions { + if f(vv.GetIdentity()) { + return true + } + } + + return false +} + +func (s *scriptGroup) key() string { + return s.b.id + "_" + s.id +} + +func (g *scriptGroup) removeNotSet() bool { + currentBuildID := g.b.buildCount + if !g.isBuilt(currentBuildID) { + // This group was never accessed in this build. + return false + } + var removed bool + + if g.instancesOptions.isBuilt(currentBuildID) { + // A new instance has been set in this group for this build. + // Remove any instance that has not been set in this build. + for k, v := range g.instancesOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.instancesOptions, k) + removed = true + } + } + + if g.runnersOptions.isBuilt(currentBuildID) { + // A new runner has been set in this group for this build. + // Remove any runner that has not been set in this build. + for k, v := range g.runnersOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.runnersOptions, k) + removed = true + } + } + + if g.scriptsOptions.isBuilt(currentBuildID) { + // A new script has been set in this group for this build. + // Remove any script that has not been set in this build. + for k, v := range g.scriptsOptions { + if v.isBuilt(currentBuildID) { + continue + } + delete(g.scriptsOptions, k) + + // Also remove any instance with this ID. + for kk := range g.instancesOptions { + if kk.scriptID == k { + delete(g.instancesOptions, kk) + } + } + removed = true + } + } + + return removed +} + +func (g *scriptGroup) removeTouchedButNotSet() bool { + currentBuildID := g.b.buildCount + var removed bool + for k, v := range g.instancesOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.instancesOptions, k) + removed = true + } + } + for k, v := range g.runnersOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.runnersOptions, k) + removed = true + } + } + for k, v := range g.scriptsOptions { + if v.isBuilt(currentBuildID) { + continue + } + if v.isTouched(currentBuildID) { + delete(g.scriptsOptions, k) + removed = true + + // Also remove any instance with this ID. + for kk := range g.instancesOptions { + if kk.scriptID == k { + delete(g.instancesOptions, kk) + } + } + } + + } + return removed +} + +type scriptGroups map[string]*scriptGroup + +func (s scriptGroups) Sorted() []*scriptGroup { + var a []*scriptGroup + for _, v := range s { + a = append(a, v) + } + sort.Slice(a, func(i, j int) bool { + return a[i].id < a[j].id + }) + return a +} + +type scriptID string + +func (s scriptID) String() string { + return string(s) +} + +type scriptInstanceBatchTemplateContext struct { + opts optionsGetSetter[instanceID, paramsOptions] +} + +func (c scriptInstanceBatchTemplateContext) ID() string { + return c.opts.Key().instanceID +} + +func (c scriptInstanceBatchTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + Params json.RawMessage `json:"params"` + }{ + ID: c.opts.Key().instanceID, + Params: c.opts.Compiled().Params, + }) +} + +type scriptOptions struct { + // The script to build. + Resource resource.Resource + + // The import context to use. + // Note that we will always fall back to the resource's own import context. + ImportContext resource.ResourceGetter + + // The export name to use for this script's group's runners (if any). + // If not set, the default export will be used. + Export string + + // Params marshaled to JSON. + Params json.RawMessage +} + +func (o *scriptOptions) Dir() string { + return path.Dir(resources.InternalResourceTargetPath(o.Resource)) +} + +func (s scriptOptions) IsZero() bool { + return s.Resource == nil +} + +func (s scriptOptions) isStaleCompiled(prev scriptOptions) bool { + if prev.IsZero() { + return false + } + + // All but the ImportContext are checked at the options/map level. + i1nil, i2nil := prev.ImportContext == nil, s.ImportContext == nil + if i1nil && i2nil { + return false + } + if i1nil || i2nil { + return true + } + // On its own this check would have too many false positives, but combined with the other checks it should be fine. + // We cannot do equality checking here. + if !prev.ImportContext.(resource.IsProbablySameResourceGetter).IsProbablySameResourceGetter(s.ImportContext) { + return true + } + + return false +} + +func (s scriptOptions) compileOptions(m map[string]any, defaults defaultOptionValues) (scriptOptions, error) { + v := struct { + Resource resource.Resource + ImportContext any + Export string + Params map[string]any + }{} + + if err := mapstructure.WeakDecode(m, &v); err != nil { + panic(err) + } + + var paramsJSON []byte + if v.Params != nil { + var err error + paramsJSON, err = json.Marshal(v.Params) + if err != nil { + panic(err) + } + } + + if v.Export == "" { + v.Export = defaults.defaultExport + } + + compiled := scriptOptions{ + Resource: v.Resource, + Export: v.Export, + ImportContext: resource.NewCachedResourceGetter(v.ImportContext), + Params: paramsJSON, + } + + if compiled.Resource == nil { + return scriptOptions{}, fmt.Errorf("resource not set") + } + + return compiled, nil +} + +type scriptRunnerTemplateContext struct { + opts optionsGetSetter[scriptID, scriptOptions] + Import string +} + +func (s *scriptRunnerTemplateContext) Export() string { + return s.opts.Compiled().Export +} + +func (c scriptRunnerTemplateContext) MarshalJSON() (b []byte, err error) { + return json.Marshal(&struct { + ID string `json:"id"` + }{ + ID: c.opts.Key().String(), + }) +} + +func (o optionsMap[K, C]) isBuilt(id uint32) bool { + for _, v := range o { + if v.isBuilt(id) { + return true + } + } + + return false +} + +func (o *opts[K, C]) isBuilt(id uint32) bool { + return o.h.isBuilt(id) +} + +func (o *opts[K, C]) isStale() bool { + if o.h.isStaleOpts() { + return true + } + if o.h.compiled.isStaleCompiled(o.h.compiledPrev) { + return true + } + return false +} + +func (o *optsHolder[C]) isStaleOpts() bool { + if o.optsSetCounter == 1 && o.resetCounter > 0 { + return false + } + isStale := func() bool { + if len(o.optsCurr) != len(o.optsPrev) { + return true + } + for k, v := range o.optsPrev { + vv, found := o.optsCurr[k] + if !found { + return true + } + if strings.EqualFold(k, propsKeyImportContext) { + // This is checked later. + } else if si, ok := vv.(resource.StaleInfo); ok { + if si.StaleVersion() > 0 { + return true + } + } else { + if !reflect.DeepEqual(v, vv) { + return true + } + } + } + return false + }() + + return isStale +} + +func (o *opts[K, C]) isTouched(id uint32) bool { + return o.h.isTouched(id) +} + +func (o *optsHolder[C]) checkCompileErr() { + if o.compileErr != nil { + panic(o.compileErr) + } +} + +func (o *opts[K, C]) currPrev() (map[string]any, map[string]any) { + return o.h.optsCurr, o.h.optsPrev +} + +func init() { + // We don't want any dependencies/change tracking on the top level Package, + // we want finer grained control via Package.Group. + var p any = &Package{} + if _, ok := p.(identity.Identity); ok { + panic("esbuid.Package should not implement identity.Identity") + } + if _, ok := p.(identity.DependencyManagerProvider); ok { + panic("esbuid.Package should not implement identity.DependencyManagerProvider") + } +} diff --git a/internal/js/esbuild/batch_integration_test.go b/internal/js/esbuild/batch_integration_test.go new file mode 100644 index 000000000..b4a2454ac --- /dev/null +++ b/internal/js/esbuild/batch_integration_test.go @@ -0,0 +1,723 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package js provides functions for building JavaScript resources +package esbuild_test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/bep/logg" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/hugolib" + "github.com/gohugoio/hugo/internal/js/esbuild" +) + +// Used to test misc. error situations etc. +const jsBatchFilesTemplate = ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import './styles.css'; +import * as params from '@params'; +import * as foo from 'mylib'; +console.log("Hello, Main!"); +console.log("params.p1", params.p1); +export default function Main() {}; +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/shortcodes/hdx.html -- +{{ $path := .Get "r" }} +{{ $r := or (.Page.Resources.Get $path) (resources.Get $path) }} +{{ $batch := (js.Batch "mybatch") }} +{{ $scriptID := $path | anchorize }} +{{ $instanceID := .Ordinal | string }} +{{ $group := .Page.RelPermalink | anchorize }} +{{ $params := .Params | default dict }} +{{ $export := .Get "export" | default "default" }} +{{ with $batch.Group $group }} + {{ with .Runner "create-elements" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script $scriptID }} + {{ .SetOptions (dict + "resource" $r + "export" $export + "importContext" (slice $.Page) + ) + }} + {{ end }} + {{ with .Instance $scriptID $instanceID }} + {{ .SetOptions (dict "params" $params) }} + {{ end }} +{{ end }} +hdx-instance: {{ $scriptID }}: {{ $instanceID }}| +-- layouts/_default/baseof.html -- +Base. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ block "main" . }}Main{{ end }} +End. +-- layouts/_default/single.html -- +{{ define "main" }} +==> Single Template Content: {{ .Content }}$ +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} +{{ end }} +-- layouts/index.html -- +{{ define "main" }} +Home. +{{ end }} +-- content/p1/index.md -- +--- +title: "P1" +--- + +Some content. + +{{< hdx r="p1script.js" myparam="p1-param-1" >}} +{{< hdx r="p1script.js" myparam="p1-param-2" >}} + +-- content/p1/p1script.js -- +console.log("P1 Script"); + + +` + +// Just to verify that the above file setup works. +func TestBatchTemplateOKBuild(t *testing.T) { + b := hugolib.Test(t, jsBatchFilesTemplate, hugolib.TestOptWithOSFs()) + b.AssertPublishDir("mybatch/mygroup.js", "mybatch/mygroup.css") +} + +func TestBatchRemoveAllInGroup(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + + b.AssertFileContent("public/p1/index.html", "p1: /mybatch/p1.js") + + b.EditFiles("content/p1/index.md", ` +--- +title: "P1" +--- +Empty. +`) + b.Build() + + b.AssertFileContent("public/p1/index.html", "! p1: /mybatch/p1.js") + + // Add one script back. + b.EditFiles("content/p1/index.md", ` +--- +title: "P1" +--- + +{{< hdx r="p1script.js" myparam="p1-param-1-new" >}} +`) + b.Build() + + b.AssertFileContent("public/mybatch/p1.js", + "p1-param-1-new", + "p1script.js") +} + +func TestBatchEditInstance(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js", "Instance 1") + b.EditFileReplaceAll("layouts/_default/single.html", "Instance 1", "Instance 1 Edit").Build() + b.AssertFileContent("public/mybatch/mygroup.js", "Instance 1 Edit") +} + +func TestBatchEditScriptParam(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main") + b.EditFileReplaceAll("layouts/_default/single.html", "param-p1-main", "param-p1-main-edited").Build() + b.AssertFileContent("public/mybatch/mygroup.js", "param-p1-main-edited") +} + +func TestBatchMultiHost(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +[languages] +[languages.en] +weight = 1 +baseURL = "https://example.com/en" +[languages.fr] +weight = 2 +baseURL = "https://example.com/fr" +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import * as foo from 'mylib'; +console.log("Hello, Main!"); +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/index.html -- +Home. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} + + +` + b := hugolib.Test(t, files, hugolib.TestOptWithOSFs()) + b.AssertPublishDir( + "en/mybatch/chunk-TOZKWCDE.js", "en/mybatch/mygroup.js ", + "fr/mybatch/mygroup.js", "fr/mybatch/chunk-TOZKWCDE.js") +} + +func TestBatchRenameBundledScript(t *testing.T) { + files := jsBatchFilesTemplate + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/p1.js", "P1 Script") + b.RenameFile("content/p1/p1script.js", "content/p1/p1script2.js") + _, err := b.BuildE() + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, "resource not set") + + // Rename it back. + b.RenameFile("content/p1/p1script2.js", "content/p1/p1script.js") + b.Build() +} + +func TestBatchErrorScriptResourceNotSet(t *testing.T) { + files := strings.Replace(jsBatchFilesTemplate, `(resources.Get "js/main.js")`, `(resources.Get "js/doesnotexist.js")`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `error calling SetOptions: resource not set`) +} + +func TestBatchSlashInBatchID(t *testing.T) { + files := strings.ReplaceAll(jsBatchFilesTemplate, `"mybatch"`, `"my/batch"`) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNil) + b.AssertPublishDir("my/batch/mygroup.js") +} + +func TestBatchSourceMaps(t *testing.T) { + filesTemplate := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term", "section"] +disableLiveReload = true +-- assets/js/styles.css -- +body { + background-color: red; +} +-- assets/js/main.js -- +import * as foo from 'mylib'; +console.log("Hello, Main!"); +-- assets/js/runner.js -- +console.log("Hello, Runner!"); +-- node_modules/mylib/index.js -- +console.log("Hello, My Lib!"); +-- layouts/shortcodes/hdx.html -- +{{ $path := .Get "r" }} +{{ $r := or (.Page.Resources.Get $path) (resources.Get $path) }} +{{ $batch := (js.Batch "mybatch") }} +{{ $scriptID := $path | anchorize }} +{{ $instanceID := .Ordinal | string }} +{{ $group := .Page.RelPermalink | anchorize }} +{{ $params := .Params | default dict }} +{{ $export := .Get "export" | default "default" }} +{{ with $batch.Group $group }} + {{ with .Runner "create-elements" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script $scriptID }} + {{ .SetOptions (dict + "resource" $r + "export" $export + "importContext" (slice $.Page) + ) + }} + {{ end }} + {{ with .Instance $scriptID $instanceID }} + {{ .SetOptions (dict "params" $params) }} + {{ end }} +{{ end }} +hdx-instance: {{ $scriptID }}: {{ $instanceID }}| +-- layouts/_default/baseof.html -- +Base. +{{ $batch := (js.Batch "mybatch") }} + {{ with $batch.Config }} + {{ .SetOptions (dict + "params" (dict "id" "config") + "sourceMap" "" + ) + }} +{{ end }} +{{ with (templates.Defer (dict "key" "global")) }} +Defer: +{{ $batch := (js.Batch "mybatch") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . -}} + {{ $k }}: {{ .RelPermalink }} + {{ end }} +{{ end -}} +{{ end }} +{{ block "main" . }}Main{{ end }} +End. +-- layouts/_default/single.html -- +{{ define "main" }} +==> Single Template Content: {{ .Content }}$ +{{ $batch := (js.Batch "mybatch") }} +{{ with $batch.Group "mygroup" }} + {{ with .Runner "run" }} + {{ .SetOptions (dict "resource" (resources.Get "js/runner.js")) }} + {{ end }} + {{ with .Script "main" }} + {{ .SetOptions (dict "resource" (resources.Get "js/main.js") "params" (dict "p1" "param-p1-main" )) }} + {{ end }} + {{ with .Instance "main" "i1" }} + {{ .SetOptions (dict "params" (dict "title" "Instance 1")) }} + {{ end }} +{{ end }} +{{ end }} +-- layouts/index.html -- +{{ define "main" }} +Home. +{{ end }} +-- content/p1/index.md -- +--- +title: "P1" +--- + +Some content. + +{{< hdx r="p1script.js" myparam="p1-param-1" >}} +{{< hdx r="p1script.js" myparam="p1-param-2" >}} + +-- content/p1/p1script.js -- +import * as foo from 'mylib'; +console.lg("Foo", foo); +console.log("P1 Script"); +export default function P1Script() {}; + + +` + files := strings.Replace(filesTemplate, `"sourceMap" ""`, `"sourceMap" "linked"`, 1) + b := hugolib.TestRunning(t, files, hugolib.TestOptWithOSFs()) + b.AssertFileContent("public/mybatch/mygroup.js.map", "main.js", "! ns-hugo") + b.AssertFileContent("public/mybatch/mygroup.js", "sourceMappingURL=mygroup.js.map") + b.AssertFileContent("public/mybatch/p1.js", "sourceMappingURL=p1.js.map") + b.AssertFileContent("public/mybatch/mygroup_run_runner.js", "sourceMappingURL=mygroup_run_runner.js.map") + b.AssertFileContent("public/mybatch/chunk-UQKPPNA6.js", "sourceMappingURL=chunk-UQKPPNA6.js.map") + + checkMap := func(p string, expectLen int) { + s := b.FileContent(p) + sources := esbuild.SourcesFromSourceMap(s) + b.Assert(sources, qt.HasLen, expectLen) + + // Check that all source files exist. + for _, src := range sources { + filename, ok := paths.UrlStringToFilename(src) + b.Assert(ok, qt.IsTrue) + _, err := os.Stat(filename) + b.Assert(err, qt.IsNil) + } + } + + checkMap("public/mybatch/mygroup.js.map", 1) + checkMap("public/mybatch/p1.js.map", 1) + checkMap("public/mybatch/mygroup_run_runner.js.map", 0) + checkMap("public/mybatch/chunk-UQKPPNA6.js.map", 1) +} + +func TestBatchErrorRunnerResourceNotSet(t *testing.T) { + files := strings.Replace(jsBatchFilesTemplate, `(resources.Get "js/runner.js")`, `(resources.Get "js/doesnotexist.js")`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `resource not set`) +} + +func TestBatchErrorScriptResourceInAssetsSyntaxError(t *testing.T) { + // Introduce JS syntax error in assets/js/main.js + files := strings.Replace(jsBatchFilesTemplate, `console.log("Hello, Main!");`, `console.log("Hello, Main!"`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`assets/js/main.js:5:0": Expected ")" but found "console"`)) +} + +func TestBatchErrorScriptResourceInBundleSyntaxError(t *testing.T) { + // Introduce JS syntax error in content/p1/p1script.js + files := strings.Replace(jsBatchFilesTemplate, `console.log("P1 Script");`, `console.log("P1 Script"`, 1) + b, err := hugolib.TestE(t, files, hugolib.TestOptWithOSFs()) + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`/content/p1/p1script.js:3:0": Expected ")" but found end of file`)) +} + +func TestBatch(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term"] +disableLiveReload = true +baseURL = "https://example.com" +-- package.json -- +{ + "devDependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + } +} +-- assets/js/shims/react.js -- +-- assets/js/shims/react-dom.js -- +module.exports = window.ReactDOM; +module.exports = window.React; +-- content/mybundle/index.md -- +--- +title: "My Bundle" +--- +-- content/mybundle/mybundlestyles.css -- +@import './foo.css'; +@import './bar.css'; +@import './otherbundlestyles.css'; + +.mybundlestyles { + background-color: blue; +} +-- content/mybundle/bundlereact.jsx -- +import * as React from "react"; +import './foo.css'; +import './mybundlestyles.css'; +window.React1 = React; + +let text = 'Click me, too!' + +export default function MyBundleButton() { + return ( + + ) +} + +-- assets/js/reactrunner.js -- +import * as ReactDOM from 'react-dom/client'; +import * as React from 'react'; + +export default function Run(group) { + for (const module of group.scripts) { + for (const instance of module.instances) { + /* This is a convention in this project. */ + let elId = §§${module.id}-${instance.id}§§; + let el = document.getElementById(elId); + if (!el) { + console.warn(§§Element with id ${elId} not found§§); + continue; + } + const root = ReactDOM.createRoot(el); + const reactEl = React.createElement(module.mod, instance.params); + root.render(reactEl); + } + } +} +-- assets/other/otherbundlestyles.css -- +.otherbundlestyles { + background-color: red; +} +-- assets/other/foo.css -- +@import './bar.css'; + +.foo { + background-color: blue; +} +-- assets/other/bar.css -- +.bar { + background-color: red; +} +-- assets/js/button.css -- +button { + background-color: red; +} +-- assets/js/bar.css -- +.bar-assets { + background-color: red; +} +-- assets/js/helper.js -- +import './bar.css' + +export function helper() { + console.log('helper'); +} + +-- assets/js/react1styles_nested.css -- +.react1styles_nested { + background-color: red; +} +-- assets/js/react1styles.css -- +@import './react1styles_nested.css'; +.react1styles { + background-color: red; +} +-- assets/js/react1.jsx -- +import * as React from "react"; +import './button.css' +import './foo.css' +import './react1styles.css' + +window.React1 = React; + +let text = 'Click me' + +export default function MyButton() { + return ( + + ) +} + +-- assets/js/react2.jsx -- +import * as React from "react"; +import { helper } from './helper.js' +import './foo.css' + +window.React2 = React; + +let text = 'Click me, too!' + +export function MyOtherButton() { + return ( + + ) +} +-- assets/js/main1.js -- +import * as React from "react"; +import * as params from '@params'; + +console.log('main1.React', React) +console.log('main1.params.id', params.id) + +-- assets/js/main2.js -- +import * as React from "react"; +import * as params from '@params'; + +console.log('main2.React', React) +console.log('main2.params.id', params.id) + +export default function Main2() {}; + +-- assets/js/main3.js -- +import * as React from "react"; +import * as params from '@params'; +import * as config from '@params/config'; + +console.log('main3.params.id', params.id) +console.log('config.params.id', config.id) + +export default function Main3() {}; + +-- layouts/_default/single.html -- +Single. + +{{ $r := .Resources.GetMatch "*.jsx" }} +{{ $batch := (js.Batch "mybundle") }} +{{ $otherCSS := (resources.Match "/other/*.css").Mount "/other" "." }} + {{ with $batch.Config }} + {{ $shims := dict "react" "js/shims/react.js" "react-dom/client" "js/shims/react-dom.js" }} + {{ .SetOptions (dict + "target" "es2018" + "params" (dict "id" "config") + "shims" $shims + ) + }} +{{ end }} +{{ with $batch.Group "reactbatch" }} + {{ with .Script "r3" }} + {{ .SetOptions (dict + "resource" $r + "importContext" (slice $ $otherCSS) + "params" (dict "id" "r3") + ) + }} + {{ end }} + {{ with .Instance "r3" "r2i1" }} + {{ .SetOptions (dict "title" "r2 instance 1")}} + {{ end }} +{{ end }} +-- layouts/index.html -- +Home. +{{ with (templates.Defer (dict "key" "global")) }} +{{ $batch := (js.Batch "mybundle") }} +{{ range $k, $v := $batch.Build.Groups }} + {{ range $kk, $vv := . }} + {{ $k }}: {{ $kk }}: {{ .RelPermalink }} + {{ end }} + {{ end }} +{{ end }} +{{ $myContentBundle := site.GetPage "mybundle" }} +{{ $batch := (js.Batch "mybundle") }} +{{ $otherCSS := (resources.Match "/other/*.css").Mount "/other" "." }} +{{ with $batch.Group "mains" }} + {{ with .Script "main1" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main1.js") + "params" (dict "id" "main1") + ) + }} + {{ end }} + {{ with .Script "main2" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main2.js") + "params" (dict "id" "main2") + ) + }} + {{ end }} + {{ with .Script "main3" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/main3.js") + ) + }} + {{ end }} +{{ with .Instance "main1" "m1i1" }}{{ .SetOptions (dict "params" (dict "title" "Main1 Instance 1"))}}{{ end }} +{{ with .Instance "main1" "m1i2" }}{{ .SetOptions (dict "params" (dict "title" "Main1 Instance 2"))}}{{ end }} +{{ end }} +{{ with $batch.Group "reactbatch" }} + {{ with .Runner "reactrunner" }} + {{ .SetOptions ( dict "resource" (resources.Get "js/reactrunner.js") )}} + {{ end }} + {{ with .Script "r1" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/react1.jsx") + "importContext" (slice $myContentBundle $otherCSS) + "params" (dict "id" "r1") + ) + }} + {{ end }} + {{ with .Instance "r1" "i1" }}{{ .SetOptions (dict "params" (dict "title" "Instance 1"))}}{{ end }} + {{ with .Instance "r1" "i2" }}{{ .SetOptions (dict "params" (dict "title" "Instance 2"))}}{{ end }} + {{ with .Script "r2" }} + {{ .SetOptions (dict + "resource" (resources.Get "js/react2.jsx") + "export" "MyOtherButton" + "importContext" $otherCSS + "params" (dict "id" "r2") + ) + }} + {{ end }} + {{ with .Instance "r2" "i1" }}{{ .SetOptions (dict "params" (dict "title" "Instance 2-1"))}}{{ end }} +{{ end }} + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + NeedsOsFS: true, + NeedsNpmInstall: true, + TxtarString: files, + Running: true, + LogLevel: logg.LevelWarn, + // PrintAndKeepTempDir: true, + }).Build() + + b.AssertFileContent("public/index.html", + "mains: 0: /mybundle/mains.js", + "reactbatch: 2: /mybundle/reactbatch.css", + ) + + b.AssertFileContent("public/mybundle/reactbatch.css", + ".bar {", + ) + + // Verify params resolution. + b.AssertFileContent("public/mybundle/mains.js", + ` +var id = "main1"; +console.log("main1.params.id", id); +var id2 = "main2"; +console.log("main2.params.id", id2); + + +# Params from top level config. +var id3 = "config"; +console.log("main3.params.id", void 0); +console.log("config.params.id", id3); +`) + + b.EditFileReplaceAll("content/mybundle/mybundlestyles.css", ".mybundlestyles", ".mybundlestyles-edit").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".mybundlestyles-edit {") + + b.EditFileReplaceAll("assets/other/bar.css", ".bar {", ".bar-edit {").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".bar-edit {") + + b.EditFileReplaceAll("assets/other/bar.css", ".bar-edit {", ".bar-edit2 {").Build() + b.AssertFileContent("public/mybundle/reactbatch.css", ".bar-edit2 {") +} diff --git a/internal/js/esbuild/build.go b/internal/js/esbuild/build.go new file mode 100644 index 000000000..33b91eafc --- /dev/null +++ b/internal/js/esbuild/build.go @@ -0,0 +1,236 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package esbuild provides functions for building JavaScript resources. +package esbuild + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/text" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources" +) + +// NewBuildClient creates a new BuildClient. +func NewBuildClient(fs *filesystems.SourceFilesystem, rs *resources.Spec) *BuildClient { + return &BuildClient{ + rs: rs, + sfs: fs, + } +} + +// BuildClient is a client for building JavaScript resources using esbuild. +type BuildClient struct { + rs *resources.Spec + sfs *filesystems.SourceFilesystem +} + +// Build builds the given JavaScript resources using esbuild with the given options. +func (c *BuildClient) Build(opts Options) (api.BuildResult, error) { + dependencyManager := opts.DependencyManager + if dependencyManager == nil { + dependencyManager = identity.NopManager + } + + opts.OutDir = c.rs.AbsPublishDir + opts.ResolveDir = c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved + opts.AbsWorkingDir = opts.ResolveDir + opts.TsConfig = c.rs.ResolveJSConfigFile("tsconfig.json") + assetsResolver := newFSResolver(c.rs.Assets.Fs) + + if err := opts.validate(); err != nil { + return api.BuildResult{}, err + } + + if err := opts.compile(); err != nil { + return api.BuildResult{}, err + } + + var err error + opts.compiled.Plugins, err = createBuildPlugins(c.rs, assetsResolver, dependencyManager, opts) + if err != nil { + return api.BuildResult{}, err + } + + if opts.Inject != nil { + // Resolve the absolute filenames. + for i, ext := range opts.Inject { + impPath := filepath.FromSlash(ext) + if filepath.IsAbs(impPath) { + return api.BuildResult{}, fmt.Errorf("inject: absolute paths not supported, must be relative to /assets") + } + + m := assetsResolver.resolveComponent(impPath) + + if m == nil { + return api.BuildResult{}, fmt.Errorf("inject: file %q not found", ext) + } + + opts.Inject[i] = m.Filename + + } + + opts.compiled.Inject = opts.Inject + + } + + result := api.Build(opts.compiled) + + if len(result.Errors) > 0 { + createErr := func(msg api.Message) error { + if msg.Location == nil { + return errors.New(msg.Text) + } + var ( + contentr hugio.ReadSeekCloser + errorMessage string + loc = msg.Location + errorPath = loc.File + err error + ) + + var resolvedError *ErrorMessageResolved + + if opts.ErrorMessageResolveFunc != nil { + resolvedError = opts.ErrorMessageResolveFunc(msg) + } + + if resolvedError == nil { + if errorPath == stdinImporter { + errorPath = opts.StdinSourcePath + } + + errorMessage = msg.Text + + var namespace string + for _, ns := range hugoNamespaces { + if strings.HasPrefix(errorPath, ns) { + namespace = ns + break + } + } + + if namespace != "" { + namespace += ":" + errorMessage = strings.ReplaceAll(errorMessage, namespace, "") + errorPath = strings.TrimPrefix(errorPath, namespace) + contentr, err = hugofs.Os.Open(errorPath) + } else { + var fi os.FileInfo + fi, err = c.sfs.Fs.Stat(errorPath) + if err == nil { + m := fi.(hugofs.FileMetaInfo).Meta() + errorPath = m.Filename + contentr, err = m.Open() + } + } + } else { + contentr = resolvedError.Content + errorPath = resolvedError.Path + errorMessage = resolvedError.Message + } + + if contentr != nil { + defer contentr.Close() + } + + if err == nil { + fe := herrors. + NewFileErrorFromName(errors.New(errorMessage), errorPath). + UpdatePosition(text.Position{Offset: -1, LineNumber: loc.Line, ColumnNumber: loc.Column}). + UpdateContent(contentr, nil) + + return fe + } + + return fmt.Errorf("%s", errorMessage) + } + + var errors []error + + for _, msg := range result.Errors { + errors = append(errors, createErr(msg)) + } + + // Return 1, log the rest. + for i, err := range errors { + if i > 0 { + c.rs.Logger.Errorf("js.Build failed: %s", err) + } + } + + return result, errors[0] + } + + inOutputPathToAbsFilename := opts.ResolveSourceMapSource + opts.ResolveSourceMapSource = func(s string) string { + if inOutputPathToAbsFilename != nil { + if filename := inOutputPathToAbsFilename(s); filename != "" { + return filename + } + } + + if m := assetsResolver.resolveComponent(s); m != nil { + return m.Filename + } + + return "" + } + + for i, o := range result.OutputFiles { + if err := fixOutputFile(&o, func(s string) string { + if s == "" { + return opts.ResolveSourceMapSource(opts.StdinSourcePath) + } + var isNsHugo bool + if strings.HasPrefix(s, "ns-hugo") { + isNsHugo = true + idxColon := strings.Index(s, ":") + s = s[idxColon+1:] + } + + if !strings.HasPrefix(s, PrefixHugoVirtual) { + if !filepath.IsAbs(s) { + s = filepath.Join(opts.OutDir, s) + } + } + + if isNsHugo { + if ss := opts.ResolveSourceMapSource(s); ss != "" { + if strings.HasPrefix(ss, PrefixHugoMemory) { + // File not on disk, mark it for removal from the sources slice. + return "" + } + return ss + } + return "" + } + return s + }); err != nil { + return result, err + } + result.OutputFiles[i] = o + } + + return result, nil +} diff --git a/internal/js/esbuild/helpers.go b/internal/js/esbuild/helpers.go new file mode 100644 index 000000000..b4cb565b8 --- /dev/null +++ b/internal/js/esbuild/helpers.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package esbuild provides functions for building JavaScript resources. +package esbuild diff --git a/internal/js/esbuild/options.go b/internal/js/esbuild/options.go new file mode 100644 index 000000000..21f9e31cd --- /dev/null +++ b/internal/js/esbuild/options.go @@ -0,0 +1,411 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "fmt" + "path/filepath" + "strings" + + "github.com/gohugoio/hugo/common/hugio" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/paths" + "github.com/gohugoio/hugo/identity" + + "github.com/evanw/esbuild/pkg/api" + + "github.com/gohugoio/hugo/media" + "github.com/mitchellh/mapstructure" +) + +var ( + nameTarget = map[string]api.Target{ + "": api.ESNext, + "esnext": api.ESNext, + "es5": api.ES5, + "es6": api.ES2015, + "es2015": api.ES2015, + "es2016": api.ES2016, + "es2017": api.ES2017, + "es2018": api.ES2018, + "es2019": api.ES2019, + "es2020": api.ES2020, + "es2021": api.ES2021, + "es2022": api.ES2022, + "es2023": api.ES2023, + "es2024": api.ES2024, + } + + // source names: https://github.com/evanw/esbuild/blob/9eca46464ed5615cb36a3beb3f7a7b9a8ffbe7cf/internal/config/config.go#L208 + nameLoader = map[string]api.Loader{ + "none": api.LoaderNone, + "base64": api.LoaderBase64, + "binary": api.LoaderBinary, + "copy": api.LoaderFile, + "css": api.LoaderCSS, + "dataurl": api.LoaderDataURL, + "default": api.LoaderDefault, + "empty": api.LoaderEmpty, + "file": api.LoaderFile, + "global-css": api.LoaderGlobalCSS, + "js": api.LoaderJS, + "json": api.LoaderJSON, + "jsx": api.LoaderJSX, + "local-css": api.LoaderLocalCSS, + "text": api.LoaderText, + "ts": api.LoaderTS, + "tsx": api.LoaderTSX, + } +) + +// DecodeExternalOptions decodes the given map into ExternalOptions. +func DecodeExternalOptions(m map[string]any) (ExternalOptions, error) { + opts := ExternalOptions{ + SourcesContent: true, + } + + if err := mapstructure.WeakDecode(m, &opts); err != nil { + return opts, err + } + + if opts.TargetPath != "" { + opts.TargetPath = paths.ToSlashTrimLeading(opts.TargetPath) + } + + opts.Target = strings.ToLower(opts.Target) + opts.Format = strings.ToLower(opts.Format) + + return opts, nil +} + +// ErrorMessageResolved holds a resolved error message. +type ErrorMessageResolved struct { + Path string + Message string + Content hugio.ReadSeekCloser +} + +// ExternalOptions holds user facing options for the js.Build template function. +type ExternalOptions struct { + // If not set, the source path will be used as the base target path. + // Note that the target path's extension may change if the target MIME type + // is different, e.g. when the source is TypeScript. + TargetPath string + + // Whether to minify to output. + Minify bool + + // One of "inline", "external", "linked" or "none". + SourceMap string + + SourcesContent bool + + // The language target. + // One of: es2015, es2016, es2017, es2018, es2019, es2020 or esnext. + // Default is esnext. + Target string + + // The output format. + // One of: iife, cjs, esm + // Default is to esm. + Format string + + // One of browser, node, neutral. + // Default is browser. + // See https://esbuild.github.io/api/#platform + Platform string + + // External dependencies, e.g. "react". + Externals []string + + // This option allows you to automatically replace a global variable with an import from another file. + // The filenames must be relative to /assets. + // See https://esbuild.github.io/api/#inject + Inject []string + + // User defined symbols. + Defines map[string]any + + // This tells esbuild to edit your source code before building to drop certain constructs. + // See https://esbuild.github.io/api/#drop + Drop string + + // Maps a component import to another. + Shims map[string]string + + // Configuring a loader for a given file type lets you load that file type with an + // import statement or a require call. For example, configuring the .png file extension + // to use the data URL loader means importing a .png file gives you a data URL + // containing the contents of that image + // + // See https://esbuild.github.io/api/#loader + Loaders map[string]string + + // User defined params. Will be marshaled to JSON and available as "@params", e.g. + // import * as params from '@params'; + Params any + + // What to use instead of React.createElement. + JSXFactory string + + // What to use instead of React.Fragment. + JSXFragment string + + // What to do about JSX syntax. + // See https://esbuild.github.io/api/#jsx + JSX string + + // Which library to use to automatically import JSX helper functions from. Only works if JSX is set to automatic. + // See https://esbuild.github.io/api/#jsx-import-source + JSXImportSource string + + // There is/was a bug in WebKit with severe performance issue with the tracking + // of TDZ checks in JavaScriptCore. + // + // Enabling this flag removes the TDZ and `const` assignment checks and + // may improve performance of larger JS codebases until the WebKit fix + // is in widespread use. + // + // See https://bugs.webkit.org/show_bug.cgi?id=199866 + // Deprecated: This no longer have any effect and will be removed. + // TODO(bep) remove. See https://github.com/evanw/esbuild/commit/869e8117b499ca1dbfc5b3021938a53ffe934dba + AvoidTDZ bool +} + +// InternalOptions holds internal options for the js.Build template function. +type InternalOptions struct { + MediaType media.Type + OutDir string + Contents string + SourceDir string + ResolveDir string + AbsWorkingDir string + Metafile bool + + StdinSourcePath string + + DependencyManager identity.Manager + + Stdin bool // Set to true to pass in the entry point as a byte slice. + Splitting bool + TsConfig string + EntryPoints []string + ImportOnResolveFunc func(string, api.OnResolveArgs) string + ImportOnLoadFunc func(api.OnLoadArgs) string + ImportParamsOnLoadFunc func(args api.OnLoadArgs) json.RawMessage + ErrorMessageResolveFunc func(api.Message) *ErrorMessageResolved + ResolveSourceMapSource func(string) string // Used to resolve paths in error source maps. +} + +// Options holds the options passed to Build. +type Options struct { + ExternalOptions + InternalOptions + + compiled api.BuildOptions +} + +func (opts *Options) compile() (err error) { + target, found := nameTarget[opts.Target] + if !found { + err = fmt.Errorf("invalid target: %q", opts.Target) + return + } + + var loaders map[string]api.Loader + if opts.Loaders != nil { + loaders = make(map[string]api.Loader) + for k, v := range opts.Loaders { + loader, found := nameLoader[v] + if !found { + err = fmt.Errorf("invalid loader: %q", v) + return + } + loaders[k] = loader + } + } + + mediaType := opts.MediaType + if mediaType.IsZero() { + mediaType = media.Builtin.JavascriptType + } + + var loader api.Loader + switch mediaType.SubType { + case media.Builtin.JavascriptType.SubType: + loader = api.LoaderJS + case media.Builtin.TypeScriptType.SubType: + loader = api.LoaderTS + case media.Builtin.TSXType.SubType: + loader = api.LoaderTSX + case media.Builtin.JSXType.SubType: + loader = api.LoaderJSX + default: + err = fmt.Errorf("unsupported Media Type: %q", opts.MediaType) + return + } + + var format api.Format + // One of: iife, cjs, esm + switch opts.Format { + case "", "iife": + format = api.FormatIIFE + case "esm": + format = api.FormatESModule + case "cjs": + format = api.FormatCommonJS + default: + err = fmt.Errorf("unsupported script output format: %q", opts.Format) + return + } + + var jsx api.JSX + switch opts.JSX { + case "", "transform": + jsx = api.JSXTransform + case "preserve": + jsx = api.JSXPreserve + case "automatic": + jsx = api.JSXAutomatic + default: + err = fmt.Errorf("unsupported jsx type: %q", opts.JSX) + return + } + + var platform api.Platform + switch opts.Platform { + case "", "browser": + platform = api.PlatformBrowser + case "node": + platform = api.PlatformNode + case "neutral": + platform = api.PlatformNeutral + default: + err = fmt.Errorf("unsupported platform type: %q", opts.Platform) + return + } + + var defines map[string]string + if opts.Defines != nil { + defines = maps.ToStringMapString(opts.Defines) + } + + var drop api.Drop + switch opts.Drop { + case "": + case "console": + drop = api.DropConsole + case "debugger": + drop = api.DropDebugger + default: + err = fmt.Errorf("unsupported drop type: %q", opts.Drop) + } + + // By default we only need to specify outDir and no outFile + outDir := opts.OutDir + outFile := "" + var sourceMap api.SourceMap + switch opts.SourceMap { + case "inline": + sourceMap = api.SourceMapInline + case "external": + sourceMap = api.SourceMapExternal + case "linked": + sourceMap = api.SourceMapLinked + case "", "none": + sourceMap = api.SourceMapNone + default: + err = fmt.Errorf("unsupported sourcemap type: %q", opts.SourceMap) + return + } + + sourcesContent := api.SourcesContentInclude + if !opts.SourcesContent { + sourcesContent = api.SourcesContentExclude + } + + opts.compiled = api.BuildOptions{ + Outfile: outFile, + Bundle: true, + Metafile: opts.Metafile, + AbsWorkingDir: opts.AbsWorkingDir, + + Target: target, + Format: format, + Platform: platform, + Sourcemap: sourceMap, + SourcesContent: sourcesContent, + + Loader: loaders, + + MinifyWhitespace: opts.Minify, + MinifyIdentifiers: opts.Minify, + MinifySyntax: opts.Minify, + + Outdir: outDir, + Splitting: opts.Splitting, + + Define: defines, + External: opts.Externals, + Drop: drop, + + JSXFactory: opts.JSXFactory, + JSXFragment: opts.JSXFragment, + + JSX: jsx, + JSXImportSource: opts.JSXImportSource, + + Tsconfig: opts.TsConfig, + + EntryPoints: opts.EntryPoints, + } + + if opts.Stdin { + // This makes ESBuild pass `stdin` as the Importer to the import. + opts.compiled.Stdin = &api.StdinOptions{ + Contents: opts.Contents, + ResolveDir: opts.ResolveDir, + Loader: loader, + } + } + return +} + +func (o Options) loaderFromFilename(filename string) api.Loader { + ext := filepath.Ext(filename) + if optsLoaders := o.compiled.Loader; optsLoaders != nil { + if l, found := optsLoaders[ext]; found { + return l + } + } + l, found := extensionToLoaderMap[ext] + if found { + return l + } + return api.LoaderJS +} + +func (opts *Options) validate() error { + if opts.ImportOnResolveFunc != nil && opts.ImportOnLoadFunc == nil { + return fmt.Errorf("ImportOnLoadFunc must be set if ImportOnResolveFunc is set") + } + if opts.ImportOnResolveFunc == nil && opts.ImportOnLoadFunc != nil { + return fmt.Errorf("ImportOnResolveFunc must be set if ImportOnLoadFunc is set") + } + if opts.AbsWorkingDir == "" { + return fmt.Errorf("AbsWorkingDir must be set") + } + return nil +} diff --git a/internal/js/esbuild/options_test.go b/internal/js/esbuild/options_test.go new file mode 100644 index 000000000..e92c3bea6 --- /dev/null +++ b/internal/js/esbuild/options_test.go @@ -0,0 +1,262 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "testing" + + "github.com/gohugoio/hugo/media" + + "github.com/evanw/esbuild/pkg/api" + + qt "github.com/frankban/quicktest" +) + +func TestToBuildOptions(t *testing.T) { + c := qt.New(t) + + opts := Options{ + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Platform: api.PlatformBrowser, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", + Format: "cjs", + Minify: true, + AvoidTDZ: true, + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, + SourcesContent: 1, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "inline", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + SourcesContent: 1, + Sourcemap: api.SourceMapInline, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "inline", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Sourcemap: api.SourceMapInline, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Target: "es2018", Format: "cjs", Minify: true, + SourceMap: "external", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + Platform: api.PlatformBrowser, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Sourcemap: api.SourceMapExternal, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + JSX: "automatic", JSXImportSource: "preact", + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + Stdin: true, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Platform: api.PlatformBrowser, + SourcesContent: 1, + Stdin: &api.StdinOptions{ + Loader: api.LoaderJS, + }, + JSX: api.JSXAutomatic, + JSXImportSource: "preact", + }) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Drop: "console", + }, + } + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled.Drop, qt.Equals, api.DropConsole) + opts = Options{ + ExternalOptions: ExternalOptions{ + Drop: "debugger", + }, + } + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled.Drop, qt.Equals, api.DropDebugger) + + opts = Options{ + ExternalOptions: ExternalOptions{ + Drop: "adsfadsf", + }, + } + c.Assert(opts.compile(), qt.ErrorMatches, `unsupported drop type: "adsfadsf"`) +} + +func TestToBuildOptionsTarget(t *testing.T) { + c := qt.New(t) + + for _, test := range []struct { + target string + expect api.Target + }{ + {"es2015", api.ES2015}, + {"es2016", api.ES2016}, + {"es2017", api.ES2017}, + {"es2018", api.ES2018}, + {"es2019", api.ES2019}, + {"es2020", api.ES2020}, + {"es2021", api.ES2021}, + {"es2022", api.ES2022}, + {"es2023", api.ES2023}, + {"", api.ESNext}, + {"esnext", api.ESNext}, + } { + c.Run(test.target, func(c *qt.C) { + opts := Options{ + ExternalOptions: ExternalOptions{ + Target: test.target, + }, + InternalOptions: InternalOptions{ + MediaType: media.Builtin.JavascriptType, + }, + } + + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled.Target, qt.Equals, test.expect) + }) + } +} + +func TestDecodeExternalOptions(t *testing.T) { + c := qt.New(t) + m := map[string]any{ + "platform": "node", + } + ext, err := DecodeExternalOptions(m) + c.Assert(err, qt.IsNil) + c.Assert(ext, qt.DeepEquals, ExternalOptions{ + SourcesContent: true, + Platform: "node", + }) + + opts := Options{ + ExternalOptions: ext, + } + c.Assert(opts.compile(), qt.IsNil) + c.Assert(opts.compiled, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Platform: api.PlatformNode, + SourcesContent: api.SourcesContentInclude, + }) +} diff --git a/internal/js/esbuild/resolve.go b/internal/js/esbuild/resolve.go new file mode 100644 index 000000000..a2516dbd2 --- /dev/null +++ b/internal/js/esbuild/resolve.go @@ -0,0 +1,323 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" + "github.com/spf13/afero" + "slices" +) + +const ( + NsHugoImport = "ns-hugo-imp" + NsHugoImportResolveFunc = "ns-hugo-imp-func" + nsHugoParams = "ns-hugo-params" + pathHugoConfigParams = "@params/config" + + stdinImporter = "" +) + +var hugoNamespaces = []string{NsHugoImport, NsHugoImportResolveFunc, nsHugoParams} + +const ( + PrefixHugoVirtual = "__hu_v" + PrefixHugoMemory = "__hu_m" +) + +var extensionToLoaderMap = map[string]api.Loader{ + ".js": api.LoaderJS, + ".mjs": api.LoaderJS, + ".cjs": api.LoaderJS, + ".jsx": api.LoaderJSX, + ".ts": api.LoaderTS, + ".tsx": api.LoaderTSX, + ".css": api.LoaderCSS, + ".json": api.LoaderJSON, + ".txt": api.LoaderText, +} + +// This is a common sub-set of ESBuild's default extensions. +// We assume that imports of JSON, CSS etc. will be using their full +// name with extension. +var commonExtensions = []string{".js", ".ts", ".tsx", ".jsx"} + +// ResolveComponent resolves a component using the given resolver. +func ResolveComponent[T any](impPath string, resolve func(string) (v T, found, isDir bool)) (v T, found bool) { + findFirst := func(base string) (v T, found, isDir bool) { + for _, ext := range commonExtensions { + if strings.HasSuffix(impPath, ext) { + // Import of foo.js.js need the full name. + continue + } + if v, found, isDir = resolve(base + ext); found { + return + } + } + + // Not found. + return + } + + // We need to check if this is a regular file imported without an extension. + // There may be ambiguous situations where both foo.js and foo/index.js exists. + // This import order is in line with both how Node and ESBuild's native + // import resolver works. + + // It may be a regular file imported without an extension, e.g. + // foo or foo/index. + v, found, _ = findFirst(impPath) + if found { + return v, found + } + + base := filepath.Base(impPath) + if base == "index" { + // try index.esm.js etc. + v, found, _ = findFirst(impPath + ".esm") + if found { + return v, found + } + } + + // Check the path as is. + var isDir bool + v, found, isDir = resolve(impPath) + if found && isDir { + v, found, _ = findFirst(filepath.Join(impPath, "index")) + if !found { + v, found, _ = findFirst(filepath.Join(impPath, "index.esm")) + } + } + + if !found && strings.HasSuffix(base, ".js") { + v, found, _ = findFirst(strings.TrimSuffix(impPath, ".js")) + } + + return +} + +// ResolveResource resolves a resource using the given resourceGetter. +func ResolveResource(impPath string, resourceGetter resource.ResourceGetter) (r resource.Resource) { + resolve := func(name string) (v resource.Resource, found, isDir bool) { + r := resourceGetter.Get(name) + return r, r != nil, false + } + r, found := ResolveComponent(impPath, resolve) + if !found { + return nil + } + return r +} + +func newFSResolver(fs afero.Fs) *fsResolver { + return &fsResolver{fs: fs, resolved: maps.NewCache[string, *hugofs.FileMeta]()} +} + +type fsResolver struct { + fs afero.Fs + resolved *maps.Cache[string, *hugofs.FileMeta] +} + +func (r *fsResolver) resolveComponent(impPath string) *hugofs.FileMeta { + v, _ := r.resolved.GetOrCreate(impPath, func() (*hugofs.FileMeta, error) { + resolve := func(name string) (*hugofs.FileMeta, bool, bool) { + if fi, err := r.fs.Stat(name); err == nil { + return fi.(hugofs.FileMetaInfo).Meta(), true, fi.IsDir() + } + return nil, false, false + } + v, _ := ResolveComponent(impPath, resolve) + return v, nil + }) + return v +} + +func createBuildPlugins(rs *resources.Spec, assetsResolver *fsResolver, depsManager identity.Manager, opts Options) ([]api.Plugin, error) { + fs := rs.Assets + + resolveImport := func(args api.OnResolveArgs) (api.OnResolveResult, error) { + impPath := args.Path + shimmed := false + if opts.Shims != nil { + override, found := opts.Shims[impPath] + if found { + impPath = override + shimmed = true + } + } + + if slices.Contains(opts.Externals, impPath) { + return api.OnResolveResult{ + Path: impPath, + External: true, + }, nil + } + + if opts.ImportOnResolveFunc != nil { + if s := opts.ImportOnResolveFunc(impPath, args); s != "" { + return api.OnResolveResult{Path: s, Namespace: NsHugoImportResolveFunc}, nil + } + } + + importer := args.Importer + + isStdin := importer == stdinImporter + var relDir string + if !isStdin { + if strings.HasPrefix(importer, PrefixHugoVirtual) { + relDir = filepath.Dir(strings.TrimPrefix(importer, PrefixHugoVirtual)) + } else { + rel, found := fs.MakePathRelative(importer, true) + + if !found { + if shimmed { + relDir = opts.SourceDir + } else { + // Not in any of the /assets folders. + // This is an import from a node_modules, let + // ESBuild resolve this. + return api.OnResolveResult{}, nil + } + } else { + relDir = filepath.Dir(rel) + } + } + } else { + relDir = opts.SourceDir + } + + // Imports not starting with a "." is assumed to live relative to /assets. + // Hugo makes no assumptions about the directory structure below /assets. + if relDir != "" && strings.HasPrefix(impPath, ".") { + impPath = filepath.Join(relDir, impPath) + } + + m := assetsResolver.resolveComponent(impPath) + + if m != nil { + depsManager.AddIdentity(m.PathInfo) + + // Store the source root so we can create a jsconfig.json + // to help IntelliSense when the build is done. + // This should be a small number of elements, and when + // in server mode, we may get stale entries on renames etc., + // but that shouldn't matter too much. + rs.JSConfigBuilder.AddSourceRoot(m.SourceRoot) + return api.OnResolveResult{Path: m.Filename, Namespace: NsHugoImport}, nil + } + + // Fall back to ESBuild's resolve. + return api.OnResolveResult{}, nil + } + + importResolver := api.Plugin{ + Name: "hugo-import-resolver", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `.*`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + return resolveImport(args) + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: NsHugoImport}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + b, err := os.ReadFile(args.Path) + if err != nil { + return api.OnLoadResult{}, fmt.Errorf("failed to read %q: %w", args.Path, err) + } + c := string(b) + + return api.OnLoadResult{ + // See https://github.com/evanw/esbuild/issues/502 + // This allows all modules to resolve dependencies + // in the main project's node_modules. + ResolveDir: opts.ResolveDir, + Contents: &c, + Loader: opts.loaderFromFilename(args.Path), + }, nil + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: NsHugoImportResolveFunc}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + c := opts.ImportOnLoadFunc(args) + if c == "" { + return api.OnLoadResult{}, fmt.Errorf("ImportOnLoadFunc failed to resolve %q", args.Path) + } + + return api.OnLoadResult{ + ResolveDir: opts.ResolveDir, + Contents: &c, + Loader: opts.loaderFromFilename(args.Path), + }, nil + }) + }, + } + + params := opts.Params + if params == nil { + // This way @params will always resolve to something. + params = make(map[string]any) + } + + b, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("failed to marshal params: %w", err) + } + + paramsPlugin := api.Plugin{ + Name: "hugo-params-plugin", + Setup: func(build api.PluginBuild) { + build.OnResolve(api.OnResolveOptions{Filter: `^@params(/config)?$`}, + func(args api.OnResolveArgs) (api.OnResolveResult, error) { + resolvedPath := args.Importer + + if args.Path == pathHugoConfigParams { + resolvedPath = pathHugoConfigParams + } + + return api.OnResolveResult{ + Path: resolvedPath, + Namespace: nsHugoParams, + }, nil + }) + build.OnLoad(api.OnLoadOptions{Filter: `.*`, Namespace: nsHugoParams}, + func(args api.OnLoadArgs) (api.OnLoadResult, error) { + bb := b + if args.Path != pathHugoConfigParams && opts.ImportParamsOnLoadFunc != nil { + bb = opts.ImportParamsOnLoadFunc(args) + } + s := string(bb) + + if s == "" { + s = "{}" + } + + return api.OnLoadResult{ + Contents: &s, + Loader: api.LoaderJSON, + }, nil + }) + }, + } + + return []api.Plugin{importResolver, paramsPlugin}, nil +} diff --git a/internal/js/esbuild/resolve_test.go b/internal/js/esbuild/resolve_test.go new file mode 100644 index 000000000..86e3138f2 --- /dev/null +++ b/internal/js/esbuild/resolve_test.go @@ -0,0 +1,86 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "path" + "path/filepath" + "testing" + + qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/testconfig" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/hugolib/paths" + "github.com/spf13/afero" +) + +func TestResolveComponentInAssets(t *testing.T) { + c := qt.New(t) + + for _, test := range []struct { + name string + files []string + impPath string + expect string + }{ + {"Basic, extension", []string{"foo.js", "bar.js"}, "foo.js", "foo.js"}, + {"Basic, no extension", []string{"foo.js", "bar.js"}, "foo", "foo.js"}, + {"Basic, no extension, typescript", []string{"foo.ts", "bar.js"}, "foo", "foo.ts"}, + {"Not found", []string{"foo.js", "bar.js"}, "moo.js", ""}, + {"Not found, double js extension", []string{"foo.js.js", "bar.js"}, "foo.js", ""}, + {"Index file, folder only", []string{"foo/index.js", "bar.js"}, "foo", "foo/index.js"}, + {"Index file, folder and index", []string{"foo/index.js", "bar.js"}, "foo/index", "foo/index.js"}, + {"Index file, folder and index and suffix", []string{"foo/index.js", "bar.js"}, "foo/index.js", "foo/index.js"}, + {"Index ESM file, folder only", []string{"foo/index.esm.js", "bar.js"}, "foo", "foo/index.esm.js"}, + {"Index ESM file, folder and index", []string{"foo/index.esm.js", "bar.js"}, "foo/index", "foo/index.esm.js"}, + {"Index ESM file, folder and index and suffix", []string{"foo/index.esm.js", "bar.js"}, "foo/index.esm.js", "foo/index.esm.js"}, + // We added these index.esm.js cases in v0.101.0. The case below is unlikely to happen in the wild, but add a test + // to document Hugo's behavior. We pick the file with the name index.js; anything else would be breaking. + {"Index and Index ESM file, folder only", []string{"foo/index.esm.js", "foo/index.js", "bar.js"}, "foo", "foo/index.js"}, + + // Issue #8949 + {"Check file before directory", []string{"foo.js", "foo/index.js"}, "foo", "foo.js"}, + } { + c.Run(test.name, func(c *qt.C) { + baseDir := "assets" + mfs := afero.NewMemMapFs() + + for _, filename := range test.files { + c.Assert(afero.WriteFile(mfs, filepath.Join(baseDir, filename), []byte("let foo='bar';"), 0o777), qt.IsNil) + } + + conf := testconfig.GetTestConfig(mfs, config.New()) + fs := hugofs.NewFrom(mfs, conf.BaseConfig()) + + p, err := paths.New(fs, conf) + c.Assert(err, qt.IsNil) + bfs, err := filesystems.NewBase(p, nil) + c.Assert(err, qt.IsNil) + resolver := newFSResolver(bfs.Assets.Fs) + + got := resolver.resolveComponent(test.impPath) + + gotPath := "" + expect := test.expect + if got != nil { + gotPath = filepath.ToSlash(got.Filename) + expect = path.Join(baseDir, test.expect) + } + + c.Assert(gotPath, qt.Equals, expect) + }) + } +} diff --git a/internal/js/esbuild/sourcemap.go b/internal/js/esbuild/sourcemap.go new file mode 100644 index 000000000..647f0c081 --- /dev/null +++ b/internal/js/esbuild/sourcemap.go @@ -0,0 +1,80 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package esbuild + +import ( + "encoding/json" + "strings" + + "github.com/evanw/esbuild/pkg/api" + "github.com/gohugoio/hugo/common/paths" +) + +type sourceMap struct { + Version int `json:"version"` + Sources []string `json:"sources"` + SourcesContent []string `json:"sourcesContent"` + Mappings string `json:"mappings"` + Names []string `json:"names"` +} + +func fixOutputFile(o *api.OutputFile, resolve func(string) string) error { + if strings.HasSuffix(o.Path, ".map") { + b, err := fixSourceMap(o.Contents, resolve) + if err != nil { + return err + } + o.Contents = b + } + return nil +} + +func fixSourceMap(s []byte, resolve func(string) string) ([]byte, error) { + var sm sourceMap + if err := json.Unmarshal([]byte(s), &sm); err != nil { + return nil, err + } + + sm.Sources = fixSourceMapSources(sm.Sources, resolve) + + b, err := json.Marshal(sm) + if err != nil { + return nil, err + } + + return b, nil +} + +func fixSourceMapSources(s []string, resolve func(string) string) []string { + var result []string + for _, src := range s { + if s := resolve(src); s != "" { + // Absolute filenames works fine on U*ix (tested in Chrome on MacOs), but works very poorly on Windows (again Chrome). + // So, convert it to a URL. + if u, err := paths.UrlFromFilename(s); err == nil { + result = append(result, u.String()) + } + } + } + return result +} + +// Used in tests. +func SourcesFromSourceMap(s string) []string { + var sm sourceMap + if err := json.Unmarshal([]byte(s), &sm); err != nil { + return nil + } + return sm.Sources +} diff --git a/internal/warpc/js/common.js b/internal/warpc/js/common.js index 49aba9b4b..61c535fb7 100644 --- a/internal/warpc/js/common.js +++ b/internal/warpc/js/common.js @@ -4,6 +4,15 @@ export function readInput(handle) { let currentLine = []; const buffer = new Uint8Array(buffSize); + // These are not implemented by QuickJS. + console.warn = (value) => { + console.log(value); + }; + + console.error = (value) => { + throw new Error(value); + }; + // Read all the available bytes while (true) { // Stdin file descriptor diff --git a/internal/warpc/js/greet.bundle.js b/internal/warpc/js/greet.bundle.js index c5aa4a13a..6828d582a 100644 --- a/internal/warpc/js/greet.bundle.js +++ b/internal/warpc/js/greet.bundle.js @@ -1,2 +1,2 @@ -(()=>{function l(r){let e=[],a=new Uint8Array(1024);for(;;){let n=0;try{n=Javy.IO.readSync(0,a)}catch(o){if(o.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(n<0)throw new Error("Error reading from stdin");if(n===0)break;if(e=[...e,...a.subarray(0,n)],!e.includes(10))continue;let t=0;for(let o=0;t{function w(r){let e=[],c=new Uint8Array(1024);for(console.warn=n=>{console.log(n)},console.error=n=>{throw new Error(n)};;){let o=0;try{o=Javy.IO.readSync(0,c)}catch(a){if(a.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(o<0)throw new Error("Error reading from stdin");if(o===0)break;if(e=[...e,...c.subarray(0,o)],!e.includes(10))continue;let t=0;for(let a=0;t{function Ut(r){let t=[],a=new Uint8Array(1024);for(;;){let s=0;try{s=Javy.IO.readSync(0,a)}catch(h){if(h.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(s<0)throw new Error("Error reading from stdin");if(s===0)break;if(t=[...t,...a.subarray(0,s)],!t.includes(10))continue;let o=0;for(let h=0;o15?p="\u2026"+h.slice(n-15,n):p=h.slice(0,n);var g;s+15":">","<":"<",'"':""","'":"'"},za=/[&><"']/g;function Aa(r){return String(r).replace(za,e=>Ma[e])}var wr=function r(e){return e.type==="ordgroup"||e.type==="color"?e.body.length===1?r(e.body[0]):e:e.type==="font"?r(e.body):e},Ta=function(e){var t=wr(e);return t.type==="mathord"||t.type==="textord"||t.type==="atom"},Ba=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e},Da=function(e){var t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?t[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?null:t[1].toLowerCase():"_relative"},N={contains:xa,deflt:wa,escape:Aa,hyphenate:Sa,getBaseElem:wr,isCharacterBox:Ta,protocolFromUrl:Da},qe={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:r=>"#"+r},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(r,e)=>(e.push(r),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:r=>Math.max(0,r),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:r=>Math.max(0,r),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:r=>Math.max(0,r),cli:"-e, --max-expand ",cliProcessor:r=>r==="Infinity"?1/0:parseInt(r)},globalGroup:{type:"boolean",cli:!1}};function Ca(r){if(r.default)return r.default;var e=r.type,t=Array.isArray(e)?e[0]:e;if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}var he=class{constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var t in qe)if(qe.hasOwnProperty(t)){var a=qe[t];this[t]=e[t]!==void 0?a.processor?a.processor(e[t]):e[t]:Ca(a)}}reportNonstrict(e,t,a){var n=this.strict;if(typeof n=="function"&&(n=n(e,t,a)),!(!n||n==="ignore")){if(n===!0||n==="error")throw new M("LaTeX-incompatible input and strict mode is set to 'error': "+(t+" ["+e+"]"),a);n==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(t+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+n+"': "+t+" ["+e+"]"))}}useStrictBehavior(e,t,a){var n=this.strict;if(typeof n=="function")try{n=n(e,t,a)}catch{n="error"}return!n||n==="ignore"?!1:n===!0||n==="error"?!0:n==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(t+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+n+"': "+t+" ["+e+"]")),!1)}isTrusted(e){if(e.url&&!e.protocol){var t=N.protocolFromUrl(e.url);if(t==null)return!1;e.protocol=t}var a=typeof this.trust=="function"?this.trust(e):this.trust;return!!a}},x0=class{constructor(e,t,a){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=a}sup(){return w0[qa[this.id]]}sub(){return w0[Na[this.id]]}fracNum(){return w0[Ea[this.id]]}fracDen(){return w0[Ra[this.id]]}cramp(){return w0[Ia[this.id]]}text(){return w0[Oa[this.id]]}isTight(){return this.size>=2}},kt=0,Ee=1,te=2,C0=3,me=4,f0=5,re=6,n0=7,w0=[new x0(kt,0,!1),new x0(Ee,0,!0),new x0(te,1,!1),new x0(C0,1,!0),new x0(me,2,!1),new x0(f0,2,!0),new x0(re,3,!1),new x0(n0,3,!0)],qa=[me,f0,me,f0,re,n0,re,n0],Na=[f0,f0,f0,f0,n0,n0,n0,n0],Ea=[te,C0,me,f0,re,n0,re,n0],Ra=[C0,C0,f0,f0,n0,n0,n0,n0],Ia=[Ee,Ee,C0,C0,f0,f0,n0,n0],Oa=[kt,Ee,te,C0,te,C0,te,C0],R={DISPLAY:w0[kt],TEXT:w0[te],SCRIPT:w0[me],SCRIPTSCRIPT:w0[re]},ht=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function Ha(r){for(var e=0;e=n[0]&&r<=n[1])return t.name}return null}var Ne=[];ht.forEach(r=>r.blocks.forEach(e=>Ne.push(...e)));function kr(r){for(var e=0;e=Ne[e]&&r<=Ne[e+1])return!0;return!1}var ee=80,Fa=function(e,t){return"M95,"+(622+e+t)+` +(()=>{function Wt(r){let t=[],a=new Uint8Array(1024);for(console.warn=n=>{console.log(n)},console.error=n=>{throw new Error(n)};;){let s=0;try{s=Javy.IO.readSync(0,a)}catch(h){if(h.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(s<0)throw new Error("Error reading from stdin");if(s===0)break;if(t=[...t,...a.subarray(0,s)],!t.includes(10))continue;let l=0;for(let h=0;l15?f="\u2026"+h.slice(n-15,n):f=h.slice(0,n);var v;s+15":">","<":"<",'"':""","'":"'"},Ba=/[&><"']/g;function Da(r){return String(r).replace(Ba,e=>qa[e])}var zr=function r(e){return e.type==="ordgroup"||e.type==="color"?e.body.length===1?r(e.body[0]):e:e.type==="font"?r(e.body):e},Ca=function(e){var t=zr(e);return t.type==="mathord"||t.type==="textord"||t.type==="atom"},_a=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e},Na=function(e){var t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?t[2]!==":"||!/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?null:t[1].toLowerCase():"_relative"},O={contains:Ma,deflt:za,escape:Da,hyphenate:Ta,getBaseElem:zr,isCharacterBox:Ca,protocolFromUrl:Na},Oe={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:r=>"#"+r},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(r,e)=>(e.push(r),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:r=>Math.max(0,r),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:r=>Math.max(0,r),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:r=>Math.max(0,r),cli:"-e, --max-expand ",cliProcessor:r=>r==="Infinity"?1/0:parseInt(r)},globalGroup:{type:"boolean",cli:!1}};function Oa(r){if(r.default)return r.default;var e=r.type,t=Array.isArray(e)?e[0]:e;if(typeof t!="string")return t.enum[0];switch(t){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}var de=class{constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(var t in Oe)if(Oe.hasOwnProperty(t)){var a=Oe[t];this[t]=e[t]!==void 0?a.processor?a.processor(e[t]):e[t]:Oa(a)}}reportNonstrict(e,t,a){var n=this.strict;if(typeof n=="function"&&(n=n(e,t,a)),!(!n||n==="ignore")){if(n===!0||n==="error")throw new z("LaTeX-incompatible input and strict mode is set to 'error': "+(t+" ["+e+"]"),a);n==="warn"?typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(t+" ["+e+"]")):typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+n+"': "+t+" ["+e+"]"))}}useStrictBehavior(e,t,a){var n=this.strict;if(typeof n=="function")try{n=n(e,t,a)}catch{n="error"}return!n||n==="ignore"?!1:n===!0||n==="error"?!0:n==="warn"?(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+(t+" ["+e+"]")),!1):(typeof console<"u"&&console.warn("LaTeX-incompatible input and strict mode is set to "+("unrecognized '"+n+"': "+t+" ["+e+"]")),!1)}isTrusted(e){if(e.url&&!e.protocol){var t=O.protocolFromUrl(e.url);if(t==null)return!1;e.protocol=t}var a=typeof this.trust=="function"?this.trust(e):this.trust;return!!a}},k0=class{constructor(e,t,a){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=a}sup(){return M0[Ia[this.id]]}sub(){return M0[Ea[this.id]]}fracNum(){return M0[Ra[this.id]]}fracDen(){return M0[$a[this.id]]}cramp(){return M0[La[this.id]]}text(){return M0[Fa[this.id]]}isTight(){return this.size>=2}},At=0,Ee=1,ae=2,N0=3,pe=4,v0=5,ne=6,o0=7,M0=[new k0(At,0,!1),new k0(Ee,0,!0),new k0(ae,1,!1),new k0(N0,1,!0),new k0(pe,2,!1),new k0(v0,2,!0),new k0(ne,3,!1),new k0(o0,3,!0)],Ia=[pe,v0,pe,v0,ne,o0,ne,o0],Ea=[v0,v0,v0,v0,o0,o0,o0,o0],Ra=[ae,N0,pe,v0,ne,o0,ne,o0],$a=[N0,N0,v0,v0,o0,o0,o0,o0],La=[Ee,Ee,N0,N0,v0,v0,o0,o0],Fa=[At,Ee,ae,N0,ae,N0,ae,N0],E={DISPLAY:M0[At],TEXT:M0[ae],SCRIPT:M0[pe],SCRIPTSCRIPT:M0[ne]},pt=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];function Ha(r){for(var e=0;e=n[0]&&r<=n[1])return t.name}return null}var Ie=[];pt.forEach(r=>r.blocks.forEach(e=>Ie.push(...e)));function Ar(r){for(var e=0;e=Ie[e]&&r<=Ie[e+1])return!0;return!1}var re=80,Pa=function(e,t){return"M95,"+(622+e+t)+` c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14 c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54 c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10 @@ -10,7 +10,7 @@ c5.3,-9.3,12,-14,20,-14 H400000v`+(40+e)+`H845.2724 s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7 c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z -M`+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},La=function(e,t){return"M263,"+(601+e+t)+`c0.7,0,18,39.7,52,119 +M`+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},Ga=function(e,t){return"M263,"+(601+e+t)+`c0.7,0,18,39.7,52,119 c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120 c340,-704.7,510.7,-1060.3,512,-1067 l`+e/2.084+" -"+e+` @@ -20,7 +20,7 @@ s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5, c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1 s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26 c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z -M`+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},Pa=function(e,t){return"M983 "+(10+e+t)+` +M`+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},Va=function(e,t){return"M983 "+(10+e+t)+` l`+e/3.13+" -"+e+` c4,-6.7,10,-10,18,-10 H400000v`+(40+e)+` H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7 @@ -29,7 +29,7 @@ c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30 c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722 c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5 c53.7,-170.3,84.5,-266.8,92.5,-289.5z -M`+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},Ga=function(e,t){return"M424,"+(2398+e+t)+` +M`+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"},Ua=function(e,t){return"M424,"+(2398+e+t)+` c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514 c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20 s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121 @@ -39,18 +39,18 @@ v`+(40+e)+`H1014.6 s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185 c-2,6,-10,9,-24,9 c-8,0,-12,-0.7,-12,-2z M`+(1001+e)+" "+t+` -h400000v`+(40+e)+"h-400000z"},Va=function(e,t){return"M473,"+(2713+e+t)+` +h400000v`+(40+e)+"h-400000z"},Xa=function(e,t){return"M473,"+(2713+e+t)+` c339.3,-1799.3,509.3,-2700,510,-2702 l`+e/5.298+" -"+e+` c3.3,-7.3,9.3,-11,18,-11 H400000v`+(40+e)+`H1017.7 s-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9 c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200 c0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26 s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104, -606zM`+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"},Ua=function(e){var t=e/2;return"M400000 "+e+" H0 L"+t+" 0 l65 45 L145 "+(e-80)+" H400000z"},$a=function(e,t,a){var n=a-54-t-e;return"M702 "+(e+t)+"H400000"+(40+e)+` +606zM`+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"},Wa=function(e){var t=e/2;return"M400000 "+e+" H0 L"+t+" 0 l65 45 L145 "+(e-80)+" H400000z"},Ya=function(e,t,a){var n=a-54-t-e;return"M702 "+(e+t)+"H400000"+(40+e)+` H742v`+n+`l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1 h-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170 c-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667 -219 661 l218 661zM702 `+t+"H400000v"+(40+e)+"H742z"},Ya=function(e,t,a){t=1e3*t;var n="";switch(e){case"sqrtMain":n=Fa(t,ee);break;case"sqrtSize1":n=La(t,ee);break;case"sqrtSize2":n=Pa(t,ee);break;case"sqrtSize3":n=Ga(t,ee);break;case"sqrtSize4":n=Va(t,ee);break;case"sqrtTall":n=$a(t,ee,a)}return n},Xa=function(e,t){switch(e){case"\u239C":return"M291 0 H417 V"+t+" H291z M291 0 H417 V"+t+" H291z";case"\u2223":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z";case"\u2225":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z"+("M367 0 H410 V"+t+" H367z M367 0 H410 V"+t+" H367z");case"\u239F":return"M457 0 H583 V"+t+" H457z M457 0 H583 V"+t+" H457z";case"\u23A2":return"M319 0 H403 V"+t+" H319z M319 0 H403 V"+t+" H319z";case"\u23A5":return"M263 0 H347 V"+t+" H263z M263 0 H347 V"+t+" H263z";case"\u23AA":return"M384 0 H504 V"+t+" H384z M384 0 H504 V"+t+" H384z";case"\u23D0":return"M312 0 H355 V"+t+" H312z M312 0 H355 V"+t+" H312z";case"\u2016":return"M257 0 H300 V"+t+" H257z M257 0 H300 V"+t+" H257z"+("M478 0 H521 V"+t+" H478z M478 0 H521 V"+t+" H478z");default:return""}},$t={doubleleftarrow:`M262 157 +219 661 l218 661zM702 `+t+"H400000v"+(40+e)+"H742z"},Za=function(e,t,a){t=1e3*t;var n="";switch(e){case"sqrtMain":n=Pa(t,re);break;case"sqrtSize1":n=Ga(t,re);break;case"sqrtSize2":n=Va(t,re);break;case"sqrtSize3":n=Ua(t,re);break;case"sqrtSize4":n=Xa(t,re);break;case"sqrtTall":n=Ya(t,re,a)}return n},ja=function(e,t){switch(e){case"\u239C":return"M291 0 H417 V"+t+" H291z M291 0 H417 V"+t+" H291z";case"\u2223":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z";case"\u2225":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z"+("M367 0 H410 V"+t+" H367z M367 0 H410 V"+t+" H367z");case"\u239F":return"M457 0 H583 V"+t+" H457z M457 0 H583 V"+t+" H457z";case"\u23A2":return"M319 0 H403 V"+t+" H319z M319 0 H403 V"+t+" H319z";case"\u23A5":return"M263 0 H347 V"+t+" H263z M263 0 H347 V"+t+" H263z";case"\u23AA":return"M384 0 H504 V"+t+" H384z M384 0 H504 V"+t+" H384z";case"\u23D0":return"M312 0 H355 V"+t+" H312z M312 0 H355 V"+t+" H312z";case"\u2016":return"M257 0 H300 V"+t+" H257z M257 0 H300 V"+t+" H257z"+("M478 0 H521 V"+t+" H478z M478 0 H521 V"+t+" H478z");default:return""}},Yt={doubleleftarrow:`M262 157 l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5 @@ -225,7 +225,7 @@ M93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z` c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199, -231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6 c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z -M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`},Wa=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+` v1759 h347 v-84 +M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`},Ka=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+` v1759 h347 v-84 H403z M403 1759 V0 H319 V1759 v`+t+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+` v1759 H0 v84 H347z M347 1759 V0 H263 V1759 v`+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+` v585 c2.667,10,9.667,15,21,15 c10,0,16.667,-5,20,-15 v-585 v`+-t+` v-585 c-2.667,-10,-9.667,-15,-21,-15 @@ -253,10 +253,10 @@ c-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6 c0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17 c242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558 l0,-`+(t+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7, --470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z`;default:throw new Error("Unknown stretchy delimiter.")}},X0=class{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return N.contains(this.classes,e)}toNode(){for(var e=document.createDocumentFragment(),t=0;tt.toText();return this.children.map(e).join("")}},k0={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},xe={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},Yt={\u00C5:"A",\u00D0:"D",\u00DE:"o",\u00E5:"a",\u00F0:"d",\u00FE:"o",\u0410:"A",\u0411:"B",\u0412:"B",\u0413:"F",\u0414:"A",\u0415:"E",\u0416:"K",\u0417:"3",\u0418:"N",\u0419:"N",\u041A:"K",\u041B:"N",\u041C:"M",\u041D:"H",\u041E:"O",\u041F:"N",\u0420:"P",\u0421:"C",\u0422:"T",\u0423:"y",\u0424:"O",\u0425:"X",\u0426:"U",\u0427:"h",\u0428:"W",\u0429:"W",\u042A:"B",\u042B:"X",\u042C:"B",\u042D:"3",\u042E:"X",\u042F:"R",\u0430:"a",\u0431:"b",\u0432:"a",\u0433:"r",\u0434:"y",\u0435:"e",\u0436:"m",\u0437:"e",\u0438:"n",\u0439:"n",\u043A:"n",\u043B:"n",\u043C:"m",\u043D:"n",\u043E:"o",\u043F:"n",\u0440:"p",\u0441:"c",\u0442:"o",\u0443:"y",\u0444:"b",\u0445:"x",\u0446:"n",\u0447:"n",\u0448:"w",\u0449:"w",\u044A:"a",\u044B:"m",\u044C:"a",\u044D:"e",\u044E:"m",\u044F:"r"};function ja(r,e){k0[r]=e}function St(r,e,t){if(!k0[e])throw new Error("Font metrics not found for font: "+e+".");var a=r.charCodeAt(0),n=k0[e][a];if(!n&&r[0]in Yt&&(a=Yt[r[0]].charCodeAt(0),n=k0[e][a]),!n&&t==="text"&&kr(a)&&(n=k0[e][77]),n)return{depth:n[0],height:n[1],italic:n[2],skew:n[3],width:n[4]}}var Qe={};function Za(r){var e;if(r>=5?e=0:r>=3?e=1:e=2,!Qe[e]){var t=Qe[e]={cssEmPerMu:xe.quad[e]/18};for(var a in xe)xe.hasOwnProperty(a)&&(t[a]=xe[a][e])}return Qe[e]}var Ka=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],Xt=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],Wt=function(e,t){return t.size<2?e:Ka[e-1][t.size-1]},Re=class r{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||r.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=Xt[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){var t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(var a in e)e.hasOwnProperty(a)&&(t[a]=e[a]);return new r(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:Wt(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:Xt[e-1]})}havingBaseStyle(e){e=e||this.style.text();var t=Wt(r.BASESIZE,e);return this.size===t&&this.textSize===r.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){var e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==r.BASESIZE?["sizing","reset-size"+this.size,"size"+r.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=Za(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}};Re.BASESIZE=6;var mt={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:803/800,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:803/800},Ja={ex:!0,em:!0,mu:!0},Sr=function(e){return typeof e!="string"&&(e=e.unit),e in mt||e in Ja||e==="ex"},J=function(e,t){var a;if(e.unit in mt)a=mt[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if(e.unit==="mu")a=t.fontMetrics().cssEmPerMu;else{var n;if(t.style.isTight()?n=t.havingStyle(t.style.text()):n=t,e.unit==="ex")a=n.fontMetrics().xHeight;else if(e.unit==="em")a=n.fontMetrics().quad;else throw new M("Invalid unit: '"+e.unit+"'");n!==t&&(a*=n.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*a,t.maxSize)},A=function(e){return+e.toFixed(4)+"em"},P0=function(e){return e.filter(t=>t).join(" ")},Mr=function(e,t,a){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=a||{},t){t.style.isTight()&&this.classes.push("mtight");var n=t.getColor();n&&(this.style.color=n)}},zr=function(e){var t=document.createElement(e);t.className=P0(this.classes);for(var a in this.style)this.style.hasOwnProperty(a)&&(t.style[a]=this.style[a]);for(var n in this.attributes)this.attributes.hasOwnProperty(n)&&t.setAttribute(n,this.attributes[n]);for(var s=0;s",t},W0=class{constructor(e,t,a,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,Mr.call(this,e,a,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return N.contains(this.classes,e)}toNode(){return zr.call(this,"span")}toMarkup(){return Ar.call(this,"span")}},ce=class{constructor(e,t,a,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,Mr.call(this,t,n),this.children=a||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return N.contains(this.classes,e)}toNode(){return zr.call(this,"a")}toMarkup(){return Ar.call(this,"a")}},ct=class{constructor(e,t,a){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.style=a}hasClass(e){return N.contains(this.classes,e)}toNode(){var e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(var t in this.style)this.style.hasOwnProperty(t)&&(e.style[t]=this.style[t]);return e}toMarkup(){var e=''+N.escape(this.alt)+'0&&(t=document.createElement("span"),t.style.marginRight=A(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=P0(this.classes));for(var a in this.style)this.style.hasOwnProperty(a)&&(t=t||document.createElement("span"),t.style[a]=this.style[a]);return t?(t.appendChild(e),t):e}toMarkup(){var e=!1,t="0&&(a+="margin-right:"+this.italic+"em;");for(var n in this.style)this.style.hasOwnProperty(n)&&(a+=N.hyphenate(n)+":"+this.style[n]+";");a&&(e=!0,t+=' style="'+N.escape(a)+'"');var s=N.escape(this.text);return e?(t+=">",t+=s,t+="",t):s}},y0=class{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"svg");for(var a in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,a)&&t.setAttribute(a,this.attributes[a]);for(var n=0;n':''}},de=class{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"line");for(var a in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,a)&&t.setAttribute(a,this.attributes[a]);return t}toMarkup(){var e=" but got "+String(r)+".")}var e1={bin:1,close:1,inner:1,open:1,punct:1,rel:1},t1={"accent-token":1,mathord:1,"op-token":1,spacing:1,textord:1},X={math:{},text:{}};function i(r,e,t,a,n,s){X[r][n]={font:e,group:t,replace:a},s&&a&&(X[r][a]=X[r][n])}var l="math",k="text",u="main",d="ams",W="accent-token",D="bin",i0="close",ae="inner",E="mathord",_="op-token",c0="open",Ge="punct",f="rel",R0="spacing",v="textord";i(l,u,f,"\u2261","\\equiv",!0);i(l,u,f,"\u227A","\\prec",!0);i(l,u,f,"\u227B","\\succ",!0);i(l,u,f,"\u223C","\\sim",!0);i(l,u,f,"\u22A5","\\perp");i(l,u,f,"\u2AAF","\\preceq",!0);i(l,u,f,"\u2AB0","\\succeq",!0);i(l,u,f,"\u2243","\\simeq",!0);i(l,u,f,"\u2223","\\mid",!0);i(l,u,f,"\u226A","\\ll",!0);i(l,u,f,"\u226B","\\gg",!0);i(l,u,f,"\u224D","\\asymp",!0);i(l,u,f,"\u2225","\\parallel");i(l,u,f,"\u22C8","\\bowtie",!0);i(l,u,f,"\u2323","\\smile",!0);i(l,u,f,"\u2291","\\sqsubseteq",!0);i(l,u,f,"\u2292","\\sqsupseteq",!0);i(l,u,f,"\u2250","\\doteq",!0);i(l,u,f,"\u2322","\\frown",!0);i(l,u,f,"\u220B","\\ni",!0);i(l,u,f,"\u221D","\\propto",!0);i(l,u,f,"\u22A2","\\vdash",!0);i(l,u,f,"\u22A3","\\dashv",!0);i(l,u,f,"\u220B","\\owns");i(l,u,Ge,".","\\ldotp");i(l,u,Ge,"\u22C5","\\cdotp");i(l,u,v,"#","\\#");i(k,u,v,"#","\\#");i(l,u,v,"&","\\&");i(k,u,v,"&","\\&");i(l,u,v,"\u2135","\\aleph",!0);i(l,u,v,"\u2200","\\forall",!0);i(l,u,v,"\u210F","\\hbar",!0);i(l,u,v,"\u2203","\\exists",!0);i(l,u,v,"\u2207","\\nabla",!0);i(l,u,v,"\u266D","\\flat",!0);i(l,u,v,"\u2113","\\ell",!0);i(l,u,v,"\u266E","\\natural",!0);i(l,u,v,"\u2663","\\clubsuit",!0);i(l,u,v,"\u2118","\\wp",!0);i(l,u,v,"\u266F","\\sharp",!0);i(l,u,v,"\u2662","\\diamondsuit",!0);i(l,u,v,"\u211C","\\Re",!0);i(l,u,v,"\u2661","\\heartsuit",!0);i(l,u,v,"\u2111","\\Im",!0);i(l,u,v,"\u2660","\\spadesuit",!0);i(l,u,v,"\xA7","\\S",!0);i(k,u,v,"\xA7","\\S");i(l,u,v,"\xB6","\\P",!0);i(k,u,v,"\xB6","\\P");i(l,u,v,"\u2020","\\dag");i(k,u,v,"\u2020","\\dag");i(k,u,v,"\u2020","\\textdagger");i(l,u,v,"\u2021","\\ddag");i(k,u,v,"\u2021","\\ddag");i(k,u,v,"\u2021","\\textdaggerdbl");i(l,u,i0,"\u23B1","\\rmoustache",!0);i(l,u,c0,"\u23B0","\\lmoustache",!0);i(l,u,i0,"\u27EF","\\rgroup",!0);i(l,u,c0,"\u27EE","\\lgroup",!0);i(l,u,D,"\u2213","\\mp",!0);i(l,u,D,"\u2296","\\ominus",!0);i(l,u,D,"\u228E","\\uplus",!0);i(l,u,D,"\u2293","\\sqcap",!0);i(l,u,D,"\u2217","\\ast");i(l,u,D,"\u2294","\\sqcup",!0);i(l,u,D,"\u25EF","\\bigcirc",!0);i(l,u,D,"\u2219","\\bullet",!0);i(l,u,D,"\u2021","\\ddagger");i(l,u,D,"\u2240","\\wr",!0);i(l,u,D,"\u2A3F","\\amalg");i(l,u,D,"&","\\And");i(l,u,f,"\u27F5","\\longleftarrow",!0);i(l,u,f,"\u21D0","\\Leftarrow",!0);i(l,u,f,"\u27F8","\\Longleftarrow",!0);i(l,u,f,"\u27F6","\\longrightarrow",!0);i(l,u,f,"\u21D2","\\Rightarrow",!0);i(l,u,f,"\u27F9","\\Longrightarrow",!0);i(l,u,f,"\u2194","\\leftrightarrow",!0);i(l,u,f,"\u27F7","\\longleftrightarrow",!0);i(l,u,f,"\u21D4","\\Leftrightarrow",!0);i(l,u,f,"\u27FA","\\Longleftrightarrow",!0);i(l,u,f,"\u21A6","\\mapsto",!0);i(l,u,f,"\u27FC","\\longmapsto",!0);i(l,u,f,"\u2197","\\nearrow",!0);i(l,u,f,"\u21A9","\\hookleftarrow",!0);i(l,u,f,"\u21AA","\\hookrightarrow",!0);i(l,u,f,"\u2198","\\searrow",!0);i(l,u,f,"\u21BC","\\leftharpoonup",!0);i(l,u,f,"\u21C0","\\rightharpoonup",!0);i(l,u,f,"\u2199","\\swarrow",!0);i(l,u,f,"\u21BD","\\leftharpoondown",!0);i(l,u,f,"\u21C1","\\rightharpoondown",!0);i(l,u,f,"\u2196","\\nwarrow",!0);i(l,u,f,"\u21CC","\\rightleftharpoons",!0);i(l,d,f,"\u226E","\\nless",!0);i(l,d,f,"\uE010","\\@nleqslant");i(l,d,f,"\uE011","\\@nleqq");i(l,d,f,"\u2A87","\\lneq",!0);i(l,d,f,"\u2268","\\lneqq",!0);i(l,d,f,"\uE00C","\\@lvertneqq");i(l,d,f,"\u22E6","\\lnsim",!0);i(l,d,f,"\u2A89","\\lnapprox",!0);i(l,d,f,"\u2280","\\nprec",!0);i(l,d,f,"\u22E0","\\npreceq",!0);i(l,d,f,"\u22E8","\\precnsim",!0);i(l,d,f,"\u2AB9","\\precnapprox",!0);i(l,d,f,"\u2241","\\nsim",!0);i(l,d,f,"\uE006","\\@nshortmid");i(l,d,f,"\u2224","\\nmid",!0);i(l,d,f,"\u22AC","\\nvdash",!0);i(l,d,f,"\u22AD","\\nvDash",!0);i(l,d,f,"\u22EA","\\ntriangleleft");i(l,d,f,"\u22EC","\\ntrianglelefteq",!0);i(l,d,f,"\u228A","\\subsetneq",!0);i(l,d,f,"\uE01A","\\@varsubsetneq");i(l,d,f,"\u2ACB","\\subsetneqq",!0);i(l,d,f,"\uE017","\\@varsubsetneqq");i(l,d,f,"\u226F","\\ngtr",!0);i(l,d,f,"\uE00F","\\@ngeqslant");i(l,d,f,"\uE00E","\\@ngeqq");i(l,d,f,"\u2A88","\\gneq",!0);i(l,d,f,"\u2269","\\gneqq",!0);i(l,d,f,"\uE00D","\\@gvertneqq");i(l,d,f,"\u22E7","\\gnsim",!0);i(l,d,f,"\u2A8A","\\gnapprox",!0);i(l,d,f,"\u2281","\\nsucc",!0);i(l,d,f,"\u22E1","\\nsucceq",!0);i(l,d,f,"\u22E9","\\succnsim",!0);i(l,d,f,"\u2ABA","\\succnapprox",!0);i(l,d,f,"\u2246","\\ncong",!0);i(l,d,f,"\uE007","\\@nshortparallel");i(l,d,f,"\u2226","\\nparallel",!0);i(l,d,f,"\u22AF","\\nVDash",!0);i(l,d,f,"\u22EB","\\ntriangleright");i(l,d,f,"\u22ED","\\ntrianglerighteq",!0);i(l,d,f,"\uE018","\\@nsupseteqq");i(l,d,f,"\u228B","\\supsetneq",!0);i(l,d,f,"\uE01B","\\@varsupsetneq");i(l,d,f,"\u2ACC","\\supsetneqq",!0);i(l,d,f,"\uE019","\\@varsupsetneqq");i(l,d,f,"\u22AE","\\nVdash",!0);i(l,d,f,"\u2AB5","\\precneqq",!0);i(l,d,f,"\u2AB6","\\succneqq",!0);i(l,d,f,"\uE016","\\@nsubseteqq");i(l,d,D,"\u22B4","\\unlhd");i(l,d,D,"\u22B5","\\unrhd");i(l,d,f,"\u219A","\\nleftarrow",!0);i(l,d,f,"\u219B","\\nrightarrow",!0);i(l,d,f,"\u21CD","\\nLeftarrow",!0);i(l,d,f,"\u21CF","\\nRightarrow",!0);i(l,d,f,"\u21AE","\\nleftrightarrow",!0);i(l,d,f,"\u21CE","\\nLeftrightarrow",!0);i(l,d,f,"\u25B3","\\vartriangle");i(l,d,v,"\u210F","\\hslash");i(l,d,v,"\u25BD","\\triangledown");i(l,d,v,"\u25CA","\\lozenge");i(l,d,v,"\u24C8","\\circledS");i(l,d,v,"\xAE","\\circledR");i(k,d,v,"\xAE","\\circledR");i(l,d,v,"\u2221","\\measuredangle",!0);i(l,d,v,"\u2204","\\nexists");i(l,d,v,"\u2127","\\mho");i(l,d,v,"\u2132","\\Finv",!0);i(l,d,v,"\u2141","\\Game",!0);i(l,d,v,"\u2035","\\backprime");i(l,d,v,"\u25B2","\\blacktriangle");i(l,d,v,"\u25BC","\\blacktriangledown");i(l,d,v,"\u25A0","\\blacksquare");i(l,d,v,"\u29EB","\\blacklozenge");i(l,d,v,"\u2605","\\bigstar");i(l,d,v,"\u2222","\\sphericalangle",!0);i(l,d,v,"\u2201","\\complement",!0);i(l,d,v,"\xF0","\\eth",!0);i(k,u,v,"\xF0","\xF0");i(l,d,v,"\u2571","\\diagup");i(l,d,v,"\u2572","\\diagdown");i(l,d,v,"\u25A1","\\square");i(l,d,v,"\u25A1","\\Box");i(l,d,v,"\u25CA","\\Diamond");i(l,d,v,"\xA5","\\yen",!0);i(k,d,v,"\xA5","\\yen",!0);i(l,d,v,"\u2713","\\checkmark",!0);i(k,d,v,"\u2713","\\checkmark");i(l,d,v,"\u2136","\\beth",!0);i(l,d,v,"\u2138","\\daleth",!0);i(l,d,v,"\u2137","\\gimel",!0);i(l,d,v,"\u03DD","\\digamma",!0);i(l,d,v,"\u03F0","\\varkappa");i(l,d,c0,"\u250C","\\@ulcorner",!0);i(l,d,i0,"\u2510","\\@urcorner",!0);i(l,d,c0,"\u2514","\\@llcorner",!0);i(l,d,i0,"\u2518","\\@lrcorner",!0);i(l,d,f,"\u2266","\\leqq",!0);i(l,d,f,"\u2A7D","\\leqslant",!0);i(l,d,f,"\u2A95","\\eqslantless",!0);i(l,d,f,"\u2272","\\lesssim",!0);i(l,d,f,"\u2A85","\\lessapprox",!0);i(l,d,f,"\u224A","\\approxeq",!0);i(l,d,D,"\u22D6","\\lessdot");i(l,d,f,"\u22D8","\\lll",!0);i(l,d,f,"\u2276","\\lessgtr",!0);i(l,d,f,"\u22DA","\\lesseqgtr",!0);i(l,d,f,"\u2A8B","\\lesseqqgtr",!0);i(l,d,f,"\u2251","\\doteqdot");i(l,d,f,"\u2253","\\risingdotseq",!0);i(l,d,f,"\u2252","\\fallingdotseq",!0);i(l,d,f,"\u223D","\\backsim",!0);i(l,d,f,"\u22CD","\\backsimeq",!0);i(l,d,f,"\u2AC5","\\subseteqq",!0);i(l,d,f,"\u22D0","\\Subset",!0);i(l,d,f,"\u228F","\\sqsubset",!0);i(l,d,f,"\u227C","\\preccurlyeq",!0);i(l,d,f,"\u22DE","\\curlyeqprec",!0);i(l,d,f,"\u227E","\\precsim",!0);i(l,d,f,"\u2AB7","\\precapprox",!0);i(l,d,f,"\u22B2","\\vartriangleleft");i(l,d,f,"\u22B4","\\trianglelefteq");i(l,d,f,"\u22A8","\\vDash",!0);i(l,d,f,"\u22AA","\\Vvdash",!0);i(l,d,f,"\u2323","\\smallsmile");i(l,d,f,"\u2322","\\smallfrown");i(l,d,f,"\u224F","\\bumpeq",!0);i(l,d,f,"\u224E","\\Bumpeq",!0);i(l,d,f,"\u2267","\\geqq",!0);i(l,d,f,"\u2A7E","\\geqslant",!0);i(l,d,f,"\u2A96","\\eqslantgtr",!0);i(l,d,f,"\u2273","\\gtrsim",!0);i(l,d,f,"\u2A86","\\gtrapprox",!0);i(l,d,D,"\u22D7","\\gtrdot");i(l,d,f,"\u22D9","\\ggg",!0);i(l,d,f,"\u2277","\\gtrless",!0);i(l,d,f,"\u22DB","\\gtreqless",!0);i(l,d,f,"\u2A8C","\\gtreqqless",!0);i(l,d,f,"\u2256","\\eqcirc",!0);i(l,d,f,"\u2257","\\circeq",!0);i(l,d,f,"\u225C","\\triangleq",!0);i(l,d,f,"\u223C","\\thicksim");i(l,d,f,"\u2248","\\thickapprox");i(l,d,f,"\u2AC6","\\supseteqq",!0);i(l,d,f,"\u22D1","\\Supset",!0);i(l,d,f,"\u2290","\\sqsupset",!0);i(l,d,f,"\u227D","\\succcurlyeq",!0);i(l,d,f,"\u22DF","\\curlyeqsucc",!0);i(l,d,f,"\u227F","\\succsim",!0);i(l,d,f,"\u2AB8","\\succapprox",!0);i(l,d,f,"\u22B3","\\vartriangleright");i(l,d,f,"\u22B5","\\trianglerighteq");i(l,d,f,"\u22A9","\\Vdash",!0);i(l,d,f,"\u2223","\\shortmid");i(l,d,f,"\u2225","\\shortparallel");i(l,d,f,"\u226C","\\between",!0);i(l,d,f,"\u22D4","\\pitchfork",!0);i(l,d,f,"\u221D","\\varpropto");i(l,d,f,"\u25C0","\\blacktriangleleft");i(l,d,f,"\u2234","\\therefore",!0);i(l,d,f,"\u220D","\\backepsilon");i(l,d,f,"\u25B6","\\blacktriangleright");i(l,d,f,"\u2235","\\because",!0);i(l,d,f,"\u22D8","\\llless");i(l,d,f,"\u22D9","\\gggtr");i(l,d,D,"\u22B2","\\lhd");i(l,d,D,"\u22B3","\\rhd");i(l,d,f,"\u2242","\\eqsim",!0);i(l,u,f,"\u22C8","\\Join");i(l,d,f,"\u2251","\\Doteq",!0);i(l,d,D,"\u2214","\\dotplus",!0);i(l,d,D,"\u2216","\\smallsetminus");i(l,d,D,"\u22D2","\\Cap",!0);i(l,d,D,"\u22D3","\\Cup",!0);i(l,d,D,"\u2A5E","\\doublebarwedge",!0);i(l,d,D,"\u229F","\\boxminus",!0);i(l,d,D,"\u229E","\\boxplus",!0);i(l,d,D,"\u22C7","\\divideontimes",!0);i(l,d,D,"\u22C9","\\ltimes",!0);i(l,d,D,"\u22CA","\\rtimes",!0);i(l,d,D,"\u22CB","\\leftthreetimes",!0);i(l,d,D,"\u22CC","\\rightthreetimes",!0);i(l,d,D,"\u22CF","\\curlywedge",!0);i(l,d,D,"\u22CE","\\curlyvee",!0);i(l,d,D,"\u229D","\\circleddash",!0);i(l,d,D,"\u229B","\\circledast",!0);i(l,d,D,"\u22C5","\\centerdot");i(l,d,D,"\u22BA","\\intercal",!0);i(l,d,D,"\u22D2","\\doublecap");i(l,d,D,"\u22D3","\\doublecup");i(l,d,D,"\u22A0","\\boxtimes",!0);i(l,d,f,"\u21E2","\\dashrightarrow",!0);i(l,d,f,"\u21E0","\\dashleftarrow",!0);i(l,d,f,"\u21C7","\\leftleftarrows",!0);i(l,d,f,"\u21C6","\\leftrightarrows",!0);i(l,d,f,"\u21DA","\\Lleftarrow",!0);i(l,d,f,"\u219E","\\twoheadleftarrow",!0);i(l,d,f,"\u21A2","\\leftarrowtail",!0);i(l,d,f,"\u21AB","\\looparrowleft",!0);i(l,d,f,"\u21CB","\\leftrightharpoons",!0);i(l,d,f,"\u21B6","\\curvearrowleft",!0);i(l,d,f,"\u21BA","\\circlearrowleft",!0);i(l,d,f,"\u21B0","\\Lsh",!0);i(l,d,f,"\u21C8","\\upuparrows",!0);i(l,d,f,"\u21BF","\\upharpoonleft",!0);i(l,d,f,"\u21C3","\\downharpoonleft",!0);i(l,u,f,"\u22B6","\\origof",!0);i(l,u,f,"\u22B7","\\imageof",!0);i(l,d,f,"\u22B8","\\multimap",!0);i(l,d,f,"\u21AD","\\leftrightsquigarrow",!0);i(l,d,f,"\u21C9","\\rightrightarrows",!0);i(l,d,f,"\u21C4","\\rightleftarrows",!0);i(l,d,f,"\u21A0","\\twoheadrightarrow",!0);i(l,d,f,"\u21A3","\\rightarrowtail",!0);i(l,d,f,"\u21AC","\\looparrowright",!0);i(l,d,f,"\u21B7","\\curvearrowright",!0);i(l,d,f,"\u21BB","\\circlearrowright",!0);i(l,d,f,"\u21B1","\\Rsh",!0);i(l,d,f,"\u21CA","\\downdownarrows",!0);i(l,d,f,"\u21BE","\\upharpoonright",!0);i(l,d,f,"\u21C2","\\downharpoonright",!0);i(l,d,f,"\u21DD","\\rightsquigarrow",!0);i(l,d,f,"\u21DD","\\leadsto");i(l,d,f,"\u21DB","\\Rrightarrow",!0);i(l,d,f,"\u21BE","\\restriction");i(l,u,v,"\u2018","`");i(l,u,v,"$","\\$");i(k,u,v,"$","\\$");i(k,u,v,"$","\\textdollar");i(l,u,v,"%","\\%");i(k,u,v,"%","\\%");i(l,u,v,"_","\\_");i(k,u,v,"_","\\_");i(k,u,v,"_","\\textunderscore");i(l,u,v,"\u2220","\\angle",!0);i(l,u,v,"\u221E","\\infty",!0);i(l,u,v,"\u2032","\\prime");i(l,u,v,"\u25B3","\\triangle");i(l,u,v,"\u0393","\\Gamma",!0);i(l,u,v,"\u0394","\\Delta",!0);i(l,u,v,"\u0398","\\Theta",!0);i(l,u,v,"\u039B","\\Lambda",!0);i(l,u,v,"\u039E","\\Xi",!0);i(l,u,v,"\u03A0","\\Pi",!0);i(l,u,v,"\u03A3","\\Sigma",!0);i(l,u,v,"\u03A5","\\Upsilon",!0);i(l,u,v,"\u03A6","\\Phi",!0);i(l,u,v,"\u03A8","\\Psi",!0);i(l,u,v,"\u03A9","\\Omega",!0);i(l,u,v,"A","\u0391");i(l,u,v,"B","\u0392");i(l,u,v,"E","\u0395");i(l,u,v,"Z","\u0396");i(l,u,v,"H","\u0397");i(l,u,v,"I","\u0399");i(l,u,v,"K","\u039A");i(l,u,v,"M","\u039C");i(l,u,v,"N","\u039D");i(l,u,v,"O","\u039F");i(l,u,v,"P","\u03A1");i(l,u,v,"T","\u03A4");i(l,u,v,"X","\u03A7");i(l,u,v,"\xAC","\\neg",!0);i(l,u,v,"\xAC","\\lnot");i(l,u,v,"\u22A4","\\top");i(l,u,v,"\u22A5","\\bot");i(l,u,v,"\u2205","\\emptyset");i(l,d,v,"\u2205","\\varnothing");i(l,u,E,"\u03B1","\\alpha",!0);i(l,u,E,"\u03B2","\\beta",!0);i(l,u,E,"\u03B3","\\gamma",!0);i(l,u,E,"\u03B4","\\delta",!0);i(l,u,E,"\u03F5","\\epsilon",!0);i(l,u,E,"\u03B6","\\zeta",!0);i(l,u,E,"\u03B7","\\eta",!0);i(l,u,E,"\u03B8","\\theta",!0);i(l,u,E,"\u03B9","\\iota",!0);i(l,u,E,"\u03BA","\\kappa",!0);i(l,u,E,"\u03BB","\\lambda",!0);i(l,u,E,"\u03BC","\\mu",!0);i(l,u,E,"\u03BD","\\nu",!0);i(l,u,E,"\u03BE","\\xi",!0);i(l,u,E,"\u03BF","\\omicron",!0);i(l,u,E,"\u03C0","\\pi",!0);i(l,u,E,"\u03C1","\\rho",!0);i(l,u,E,"\u03C3","\\sigma",!0);i(l,u,E,"\u03C4","\\tau",!0);i(l,u,E,"\u03C5","\\upsilon",!0);i(l,u,E,"\u03D5","\\phi",!0);i(l,u,E,"\u03C7","\\chi",!0);i(l,u,E,"\u03C8","\\psi",!0);i(l,u,E,"\u03C9","\\omega",!0);i(l,u,E,"\u03B5","\\varepsilon",!0);i(l,u,E,"\u03D1","\\vartheta",!0);i(l,u,E,"\u03D6","\\varpi",!0);i(l,u,E,"\u03F1","\\varrho",!0);i(l,u,E,"\u03C2","\\varsigma",!0);i(l,u,E,"\u03C6","\\varphi",!0);i(l,u,D,"\u2217","*",!0);i(l,u,D,"+","+");i(l,u,D,"\u2212","-",!0);i(l,u,D,"\u22C5","\\cdot",!0);i(l,u,D,"\u2218","\\circ",!0);i(l,u,D,"\xF7","\\div",!0);i(l,u,D,"\xB1","\\pm",!0);i(l,u,D,"\xD7","\\times",!0);i(l,u,D,"\u2229","\\cap",!0);i(l,u,D,"\u222A","\\cup",!0);i(l,u,D,"\u2216","\\setminus",!0);i(l,u,D,"\u2227","\\land");i(l,u,D,"\u2228","\\lor");i(l,u,D,"\u2227","\\wedge",!0);i(l,u,D,"\u2228","\\vee",!0);i(l,u,v,"\u221A","\\surd");i(l,u,c0,"\u27E8","\\langle",!0);i(l,u,c0,"\u2223","\\lvert");i(l,u,c0,"\u2225","\\lVert");i(l,u,i0,"?","?");i(l,u,i0,"!","!");i(l,u,i0,"\u27E9","\\rangle",!0);i(l,u,i0,"\u2223","\\rvert");i(l,u,i0,"\u2225","\\rVert");i(l,u,f,"=","=");i(l,u,f,":",":");i(l,u,f,"\u2248","\\approx",!0);i(l,u,f,"\u2245","\\cong",!0);i(l,u,f,"\u2265","\\ge");i(l,u,f,"\u2265","\\geq",!0);i(l,u,f,"\u2190","\\gets");i(l,u,f,">","\\gt",!0);i(l,u,f,"\u2208","\\in",!0);i(l,u,f,"\uE020","\\@not");i(l,u,f,"\u2282","\\subset",!0);i(l,u,f,"\u2283","\\supset",!0);i(l,u,f,"\u2286","\\subseteq",!0);i(l,u,f,"\u2287","\\supseteq",!0);i(l,d,f,"\u2288","\\nsubseteq",!0);i(l,d,f,"\u2289","\\nsupseteq",!0);i(l,u,f,"\u22A8","\\models");i(l,u,f,"\u2190","\\leftarrow",!0);i(l,u,f,"\u2264","\\le");i(l,u,f,"\u2264","\\leq",!0);i(l,u,f,"<","\\lt",!0);i(l,u,f,"\u2192","\\rightarrow",!0);i(l,u,f,"\u2192","\\to");i(l,d,f,"\u2271","\\ngeq",!0);i(l,d,f,"\u2270","\\nleq",!0);i(l,u,R0,"\xA0","\\ ");i(l,u,R0,"\xA0","\\space");i(l,u,R0,"\xA0","\\nobreakspace");i(k,u,R0,"\xA0","\\ ");i(k,u,R0,"\xA0"," ");i(k,u,R0,"\xA0","\\space");i(k,u,R0,"\xA0","\\nobreakspace");i(l,u,R0,null,"\\nobreak");i(l,u,R0,null,"\\allowbreak");i(l,u,Ge,",",",");i(l,u,Ge,";",";");i(l,d,D,"\u22BC","\\barwedge",!0);i(l,d,D,"\u22BB","\\veebar",!0);i(l,u,D,"\u2299","\\odot",!0);i(l,u,D,"\u2295","\\oplus",!0);i(l,u,D,"\u2297","\\otimes",!0);i(l,u,v,"\u2202","\\partial",!0);i(l,u,D,"\u2298","\\oslash",!0);i(l,d,D,"\u229A","\\circledcirc",!0);i(l,d,D,"\u22A1","\\boxdot",!0);i(l,u,D,"\u25B3","\\bigtriangleup");i(l,u,D,"\u25BD","\\bigtriangledown");i(l,u,D,"\u2020","\\dagger");i(l,u,D,"\u22C4","\\diamond");i(l,u,D,"\u22C6","\\star");i(l,u,D,"\u25C3","\\triangleleft");i(l,u,D,"\u25B9","\\triangleright");i(l,u,c0,"{","\\{");i(k,u,v,"{","\\{");i(k,u,v,"{","\\textbraceleft");i(l,u,i0,"}","\\}");i(k,u,v,"}","\\}");i(k,u,v,"}","\\textbraceright");i(l,u,c0,"{","\\lbrace");i(l,u,i0,"}","\\rbrace");i(l,u,c0,"[","\\lbrack",!0);i(k,u,v,"[","\\lbrack",!0);i(l,u,i0,"]","\\rbrack",!0);i(k,u,v,"]","\\rbrack",!0);i(l,u,c0,"(","\\lparen",!0);i(l,u,i0,")","\\rparen",!0);i(k,u,v,"<","\\textless",!0);i(k,u,v,">","\\textgreater",!0);i(l,u,c0,"\u230A","\\lfloor",!0);i(l,u,i0,"\u230B","\\rfloor",!0);i(l,u,c0,"\u2308","\\lceil",!0);i(l,u,i0,"\u2309","\\rceil",!0);i(l,u,v,"\\","\\backslash");i(l,u,v,"\u2223","|");i(l,u,v,"\u2223","\\vert");i(k,u,v,"|","\\textbar",!0);i(l,u,v,"\u2225","\\|");i(l,u,v,"\u2225","\\Vert");i(k,u,v,"\u2225","\\textbardbl");i(k,u,v,"~","\\textasciitilde");i(k,u,v,"\\","\\textbackslash");i(k,u,v,"^","\\textasciicircum");i(l,u,f,"\u2191","\\uparrow",!0);i(l,u,f,"\u21D1","\\Uparrow",!0);i(l,u,f,"\u2193","\\downarrow",!0);i(l,u,f,"\u21D3","\\Downarrow",!0);i(l,u,f,"\u2195","\\updownarrow",!0);i(l,u,f,"\u21D5","\\Updownarrow",!0);i(l,u,_,"\u2210","\\coprod");i(l,u,_,"\u22C1","\\bigvee");i(l,u,_,"\u22C0","\\bigwedge");i(l,u,_,"\u2A04","\\biguplus");i(l,u,_,"\u22C2","\\bigcap");i(l,u,_,"\u22C3","\\bigcup");i(l,u,_,"\u222B","\\int");i(l,u,_,"\u222B","\\intop");i(l,u,_,"\u222C","\\iint");i(l,u,_,"\u222D","\\iiint");i(l,u,_,"\u220F","\\prod");i(l,u,_,"\u2211","\\sum");i(l,u,_,"\u2A02","\\bigotimes");i(l,u,_,"\u2A01","\\bigoplus");i(l,u,_,"\u2A00","\\bigodot");i(l,u,_,"\u222E","\\oint");i(l,u,_,"\u222F","\\oiint");i(l,u,_,"\u2230","\\oiiint");i(l,u,_,"\u2A06","\\bigsqcup");i(l,u,_,"\u222B","\\smallint");i(k,u,ae,"\u2026","\\textellipsis");i(l,u,ae,"\u2026","\\mathellipsis");i(k,u,ae,"\u2026","\\ldots",!0);i(l,u,ae,"\u2026","\\ldots",!0);i(l,u,ae,"\u22EF","\\@cdots",!0);i(l,u,ae,"\u22F1","\\ddots",!0);i(l,u,v,"\u22EE","\\varvdots");i(l,u,W,"\u02CA","\\acute");i(l,u,W,"\u02CB","\\grave");i(l,u,W,"\xA8","\\ddot");i(l,u,W,"~","\\tilde");i(l,u,W,"\u02C9","\\bar");i(l,u,W,"\u02D8","\\breve");i(l,u,W,"\u02C7","\\check");i(l,u,W,"^","\\hat");i(l,u,W,"\u20D7","\\vec");i(l,u,W,"\u02D9","\\dot");i(l,u,W,"\u02DA","\\mathring");i(l,u,E,"\uE131","\\@imath");i(l,u,E,"\uE237","\\@jmath");i(l,u,v,"\u0131","\u0131");i(l,u,v,"\u0237","\u0237");i(k,u,v,"\u0131","\\i",!0);i(k,u,v,"\u0237","\\j",!0);i(k,u,v,"\xDF","\\ss",!0);i(k,u,v,"\xE6","\\ae",!0);i(k,u,v,"\u0153","\\oe",!0);i(k,u,v,"\xF8","\\o",!0);i(k,u,v,"\xC6","\\AE",!0);i(k,u,v,"\u0152","\\OE",!0);i(k,u,v,"\xD8","\\O",!0);i(k,u,W,"\u02CA","\\'");i(k,u,W,"\u02CB","\\`");i(k,u,W,"\u02C6","\\^");i(k,u,W,"\u02DC","\\~");i(k,u,W,"\u02C9","\\=");i(k,u,W,"\u02D8","\\u");i(k,u,W,"\u02D9","\\.");i(k,u,W,"\xB8","\\c");i(k,u,W,"\u02DA","\\r");i(k,u,W,"\u02C7","\\v");i(k,u,W,"\xA8",'\\"');i(k,u,W,"\u02DD","\\H");i(k,u,W,"\u25EF","\\textcircled");var Tr={"--":!0,"---":!0,"``":!0,"''":!0};i(k,u,v,"\u2013","--",!0);i(k,u,v,"\u2013","\\textendash");i(k,u,v,"\u2014","---",!0);i(k,u,v,"\u2014","\\textemdash");i(k,u,v,"\u2018","`",!0);i(k,u,v,"\u2018","\\textquoteleft");i(k,u,v,"\u2019","'",!0);i(k,u,v,"\u2019","\\textquoteright");i(k,u,v,"\u201C","``",!0);i(k,u,v,"\u201C","\\textquotedblleft");i(k,u,v,"\u201D","''",!0);i(k,u,v,"\u201D","\\textquotedblright");i(l,u,v,"\xB0","\\degree",!0);i(k,u,v,"\xB0","\\degree");i(k,u,v,"\xB0","\\textdegree",!0);i(l,u,v,"\xA3","\\pounds");i(l,u,v,"\xA3","\\mathsterling",!0);i(k,u,v,"\xA3","\\pounds");i(k,u,v,"\xA3","\\textsterling",!0);i(l,d,v,"\u2720","\\maltese");i(k,d,v,"\u2720","\\maltese");var Zt='0123456789/@."';for(we=0;we0)return b0(s,p,n,t,o.concat(g));if(c){var b,x;if(c==="boldsymbol"){var w=n1(s,n,t,o,a);b=w.fontName,x=[w.fontClass]}else h?(b=Cr[c].fontName,x=[c]):(b=Ae(c,t.fontWeight,t.fontShape),x=[c,t.fontWeight,t.fontShape]);if(Ve(s,b,n).metrics)return b0(s,b,n,t,o.concat(x));if(Tr.hasOwnProperty(s)&&b.slice(0,10)==="Typewriter"){for(var z=[],T=0;T{if(P0(r.classes)!==P0(e.classes)||r.skew!==e.skew||r.maxFontSize!==e.maxFontSize)return!1;if(r.classes.length===1){var t=r.classes[0];if(t==="mbin"||t==="mord")return!1}for(var a in r.style)if(r.style.hasOwnProperty(a)&&r.style[a]!==e.style[a])return!1;for(var n in e.style)if(e.style.hasOwnProperty(n)&&r.style[n]!==e.style[n])return!1;return!0},l1=r=>{for(var e=0;et&&(t=o.height),o.depth>a&&(a=o.depth),o.maxFontSize>n&&(n=o.maxFontSize)}e.height=t,e.depth=a,e.maxFontSize=n},l0=function(e,t,a,n){var s=new W0(e,t,a,n);return Mt(s),s},Br=(r,e,t,a)=>new W0(r,e,t,a),o1=function(e,t,a){var n=l0([e],[],t);return n.height=Math.max(a||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=A(n.height),n.maxFontSize=1,n},u1=function(e,t,a,n){var s=new ce(e,t,a,n);return Mt(s),s},Dr=function(e){var t=new X0(e);return Mt(t),t},h1=function(e,t){return e instanceof X0?l0([],[e],t):e},m1=function(e){if(e.positionType==="individualShift"){for(var t=e.children,a=[t[0]],n=-t[0].shift-t[0].elem.depth,s=n,o=1;o{var t=l0(["mspace"],[],e),a=J(r,e);return t.style.marginRight=A(a),t},Ae=function(e,t,a){var n="";switch(e){case"amsrm":n="AMS";break;case"textrm":n="Main";break;case"textsf":n="SansSerif";break;case"texttt":n="Typewriter";break;default:n=e}var s;return t==="textbf"&&a==="textit"?s="BoldItalic":t==="textbf"?s="Bold":t==="textit"?s="Italic":s="Regular",n+"-"+s},Cr={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},qr={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},f1=function(e,t){var[a,n,s]=qr[e],o=new S0(a),h=new y0([o],{width:A(n),height:A(s),style:"width:"+A(n),viewBox:"0 0 "+1e3*n+" "+1e3*s,preserveAspectRatio:"xMinYMin"}),c=Br(["overlay"],[h],t);return c.height=s,c.style.height=A(s),c.style.width=A(n),c},y={fontMap:Cr,makeSymbol:b0,mathsym:a1,makeSpan:l0,makeSvgSpan:Br,makeLineSpan:o1,makeAnchor:u1,makeFragment:Dr,wrapFragment:h1,makeVList:c1,makeOrd:i1,makeGlue:d1,staticSvg:f1,svgData:qr,tryCombineChars:l1},K={number:3,unit:"mu"},$0={number:4,unit:"mu"},D0={number:5,unit:"mu"},p1={mord:{mop:K,mbin:$0,mrel:D0,minner:K},mop:{mord:K,mop:K,mrel:D0,minner:K},mbin:{mord:$0,mop:$0,mopen:$0,minner:$0},mrel:{mord:D0,mop:D0,mopen:D0,minner:D0},mopen:{},mclose:{mop:K,mbin:$0,mrel:D0,minner:K},mpunct:{mord:K,mop:K,mrel:D0,mopen:K,mclose:K,mpunct:K,minner:K},minner:{mord:K,mop:K,mbin:$0,mrel:D0,mopen:K,mpunct:K,minner:K}},v1={mord:{mop:K},mop:{mord:K,mop:K},mbin:{},mrel:{},mopen:{},mclose:{mop:K},mpunct:{},minner:{mop:K}},Nr={},Oe={},He={};function B(r){for(var{type:e,names:t,props:a,handler:n,htmlBuilder:s,mathmlBuilder:o}=r,h={type:e,numArgs:a.numArgs,argTypes:a.argTypes,allowedInArgument:!!a.allowedInArgument,allowedInText:!!a.allowedInText,allowedInMath:a.allowedInMath===void 0?!0:a.allowedInMath,numOptionalArgs:a.numOptionalArgs||0,infix:!!a.infix,primitive:!!a.primitive,handler:n},c=0;c{var C=T.classes[0],q=z.classes[0];C==="mbin"&&N.contains(b1,q)?T.classes[0]="mord":q==="mbin"&&N.contains(g1,C)&&(z.classes[0]="mord")},{node:b},x,w),Qt(s,(z,T)=>{var C=ft(T),q=ft(z),O=C&&q?z.hasClass("mtight")?v1[C][q]:p1[C][q]:null;if(O)return y.makeGlue(O,p)},{node:b},x,w),s},Qt=function r(e,t,a,n,s){n&&e.push(n);for(var o=0;ox=>{e.splice(b+1,0,x),o++})(o)}n&&e.pop()},Er=function(e){return e instanceof X0||e instanceof ce||e instanceof W0&&e.hasClass("enclosing")?e:null},w1=function r(e,t){var a=Er(e);if(a){var n=a.children;if(n.length){if(t==="right")return r(n[n.length-1],"right");if(t==="left")return r(n[0],"left")}}return e},ft=function(e,t){return e?(t&&(e=w1(e,t)),x1[e.classes[0]]||null):null},fe=function(e,t){var a=["nulldelimiter"].concat(e.baseSizingClasses());return N0(t.concat(a))},P=function(e,t,a){if(!e)return N0();if(Oe[e.type]){var n=Oe[e.type](e,t);if(a&&t.size!==a.size){n=N0(t.sizingClasses(a),[n],t);var s=t.sizeMultiplier/a.sizeMultiplier;n.height*=s,n.depth*=s}return n}else throw new M("Got group of unknown type: '"+e.type+"'")};function Te(r,e){var t=N0(["base"],r,e),a=N0(["strut"]);return a.style.height=A(t.height+t.depth),t.depth&&(a.style.verticalAlign=A(-t.depth)),t.children.unshift(a),t}function pt(r,e){var t=null;r.length===1&&r[0].type==="tag"&&(t=r[0].tag,r=r[0].body);var a=t0(r,e,"root"),n;a.length===2&&a[1].hasClass("tag")&&(n=a.pop());for(var s=[],o=[],h=0;h0&&(s.push(Te(o,e)),o=[]),s.push(a[h]));o.length>0&&s.push(Te(o,e));var p;t?(p=Te(t0(t,e,!0)),p.classes=["tag"],s.push(p)):n&&s.push(n);var g=N0(["katex-html"],s);if(g.setAttribute("aria-hidden","true"),p){var b=p.children[0];b.style.height=A(g.height+g.depth),g.depth&&(b.style.verticalAlign=A(-g.depth))}return g}function Rr(r){return new X0(r)}var o0=class{constructor(e,t,a){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=a||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){var e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=P0(this.classes));for(var a=0;a0&&(e+=' class ="'+N.escape(P0(this.classes))+'"'),e+=">";for(var a=0;a",e}toText(){return this.children.map(e=>e.toText()).join("")}},Y0=class{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return N.escape(this.toText())}toText(){return this.text}},vt=class{constructor(e){this.width=void 0,this.character=void 0,this.width=e,e>=.05555&&e<=.05556?this.character="\u200A":e>=.1666&&e<=.1667?this.character="\u2009":e>=.2222&&e<=.2223?this.character="\u2005":e>=.2777&&e<=.2778?this.character="\u2005\u200A":e>=-.05556&&e<=-.05555?this.character="\u200A\u2063":e>=-.1667&&e<=-.1666?this.character="\u2009\u2063":e>=-.2223&&e<=-.2222?this.character="\u205F\u2063":e>=-.2778&&e<=-.2777?this.character="\u2005\u2063":this.character=null}toNode(){if(this.character)return document.createTextNode(this.character);var e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",A(this.width)),e}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},S={MathNode:o0,TextNode:Y0,SpaceNode:vt,newDocumentFragment:Rr},v0=function(e,t,a){return X[t][e]&&X[t][e].replace&&e.charCodeAt(0)!==55349&&!(Tr.hasOwnProperty(e)&&a&&(a.fontFamily&&a.fontFamily.slice(4,6)==="tt"||a.font&&a.font.slice(4,6)==="tt"))&&(e=X[t][e].replace),new S.TextNode(e)},zt=function(e){return e.length===1?e[0]:new S.MathNode("mrow",e)},At=function(e,t){if(t.fontFamily==="texttt")return"monospace";if(t.fontFamily==="textsf")return t.fontShape==="textit"&&t.fontWeight==="textbf"?"sans-serif-bold-italic":t.fontShape==="textit"?"sans-serif-italic":t.fontWeight==="textbf"?"bold-sans-serif":"sans-serif";if(t.fontShape==="textit"&&t.fontWeight==="textbf")return"bold-italic";if(t.fontShape==="textit")return"italic";if(t.fontWeight==="textbf")return"bold";var a=t.font;if(!a||a==="mathnormal")return null;var n=e.mode;if(a==="mathit")return"italic";if(a==="boldsymbol")return e.type==="textord"?"bold":"bold-italic";if(a==="mathbf")return"bold";if(a==="mathbb")return"double-struck";if(a==="mathfrak")return"fraktur";if(a==="mathscr"||a==="mathcal")return"script";if(a==="mathsf")return"sans-serif";if(a==="mathtt")return"monospace";var s=e.text;if(N.contains(["\\imath","\\jmath"],s))return null;X[n][s]&&X[n][s].replace&&(s=X[n][s].replace);var o=y.fontMap[a].fontName;return St(s,o,n)?y.fontMap[a].variant:null},h0=function(e,t,a){if(e.length===1){var n=Y(e[0],t);return a&&n instanceof o0&&n.type==="mo"&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}for(var s=[],o,h=0;h0&&(b.text=b.text.slice(0,1)+"\u0338"+b.text.slice(1),s.pop())}}}s.push(c),o=c}return s},G0=function(e,t,a){return zt(h0(e,t,a))},Y=function(e,t){if(!e)return new S.MathNode("mrow");if(He[e.type]){var a=He[e.type](e,t);return a}else throw new M("Got group of unknown type: '"+e.type+"'")};function _t(r,e,t,a,n){var s=h0(r,t),o;s.length===1&&s[0]instanceof o0&&N.contains(["mrow","mtable"],s[0].type)?o=s[0]:o=new S.MathNode("mrow",s);var h=new S.MathNode("annotation",[new S.TextNode(e)]);h.setAttribute("encoding","application/x-tex");var c=new S.MathNode("semantics",[o,h]),p=new S.MathNode("math",[c]);p.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),a&&p.setAttribute("display","block");var g=n?"katex":"katex-mathml";return y.makeSpan([g],[p])}var Ir=function(e){return new Re({style:e.displayMode?R.DISPLAY:R.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},Or=function(e,t){if(t.displayMode){var a=["katex-display"];t.leqno&&a.push("leqno"),t.fleqn&&a.push("fleqn"),e=y.makeSpan(a,[e])}return e},k1=function(e,t,a){var n=Ir(a),s;if(a.output==="mathml")return _t(e,t,n,a.displayMode,!0);if(a.output==="html"){var o=pt(e,n);s=y.makeSpan(["katex"],[o])}else{var h=_t(e,t,n,a.displayMode,!1),c=pt(e,n);s=y.makeSpan(["katex"],[h,c])}return Or(s,a)},S1=function(e,t,a){var n=Ir(a),s=pt(e,n),o=y.makeSpan(["katex"],[s]);return Or(o,a)},M1={widehat:"^",widecheck:"\u02C7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23DF",overbrace:"\u23DE",overgroup:"\u23E0",undergroup:"\u23E1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21D2",xRightarrow:"\u21D2",overleftharpoon:"\u21BC",xleftharpoonup:"\u21BC",overrightharpoon:"\u21C0",xrightharpoonup:"\u21C0",xLeftarrow:"\u21D0",xLeftrightarrow:"\u21D4",xhookleftarrow:"\u21A9",xhookrightarrow:"\u21AA",xmapsto:"\u21A6",xrightharpoondown:"\u21C1",xleftharpoondown:"\u21BD",xrightleftharpoons:"\u21CC",xleftrightharpoons:"\u21CB",xtwoheadleftarrow:"\u219E",xtwoheadrightarrow:"\u21A0",xlongequal:"=",xtofrom:"\u21C4",xrightleftarrows:"\u21C4",xrightequilibrium:"\u21CC",xleftequilibrium:"\u21CB","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},z1=function(e){var t=new S.MathNode("mo",[new S.TextNode(M1[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},A1={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},T1=function(e){return e.type==="ordgroup"?e.body.length:1},B1=function(e,t){function a(){var h=4e5,c=e.label.slice(1);if(N.contains(["widehat","widecheck","widetilde","utilde"],c)){var p=e,g=T1(p.base),b,x,w;if(g>5)c==="widehat"||c==="widecheck"?(b=420,h=2364,w=.42,x=c+"4"):(b=312,h=2340,w=.34,x="tilde4");else{var z=[1,1,2,2,3,3][g];c==="widehat"||c==="widecheck"?(h=[0,1062,2364,2364,2364][z],b=[0,239,300,360,420][z],w=[0,.24,.3,.3,.36,.42][z],x=c+z):(h=[0,600,1033,2339,2340][z],b=[0,260,286,306,312][z],w=[0,.26,.286,.3,.306,.34][z],x="tilde"+z)}var T=new S0(x),C=new y0([T],{width:"100%",height:A(w),viewBox:"0 0 "+h+" "+b,preserveAspectRatio:"none"});return{span:y.makeSvgSpan([],[C],t),minWidth:0,height:w}}else{var q=[],O=A1[c],[H,V,L]=O,U=L/1e3,G=H.length,j,$;if(G===1){var T0=O[3];j=["hide-tail"],$=[T0]}else if(G===2)j=["halfarrow-left","halfarrow-right"],$=["xMinYMin","xMaxYMin"];else if(G===3)j=["brace-left","brace-center","brace-right"],$=["xMinYMin","xMidYMin","xMaxYMin"];else throw new Error(`Correct katexImagesData or update code here to support - `+G+" children.");for(var a0=0;a00&&(n.style.minWidth=A(s)),n},D1=function(e,t,a,n,s){var o,h=e.height+e.depth+a+n;if(/fbox|color|angl/.test(t)){if(o=y.makeSpan(["stretchy",t],[],s),t==="fbox"){var c=s.color&&s.getColor();c&&(o.style.borderColor=c)}}else{var p=[];/^[bx]cancel$/.test(t)&&p.push(new de({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&p.push(new de({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var g=new y0(p,{width:"100%",height:A(h)});o=y.makeSvgSpan([],[g],s)}return o.height=h,o.style.height=A(h),o},E0={encloseSpan:D1,mathMLnode:z1,svgSpan:B1};function F(r,e){if(!r||r.type!==e)throw new Error("Expected node of type "+e+", but got "+(r?"node of type "+r.type:String(r)));return r}function Tt(r){var e=Ue(r);if(!e)throw new Error("Expected node of symbol group type, but got "+(r?"node of type "+r.type:String(r)));return e}function Ue(r){return r&&(r.type==="atom"||t1.hasOwnProperty(r.type))?r:null}var Bt=(r,e)=>{var t,a,n;r&&r.type==="supsub"?(a=F(r.base,"accent"),t=a.base,r.base=t,n=_a(P(r,e)),r.base=a):(a=F(r,"accent"),t=a.base);var s=P(t,e.havingCrampedStyle()),o=a.isShifty&&N.isCharacterBox(t),h=0;if(o){var c=N.getBaseElem(t),p=P(c,e.havingCrampedStyle());h=jt(p).skew}var g=a.label==="\\c",b=g?s.height+s.depth:Math.min(s.height,e.fontMetrics().xHeight),x;if(a.isStretchy)x=E0.svgSpan(a,e),x=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:x,wrapperClasses:["svg-align"],wrapperStyle:h>0?{width:"calc(100% - "+A(2*h)+")",marginLeft:A(2*h)}:void 0}]},e);else{var w,z;a.label==="\\vec"?(w=y.staticSvg("vec",e),z=y.svgData.vec[1]):(w=y.makeOrd({mode:a.mode,text:a.label},e,"textord"),w=jt(w),w.italic=0,z=w.width,g&&(b+=w.depth)),x=y.makeSpan(["accent-body"],[w]);var T=a.label==="\\textcircled";T&&(x.classes.push("accent-full"),b=s.height);var C=h;T||(C-=z/2),x.style.left=A(C),a.label==="\\textcircled"&&(x.style.top=".2em"),x=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-b},{type:"elem",elem:x}]},e)}var q=y.makeSpan(["mord","accent"],[x],e);return n?(n.children[0]=q,n.height=Math.max(q.height,n.height),n.classes[0]="mord",n):q},Hr=(r,e)=>{var t=r.isStretchy?E0.mathMLnode(r.label):new S.MathNode("mo",[v0(r.label,r.mode)]),a=new S.MathNode("mover",[Y(r.base,e),t]);return a.setAttribute("accent","true"),a},C1=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(r=>"\\"+r).join("|"));B({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(r,e)=>{var t=Fe(e[0]),a=!C1.test(r.funcName),n=!a||r.funcName==="\\widehat"||r.funcName==="\\widetilde"||r.funcName==="\\widecheck";return{type:"accent",mode:r.parser.mode,label:r.funcName,isStretchy:a,isShifty:n,base:t}},htmlBuilder:Bt,mathmlBuilder:Hr});B({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(r,e)=>{var t=e[0],a=r.parser.mode;return a==="math"&&(r.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+r.funcName+" works only in text mode"),a="text"),{type:"accent",mode:a,label:r.funcName,isStretchy:!1,isShifty:!0,base:t}},htmlBuilder:Bt,mathmlBuilder:Hr});B({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"accentUnder",mode:t.mode,label:a,base:n}},htmlBuilder:(r,e)=>{var t=P(r.base,e),a=E0.svgSpan(r,e),n=r.label==="\\utilde"?.12:0,s=y.makeVList({positionType:"top",positionData:t.height,children:[{type:"elem",elem:a,wrapperClasses:["svg-align"]},{type:"kern",size:n},{type:"elem",elem:t}]},e);return y.makeSpan(["mord","accentunder"],[s],e)},mathmlBuilder:(r,e)=>{var t=E0.mathMLnode(r.label),a=new S.MathNode("munder",[Y(r.base,e),t]);return a.setAttribute("accentunder","true"),a}});var Be=r=>{var e=new S.MathNode("mpadded",r?[r]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};B({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(r,e,t){var{parser:a,funcName:n}=r;return{type:"xArrow",mode:a.mode,label:n,body:e[0],below:t[0]}},htmlBuilder(r,e){var t=e.style,a=e.havingStyle(t.sup()),n=y.wrapFragment(P(r.body,a,e),e),s=r.label.slice(0,2)==="\\x"?"x":"cd";n.classes.push(s+"-arrow-pad");var o;r.below&&(a=e.havingStyle(t.sub()),o=y.wrapFragment(P(r.below,a,e),e),o.classes.push(s+"-arrow-pad"));var h=E0.svgSpan(r,e),c=-e.fontMetrics().axisHeight+.5*h.height,p=-e.fontMetrics().axisHeight-.5*h.height-.111;(n.depth>.25||r.label==="\\xleftequilibrium")&&(p-=n.depth);var g;if(o){var b=-e.fontMetrics().axisHeight+o.height+.5*h.height+.111;g=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:p},{type:"elem",elem:h,shift:c},{type:"elem",elem:o,shift:b}]},e)}else g=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:p},{type:"elem",elem:h,shift:c}]},e);return g.children[0].children[0].children[1].classes.push("svg-align"),y.makeSpan(["mrel","x-arrow"],[g],e)},mathmlBuilder(r,e){var t=E0.mathMLnode(r.label);t.setAttribute("minsize",r.label.charAt(0)==="x"?"1.75em":"3.0em");var a;if(r.body){var n=Be(Y(r.body,e));if(r.below){var s=Be(Y(r.below,e));a=new S.MathNode("munderover",[t,s,n])}else a=new S.MathNode("mover",[t,n])}else if(r.below){var o=Be(Y(r.below,e));a=new S.MathNode("munder",[t,o])}else a=Be(),a=new S.MathNode("mover",[t,a]);return a}});var q1=y.makeSpan;function Fr(r,e){var t=t0(r.body,e,!0);return q1([r.mclass],t,e)}function Lr(r,e){var t,a=h0(r.body,e);return r.mclass==="minner"?t=new S.MathNode("mpadded",a):r.mclass==="mord"?r.isCharacterBox?(t=a[0],t.type="mi"):t=new S.MathNode("mi",a):(r.isCharacterBox?(t=a[0],t.type="mo"):t=new S.MathNode("mo",a),r.mclass==="mbin"?(t.attributes.lspace="0.22em",t.attributes.rspace="0.22em"):r.mclass==="mpunct"?(t.attributes.lspace="0em",t.attributes.rspace="0.17em"):r.mclass==="mopen"||r.mclass==="mclose"?(t.attributes.lspace="0em",t.attributes.rspace="0em"):r.mclass==="minner"&&(t.attributes.lspace="0.0556em",t.attributes.width="+0.1111em")),t}B({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"mclass",mode:t.mode,mclass:"m"+a.slice(5),body:Q(n),isCharacterBox:N.isCharacterBox(n)}},htmlBuilder:Fr,mathmlBuilder:Lr});var $e=r=>{var e=r.type==="ordgroup"&&r.body.length?r.body[0]:r;return e.type==="atom"&&(e.family==="bin"||e.family==="rel")?"m"+e.family:"mord"};B({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(r,e){var{parser:t}=r;return{type:"mclass",mode:t.mode,mclass:$e(e[0]),body:Q(e[1]),isCharacterBox:N.isCharacterBox(e[1])}}});B({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(r,e){var{parser:t,funcName:a}=r,n=e[1],s=e[0],o;a!=="\\stackrel"?o=$e(n):o="mrel";var h={type:"op",mode:n.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:a!=="\\stackrel",body:Q(n)},c={type:"supsub",mode:s.mode,base:h,sup:a==="\\underset"?null:s,sub:a==="\\underset"?s:null};return{type:"mclass",mode:t.mode,mclass:o,body:[c],isCharacterBox:N.isCharacterBox(c)}},htmlBuilder:Fr,mathmlBuilder:Lr});B({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"pmb",mode:t.mode,mclass:$e(e[0]),body:Q(e[0])}},htmlBuilder(r,e){var t=t0(r.body,e,!0),a=y.makeSpan([r.mclass],t,e);return a.style.textShadow="0.02em 0.01em 0.04px",a},mathmlBuilder(r,e){var t=h0(r.body,e),a=new S.MathNode("mstyle",t);return a.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),a}});var N1={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},er=()=>({type:"styling",body:[],mode:"math",style:"display"}),tr=r=>r.type==="textord"&&r.text==="@",E1=(r,e)=>(r.type==="mathord"||r.type==="atom")&&r.text===e;function R1(r,e,t){var a=N1[r];switch(a){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return t.callFunction(a,[e[0]],[e[1]]);case"\\uparrow":case"\\downarrow":{var n=t.callFunction("\\\\cdleft",[e[0]],[]),s={type:"atom",text:a,mode:"math",family:"rel"},o=t.callFunction("\\Big",[s],[]),h=t.callFunction("\\\\cdright",[e[1]],[]),c={type:"ordgroup",mode:"math",body:[n,o,h]};return t.callFunction("\\\\cdparent",[c],[])}case"\\\\cdlongequal":return t.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{var p={type:"textord",text:"\\Vert",mode:"math"};return t.callFunction("\\Big",[p],[])}default:return{type:"textord",text:" ",mode:"math"}}}function I1(r){var e=[];for(r.gullet.beginGroup(),r.gullet.macros.set("\\cr","\\\\\\relax"),r.gullet.beginGroup();;){e.push(r.parseExpression(!1,"\\\\")),r.gullet.endGroup(),r.gullet.beginGroup();var t=r.fetch().text;if(t==="&"||t==="\\\\")r.consume();else if(t==="\\end"){e[e.length-1].length===0&&e.pop();break}else throw new M("Expected \\\\ or \\cr or \\end",r.nextToken)}for(var a=[],n=[a],s=0;s-1))if("<>AV".indexOf(p)>-1)for(var b=0;b<2;b++){for(var x=!0,w=c+1;wAV=|." after @',o[c]);var z=R1(p,g,r),T={type:"styling",body:[z],mode:"math",style:"display"};a.push(T),h=er()}s%2===0?a.push(h):a.shift(),a=[],n.push(a)}r.gullet.endGroup(),r.gullet.endGroup();var C=new Array(n[0].length).fill({type:"align",align:"c",pregap:.25,postgap:.25});return{type:"array",mode:"math",body:n,arraystretch:1,addJot:!0,rowGaps:[null],cols:C,colSeparationType:"CD",hLinesBeforeRow:new Array(n.length+1).fill([])}}B({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r;return{type:"cdlabel",mode:t.mode,side:a.slice(4),label:e[0]}},htmlBuilder(r,e){var t=e.havingStyle(e.style.sup()),a=y.wrapFragment(P(r.label,t,e),e);return a.classes.push("cd-label-"+r.side),a.style.bottom=A(.8-a.depth),a.height=0,a.depth=0,a},mathmlBuilder(r,e){var t=new S.MathNode("mrow",[Y(r.label,e)]);return t=new S.MathNode("mpadded",[t]),t.setAttribute("width","0"),r.side==="left"&&t.setAttribute("lspace","-1width"),t.setAttribute("voffset","0.7em"),t=new S.MathNode("mstyle",[t]),t.setAttribute("displaystyle","false"),t.setAttribute("scriptlevel","1"),t}});B({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(r,e){var{parser:t}=r;return{type:"cdlabelparent",mode:t.mode,fragment:e[0]}},htmlBuilder(r,e){var t=y.wrapFragment(P(r.fragment,e),e);return t.classes.push("cd-vert-arrow"),t},mathmlBuilder(r,e){return new S.MathNode("mrow",[Y(r.fragment,e)])}});B({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(r,e){for(var{parser:t}=r,a=F(e[0],"ordgroup"),n=a.body,s="",o=0;o=1114111)throw new M("\\@char with invalid code point "+s);return c<=65535?p=String.fromCharCode(c):(c-=65536,p=String.fromCharCode((c>>10)+55296,(c&1023)+56320)),{type:"textord",mode:t.mode,text:p}}});var Pr=(r,e)=>{var t=t0(r.body,e.withColor(r.color),!1);return y.makeFragment(t)},Gr=(r,e)=>{var t=h0(r.body,e.withColor(r.color)),a=new S.MathNode("mstyle",t);return a.setAttribute("mathcolor",r.color),a};B({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(r,e){var{parser:t}=r,a=F(e[0],"color-token").color,n=e[1];return{type:"color",mode:t.mode,color:a,body:Q(n)}},htmlBuilder:Pr,mathmlBuilder:Gr});B({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(r,e){var{parser:t,breakOnTokenText:a}=r,n=F(e[0],"color-token").color;t.gullet.macros.set("\\current@color",n);var s=t.parseExpression(!0,a);return{type:"color",mode:t.mode,color:n,body:s}},htmlBuilder:Pr,mathmlBuilder:Gr});B({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(r,e,t){var{parser:a}=r,n=a.gullet.future().text==="["?a.parseSizeGroup(!0):null,s=!a.settings.displayMode||!a.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:a.mode,newLine:s,size:n&&F(n,"size").value}},htmlBuilder(r,e){var t=y.makeSpan(["mspace"],[],e);return r.newLine&&(t.classes.push("newline"),r.size&&(t.style.marginTop=A(J(r.size,e)))),t},mathmlBuilder(r,e){var t=new S.MathNode("mspace");return r.newLine&&(t.setAttribute("linebreak","newline"),r.size&&t.setAttribute("height",A(J(r.size,e)))),t}});var gt={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},Vr=r=>{var e=r.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(e))throw new M("Expected a control sequence",r);return e},O1=r=>{var e=r.gullet.popToken();return e.text==="="&&(e=r.gullet.popToken(),e.text===" "&&(e=r.gullet.popToken())),e},Ur=(r,e,t,a)=>{var n=r.gullet.macros.get(t.text);n==null&&(t.noexpand=!0,n={tokens:[t],numArgs:0,unexpandable:!r.gullet.isExpandable(t.text)}),r.gullet.macros.set(e,n,a)};B({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(r){var{parser:e,funcName:t}=r;e.consumeSpaces();var a=e.fetch();if(gt[a.text])return(t==="\\global"||t==="\\\\globallong")&&(a.text=gt[a.text]),F(e.parseFunction(),"internal");throw new M("Invalid token after macro prefix",a)}});B({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=e.gullet.popToken(),n=a.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(n))throw new M("Expected a control sequence",a);for(var s=0,o,h=[[]];e.gullet.future().text!=="{";)if(a=e.gullet.popToken(),a.text==="#"){if(e.gullet.future().text==="{"){o=e.gullet.future(),h[s].push("{");break}if(a=e.gullet.popToken(),!/^[1-9]$/.test(a.text))throw new M('Invalid argument number "'+a.text+'"');if(parseInt(a.text)!==s+1)throw new M('Argument number "'+a.text+'" out of order');s++,h.push([])}else{if(a.text==="EOF")throw new M("Expected a macro definition");h[s].push(a.text)}var{tokens:c}=e.gullet.consumeArg();return o&&c.unshift(o),(t==="\\edef"||t==="\\xdef")&&(c=e.gullet.expandTokens(c),c.reverse()),e.gullet.macros.set(n,{tokens:c,numArgs:s,delimiters:h},t===gt[t]),{type:"internal",mode:e.mode}}});B({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=Vr(e.gullet.popToken());e.gullet.consumeSpaces();var n=O1(e);return Ur(e,a,n,t==="\\\\globallet"),{type:"internal",mode:e.mode}}});B({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=Vr(e.gullet.popToken()),n=e.gullet.popToken(),s=e.gullet.popToken();return Ur(e,a,s,t==="\\\\globalfuture"),e.gullet.pushToken(s),e.gullet.pushToken(n),{type:"internal",mode:e.mode}}});var oe=function(e,t,a){var n=X.math[e]&&X.math[e].replace,s=St(n||e,t,a);if(!s)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return s},Dt=function(e,t,a,n){var s=a.havingBaseStyle(t),o=y.makeSpan(n.concat(s.sizingClasses(a)),[e],a),h=s.sizeMultiplier/a.sizeMultiplier;return o.height*=h,o.depth*=h,o.maxFontSize=s.sizeMultiplier,o},$r=function(e,t,a){var n=t.havingBaseStyle(a),s=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=A(s),e.height-=s,e.depth+=s},H1=function(e,t,a,n,s,o){var h=y.makeSymbol(e,"Main-Regular",s,n),c=Dt(h,t,n,o);return a&&$r(c,n,t),c},F1=function(e,t,a,n){return y.makeSymbol(e,"Size"+t+"-Regular",a,n)},Yr=function(e,t,a,n,s,o){var h=F1(e,t,s,n),c=Dt(y.makeSpan(["delimsizing","size"+t],[h],n),R.TEXT,n,o);return a&&$r(c,n,R.TEXT),c},tt=function(e,t,a){var n;t==="Size1-Regular"?n="delim-size1":n="delim-size4";var s=y.makeSpan(["delimsizinginner",n],[y.makeSpan([],[y.makeSymbol(e,t,a)])]);return{type:"elem",elem:s}},rt=function(e,t,a){var n=k0["Size4-Regular"][e.charCodeAt(0)]?k0["Size4-Regular"][e.charCodeAt(0)][4]:k0["Size1-Regular"][e.charCodeAt(0)][4],s=new S0("inner",Xa(e,Math.round(1e3*t))),o=new y0([s],{width:A(n),height:A(t),style:"width:"+A(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),h=y.makeSvgSpan([],[o],a);return h.height=t,h.style.height=A(t),h.style.width=A(n),{type:"elem",elem:h}},bt=.008,De={type:"kern",size:-1*bt},L1=["|","\\lvert","\\rvert","\\vert"],P1=["\\|","\\lVert","\\rVert","\\Vert"],Xr=function(e,t,a,n,s,o){var h,c,p,g,b="",x=0;h=p=g=e,c=null;var w="Size1-Regular";e==="\\uparrow"?p=g="\u23D0":e==="\\Uparrow"?p=g="\u2016":e==="\\downarrow"?h=p="\u23D0":e==="\\Downarrow"?h=p="\u2016":e==="\\updownarrow"?(h="\\uparrow",p="\u23D0",g="\\downarrow"):e==="\\Updownarrow"?(h="\\Uparrow",p="\u2016",g="\\Downarrow"):N.contains(L1,e)?(p="\u2223",b="vert",x=333):N.contains(P1,e)?(p="\u2225",b="doublevert",x=556):e==="["||e==="\\lbrack"?(h="\u23A1",p="\u23A2",g="\u23A3",w="Size4-Regular",b="lbrack",x=667):e==="]"||e==="\\rbrack"?(h="\u23A4",p="\u23A5",g="\u23A6",w="Size4-Regular",b="rbrack",x=667):e==="\\lfloor"||e==="\u230A"?(p=h="\u23A2",g="\u23A3",w="Size4-Regular",b="lfloor",x=667):e==="\\lceil"||e==="\u2308"?(h="\u23A1",p=g="\u23A2",w="Size4-Regular",b="lceil",x=667):e==="\\rfloor"||e==="\u230B"?(p=h="\u23A5",g="\u23A6",w="Size4-Regular",b="rfloor",x=667):e==="\\rceil"||e==="\u2309"?(h="\u23A4",p=g="\u23A5",w="Size4-Regular",b="rceil",x=667):e==="("||e==="\\lparen"?(h="\u239B",p="\u239C",g="\u239D",w="Size4-Regular",b="lparen",x=875):e===")"||e==="\\rparen"?(h="\u239E",p="\u239F",g="\u23A0",w="Size4-Regular",b="rparen",x=875):e==="\\{"||e==="\\lbrace"?(h="\u23A7",c="\u23A8",g="\u23A9",p="\u23AA",w="Size4-Regular"):e==="\\}"||e==="\\rbrace"?(h="\u23AB",c="\u23AC",g="\u23AD",p="\u23AA",w="Size4-Regular"):e==="\\lgroup"||e==="\u27EE"?(h="\u23A7",g="\u23A9",p="\u23AA",w="Size4-Regular"):e==="\\rgroup"||e==="\u27EF"?(h="\u23AB",g="\u23AD",p="\u23AA",w="Size4-Regular"):e==="\\lmoustache"||e==="\u23B0"?(h="\u23A7",g="\u23AD",p="\u23AA",w="Size4-Regular"):(e==="\\rmoustache"||e==="\u23B1")&&(h="\u23AB",g="\u23A9",p="\u23AA",w="Size4-Regular");var z=oe(h,w,s),T=z.height+z.depth,C=oe(p,w,s),q=C.height+C.depth,O=oe(g,w,s),H=O.height+O.depth,V=0,L=1;if(c!==null){var U=oe(c,w,s);V=U.height+U.depth,L=2}var G=T+H+V,j=Math.max(0,Math.ceil((t-G)/(L*q))),$=G+j*L*q,T0=n.fontMetrics().axisHeight;a&&(T0*=n.sizeMultiplier);var a0=$/2-T0,e0=[];if(b.length>0){var U0=$-T-H,s0=Math.round($*1e3),g0=Wa(b,Math.round(U0*1e3)),I0=new S0(b,g0),Z0=(x/1e3).toFixed(3)+"em",K0=(s0/1e3).toFixed(3)+"em",We=new y0([I0],{width:Z0,height:K0,viewBox:"0 0 "+x+" "+s0}),O0=y.makeSvgSpan([],[We],n);O0.height=s0/1e3,O0.style.width=Z0,O0.style.height=K0,e0.push({type:"elem",elem:O0})}else{if(e0.push(tt(g,w,s)),e0.push(De),c===null){var H0=$-T-H+2*bt;e0.push(rt(p,H0,n))}else{var d0=($-T-H-V)/2+2*bt;e0.push(rt(p,d0,n)),e0.push(De),e0.push(tt(c,w,s)),e0.push(De),e0.push(rt(p,d0,n))}e0.push(De),e0.push(tt(h,w,s))}var ie=n.havingBaseStyle(R.TEXT),je=y.makeVList({positionType:"bottom",positionData:a0,children:e0},ie);return Dt(y.makeSpan(["delimsizing","mult"],[je],ie),R.TEXT,n,o)},at=80,nt=.08,it=function(e,t,a,n,s){var o=Ya(e,n,a),h=new S0(e,o),c=new y0([h],{width:"400em",height:A(t),viewBox:"0 0 400000 "+a,preserveAspectRatio:"xMinYMin slice"});return y.makeSvgSpan(["hide-tail"],[c],s)},G1=function(e,t){var a=t.havingBaseSizing(),n=Kr("\\surd",e*a.sizeMultiplier,Zr,a),s=a.sizeMultiplier,o=Math.max(0,t.minRuleThickness-t.fontMetrics().sqrtRuleThickness),h,c=0,p=0,g=0,b;return n.type==="small"?(g=1e3+1e3*o+at,e<1?s=1:e<1.4&&(s=.7),c=(1+o+nt)/s,p=(1+o)/s,h=it("sqrtMain",c,g,o,t),h.style.minWidth="0.853em",b=.833/s):n.type==="large"?(g=(1e3+at)*ue[n.size],p=(ue[n.size]+o)/s,c=(ue[n.size]+o+nt)/s,h=it("sqrtSize"+n.size,c,g,o,t),h.style.minWidth="1.02em",b=1/s):(c=e+o+nt,p=e+o,g=Math.floor(1e3*e+o)+at,h=it("sqrtTall",c,g,o,t),h.style.minWidth="0.742em",b=1.056),h.height=p,h.style.height=A(c),{span:h,advanceWidth:b,ruleWidth:(t.fontMetrics().sqrtRuleThickness+o)*s}},Wr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","\\surd"],V1=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1"],jr=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],ue=[0,1.2,1.8,2.4,3],U1=function(e,t,a,n,s){if(e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle"),N.contains(Wr,e)||N.contains(jr,e))return Yr(e,t,!1,a,n,s);if(N.contains(V1,e))return Xr(e,ue[t],!1,a,n,s);throw new M("Illegal delimiter: '"+e+"'")},$1=[{type:"small",style:R.SCRIPTSCRIPT},{type:"small",style:R.SCRIPT},{type:"small",style:R.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],Y1=[{type:"small",style:R.SCRIPTSCRIPT},{type:"small",style:R.SCRIPT},{type:"small",style:R.TEXT},{type:"stack"}],Zr=[{type:"small",style:R.SCRIPTSCRIPT},{type:"small",style:R.SCRIPT},{type:"small",style:R.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],X1=function(e){if(e.type==="small")return"Main-Regular";if(e.type==="large")return"Size"+e.size+"-Regular";if(e.type==="stack")return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},Kr=function(e,t,a,n){for(var s=Math.min(2,3-n.style.size),o=s;ot)return a[o]}return a[a.length-1]},Jr=function(e,t,a,n,s,o){e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle");var h;N.contains(jr,e)?h=$1:N.contains(Wr,e)?h=Zr:h=Y1;var c=Kr(e,t,h,n);return c.type==="small"?H1(e,c.style,a,n,s,o):c.type==="large"?Yr(e,c.size,a,n,s,o):Xr(e,t,a,n,s,o)},W1=function(e,t,a,n,s,o){var h=n.fontMetrics().axisHeight*n.sizeMultiplier,c=901,p=5/n.fontMetrics().ptPerEm,g=Math.max(t-h,a+h),b=Math.max(g/500*c,2*g-p);return Jr(e,b,!0,n,s,o)},q0={sqrtImage:G1,sizedDelim:U1,sizeToMaxHeight:ue,customSizedDelim:Jr,leftRightDelim:W1},rr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},j1=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27E8","\\rangle","\u27E9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function Ye(r,e){var t=Ue(r);if(t&&N.contains(j1,t.text))return t;throw t?new M("Invalid delimiter '"+t.text+"' after '"+e.funcName+"'",r):new M("Invalid delimiter type '"+r.type+"'",r)}B({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(r,e)=>{var t=Ye(e[0],r);return{type:"delimsizing",mode:r.parser.mode,size:rr[r.funcName].size,mclass:rr[r.funcName].mclass,delim:t.text}},htmlBuilder:(r,e)=>r.delim==="."?y.makeSpan([r.mclass]):q0.sizedDelim(r.delim,r.size,e,r.mode,[r.mclass]),mathmlBuilder:r=>{var e=[];r.delim!=="."&&e.push(v0(r.delim,r.mode));var t=new S.MathNode("mo",e);r.mclass==="mopen"||r.mclass==="mclose"?t.setAttribute("fence","true"):t.setAttribute("fence","false"),t.setAttribute("stretchy","true");var a=A(q0.sizeToMaxHeight[r.size]);return t.setAttribute("minsize",a),t.setAttribute("maxsize",a),t}});function ar(r){if(!r.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}B({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=r.parser.gullet.macros.get("\\current@color");if(t&&typeof t!="string")throw new M("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:r.parser.mode,delim:Ye(e[0],r).text,color:t}}});B({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=Ye(e[0],r),a=r.parser;++a.leftrightDepth;var n=a.parseExpression(!1);--a.leftrightDepth,a.expect("\\right",!1);var s=F(a.parseFunction(),"leftright-right");return{type:"leftright",mode:a.mode,body:n,left:t.text,right:s.delim,rightColor:s.color}},htmlBuilder:(r,e)=>{ar(r);for(var t=t0(r.body,e,!0,["mopen","mclose"]),a=0,n=0,s=!1,o=0;o{ar(r);var t=h0(r.body,e);if(r.left!=="."){var a=new S.MathNode("mo",[v0(r.left,r.mode)]);a.setAttribute("fence","true"),t.unshift(a)}if(r.right!=="."){var n=new S.MathNode("mo",[v0(r.right,r.mode)]);n.setAttribute("fence","true"),r.rightColor&&n.setAttribute("mathcolor",r.rightColor),t.push(n)}return zt(t)}});B({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=Ye(e[0],r);if(!r.parser.leftrightDepth)throw new M("\\middle without preceding \\left",t);return{type:"middle",mode:r.parser.mode,delim:t.text}},htmlBuilder:(r,e)=>{var t;if(r.delim===".")t=fe(e,[]);else{t=q0.sizedDelim(r.delim,1,e,r.mode,[]);var a={delim:r.delim,options:e};t.isMiddle=a}return t},mathmlBuilder:(r,e)=>{var t=r.delim==="\\vert"||r.delim==="|"?v0("|","text"):v0(r.delim,r.mode),a=new S.MathNode("mo",[t]);return a.setAttribute("fence","true"),a.setAttribute("lspace","0.05em"),a.setAttribute("rspace","0.05em"),a}});var Ct=(r,e)=>{var t=y.wrapFragment(P(r.body,e),e),a=r.label.slice(1),n=e.sizeMultiplier,s,o=0,h=N.isCharacterBox(r.body);if(a==="sout")s=y.makeSpan(["stretchy","sout"]),s.height=e.fontMetrics().defaultRuleThickness/n,o=-.5*e.fontMetrics().xHeight;else if(a==="phase"){var c=J({number:.6,unit:"pt"},e),p=J({number:.35,unit:"ex"},e),g=e.havingBaseSizing();n=n/g.sizeMultiplier;var b=t.height+t.depth+c+p;t.style.paddingLeft=A(b/2+c);var x=Math.floor(1e3*b*n),w=Ua(x),z=new y0([new S0("phase",w)],{width:"400em",height:A(x/1e3),viewBox:"0 0 400000 "+x,preserveAspectRatio:"xMinYMin slice"});s=y.makeSvgSpan(["hide-tail"],[z],e),s.style.height=A(b),o=t.depth+c+p}else{/cancel/.test(a)?h||t.classes.push("cancel-pad"):a==="angl"?t.classes.push("anglpad"):t.classes.push("boxpad");var T=0,C=0,q=0;/box/.test(a)?(q=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness),T=e.fontMetrics().fboxsep+(a==="colorbox"?0:q),C=T):a==="angl"?(q=Math.max(e.fontMetrics().defaultRuleThickness,e.minRuleThickness),T=4*q,C=Math.max(0,.25-t.depth)):(T=h?.2:0,C=T),s=E0.encloseSpan(t,a,T,C,e),/fbox|boxed|fcolorbox/.test(a)?(s.style.borderStyle="solid",s.style.borderWidth=A(q)):a==="angl"&&q!==.049&&(s.style.borderTopWidth=A(q),s.style.borderRightWidth=A(q)),o=t.depth+C,r.backgroundColor&&(s.style.backgroundColor=r.backgroundColor,r.borderColor&&(s.style.borderColor=r.borderColor))}var O;if(r.backgroundColor)O=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:s,shift:o},{type:"elem",elem:t,shift:0}]},e);else{var H=/cancel|phase/.test(a)?["svg-align"]:[];O=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:t,shift:0},{type:"elem",elem:s,shift:o,wrapperClasses:H}]},e)}return/cancel/.test(a)&&(O.height=t.height,O.depth=t.depth),/cancel/.test(a)&&!h?y.makeSpan(["mord","cancel-lap"],[O],e):y.makeSpan(["mord"],[O],e)},qt=(r,e)=>{var t=0,a=new S.MathNode(r.label.indexOf("colorbox")>-1?"mpadded":"menclose",[Y(r.body,e)]);switch(r.label){case"\\cancel":a.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":a.setAttribute("notation","downdiagonalstrike");break;case"\\phase":a.setAttribute("notation","phasorangle");break;case"\\sout":a.setAttribute("notation","horizontalstrike");break;case"\\fbox":a.setAttribute("notation","box");break;case"\\angl":a.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(t=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,a.setAttribute("width","+"+2*t+"pt"),a.setAttribute("height","+"+2*t+"pt"),a.setAttribute("lspace",t+"pt"),a.setAttribute("voffset",t+"pt"),r.label==="\\fcolorbox"){var n=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);a.setAttribute("style","border: "+n+"em solid "+String(r.borderColor))}break;case"\\xcancel":a.setAttribute("notation","updiagonalstrike downdiagonalstrike");break}return r.backgroundColor&&a.setAttribute("mathbackground",r.backgroundColor),a};B({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(r,e,t){var{parser:a,funcName:n}=r,s=F(e[0],"color-token").color,o=e[1];return{type:"enclose",mode:a.mode,label:n,backgroundColor:s,body:o}},htmlBuilder:Ct,mathmlBuilder:qt});B({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(r,e,t){var{parser:a,funcName:n}=r,s=F(e[0],"color-token").color,o=F(e[1],"color-token").color,h=e[2];return{type:"enclose",mode:a.mode,label:n,backgroundColor:o,borderColor:s,body:h}},htmlBuilder:Ct,mathmlBuilder:qt});B({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"enclose",mode:t.mode,label:"\\fbox",body:e[0]}}});B({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"enclose",mode:t.mode,label:a,body:n}},htmlBuilder:Ct,mathmlBuilder:qt});B({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(r,e){var{parser:t}=r;return{type:"enclose",mode:t.mode,label:"\\angl",body:e[0]}}});var Qr={};function M0(r){for(var{type:e,names:t,props:a,handler:n,htmlBuilder:s,mathmlBuilder:o}=r,h={type:e,numArgs:a.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:n},c=0;c{var e=r.parser.settings;if(!e.displayMode)throw new M("{"+r.envName+"} can be used only in display mode.")};function Nt(r){if(r.indexOf("ed")===-1)return r.indexOf("*")===-1}function V0(r,e,t){var{hskipBeforeAndAfter:a,addJot:n,cols:s,arraystretch:o,colSeparationType:h,autoTag:c,singleRow:p,emptySingleRow:g,maxNumCols:b,leqno:x}=e;if(r.gullet.beginGroup(),p||r.gullet.macros.set("\\cr","\\\\\\relax"),!o){var w=r.gullet.expandMacroAsText("\\arraystretch");if(w==null)o=1;else if(o=parseFloat(w),!o||o<0)throw new M("Invalid \\arraystretch: "+w)}r.gullet.beginGroup();var z=[],T=[z],C=[],q=[],O=c!=null?[]:void 0;function H(){c&&r.gullet.macros.set("\\@eqnsw","1",!0)}function V(){O&&(r.gullet.macros.get("\\df@tag")?(O.push(r.subparse([new p0("\\df@tag")])),r.gullet.macros.set("\\df@tag",void 0,!0)):O.push(!!c&&r.gullet.macros.get("\\@eqnsw")==="1"))}for(H(),q.push(nr(r));;){var L=r.parseExpression(!1,p?"\\end":"\\\\");r.gullet.endGroup(),r.gullet.beginGroup(),L={type:"ordgroup",mode:r.mode,body:L},t&&(L={type:"styling",mode:r.mode,style:t,body:[L]}),z.push(L);var U=r.fetch().text;if(U==="&"){if(b&&z.length===b){if(p||h)throw new M("Too many tab characters: &",r.nextToken);r.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}r.consume()}else if(U==="\\end"){V(),z.length===1&&L.type==="styling"&&L.body[0].body.length===0&&(T.length>1||!g)&&T.pop(),q.length0&&(H+=.25),p.push({pos:H,isDashed:be[ye]})}for(V(o[0]),a=0;a0&&(a0+=O,Gbe))for(a=0;a=h)){var Q0=void 0;(n>0||e.hskipBeforeAndAfter)&&(Q0=N.deflt(d0.pregap,x),Q0!==0&&(g0=y.makeSpan(["arraycolsep"],[]),g0.style.width=A(Q0),s0.push(g0)));var _0=[];for(a=0;a0){for(var ba=y.makeLineSpan("hline",t,g),ya=y.makeLineSpan("hdashline",t,g),Ze=[{type:"elem",elem:c,shift:0}];p.length>0;){var Gt=p.pop(),Vt=Gt.pos-e0;Gt.isDashed?Ze.push({type:"elem",elem:ya,shift:Vt}):Ze.push({type:"elem",elem:ba,shift:Vt})}c=y.makeVList({positionType:"individualShift",children:Ze},t)}if(Z0.length===0)return y.makeSpan(["mord"],[c],t);var Ke=y.makeVList({positionType:"individualShift",children:Z0},t);return Ke=y.makeSpan(["tag"],[Ke],t),y.makeFragment([c,Ke])},Z1={c:"center ",l:"left ",r:"right "},A0=function(e,t){for(var a=[],n=new S.MathNode("mtd",[],["mtr-glue"]),s=new S.MathNode("mtd",[],["mml-eqn-num"]),o=0;o0){var z=e.cols,T="",C=!1,q=0,O=z.length;z[0].type==="separator"&&(x+="top ",q=1),z[z.length-1].type==="separator"&&(x+="bottom ",O-=1);for(var H=q;H0?"left ":"",x+=j[j.length-1].length>0?"right ":"";for(var $=1;$-1?"alignat":"align",s=e.envName==="split",o=V0(e.parser,{cols:a,addJot:!0,autoTag:s?void 0:Nt(e.envName),emptySingleRow:!0,colSeparationType:n,maxNumCols:s?2:void 0,leqno:e.parser.settings.leqno},"display"),h,c=0,p={type:"ordgroup",mode:e.mode,body:[]};if(t[0]&&t[0].type==="ordgroup"){for(var g="",b=0;b0&&w&&(C=1),a[z]={type:"align",align:T,pregap:C,postgap:0}}return o.colSeparationType=w?"align":"alignat",o};M0({type:"array",names:["array","darray"],props:{numArgs:1},handler(r,e){var t=Ue(e[0]),a=t?[e[0]]:F(e[0],"ordgroup").body,n=a.map(function(o){var h=Tt(o),c=h.text;if("lcr".indexOf(c)!==-1)return{type:"align",align:c};if(c==="|")return{type:"separator",separator:"|"};if(c===":")return{type:"separator",separator:":"};throw new M("Unknown column alignment: "+c,o)}),s={cols:n,hskipBeforeAndAfter:!0,maxNumCols:n.length};return V0(r.parser,s,Et(r.envName))},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(r){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[r.envName.replace("*","")],t="c",a={hskipBeforeAndAfter:!1,cols:[{type:"align",align:t}]};if(r.envName.charAt(r.envName.length-1)==="*"){var n=r.parser;if(n.consumeSpaces(),n.fetch().text==="["){if(n.consume(),n.consumeSpaces(),t=n.fetch().text,"lcr".indexOf(t)===-1)throw new M("Expected l or c or r",n.nextToken);n.consume(),n.consumeSpaces(),n.expect("]"),n.consume(),a.cols=[{type:"align",align:t}]}}var s=V0(r.parser,a,Et(r.envName)),o=Math.max(0,...s.body.map(h=>h.length));return s.cols=new Array(o).fill({type:"align",align:t}),e?{type:"leftright",mode:r.mode,body:[s],left:e[0],right:e[1],rightColor:void 0}:s},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(r){var e={arraystretch:.5},t=V0(r.parser,e,"script");return t.colSeparationType="small",t},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["subarray"],props:{numArgs:1},handler(r,e){var t=Ue(e[0]),a=t?[e[0]]:F(e[0],"ordgroup").body,n=a.map(function(o){var h=Tt(o),c=h.text;if("lc".indexOf(c)!==-1)return{type:"align",align:c};throw new M("Unknown column alignment: "+c,o)});if(n.length>1)throw new M("{subarray} can contain only one column");var s={cols:n,hskipBeforeAndAfter:!1,arraystretch:.5};if(s=V0(r.parser,s,"script"),s.body.length>0&&s.body[0].length>1)throw new M("{subarray} can contain only one column");return s},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(r){var e={arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},t=V0(r.parser,e,Et(r.envName));return{type:"leftright",mode:r.mode,body:[t],left:r.envName.indexOf("r")>-1?".":"\\{",right:r.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:ea,htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(r){N.contains(["gather","gather*"],r.envName)&&Xe(r);var e={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Nt(r.envName),emptySingleRow:!0,leqno:r.parser.settings.leqno};return V0(r.parser,e,"display")},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:ea,htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(r){Xe(r);var e={autoTag:Nt(r.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:r.parser.settings.leqno};return V0(r.parser,e,"display")},htmlBuilder:z0,mathmlBuilder:A0});M0({type:"array",names:["CD"],props:{numArgs:0},handler(r){return Xe(r),I1(r.parser)},htmlBuilder:z0,mathmlBuilder:A0});m("\\nonumber","\\gdef\\@eqnsw{0}");m("\\notag","\\nonumber");B({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler(r,e){throw new M(r.funcName+" valid only within array environment")}});var ir=Qr;B({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];if(n.type!=="ordgroup")throw new M("Invalid environment name",n);for(var s="",o=0;o{var t=r.font,a=e.withFont(t);return P(r.body,a)},ra=(r,e)=>{var t=r.font,a=e.withFont(t);return Y(r.body,a)},sr={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};B({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=Fe(e[0]),s=a;return s in sr&&(s=sr[s]),{type:"font",mode:t.mode,font:s.slice(1),body:n}},htmlBuilder:ta,mathmlBuilder:ra});B({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(r,e)=>{var{parser:t}=r,a=e[0],n=N.isCharacterBox(a);return{type:"mclass",mode:t.mode,mclass:$e(a),body:[{type:"font",mode:t.mode,font:"boldsymbol",body:a}],isCharacterBox:n}}});B({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(r,e)=>{var{parser:t,funcName:a,breakOnTokenText:n}=r,{mode:s}=t,o=t.parseExpression(!0,n),h="math"+a.slice(1);return{type:"font",mode:s,font:h,body:{type:"ordgroup",mode:t.mode,body:o}}},htmlBuilder:ta,mathmlBuilder:ra});var aa=(r,e)=>{var t=e;return r==="display"?t=t.id>=R.SCRIPT.id?t.text():R.DISPLAY:r==="text"&&t.size===R.DISPLAY.size?t=R.TEXT:r==="script"?t=R.SCRIPT:r==="scriptscript"&&(t=R.SCRIPTSCRIPT),t},Rt=(r,e)=>{var t=aa(r.size,e.style),a=t.fracNum(),n=t.fracDen(),s;s=e.havingStyle(a);var o=P(r.numer,s,e);if(r.continued){var h=8.5/e.fontMetrics().ptPerEm,c=3.5/e.fontMetrics().ptPerEm;o.height=o.height0?z=3*x:z=7*x,T=e.fontMetrics().denom1):(b>0?(w=e.fontMetrics().num2,z=x):(w=e.fontMetrics().num3,z=3*x),T=e.fontMetrics().denom2);var C;if(g){var O=e.fontMetrics().axisHeight;w-o.depth-(O+.5*b){var t=new S.MathNode("mfrac",[Y(r.numer,e),Y(r.denom,e)]);if(!r.hasBarLine)t.setAttribute("linethickness","0px");else if(r.barSize){var a=J(r.barSize,e);t.setAttribute("linethickness",A(a))}var n=aa(r.size,e.style);if(n.size!==e.style.size){t=new S.MathNode("mstyle",[t]);var s=n.size===R.DISPLAY.size?"true":"false";t.setAttribute("displaystyle",s),t.setAttribute("scriptlevel","0")}if(r.leftDelim!=null||r.rightDelim!=null){var o=[];if(r.leftDelim!=null){var h=new S.MathNode("mo",[new S.TextNode(r.leftDelim.replace("\\",""))]);h.setAttribute("fence","true"),o.push(h)}if(o.push(t),r.rightDelim!=null){var c=new S.MathNode("mo",[new S.TextNode(r.rightDelim.replace("\\",""))]);c.setAttribute("fence","true"),o.push(c)}return zt(o)}return t};B({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=e[1],o,h=null,c=null,p="auto";switch(a){case"\\dfrac":case"\\frac":case"\\tfrac":o=!0;break;case"\\\\atopfrac":o=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":o=!1,h="(",c=")";break;case"\\\\bracefrac":o=!1,h="\\{",c="\\}";break;case"\\\\brackfrac":o=!1,h="[",c="]";break;default:throw new Error("Unrecognized genfrac command")}switch(a){case"\\dfrac":case"\\dbinom":p="display";break;case"\\tfrac":case"\\tbinom":p="text";break}return{type:"genfrac",mode:t.mode,continued:!1,numer:n,denom:s,hasBarLine:o,leftDelim:h,rightDelim:c,size:p,barSize:null}},htmlBuilder:Rt,mathmlBuilder:It});B({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=e[1];return{type:"genfrac",mode:t.mode,continued:!0,numer:n,denom:s,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}}});B({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(r){var{parser:e,funcName:t,token:a}=r,n;switch(t){case"\\over":n="\\frac";break;case"\\choose":n="\\binom";break;case"\\atop":n="\\\\atopfrac";break;case"\\brace":n="\\\\bracefrac";break;case"\\brack":n="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:e.mode,replaceWith:n,token:a}}});var lr=["display","text","script","scriptscript"],or=function(e){var t=null;return e.length>0&&(t=e,t=t==="."?null:t),t};B({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(r,e){var{parser:t}=r,a=e[4],n=e[5],s=Fe(e[0]),o=s.type==="atom"&&s.family==="open"?or(s.text):null,h=Fe(e[1]),c=h.type==="atom"&&h.family==="close"?or(h.text):null,p=F(e[2],"size"),g,b=null;p.isBlank?g=!0:(b=p.value,g=b.number>0);var x="auto",w=e[3];if(w.type==="ordgroup"){if(w.body.length>0){var z=F(w.body[0],"textord");x=lr[Number(z.text)]}}else w=F(w,"textord"),x=lr[Number(w.text)];return{type:"genfrac",mode:t.mode,numer:a,denom:n,continued:!1,hasBarLine:g,barSize:b,leftDelim:o,rightDelim:c,size:x}},htmlBuilder:Rt,mathmlBuilder:It});B({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(r,e){var{parser:t,funcName:a,token:n}=r;return{type:"infix",mode:t.mode,replaceWith:"\\\\abovefrac",size:F(e[0],"size").value,token:n}}});B({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=Ba(F(e[1],"infix").size),o=e[2],h=s.number>0;return{type:"genfrac",mode:t.mode,numer:n,denom:o,continued:!1,hasBarLine:h,barSize:s,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Rt,mathmlBuilder:It});var na=(r,e)=>{var t=e.style,a,n;r.type==="supsub"?(a=r.sup?P(r.sup,e.havingStyle(t.sup()),e):P(r.sub,e.havingStyle(t.sub()),e),n=F(r.base,"horizBrace")):n=F(r,"horizBrace");var s=P(n.base,e.havingBaseStyle(R.DISPLAY)),o=E0.svgSpan(n,e),h;if(n.isOver?(h=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:o}]},e),h.children[0].children[0].children[1].classes.push("svg-align")):(h=y.makeVList({positionType:"bottom",positionData:s.depth+.1+o.height,children:[{type:"elem",elem:o},{type:"kern",size:.1},{type:"elem",elem:s}]},e),h.children[0].children[0].children[0].classes.push("svg-align")),a){var c=y.makeSpan(["mord",n.isOver?"mover":"munder"],[h],e);n.isOver?h=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:c},{type:"kern",size:.2},{type:"elem",elem:a}]},e):h=y.makeVList({positionType:"bottom",positionData:c.depth+.2+a.height+a.depth,children:[{type:"elem",elem:a},{type:"kern",size:.2},{type:"elem",elem:c}]},e)}return y.makeSpan(["mord",n.isOver?"mover":"munder"],[h],e)},K1=(r,e)=>{var t=E0.mathMLnode(r.label);return new S.MathNode(r.isOver?"mover":"munder",[Y(r.base,e),t])};B({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r;return{type:"horizBrace",mode:t.mode,label:a,isOver:/^\\over/.test(a),base:e[0]}},htmlBuilder:na,mathmlBuilder:K1});B({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[1],n=F(e[0],"url").url;return t.settings.isTrusted({command:"\\href",url:n})?{type:"href",mode:t.mode,href:n,body:Q(a)}:t.formatUnsupportedCmd("\\href")},htmlBuilder:(r,e)=>{var t=t0(r.body,e,!1);return y.makeAnchor(r.href,[],t,e)},mathmlBuilder:(r,e)=>{var t=G0(r.body,e);return t instanceof o0||(t=new o0("mrow",[t])),t.setAttribute("href",r.href),t}});B({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=F(e[0],"url").url;if(!t.settings.isTrusted({command:"\\url",url:a}))return t.formatUnsupportedCmd("\\url");for(var n=[],s=0;s{var{parser:t,funcName:a,token:n}=r,s=F(e[0],"raw").string,o=e[1];t.settings.strict&&t.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");var h,c={};switch(a){case"\\htmlClass":c.class=s,h={command:"\\htmlClass",class:s};break;case"\\htmlId":c.id=s,h={command:"\\htmlId",id:s};break;case"\\htmlStyle":c.style=s,h={command:"\\htmlStyle",style:s};break;case"\\htmlData":{for(var p=s.split(","),g=0;g{var t=t0(r.body,e,!1),a=["enclosing"];r.attributes.class&&a.push(...r.attributes.class.trim().split(/\s+/));var n=y.makeSpan(a,t,e);for(var s in r.attributes)s!=="class"&&r.attributes.hasOwnProperty(s)&&n.setAttribute(s,r.attributes[s]);return n},mathmlBuilder:(r,e)=>G0(r.body,e)});B({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r;return{type:"htmlmathml",mode:t.mode,html:Q(e[0]),mathml:Q(e[1])}},htmlBuilder:(r,e)=>{var t=t0(r.html,e,!1);return y.makeFragment(t)},mathmlBuilder:(r,e)=>G0(r.mathml,e)});var st=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};var t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new M("Invalid size: '"+e+"' in \\includegraphics");var a={number:+(t[1]+t[2]),unit:t[3]};if(!Sr(a))throw new M("Invalid unit: '"+a.unit+"' in \\includegraphics.");return a};B({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(r,e,t)=>{var{parser:a}=r,n={number:0,unit:"em"},s={number:.9,unit:"em"},o={number:0,unit:"em"},h="";if(t[0])for(var c=F(t[0],"raw").string,p=c.split(","),g=0;g{var t=J(r.height,e),a=0;r.totalheight.number>0&&(a=J(r.totalheight,e)-t);var n=0;r.width.number>0&&(n=J(r.width,e));var s={height:A(t+a)};n>0&&(s.width=A(n)),a>0&&(s.verticalAlign=A(-a));var o=new ct(r.src,r.alt,s);return o.height=t,o.depth=a,o},mathmlBuilder:(r,e)=>{var t=new S.MathNode("mglyph",[]);t.setAttribute("alt",r.alt);var a=J(r.height,e),n=0;if(r.totalheight.number>0&&(n=J(r.totalheight,e)-a,t.setAttribute("valign",A(-n))),t.setAttribute("height",A(a+n)),r.width.number>0){var s=J(r.width,e);t.setAttribute("width",A(s))}return t.setAttribute("src",r.src),t}});B({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(r,e){var{parser:t,funcName:a}=r,n=F(e[0],"size");if(t.settings.strict){var s=a[1]==="m",o=n.value.unit==="mu";s?(o||t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" supports only mu units, "+("not "+n.value.unit+" units")),t.mode!=="math"&&t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" works only in math mode")):o&&t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" doesn't support mu units")}return{type:"kern",mode:t.mode,dimension:n.value}},htmlBuilder(r,e){return y.makeGlue(r.dimension,e)},mathmlBuilder(r,e){var t=J(r.dimension,e);return new S.SpaceNode(t)}});B({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"lap",mode:t.mode,alignment:a.slice(5),body:n}},htmlBuilder:(r,e)=>{var t;r.alignment==="clap"?(t=y.makeSpan([],[P(r.body,e)]),t=y.makeSpan(["inner"],[t],e)):t=y.makeSpan(["inner"],[P(r.body,e)]);var a=y.makeSpan(["fix"],[]),n=y.makeSpan([r.alignment],[t,a],e),s=y.makeSpan(["strut"]);return s.style.height=A(n.height+n.depth),n.depth&&(s.style.verticalAlign=A(-n.depth)),n.children.unshift(s),n=y.makeSpan(["thinbox"],[n],e),y.makeSpan(["mord","vbox"],[n],e)},mathmlBuilder:(r,e)=>{var t=new S.MathNode("mpadded",[Y(r.body,e)]);if(r.alignment!=="rlap"){var a=r.alignment==="llap"?"-1":"-0.5";t.setAttribute("lspace",a+"width")}return t.setAttribute("width","0px"),t}});B({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(r,e){var{funcName:t,parser:a}=r,n=a.mode;a.switchMode("math");var s=t==="\\("?"\\)":"$",o=a.parseExpression(!1,s);return a.expect(s),a.switchMode(n),{type:"styling",mode:a.mode,style:"text",body:o}}});B({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(r,e){throw new M("Mismatched "+r.funcName)}});var ur=(r,e)=>{switch(e.style.size){case R.DISPLAY.size:return r.display;case R.TEXT.size:return r.text;case R.SCRIPT.size:return r.script;case R.SCRIPTSCRIPT.size:return r.scriptscript;default:return r.text}};B({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(r,e)=>{var{parser:t}=r;return{type:"mathchoice",mode:t.mode,display:Q(e[0]),text:Q(e[1]),script:Q(e[2]),scriptscript:Q(e[3])}},htmlBuilder:(r,e)=>{var t=ur(r,e),a=t0(t,e,!1);return y.makeFragment(a)},mathmlBuilder:(r,e)=>{var t=ur(r,e);return G0(t,e)}});var ia=(r,e,t,a,n,s,o)=>{r=y.makeSpan([],[r]);var h=t&&N.isCharacterBox(t),c,p;if(e){var g=P(e,a.havingStyle(n.sup()),a);p={elem:g,kern:Math.max(a.fontMetrics().bigOpSpacing1,a.fontMetrics().bigOpSpacing3-g.depth)}}if(t){var b=P(t,a.havingStyle(n.sub()),a);c={elem:b,kern:Math.max(a.fontMetrics().bigOpSpacing2,a.fontMetrics().bigOpSpacing4-b.height)}}var x;if(p&&c){var w=a.fontMetrics().bigOpSpacing5+c.elem.height+c.elem.depth+c.kern+r.depth+o;x=y.makeVList({positionType:"bottom",positionData:w,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:c.elem,marginLeft:A(-s)},{type:"kern",size:c.kern},{type:"elem",elem:r},{type:"kern",size:p.kern},{type:"elem",elem:p.elem,marginLeft:A(s)},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else if(c){var z=r.height-o;x=y.makeVList({positionType:"top",positionData:z,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:c.elem,marginLeft:A(-s)},{type:"kern",size:c.kern},{type:"elem",elem:r}]},a)}else if(p){var T=r.depth+o;x=y.makeVList({positionType:"bottom",positionData:T,children:[{type:"elem",elem:r},{type:"kern",size:p.kern},{type:"elem",elem:p.elem,marginLeft:A(s)},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else return r;var C=[x];if(c&&s!==0&&!h){var q=y.makeSpan(["mspace"],[],a);q.style.marginRight=A(s),C.unshift(q)}return y.makeSpan(["mop","op-limits"],C,a)},sa=["\\smallint"],ne=(r,e)=>{var t,a,n=!1,s;r.type==="supsub"?(t=r.sup,a=r.sub,s=F(r.base,"op"),n=!0):s=F(r,"op");var o=e.style,h=!1;o.size===R.DISPLAY.size&&s.symbol&&!N.contains(sa,s.name)&&(h=!0);var c;if(s.symbol){var p=h?"Size2-Regular":"Size1-Regular",g="";if((s.name==="\\oiint"||s.name==="\\oiiint")&&(g=s.name.slice(1),s.name=g==="oiint"?"\\iint":"\\iiint"),c=y.makeSymbol(s.name,p,"math",e,["mop","op-symbol",h?"large-op":"small-op"]),g.length>0){var b=c.italic,x=y.staticSvg(g+"Size"+(h?"2":"1"),e);c=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:c,shift:0},{type:"elem",elem:x,shift:h?.08:0}]},e),s.name="\\"+g,c.classes.unshift("mop"),c.italic=b}}else if(s.body){var w=t0(s.body,e,!0);w.length===1&&w[0]instanceof u0?(c=w[0],c.classes[0]="mop"):c=y.makeSpan(["mop"],w,e)}else{for(var z=[],T=1;T{var t;if(r.symbol)t=new o0("mo",[v0(r.name,r.mode)]),N.contains(sa,r.name)&&t.setAttribute("largeop","false");else if(r.body)t=new o0("mo",h0(r.body,e));else{t=new o0("mi",[new Y0(r.name.slice(1))]);var a=new o0("mo",[v0("\u2061","text")]);r.parentIsSupSub?t=new o0("mrow",[t,a]):t=Rr([t,a])}return t},J1={"\u220F":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22C0":"\\bigwedge","\u22C1":"\\bigvee","\u22C2":"\\bigcap","\u22C3":"\\bigcup","\u2A00":"\\bigodot","\u2A01":"\\bigoplus","\u2A02":"\\bigotimes","\u2A04":"\\biguplus","\u2A06":"\\bigsqcup"};B({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220F","\u2210","\u2211","\u22C0","\u22C1","\u22C2","\u22C3","\u2A00","\u2A01","\u2A02","\u2A04","\u2A06"],props:{numArgs:0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=a;return n.length===1&&(n=J1[n]),{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:ne,mathmlBuilder:pe});B({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:Q(a)}},htmlBuilder:ne,mathmlBuilder:pe});var Q1={"\u222B":"\\int","\u222C":"\\iint","\u222D":"\\iiint","\u222E":"\\oint","\u222F":"\\oiint","\u2230":"\\oiiint"};B({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:t}},htmlBuilder:ne,mathmlBuilder:pe});B({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:t}},htmlBuilder:ne,mathmlBuilder:pe});B({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222B","\u222C","\u222D","\u222E","\u222F","\u2230"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r,a=t;return a.length===1&&(a=Q1[a]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:a}},htmlBuilder:ne,mathmlBuilder:pe});var la=(r,e)=>{var t,a,n=!1,s;r.type==="supsub"?(t=r.sup,a=r.sub,s=F(r.base,"operatorname"),n=!0):s=F(r,"operatorname");var o;if(s.body.length>0){for(var h=s.body.map(b=>{var x=b.text;return typeof x=="string"?{type:"textord",mode:b.mode,text:x}:b}),c=t0(h,e.withFont("mathrm"),!0),p=0;p{for(var t=h0(r.body,e.withFont("mathrm")),a=!0,n=0;ng.toText()).join("");t=[new S.TextNode(h)]}var c=new S.MathNode("mi",t);c.setAttribute("mathvariant","normal");var p=new S.MathNode("mo",[v0("\u2061","text")]);return r.parentIsSupSub?new S.MathNode("mrow",[c,p]):S.newDocumentFragment([c,p])};B({type:"operatorname",names:["\\operatorname@","\\operatornamewithlimits"],props:{numArgs:1},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"operatorname",mode:t.mode,body:Q(n),alwaysHandleSupSub:a==="\\operatornamewithlimits",limits:!1,parentIsSupSub:!1}},htmlBuilder:la,mathmlBuilder:_1});m("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@");j0({type:"ordgroup",htmlBuilder(r,e){return r.semisimple?y.makeFragment(t0(r.body,e,!1)):y.makeSpan(["mord"],t0(r.body,e,!0),e)},mathmlBuilder(r,e){return G0(r.body,e,!0)}});B({type:"overline",names:["\\overline"],props:{numArgs:1},handler(r,e){var{parser:t}=r,a=e[0];return{type:"overline",mode:t.mode,body:a}},htmlBuilder(r,e){var t=P(r.body,e.havingCrampedStyle()),a=y.makeLineSpan("overline-line",e),n=e.fontMetrics().defaultRuleThickness,s=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:t},{type:"kern",size:3*n},{type:"elem",elem:a},{type:"kern",size:n}]},e);return y.makeSpan(["mord","overline"],[s],e)},mathmlBuilder(r,e){var t=new S.MathNode("mo",[new S.TextNode("\u203E")]);t.setAttribute("stretchy","true");var a=new S.MathNode("mover",[Y(r.body,e),t]);return a.setAttribute("accent","true"),a}});B({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"phantom",mode:t.mode,body:Q(a)}},htmlBuilder:(r,e)=>{var t=t0(r.body,e.withPhantom(),!1);return y.makeFragment(t)},mathmlBuilder:(r,e)=>{var t=h0(r.body,e);return new S.MathNode("mphantom",t)}});B({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"hphantom",mode:t.mode,body:a}},htmlBuilder:(r,e)=>{var t=y.makeSpan([],[P(r.body,e.withPhantom())]);if(t.height=0,t.depth=0,t.children)for(var a=0;a{var t=h0(Q(r.body),e),a=new S.MathNode("mphantom",t),n=new S.MathNode("mpadded",[a]);return n.setAttribute("height","0px"),n.setAttribute("depth","0px"),n}});B({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"vphantom",mode:t.mode,body:a}},htmlBuilder:(r,e)=>{var t=y.makeSpan(["inner"],[P(r.body,e.withPhantom())]),a=y.makeSpan(["fix"],[]);return y.makeSpan(["mord","rlap"],[t,a],e)},mathmlBuilder:(r,e)=>{var t=h0(Q(r.body),e),a=new S.MathNode("mphantom",t),n=new S.MathNode("mpadded",[a]);return n.setAttribute("width","0px"),n}});B({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(r,e){var{parser:t}=r,a=F(e[0],"size").value,n=e[1];return{type:"raisebox",mode:t.mode,dy:a,body:n}},htmlBuilder(r,e){var t=P(r.body,e),a=J(r.dy,e);return y.makeVList({positionType:"shift",positionData:-a,children:[{type:"elem",elem:t}]},e)},mathmlBuilder(r,e){var t=new S.MathNode("mpadded",[Y(r.body,e)]),a=r.dy.number+r.dy.unit;return t.setAttribute("voffset",a),t}});B({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0},handler(r){var{parser:e}=r;return{type:"internal",mode:e.mode}}});B({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler(r,e,t){var{parser:a}=r,n=t[0],s=F(e[0],"size"),o=F(e[1],"size");return{type:"rule",mode:a.mode,shift:n&&F(n,"size").value,width:s.value,height:o.value}},htmlBuilder(r,e){var t=y.makeSpan(["mord","rule"],[],e),a=J(r.width,e),n=J(r.height,e),s=r.shift?J(r.shift,e):0;return t.style.borderRightWidth=A(a),t.style.borderTopWidth=A(n),t.style.bottom=A(s),t.width=a,t.height=n+s,t.depth=-s,t.maxFontSize=n*1.125*e.sizeMultiplier,t},mathmlBuilder(r,e){var t=J(r.width,e),a=J(r.height,e),n=r.shift?J(r.shift,e):0,s=e.color&&e.getColor()||"black",o=new S.MathNode("mspace");o.setAttribute("mathbackground",s),o.setAttribute("width",A(t)),o.setAttribute("height",A(a));var h=new S.MathNode("mpadded",[o]);return n>=0?h.setAttribute("height",A(n)):(h.setAttribute("height",A(n)),h.setAttribute("depth",A(-n))),h.setAttribute("voffset",A(n)),h}});function oa(r,e,t){for(var a=t0(r,e,!1),n=e.sizeMultiplier/t.sizeMultiplier,s=0;s{var t=e.havingSize(r.size);return oa(r.body,t,e)};B({type:"sizing",names:hr,props:{numArgs:0,allowedInText:!0},handler:(r,e)=>{var{breakOnTokenText:t,funcName:a,parser:n}=r,s=n.parseExpression(!1,t);return{type:"sizing",mode:n.mode,size:hr.indexOf(a)+1,body:s}},htmlBuilder:e4,mathmlBuilder:(r,e)=>{var t=e.havingSize(r.size),a=h0(r.body,t),n=new S.MathNode("mstyle",a);return n.setAttribute("mathsize",A(t.sizeMultiplier)),n}});B({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(r,e,t)=>{var{parser:a}=r,n=!1,s=!1,o=t[0]&&F(t[0],"ordgroup");if(o)for(var h="",c=0;c{var t=y.makeSpan([],[P(r.body,e)]);if(!r.smashHeight&&!r.smashDepth)return t;if(r.smashHeight&&(t.height=0,t.children))for(var a=0;a{var t=new S.MathNode("mpadded",[Y(r.body,e)]);return r.smashHeight&&t.setAttribute("height","0px"),r.smashDepth&&t.setAttribute("depth","0px"),t}});B({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(r,e,t){var{parser:a}=r,n=t[0],s=e[0];return{type:"sqrt",mode:a.mode,body:s,index:n}},htmlBuilder(r,e){var t=P(r.body,e.havingCrampedStyle());t.height===0&&(t.height=e.fontMetrics().xHeight),t=y.wrapFragment(t,e);var a=e.fontMetrics(),n=a.defaultRuleThickness,s=n;e.style.idt.height+t.depth+o&&(o=(o+b-t.height-t.depth)/2);var x=c.height-t.height-o-p;t.style.paddingLeft=A(g);var w=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:t,wrapperClasses:["svg-align"]},{type:"kern",size:-(t.height+x)},{type:"elem",elem:c},{type:"kern",size:p}]},e);if(r.index){var z=e.havingStyle(R.SCRIPTSCRIPT),T=P(r.index,z,e),C=.6*(w.height-w.depth),q=y.makeVList({positionType:"shift",positionData:-C,children:[{type:"elem",elem:T}]},e),O=y.makeSpan(["root"],[q]);return y.makeSpan(["mord","sqrt"],[O,w],e)}else return y.makeSpan(["mord","sqrt"],[w],e)},mathmlBuilder(r,e){var{body:t,index:a}=r;return a?new S.MathNode("mroot",[Y(t,e),Y(a,e)]):new S.MathNode("msqrt",[Y(t,e)])}});var mr={display:R.DISPLAY,text:R.TEXT,script:R.SCRIPT,scriptscript:R.SCRIPTSCRIPT};B({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r,e){var{breakOnTokenText:t,funcName:a,parser:n}=r,s=n.parseExpression(!0,t),o=a.slice(1,a.length-5);return{type:"styling",mode:n.mode,style:o,body:s}},htmlBuilder(r,e){var t=mr[r.style],a=e.havingStyle(t).withFont("");return oa(r.body,a,e)},mathmlBuilder(r,e){var t=mr[r.style],a=e.havingStyle(t),n=h0(r.body,a),s=new S.MathNode("mstyle",n),o={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]},h=o[r.style];return s.setAttribute("scriptlevel",h[0]),s.setAttribute("displaystyle",h[1]),s}});var t4=function(e,t){var a=e.base;if(a)if(a.type==="op"){var n=a.limits&&(t.style.size===R.DISPLAY.size||a.alwaysHandleSupSub);return n?ne:null}else if(a.type==="operatorname"){var s=a.alwaysHandleSupSub&&(t.style.size===R.DISPLAY.size||a.limits);return s?la:null}else{if(a.type==="accent")return N.isCharacterBox(a.base)?Bt:null;if(a.type==="horizBrace"){var o=!e.sub;return o===a.isOver?na:null}else return null}else return null};j0({type:"supsub",htmlBuilder(r,e){var t=t4(r,e);if(t)return t(r,e);var{base:a,sup:n,sub:s}=r,o=P(a,e),h,c,p=e.fontMetrics(),g=0,b=0,x=a&&N.isCharacterBox(a);if(n){var w=e.havingStyle(e.style.sup());h=P(n,w,e),x||(g=o.height-w.fontMetrics().supDrop*w.sizeMultiplier/e.sizeMultiplier)}if(s){var z=e.havingStyle(e.style.sub());c=P(s,z,e),x||(b=o.depth+z.fontMetrics().subDrop*z.sizeMultiplier/e.sizeMultiplier)}var T;e.style===R.DISPLAY?T=p.sup1:e.style.cramped?T=p.sup3:T=p.sup2;var C=e.sizeMultiplier,q=A(.5/p.ptPerEm/C),O=null;if(c){var H=r.base&&r.base.type==="op"&&r.base.name&&(r.base.name==="\\oiint"||r.base.name==="\\oiiint");(o instanceof u0||H)&&(O=A(-o.italic))}var V;if(h&&c){g=Math.max(g,T,h.depth+.25*p.xHeight),b=Math.max(b,p.sub2);var L=p.defaultRuleThickness,U=4*L;if(g-h.depth-(c.height-b)0&&(g+=G,b-=G)}var j=[{type:"elem",elem:c,shift:b,marginRight:q,marginLeft:O},{type:"elem",elem:h,shift:-g,marginRight:q}];V=y.makeVList({positionType:"individualShift",children:j},e)}else if(c){b=Math.max(b,p.sub1,c.height-.8*p.xHeight);var $=[{type:"elem",elem:c,marginLeft:O,marginRight:q}];V=y.makeVList({positionType:"shift",positionData:b,children:$},e)}else if(h)g=Math.max(g,T,h.depth+.25*p.xHeight),V=y.makeVList({positionType:"shift",positionData:-g,children:[{type:"elem",elem:h,marginRight:q}]},e);else throw new Error("supsub must have either sup or sub.");var T0=ft(o,"right")||"mord";return y.makeSpan([T0],[o,y.makeSpan(["msupsub"],[V])],e)},mathmlBuilder(r,e){var t=!1,a,n;r.base&&r.base.type==="horizBrace"&&(n=!!r.sup,n===r.base.isOver&&(t=!0,a=r.base.isOver)),r.base&&(r.base.type==="op"||r.base.type==="operatorname")&&(r.base.parentIsSupSub=!0);var s=[Y(r.base,e)];r.sub&&s.push(Y(r.sub,e)),r.sup&&s.push(Y(r.sup,e));var o;if(t)o=a?"mover":"munder";else if(r.sub)if(r.sup){var p=r.base;p&&p.type==="op"&&p.limits&&e.style===R.DISPLAY||p&&p.type==="operatorname"&&p.alwaysHandleSupSub&&(e.style===R.DISPLAY||p.limits)?o="munderover":o="msubsup"}else{var c=r.base;c&&c.type==="op"&&c.limits&&(e.style===R.DISPLAY||c.alwaysHandleSupSub)||c&&c.type==="operatorname"&&c.alwaysHandleSupSub&&(c.limits||e.style===R.DISPLAY)?o="munder":o="msub"}else{var h=r.base;h&&h.type==="op"&&h.limits&&(e.style===R.DISPLAY||h.alwaysHandleSupSub)||h&&h.type==="operatorname"&&h.alwaysHandleSupSub&&(h.limits||e.style===R.DISPLAY)?o="mover":o="msup"}return new S.MathNode(o,s)}});j0({type:"atom",htmlBuilder(r,e){return y.mathsym(r.text,r.mode,e,["m"+r.family])},mathmlBuilder(r,e){var t=new S.MathNode("mo",[v0(r.text,r.mode)]);if(r.family==="bin"){var a=At(r,e);a==="bold-italic"&&t.setAttribute("mathvariant",a)}else r.family==="punct"?t.setAttribute("separator","true"):(r.family==="open"||r.family==="close")&&t.setAttribute("stretchy","false");return t}});var ua={mi:"italic",mn:"normal",mtext:"normal"};j0({type:"mathord",htmlBuilder(r,e){return y.makeOrd(r,e,"mathord")},mathmlBuilder(r,e){var t=new S.MathNode("mi",[v0(r.text,r.mode,e)]),a=At(r,e)||"italic";return a!==ua[t.type]&&t.setAttribute("mathvariant",a),t}});j0({type:"textord",htmlBuilder(r,e){return y.makeOrd(r,e,"textord")},mathmlBuilder(r,e){var t=v0(r.text,r.mode,e),a=At(r,e)||"normal",n;return r.mode==="text"?n=new S.MathNode("mtext",[t]):/[0-9]/.test(r.text)?n=new S.MathNode("mn",[t]):r.text==="\\prime"?n=new S.MathNode("mo",[t]):n=new S.MathNode("mi",[t]),a!==ua[n.type]&&n.setAttribute("mathvariant",a),n}});var lt={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},ot={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};j0({type:"spacing",htmlBuilder(r,e){if(ot.hasOwnProperty(r.text)){var t=ot[r.text].className||"";if(r.mode==="text"){var a=y.makeOrd(r,e,"textord");return a.classes.push(t),a}else return y.makeSpan(["mspace",t],[y.mathsym(r.text,r.mode,e)],e)}else{if(lt.hasOwnProperty(r.text))return y.makeSpan(["mspace",lt[r.text]],[],e);throw new M('Unknown type of space "'+r.text+'"')}},mathmlBuilder(r,e){var t;if(ot.hasOwnProperty(r.text))t=new S.MathNode("mtext",[new S.TextNode("\xA0")]);else{if(lt.hasOwnProperty(r.text))return new S.MathNode("mspace");throw new M('Unknown type of space "'+r.text+'"')}return t}});var cr=()=>{var r=new S.MathNode("mtd",[]);return r.setAttribute("width","50%"),r};j0({type:"tag",mathmlBuilder(r,e){var t=new S.MathNode("mtable",[new S.MathNode("mtr",[cr(),new S.MathNode("mtd",[G0(r.body,e)]),cr(),new S.MathNode("mtd",[G0(r.tag,e)])])]);return t.setAttribute("width","100%"),t}});var dr={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},fr={"\\textbf":"textbf","\\textmd":"textmd"},r4={"\\textit":"textit","\\textup":"textup"},pr=(r,e)=>{var t=r.font;if(t){if(dr[t])return e.withTextFontFamily(dr[t]);if(fr[t])return e.withTextFontWeight(fr[t]);if(t==="\\emph")return e.fontShape==="textit"?e.withTextFontShape("textup"):e.withTextFontShape("textit")}else return e;return e.withTextFontShape(r4[t])};B({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"text",mode:t.mode,body:Q(n),font:a}},htmlBuilder(r,e){var t=pr(r,e),a=t0(r.body,t,!0);return y.makeSpan(["mord","text"],a,t)},mathmlBuilder(r,e){var t=pr(r,e);return G0(r.body,t)}});B({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"underline",mode:t.mode,body:e[0]}},htmlBuilder(r,e){var t=P(r.body,e),a=y.makeLineSpan("underline-line",e),n=e.fontMetrics().defaultRuleThickness,s=y.makeVList({positionType:"top",positionData:t.height,children:[{type:"kern",size:n},{type:"elem",elem:a},{type:"kern",size:3*n},{type:"elem",elem:t}]},e);return y.makeSpan(["mord","underline"],[s],e)},mathmlBuilder(r,e){var t=new S.MathNode("mo",[new S.TextNode("\u203E")]);t.setAttribute("stretchy","true");var a=new S.MathNode("munder",[Y(r.body,e),t]);return a.setAttribute("accentunder","true"),a}});B({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(r,e){var{parser:t}=r;return{type:"vcenter",mode:t.mode,body:e[0]}},htmlBuilder(r,e){var t=P(r.body,e),a=e.fontMetrics().axisHeight,n=.5*(t.height-a-(t.depth+a));return y.makeVList({positionType:"shift",positionData:n,children:[{type:"elem",elem:t}]},e)},mathmlBuilder(r,e){return new S.MathNode("mpadded",[Y(r.body,e)],["vcenter"])}});B({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(r,e,t){throw new M("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(r,e){for(var t=vr(r),a=[],n=e.havingStyle(e.style.text()),s=0;sr.body.replace(/ /g,r.star?"\u2423":"\xA0"),L0=Nr,ha=`[ \r - ]`,a4="\\\\[a-zA-Z@]+",n4="\\\\[^\uD800-\uDFFF]",i4="("+a4+")"+ha+"*",s4=`\\\\( +-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z`;default:throw new Error("Unknown stretchy delimiter.")}},Y0=class{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return O.contains(this.classes,e)}toNode(){for(var e=document.createDocumentFragment(),t=0;tt.toText();return this.children.map(e).join("")}},z0={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},ke={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},Zt={\u00C5:"A",\u00D0:"D",\u00DE:"o",\u00E5:"a",\u00F0:"d",\u00FE:"o",\u0410:"A",\u0411:"B",\u0412:"B",\u0413:"F",\u0414:"A",\u0415:"E",\u0416:"K",\u0417:"3",\u0418:"N",\u0419:"N",\u041A:"K",\u041B:"N",\u041C:"M",\u041D:"H",\u041E:"O",\u041F:"N",\u0420:"P",\u0421:"C",\u0422:"T",\u0423:"y",\u0424:"O",\u0425:"X",\u0426:"U",\u0427:"h",\u0428:"W",\u0429:"W",\u042A:"B",\u042B:"X",\u042C:"B",\u042D:"3",\u042E:"X",\u042F:"R",\u0430:"a",\u0431:"b",\u0432:"a",\u0433:"r",\u0434:"y",\u0435:"e",\u0436:"m",\u0437:"e",\u0438:"n",\u0439:"n",\u043A:"n",\u043B:"n",\u043C:"m",\u043D:"n",\u043E:"o",\u043F:"n",\u0440:"p",\u0441:"c",\u0442:"o",\u0443:"y",\u0444:"b",\u0445:"x",\u0446:"n",\u0447:"n",\u0448:"w",\u0449:"w",\u044A:"a",\u044B:"m",\u044C:"a",\u044D:"e",\u044E:"m",\u044F:"r"};function Ja(r,e){z0[r]=e}function Tt(r,e,t){if(!z0[e])throw new Error("Font metrics not found for font: "+e+".");var a=r.charCodeAt(0),n=z0[e][a];if(!n&&r[0]in Zt&&(a=Zt[r[0]].charCodeAt(0),n=z0[e][a]),!n&&t==="text"&&Ar(a)&&(n=z0[e][77]),n)return{depth:n[0],height:n[1],italic:n[2],skew:n[3],width:n[4]}}var tt={};function Qa(r){var e;if(r>=5?e=0:r>=3?e=1:e=2,!tt[e]){var t=tt[e]={cssEmPerMu:ke.quad[e]/18};for(var a in ke)ke.hasOwnProperty(a)&&(t[a]=ke[a][e])}return tt[e]}var e1=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],jt=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],Kt=function(e,t){return t.size<2?e:e1[e-1][t.size-1]},Re=class r{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||r.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=jt[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){var t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(var a in e)e.hasOwnProperty(a)&&(t[a]=e[a]);return new r(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:Kt(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:jt[e-1]})}havingBaseStyle(e){e=e||this.style.text();var t=Kt(r.BASESIZE,e);return this.size===t&&this.textSize===r.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){var e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==r.BASESIZE?["sizing","reset-size"+this.size,"size"+r.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=Qa(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}};Re.BASESIZE=6;var ft={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:803/800,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:803/800},t1={ex:!0,em:!0,mu:!0},Tr=function(e){return typeof e!="string"&&(e=e.unit),e in ft||e in t1||e==="ex"},Q=function(e,t){var a;if(e.unit in ft)a=ft[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if(e.unit==="mu")a=t.fontMetrics().cssEmPerMu;else{var n;if(t.style.isTight()?n=t.havingStyle(t.style.text()):n=t,e.unit==="ex")a=n.fontMetrics().xHeight;else if(e.unit==="em")a=n.fontMetrics().quad;else throw new z("Invalid unit: '"+e.unit+"'");n!==t&&(a*=n.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*a,t.maxSize)},T=function(e){return+e.toFixed(4)+"em"},G0=function(e){return e.filter(t=>t).join(" ")},qr=function(e,t,a){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=a||{},t){t.style.isTight()&&this.classes.push("mtight");var n=t.getColor();n&&(this.style.color=n)}},Br=function(e){var t=document.createElement(e);t.className=G0(this.classes);for(var a in this.style)this.style.hasOwnProperty(a)&&(t.style[a]=this.style[a]);for(var n in this.attributes)this.attributes.hasOwnProperty(n)&&t.setAttribute(n,this.attributes[n]);for(var s=0;s/=\x00-\x1f]/,Dr=function(e){var t="<"+e;this.classes.length&&(t+=' class="'+O.escape(G0(this.classes))+'"');var a="";for(var n in this.style)this.style.hasOwnProperty(n)&&(a+=O.hyphenate(n)+":"+this.style[n]+";");a&&(t+=' style="'+O.escape(a)+'"');for(var s in this.attributes)if(this.attributes.hasOwnProperty(s)){if(r1.test(s))throw new z("Invalid attribute name '"+s+"'");t+=" "+s+'="'+O.escape(this.attributes[s])+'"'}t+=">";for(var l=0;l",t},Z0=class{constructor(e,t,a,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,qr.call(this,e,a,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return O.contains(this.classes,e)}toNode(){return Br.call(this,"span")}toMarkup(){return Dr.call(this,"span")}},fe=class{constructor(e,t,a,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,qr.call(this,t,n),this.children=a||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return O.contains(this.classes,e)}toNode(){return Br.call(this,"a")}toMarkup(){return Dr.call(this,"a")}},vt=class{constructor(e,t,a){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.style=a}hasClass(e){return O.contains(this.classes,e)}toNode(){var e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(var t in this.style)this.style.hasOwnProperty(t)&&(e.style[t]=this.style[t]);return e}toMarkup(){var e=''+O.escape(this.alt)+'0&&(t=document.createElement("span"),t.style.marginRight=T(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=G0(this.classes));for(var a in this.style)this.style.hasOwnProperty(a)&&(t=t||document.createElement("span"),t.style[a]=this.style[a]);return t?(t.appendChild(e),t):e}toMarkup(){var e=!1,t="0&&(a+="margin-right:"+this.italic+"em;");for(var n in this.style)this.style.hasOwnProperty(n)&&(a+=O.hyphenate(n)+":"+this.style[n]+";");a&&(e=!0,t+=' style="'+O.escape(a)+'"');var s=O.escape(this.text);return e?(t+=">",t+=s,t+="",t):s}},S0=class{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"svg");for(var a in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,a)&&t.setAttribute(a,this.attributes[a]);for(var n=0;n':''}},ve=class{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"line");for(var a in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,a)&&t.setAttribute(a,this.attributes[a]);return t}toMarkup(){var e=" but got "+String(r)+".")}var i1={bin:1,close:1,inner:1,open:1,punct:1,rel:1},s1={"accent-token":1,mathord:1,"op-token":1,spacing:1,textord:1},Y={math:{},text:{}};function i(r,e,t,a,n,s){Y[r][n]={font:e,group:t,replace:a},s&&a&&(Y[r][a]=Y[r][n])}var o="math",k="text",u="main",d="ams",Z="accent-token",C="bin",l0="close",ie="inner",I="mathord",t0="op-token",p0="open",Ve="punct",p="rel",R0="spacing",g="textord";i(o,u,p,"\u2261","\\equiv",!0);i(o,u,p,"\u227A","\\prec",!0);i(o,u,p,"\u227B","\\succ",!0);i(o,u,p,"\u223C","\\sim",!0);i(o,u,p,"\u22A5","\\perp");i(o,u,p,"\u2AAF","\\preceq",!0);i(o,u,p,"\u2AB0","\\succeq",!0);i(o,u,p,"\u2243","\\simeq",!0);i(o,u,p,"\u2223","\\mid",!0);i(o,u,p,"\u226A","\\ll",!0);i(o,u,p,"\u226B","\\gg",!0);i(o,u,p,"\u224D","\\asymp",!0);i(o,u,p,"\u2225","\\parallel");i(o,u,p,"\u22C8","\\bowtie",!0);i(o,u,p,"\u2323","\\smile",!0);i(o,u,p,"\u2291","\\sqsubseteq",!0);i(o,u,p,"\u2292","\\sqsupseteq",!0);i(o,u,p,"\u2250","\\doteq",!0);i(o,u,p,"\u2322","\\frown",!0);i(o,u,p,"\u220B","\\ni",!0);i(o,u,p,"\u221D","\\propto",!0);i(o,u,p,"\u22A2","\\vdash",!0);i(o,u,p,"\u22A3","\\dashv",!0);i(o,u,p,"\u220B","\\owns");i(o,u,Ve,".","\\ldotp");i(o,u,Ve,"\u22C5","\\cdotp");i(o,u,g,"#","\\#");i(k,u,g,"#","\\#");i(o,u,g,"&","\\&");i(k,u,g,"&","\\&");i(o,u,g,"\u2135","\\aleph",!0);i(o,u,g,"\u2200","\\forall",!0);i(o,u,g,"\u210F","\\hbar",!0);i(o,u,g,"\u2203","\\exists",!0);i(o,u,g,"\u2207","\\nabla",!0);i(o,u,g,"\u266D","\\flat",!0);i(o,u,g,"\u2113","\\ell",!0);i(o,u,g,"\u266E","\\natural",!0);i(o,u,g,"\u2663","\\clubsuit",!0);i(o,u,g,"\u2118","\\wp",!0);i(o,u,g,"\u266F","\\sharp",!0);i(o,u,g,"\u2662","\\diamondsuit",!0);i(o,u,g,"\u211C","\\Re",!0);i(o,u,g,"\u2661","\\heartsuit",!0);i(o,u,g,"\u2111","\\Im",!0);i(o,u,g,"\u2660","\\spadesuit",!0);i(o,u,g,"\xA7","\\S",!0);i(k,u,g,"\xA7","\\S");i(o,u,g,"\xB6","\\P",!0);i(k,u,g,"\xB6","\\P");i(o,u,g,"\u2020","\\dag");i(k,u,g,"\u2020","\\dag");i(k,u,g,"\u2020","\\textdagger");i(o,u,g,"\u2021","\\ddag");i(k,u,g,"\u2021","\\ddag");i(k,u,g,"\u2021","\\textdaggerdbl");i(o,u,l0,"\u23B1","\\rmoustache",!0);i(o,u,p0,"\u23B0","\\lmoustache",!0);i(o,u,l0,"\u27EF","\\rgroup",!0);i(o,u,p0,"\u27EE","\\lgroup",!0);i(o,u,C,"\u2213","\\mp",!0);i(o,u,C,"\u2296","\\ominus",!0);i(o,u,C,"\u228E","\\uplus",!0);i(o,u,C,"\u2293","\\sqcap",!0);i(o,u,C,"\u2217","\\ast");i(o,u,C,"\u2294","\\sqcup",!0);i(o,u,C,"\u25EF","\\bigcirc",!0);i(o,u,C,"\u2219","\\bullet",!0);i(o,u,C,"\u2021","\\ddagger");i(o,u,C,"\u2240","\\wr",!0);i(o,u,C,"\u2A3F","\\amalg");i(o,u,C,"&","\\And");i(o,u,p,"\u27F5","\\longleftarrow",!0);i(o,u,p,"\u21D0","\\Leftarrow",!0);i(o,u,p,"\u27F8","\\Longleftarrow",!0);i(o,u,p,"\u27F6","\\longrightarrow",!0);i(o,u,p,"\u21D2","\\Rightarrow",!0);i(o,u,p,"\u27F9","\\Longrightarrow",!0);i(o,u,p,"\u2194","\\leftrightarrow",!0);i(o,u,p,"\u27F7","\\longleftrightarrow",!0);i(o,u,p,"\u21D4","\\Leftrightarrow",!0);i(o,u,p,"\u27FA","\\Longleftrightarrow",!0);i(o,u,p,"\u21A6","\\mapsto",!0);i(o,u,p,"\u27FC","\\longmapsto",!0);i(o,u,p,"\u2197","\\nearrow",!0);i(o,u,p,"\u21A9","\\hookleftarrow",!0);i(o,u,p,"\u21AA","\\hookrightarrow",!0);i(o,u,p,"\u2198","\\searrow",!0);i(o,u,p,"\u21BC","\\leftharpoonup",!0);i(o,u,p,"\u21C0","\\rightharpoonup",!0);i(o,u,p,"\u2199","\\swarrow",!0);i(o,u,p,"\u21BD","\\leftharpoondown",!0);i(o,u,p,"\u21C1","\\rightharpoondown",!0);i(o,u,p,"\u2196","\\nwarrow",!0);i(o,u,p,"\u21CC","\\rightleftharpoons",!0);i(o,d,p,"\u226E","\\nless",!0);i(o,d,p,"\uE010","\\@nleqslant");i(o,d,p,"\uE011","\\@nleqq");i(o,d,p,"\u2A87","\\lneq",!0);i(o,d,p,"\u2268","\\lneqq",!0);i(o,d,p,"\uE00C","\\@lvertneqq");i(o,d,p,"\u22E6","\\lnsim",!0);i(o,d,p,"\u2A89","\\lnapprox",!0);i(o,d,p,"\u2280","\\nprec",!0);i(o,d,p,"\u22E0","\\npreceq",!0);i(o,d,p,"\u22E8","\\precnsim",!0);i(o,d,p,"\u2AB9","\\precnapprox",!0);i(o,d,p,"\u2241","\\nsim",!0);i(o,d,p,"\uE006","\\@nshortmid");i(o,d,p,"\u2224","\\nmid",!0);i(o,d,p,"\u22AC","\\nvdash",!0);i(o,d,p,"\u22AD","\\nvDash",!0);i(o,d,p,"\u22EA","\\ntriangleleft");i(o,d,p,"\u22EC","\\ntrianglelefteq",!0);i(o,d,p,"\u228A","\\subsetneq",!0);i(o,d,p,"\uE01A","\\@varsubsetneq");i(o,d,p,"\u2ACB","\\subsetneqq",!0);i(o,d,p,"\uE017","\\@varsubsetneqq");i(o,d,p,"\u226F","\\ngtr",!0);i(o,d,p,"\uE00F","\\@ngeqslant");i(o,d,p,"\uE00E","\\@ngeqq");i(o,d,p,"\u2A88","\\gneq",!0);i(o,d,p,"\u2269","\\gneqq",!0);i(o,d,p,"\uE00D","\\@gvertneqq");i(o,d,p,"\u22E7","\\gnsim",!0);i(o,d,p,"\u2A8A","\\gnapprox",!0);i(o,d,p,"\u2281","\\nsucc",!0);i(o,d,p,"\u22E1","\\nsucceq",!0);i(o,d,p,"\u22E9","\\succnsim",!0);i(o,d,p,"\u2ABA","\\succnapprox",!0);i(o,d,p,"\u2246","\\ncong",!0);i(o,d,p,"\uE007","\\@nshortparallel");i(o,d,p,"\u2226","\\nparallel",!0);i(o,d,p,"\u22AF","\\nVDash",!0);i(o,d,p,"\u22EB","\\ntriangleright");i(o,d,p,"\u22ED","\\ntrianglerighteq",!0);i(o,d,p,"\uE018","\\@nsupseteqq");i(o,d,p,"\u228B","\\supsetneq",!0);i(o,d,p,"\uE01B","\\@varsupsetneq");i(o,d,p,"\u2ACC","\\supsetneqq",!0);i(o,d,p,"\uE019","\\@varsupsetneqq");i(o,d,p,"\u22AE","\\nVdash",!0);i(o,d,p,"\u2AB5","\\precneqq",!0);i(o,d,p,"\u2AB6","\\succneqq",!0);i(o,d,p,"\uE016","\\@nsubseteqq");i(o,d,C,"\u22B4","\\unlhd");i(o,d,C,"\u22B5","\\unrhd");i(o,d,p,"\u219A","\\nleftarrow",!0);i(o,d,p,"\u219B","\\nrightarrow",!0);i(o,d,p,"\u21CD","\\nLeftarrow",!0);i(o,d,p,"\u21CF","\\nRightarrow",!0);i(o,d,p,"\u21AE","\\nleftrightarrow",!0);i(o,d,p,"\u21CE","\\nLeftrightarrow",!0);i(o,d,p,"\u25B3","\\vartriangle");i(o,d,g,"\u210F","\\hslash");i(o,d,g,"\u25BD","\\triangledown");i(o,d,g,"\u25CA","\\lozenge");i(o,d,g,"\u24C8","\\circledS");i(o,d,g,"\xAE","\\circledR");i(k,d,g,"\xAE","\\circledR");i(o,d,g,"\u2221","\\measuredangle",!0);i(o,d,g,"\u2204","\\nexists");i(o,d,g,"\u2127","\\mho");i(o,d,g,"\u2132","\\Finv",!0);i(o,d,g,"\u2141","\\Game",!0);i(o,d,g,"\u2035","\\backprime");i(o,d,g,"\u25B2","\\blacktriangle");i(o,d,g,"\u25BC","\\blacktriangledown");i(o,d,g,"\u25A0","\\blacksquare");i(o,d,g,"\u29EB","\\blacklozenge");i(o,d,g,"\u2605","\\bigstar");i(o,d,g,"\u2222","\\sphericalangle",!0);i(o,d,g,"\u2201","\\complement",!0);i(o,d,g,"\xF0","\\eth",!0);i(k,u,g,"\xF0","\xF0");i(o,d,g,"\u2571","\\diagup");i(o,d,g,"\u2572","\\diagdown");i(o,d,g,"\u25A1","\\square");i(o,d,g,"\u25A1","\\Box");i(o,d,g,"\u25CA","\\Diamond");i(o,d,g,"\xA5","\\yen",!0);i(k,d,g,"\xA5","\\yen",!0);i(o,d,g,"\u2713","\\checkmark",!0);i(k,d,g,"\u2713","\\checkmark");i(o,d,g,"\u2136","\\beth",!0);i(o,d,g,"\u2138","\\daleth",!0);i(o,d,g,"\u2137","\\gimel",!0);i(o,d,g,"\u03DD","\\digamma",!0);i(o,d,g,"\u03F0","\\varkappa");i(o,d,p0,"\u250C","\\@ulcorner",!0);i(o,d,l0,"\u2510","\\@urcorner",!0);i(o,d,p0,"\u2514","\\@llcorner",!0);i(o,d,l0,"\u2518","\\@lrcorner",!0);i(o,d,p,"\u2266","\\leqq",!0);i(o,d,p,"\u2A7D","\\leqslant",!0);i(o,d,p,"\u2A95","\\eqslantless",!0);i(o,d,p,"\u2272","\\lesssim",!0);i(o,d,p,"\u2A85","\\lessapprox",!0);i(o,d,p,"\u224A","\\approxeq",!0);i(o,d,C,"\u22D6","\\lessdot");i(o,d,p,"\u22D8","\\lll",!0);i(o,d,p,"\u2276","\\lessgtr",!0);i(o,d,p,"\u22DA","\\lesseqgtr",!0);i(o,d,p,"\u2A8B","\\lesseqqgtr",!0);i(o,d,p,"\u2251","\\doteqdot");i(o,d,p,"\u2253","\\risingdotseq",!0);i(o,d,p,"\u2252","\\fallingdotseq",!0);i(o,d,p,"\u223D","\\backsim",!0);i(o,d,p,"\u22CD","\\backsimeq",!0);i(o,d,p,"\u2AC5","\\subseteqq",!0);i(o,d,p,"\u22D0","\\Subset",!0);i(o,d,p,"\u228F","\\sqsubset",!0);i(o,d,p,"\u227C","\\preccurlyeq",!0);i(o,d,p,"\u22DE","\\curlyeqprec",!0);i(o,d,p,"\u227E","\\precsim",!0);i(o,d,p,"\u2AB7","\\precapprox",!0);i(o,d,p,"\u22B2","\\vartriangleleft");i(o,d,p,"\u22B4","\\trianglelefteq");i(o,d,p,"\u22A8","\\vDash",!0);i(o,d,p,"\u22AA","\\Vvdash",!0);i(o,d,p,"\u2323","\\smallsmile");i(o,d,p,"\u2322","\\smallfrown");i(o,d,p,"\u224F","\\bumpeq",!0);i(o,d,p,"\u224E","\\Bumpeq",!0);i(o,d,p,"\u2267","\\geqq",!0);i(o,d,p,"\u2A7E","\\geqslant",!0);i(o,d,p,"\u2A96","\\eqslantgtr",!0);i(o,d,p,"\u2273","\\gtrsim",!0);i(o,d,p,"\u2A86","\\gtrapprox",!0);i(o,d,C,"\u22D7","\\gtrdot");i(o,d,p,"\u22D9","\\ggg",!0);i(o,d,p,"\u2277","\\gtrless",!0);i(o,d,p,"\u22DB","\\gtreqless",!0);i(o,d,p,"\u2A8C","\\gtreqqless",!0);i(o,d,p,"\u2256","\\eqcirc",!0);i(o,d,p,"\u2257","\\circeq",!0);i(o,d,p,"\u225C","\\triangleq",!0);i(o,d,p,"\u223C","\\thicksim");i(o,d,p,"\u2248","\\thickapprox");i(o,d,p,"\u2AC6","\\supseteqq",!0);i(o,d,p,"\u22D1","\\Supset",!0);i(o,d,p,"\u2290","\\sqsupset",!0);i(o,d,p,"\u227D","\\succcurlyeq",!0);i(o,d,p,"\u22DF","\\curlyeqsucc",!0);i(o,d,p,"\u227F","\\succsim",!0);i(o,d,p,"\u2AB8","\\succapprox",!0);i(o,d,p,"\u22B3","\\vartriangleright");i(o,d,p,"\u22B5","\\trianglerighteq");i(o,d,p,"\u22A9","\\Vdash",!0);i(o,d,p,"\u2223","\\shortmid");i(o,d,p,"\u2225","\\shortparallel");i(o,d,p,"\u226C","\\between",!0);i(o,d,p,"\u22D4","\\pitchfork",!0);i(o,d,p,"\u221D","\\varpropto");i(o,d,p,"\u25C0","\\blacktriangleleft");i(o,d,p,"\u2234","\\therefore",!0);i(o,d,p,"\u220D","\\backepsilon");i(o,d,p,"\u25B6","\\blacktriangleright");i(o,d,p,"\u2235","\\because",!0);i(o,d,p,"\u22D8","\\llless");i(o,d,p,"\u22D9","\\gggtr");i(o,d,C,"\u22B2","\\lhd");i(o,d,C,"\u22B3","\\rhd");i(o,d,p,"\u2242","\\eqsim",!0);i(o,u,p,"\u22C8","\\Join");i(o,d,p,"\u2251","\\Doteq",!0);i(o,d,C,"\u2214","\\dotplus",!0);i(o,d,C,"\u2216","\\smallsetminus");i(o,d,C,"\u22D2","\\Cap",!0);i(o,d,C,"\u22D3","\\Cup",!0);i(o,d,C,"\u2A5E","\\doublebarwedge",!0);i(o,d,C,"\u229F","\\boxminus",!0);i(o,d,C,"\u229E","\\boxplus",!0);i(o,d,C,"\u22C7","\\divideontimes",!0);i(o,d,C,"\u22C9","\\ltimes",!0);i(o,d,C,"\u22CA","\\rtimes",!0);i(o,d,C,"\u22CB","\\leftthreetimes",!0);i(o,d,C,"\u22CC","\\rightthreetimes",!0);i(o,d,C,"\u22CF","\\curlywedge",!0);i(o,d,C,"\u22CE","\\curlyvee",!0);i(o,d,C,"\u229D","\\circleddash",!0);i(o,d,C,"\u229B","\\circledast",!0);i(o,d,C,"\u22C5","\\centerdot");i(o,d,C,"\u22BA","\\intercal",!0);i(o,d,C,"\u22D2","\\doublecap");i(o,d,C,"\u22D3","\\doublecup");i(o,d,C,"\u22A0","\\boxtimes",!0);i(o,d,p,"\u21E2","\\dashrightarrow",!0);i(o,d,p,"\u21E0","\\dashleftarrow",!0);i(o,d,p,"\u21C7","\\leftleftarrows",!0);i(o,d,p,"\u21C6","\\leftrightarrows",!0);i(o,d,p,"\u21DA","\\Lleftarrow",!0);i(o,d,p,"\u219E","\\twoheadleftarrow",!0);i(o,d,p,"\u21A2","\\leftarrowtail",!0);i(o,d,p,"\u21AB","\\looparrowleft",!0);i(o,d,p,"\u21CB","\\leftrightharpoons",!0);i(o,d,p,"\u21B6","\\curvearrowleft",!0);i(o,d,p,"\u21BA","\\circlearrowleft",!0);i(o,d,p,"\u21B0","\\Lsh",!0);i(o,d,p,"\u21C8","\\upuparrows",!0);i(o,d,p,"\u21BF","\\upharpoonleft",!0);i(o,d,p,"\u21C3","\\downharpoonleft",!0);i(o,u,p,"\u22B6","\\origof",!0);i(o,u,p,"\u22B7","\\imageof",!0);i(o,d,p,"\u22B8","\\multimap",!0);i(o,d,p,"\u21AD","\\leftrightsquigarrow",!0);i(o,d,p,"\u21C9","\\rightrightarrows",!0);i(o,d,p,"\u21C4","\\rightleftarrows",!0);i(o,d,p,"\u21A0","\\twoheadrightarrow",!0);i(o,d,p,"\u21A3","\\rightarrowtail",!0);i(o,d,p,"\u21AC","\\looparrowright",!0);i(o,d,p,"\u21B7","\\curvearrowright",!0);i(o,d,p,"\u21BB","\\circlearrowright",!0);i(o,d,p,"\u21B1","\\Rsh",!0);i(o,d,p,"\u21CA","\\downdownarrows",!0);i(o,d,p,"\u21BE","\\upharpoonright",!0);i(o,d,p,"\u21C2","\\downharpoonright",!0);i(o,d,p,"\u21DD","\\rightsquigarrow",!0);i(o,d,p,"\u21DD","\\leadsto");i(o,d,p,"\u21DB","\\Rrightarrow",!0);i(o,d,p,"\u21BE","\\restriction");i(o,u,g,"\u2018","`");i(o,u,g,"$","\\$");i(k,u,g,"$","\\$");i(k,u,g,"$","\\textdollar");i(o,u,g,"%","\\%");i(k,u,g,"%","\\%");i(o,u,g,"_","\\_");i(k,u,g,"_","\\_");i(k,u,g,"_","\\textunderscore");i(o,u,g,"\u2220","\\angle",!0);i(o,u,g,"\u221E","\\infty",!0);i(o,u,g,"\u2032","\\prime");i(o,u,g,"\u25B3","\\triangle");i(o,u,g,"\u0393","\\Gamma",!0);i(o,u,g,"\u0394","\\Delta",!0);i(o,u,g,"\u0398","\\Theta",!0);i(o,u,g,"\u039B","\\Lambda",!0);i(o,u,g,"\u039E","\\Xi",!0);i(o,u,g,"\u03A0","\\Pi",!0);i(o,u,g,"\u03A3","\\Sigma",!0);i(o,u,g,"\u03A5","\\Upsilon",!0);i(o,u,g,"\u03A6","\\Phi",!0);i(o,u,g,"\u03A8","\\Psi",!0);i(o,u,g,"\u03A9","\\Omega",!0);i(o,u,g,"A","\u0391");i(o,u,g,"B","\u0392");i(o,u,g,"E","\u0395");i(o,u,g,"Z","\u0396");i(o,u,g,"H","\u0397");i(o,u,g,"I","\u0399");i(o,u,g,"K","\u039A");i(o,u,g,"M","\u039C");i(o,u,g,"N","\u039D");i(o,u,g,"O","\u039F");i(o,u,g,"P","\u03A1");i(o,u,g,"T","\u03A4");i(o,u,g,"X","\u03A7");i(o,u,g,"\xAC","\\neg",!0);i(o,u,g,"\xAC","\\lnot");i(o,u,g,"\u22A4","\\top");i(o,u,g,"\u22A5","\\bot");i(o,u,g,"\u2205","\\emptyset");i(o,d,g,"\u2205","\\varnothing");i(o,u,I,"\u03B1","\\alpha",!0);i(o,u,I,"\u03B2","\\beta",!0);i(o,u,I,"\u03B3","\\gamma",!0);i(o,u,I,"\u03B4","\\delta",!0);i(o,u,I,"\u03F5","\\epsilon",!0);i(o,u,I,"\u03B6","\\zeta",!0);i(o,u,I,"\u03B7","\\eta",!0);i(o,u,I,"\u03B8","\\theta",!0);i(o,u,I,"\u03B9","\\iota",!0);i(o,u,I,"\u03BA","\\kappa",!0);i(o,u,I,"\u03BB","\\lambda",!0);i(o,u,I,"\u03BC","\\mu",!0);i(o,u,I,"\u03BD","\\nu",!0);i(o,u,I,"\u03BE","\\xi",!0);i(o,u,I,"\u03BF","\\omicron",!0);i(o,u,I,"\u03C0","\\pi",!0);i(o,u,I,"\u03C1","\\rho",!0);i(o,u,I,"\u03C3","\\sigma",!0);i(o,u,I,"\u03C4","\\tau",!0);i(o,u,I,"\u03C5","\\upsilon",!0);i(o,u,I,"\u03D5","\\phi",!0);i(o,u,I,"\u03C7","\\chi",!0);i(o,u,I,"\u03C8","\\psi",!0);i(o,u,I,"\u03C9","\\omega",!0);i(o,u,I,"\u03B5","\\varepsilon",!0);i(o,u,I,"\u03D1","\\vartheta",!0);i(o,u,I,"\u03D6","\\varpi",!0);i(o,u,I,"\u03F1","\\varrho",!0);i(o,u,I,"\u03C2","\\varsigma",!0);i(o,u,I,"\u03C6","\\varphi",!0);i(o,u,C,"\u2217","*",!0);i(o,u,C,"+","+");i(o,u,C,"\u2212","-",!0);i(o,u,C,"\u22C5","\\cdot",!0);i(o,u,C,"\u2218","\\circ",!0);i(o,u,C,"\xF7","\\div",!0);i(o,u,C,"\xB1","\\pm",!0);i(o,u,C,"\xD7","\\times",!0);i(o,u,C,"\u2229","\\cap",!0);i(o,u,C,"\u222A","\\cup",!0);i(o,u,C,"\u2216","\\setminus",!0);i(o,u,C,"\u2227","\\land");i(o,u,C,"\u2228","\\lor");i(o,u,C,"\u2227","\\wedge",!0);i(o,u,C,"\u2228","\\vee",!0);i(o,u,g,"\u221A","\\surd");i(o,u,p0,"\u27E8","\\langle",!0);i(o,u,p0,"\u2223","\\lvert");i(o,u,p0,"\u2225","\\lVert");i(o,u,l0,"?","?");i(o,u,l0,"!","!");i(o,u,l0,"\u27E9","\\rangle",!0);i(o,u,l0,"\u2223","\\rvert");i(o,u,l0,"\u2225","\\rVert");i(o,u,p,"=","=");i(o,u,p,":",":");i(o,u,p,"\u2248","\\approx",!0);i(o,u,p,"\u2245","\\cong",!0);i(o,u,p,"\u2265","\\ge");i(o,u,p,"\u2265","\\geq",!0);i(o,u,p,"\u2190","\\gets");i(o,u,p,">","\\gt",!0);i(o,u,p,"\u2208","\\in",!0);i(o,u,p,"\uE020","\\@not");i(o,u,p,"\u2282","\\subset",!0);i(o,u,p,"\u2283","\\supset",!0);i(o,u,p,"\u2286","\\subseteq",!0);i(o,u,p,"\u2287","\\supseteq",!0);i(o,d,p,"\u2288","\\nsubseteq",!0);i(o,d,p,"\u2289","\\nsupseteq",!0);i(o,u,p,"\u22A8","\\models");i(o,u,p,"\u2190","\\leftarrow",!0);i(o,u,p,"\u2264","\\le");i(o,u,p,"\u2264","\\leq",!0);i(o,u,p,"<","\\lt",!0);i(o,u,p,"\u2192","\\rightarrow",!0);i(o,u,p,"\u2192","\\to");i(o,d,p,"\u2271","\\ngeq",!0);i(o,d,p,"\u2270","\\nleq",!0);i(o,u,R0,"\xA0","\\ ");i(o,u,R0,"\xA0","\\space");i(o,u,R0,"\xA0","\\nobreakspace");i(k,u,R0,"\xA0","\\ ");i(k,u,R0,"\xA0"," ");i(k,u,R0,"\xA0","\\space");i(k,u,R0,"\xA0","\\nobreakspace");i(o,u,R0,null,"\\nobreak");i(o,u,R0,null,"\\allowbreak");i(o,u,Ve,",",",");i(o,u,Ve,";",";");i(o,d,C,"\u22BC","\\barwedge",!0);i(o,d,C,"\u22BB","\\veebar",!0);i(o,u,C,"\u2299","\\odot",!0);i(o,u,C,"\u2295","\\oplus",!0);i(o,u,C,"\u2297","\\otimes",!0);i(o,u,g,"\u2202","\\partial",!0);i(o,u,C,"\u2298","\\oslash",!0);i(o,d,C,"\u229A","\\circledcirc",!0);i(o,d,C,"\u22A1","\\boxdot",!0);i(o,u,C,"\u25B3","\\bigtriangleup");i(o,u,C,"\u25BD","\\bigtriangledown");i(o,u,C,"\u2020","\\dagger");i(o,u,C,"\u22C4","\\diamond");i(o,u,C,"\u22C6","\\star");i(o,u,C,"\u25C3","\\triangleleft");i(o,u,C,"\u25B9","\\triangleright");i(o,u,p0,"{","\\{");i(k,u,g,"{","\\{");i(k,u,g,"{","\\textbraceleft");i(o,u,l0,"}","\\}");i(k,u,g,"}","\\}");i(k,u,g,"}","\\textbraceright");i(o,u,p0,"{","\\lbrace");i(o,u,l0,"}","\\rbrace");i(o,u,p0,"[","\\lbrack",!0);i(k,u,g,"[","\\lbrack",!0);i(o,u,l0,"]","\\rbrack",!0);i(k,u,g,"]","\\rbrack",!0);i(o,u,p0,"(","\\lparen",!0);i(o,u,l0,")","\\rparen",!0);i(k,u,g,"<","\\textless",!0);i(k,u,g,">","\\textgreater",!0);i(o,u,p0,"\u230A","\\lfloor",!0);i(o,u,l0,"\u230B","\\rfloor",!0);i(o,u,p0,"\u2308","\\lceil",!0);i(o,u,l0,"\u2309","\\rceil",!0);i(o,u,g,"\\","\\backslash");i(o,u,g,"\u2223","|");i(o,u,g,"\u2223","\\vert");i(k,u,g,"|","\\textbar",!0);i(o,u,g,"\u2225","\\|");i(o,u,g,"\u2225","\\Vert");i(k,u,g,"\u2225","\\textbardbl");i(k,u,g,"~","\\textasciitilde");i(k,u,g,"\\","\\textbackslash");i(k,u,g,"^","\\textasciicircum");i(o,u,p,"\u2191","\\uparrow",!0);i(o,u,p,"\u21D1","\\Uparrow",!0);i(o,u,p,"\u2193","\\downarrow",!0);i(o,u,p,"\u21D3","\\Downarrow",!0);i(o,u,p,"\u2195","\\updownarrow",!0);i(o,u,p,"\u21D5","\\Updownarrow",!0);i(o,u,t0,"\u2210","\\coprod");i(o,u,t0,"\u22C1","\\bigvee");i(o,u,t0,"\u22C0","\\bigwedge");i(o,u,t0,"\u2A04","\\biguplus");i(o,u,t0,"\u22C2","\\bigcap");i(o,u,t0,"\u22C3","\\bigcup");i(o,u,t0,"\u222B","\\int");i(o,u,t0,"\u222B","\\intop");i(o,u,t0,"\u222C","\\iint");i(o,u,t0,"\u222D","\\iiint");i(o,u,t0,"\u220F","\\prod");i(o,u,t0,"\u2211","\\sum");i(o,u,t0,"\u2A02","\\bigotimes");i(o,u,t0,"\u2A01","\\bigoplus");i(o,u,t0,"\u2A00","\\bigodot");i(o,u,t0,"\u222E","\\oint");i(o,u,t0,"\u222F","\\oiint");i(o,u,t0,"\u2230","\\oiiint");i(o,u,t0,"\u2A06","\\bigsqcup");i(o,u,t0,"\u222B","\\smallint");i(k,u,ie,"\u2026","\\textellipsis");i(o,u,ie,"\u2026","\\mathellipsis");i(k,u,ie,"\u2026","\\ldots",!0);i(o,u,ie,"\u2026","\\ldots",!0);i(o,u,ie,"\u22EF","\\@cdots",!0);i(o,u,ie,"\u22F1","\\ddots",!0);i(o,u,g,"\u22EE","\\varvdots");i(k,u,g,"\u22EE","\\varvdots");i(o,u,Z,"\u02CA","\\acute");i(o,u,Z,"\u02CB","\\grave");i(o,u,Z,"\xA8","\\ddot");i(o,u,Z,"~","\\tilde");i(o,u,Z,"\u02C9","\\bar");i(o,u,Z,"\u02D8","\\breve");i(o,u,Z,"\u02C7","\\check");i(o,u,Z,"^","\\hat");i(o,u,Z,"\u20D7","\\vec");i(o,u,Z,"\u02D9","\\dot");i(o,u,Z,"\u02DA","\\mathring");i(o,u,I,"\uE131","\\@imath");i(o,u,I,"\uE237","\\@jmath");i(o,u,g,"\u0131","\u0131");i(o,u,g,"\u0237","\u0237");i(k,u,g,"\u0131","\\i",!0);i(k,u,g,"\u0237","\\j",!0);i(k,u,g,"\xDF","\\ss",!0);i(k,u,g,"\xE6","\\ae",!0);i(k,u,g,"\u0153","\\oe",!0);i(k,u,g,"\xF8","\\o",!0);i(k,u,g,"\xC6","\\AE",!0);i(k,u,g,"\u0152","\\OE",!0);i(k,u,g,"\xD8","\\O",!0);i(k,u,Z,"\u02CA","\\'");i(k,u,Z,"\u02CB","\\`");i(k,u,Z,"\u02C6","\\^");i(k,u,Z,"\u02DC","\\~");i(k,u,Z,"\u02C9","\\=");i(k,u,Z,"\u02D8","\\u");i(k,u,Z,"\u02D9","\\.");i(k,u,Z,"\xB8","\\c");i(k,u,Z,"\u02DA","\\r");i(k,u,Z,"\u02C7","\\v");i(k,u,Z,"\xA8",'\\"');i(k,u,Z,"\u02DD","\\H");i(k,u,Z,"\u25EF","\\textcircled");var Cr={"--":!0,"---":!0,"``":!0,"''":!0};i(k,u,g,"\u2013","--",!0);i(k,u,g,"\u2013","\\textendash");i(k,u,g,"\u2014","---",!0);i(k,u,g,"\u2014","\\textemdash");i(k,u,g,"\u2018","`",!0);i(k,u,g,"\u2018","\\textquoteleft");i(k,u,g,"\u2019","'",!0);i(k,u,g,"\u2019","\\textquoteright");i(k,u,g,"\u201C","``",!0);i(k,u,g,"\u201C","\\textquotedblleft");i(k,u,g,"\u201D","''",!0);i(k,u,g,"\u201D","\\textquotedblright");i(o,u,g,"\xB0","\\degree",!0);i(k,u,g,"\xB0","\\degree");i(k,u,g,"\xB0","\\textdegree",!0);i(o,u,g,"\xA3","\\pounds");i(o,u,g,"\xA3","\\mathsterling",!0);i(k,u,g,"\xA3","\\pounds");i(k,u,g,"\xA3","\\textsterling",!0);i(o,d,g,"\u2720","\\maltese");i(k,d,g,"\u2720","\\maltese");var Qt='0123456789/@."';for(Me=0;Me0)return w0(s,f,n,t,l.concat(v));if(c){var b,x;if(c==="boldsymbol"){var w=u1(s,n,t,l,a);b=w.fontName,x=[w.fontClass]}else h?(b=Or[c].fontName,x=[c]):(b=Be(c,t.fontWeight,t.fontShape),x=[c,t.fontWeight,t.fontShape]);if(Ue(s,b,n).metrics)return w0(s,b,n,t,l.concat(x));if(Cr.hasOwnProperty(s)&&b.slice(0,10)==="Typewriter"){for(var A=[],q=0;q{if(G0(r.classes)!==G0(e.classes)||r.skew!==e.skew||r.maxFontSize!==e.maxFontSize)return!1;if(r.classes.length===1){var t=r.classes[0];if(t==="mbin"||t==="mord")return!1}for(var a in r.style)if(r.style.hasOwnProperty(a)&&r.style[a]!==e.style[a])return!1;for(var n in e.style)if(e.style.hasOwnProperty(n)&&r.style[n]!==e.style[n])return!1;return!0},m1=r=>{for(var e=0;et&&(t=l.height),l.depth>a&&(a=l.depth),l.maxFontSize>n&&(n=l.maxFontSize)}e.height=t,e.depth=a,e.maxFontSize=n},h0=function(e,t,a,n){var s=new Z0(e,t,a,n);return qt(s),s},_r=(r,e,t,a)=>new Z0(r,e,t,a),d1=function(e,t,a){var n=h0([e],[],t);return n.height=Math.max(a||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=T(n.height),n.maxFontSize=1,n},p1=function(e,t,a,n){var s=new fe(e,t,a,n);return qt(s),s},Nr=function(e){var t=new Y0(e);return qt(t),t},f1=function(e,t){return e instanceof Y0?h0([],[e],t):e},v1=function(e){if(e.positionType==="individualShift"){for(var t=e.children,a=[t[0]],n=-t[0].shift-t[0].elem.depth,s=n,l=1;l{var t=h0(["mspace"],[],e),a=Q(r,e);return t.style.marginRight=T(a),t},Be=function(e,t,a){var n="";switch(e){case"amsrm":n="AMS";break;case"textrm":n="Main";break;case"textsf":n="SansSerif";break;case"texttt":n="Typewriter";break;default:n=e}var s;return t==="textbf"&&a==="textit"?s="BoldItalic":t==="textbf"?s="Bold":t==="textit"?s="Italic":s="Regular",n+"-"+s},Or={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathsfit:{variant:"sans-serif-italic",fontName:"SansSerif-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Ir={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},y1=function(e,t){var[a,n,s]=Ir[e],l=new A0(a),h=new S0([l],{width:T(n),height:T(s),style:"width:"+T(n),viewBox:"0 0 "+1e3*n+" "+1e3*s,preserveAspectRatio:"xMinYMin"}),c=_r(["overlay"],[h],t);return c.height=s,c.style.height=T(s),c.style.width=T(n),c},y={fontMap:Or,makeSymbol:w0,mathsym:l1,makeSpan:h0,makeSvgSpan:_r,makeLineSpan:d1,makeAnchor:p1,makeFragment:Nr,wrapFragment:f1,makeVList:g1,makeOrd:h1,makeGlue:b1,staticSvg:y1,svgData:Ir,tryCombineChars:m1},J={number:3,unit:"mu"},W0={number:4,unit:"mu"},_0={number:5,unit:"mu"},x1={mord:{mop:J,mbin:W0,mrel:_0,minner:J},mop:{mord:J,mop:J,mrel:_0,minner:J},mbin:{mord:W0,mop:W0,mopen:W0,minner:W0},mrel:{mord:_0,mop:_0,mopen:_0,minner:_0},mopen:{},mclose:{mop:J,mbin:W0,mrel:_0,minner:J},mpunct:{mord:J,mop:J,mrel:_0,mopen:J,mclose:J,mpunct:J,minner:J},minner:{mord:J,mop:J,mbin:W0,mrel:_0,mopen:J,mpunct:J,minner:J}},w1={mord:{mop:J},mop:{mord:J,mop:J},mbin:{},mrel:{},mopen:{},mclose:{mop:J},mpunct:{},minner:{mop:J}},Er={},Le={},Fe={};function B(r){for(var{type:e,names:t,props:a,handler:n,htmlBuilder:s,mathmlBuilder:l}=r,h={type:e,numArgs:a.numArgs,argTypes:a.argTypes,allowedInArgument:!!a.allowedInArgument,allowedInText:!!a.allowedInText,allowedInMath:a.allowedInMath===void 0?!0:a.allowedInMath,numOptionalArgs:a.numOptionalArgs||0,infix:!!a.infix,primitive:!!a.primitive,handler:n},c=0;c{var _=q.classes[0],D=A.classes[0];_==="mbin"&&O.contains(k1,D)?q.classes[0]="mord":D==="mbin"&&O.contains(S1,_)&&(A.classes[0]="mord")},{node:b},x,w),rr(s,(A,q)=>{var _=bt(q),D=bt(A),N=_&&D?A.hasClass("mtight")?w1[_][D]:x1[_][D]:null;if(N)return y.makeGlue(N,f)},{node:b},x,w),s},rr=function r(e,t,a,n,s){n&&e.push(n);for(var l=0;lx=>{e.splice(b+1,0,x),l++})(l)}n&&e.pop()},Rr=function(e){return e instanceof Y0||e instanceof fe||e instanceof Z0&&e.hasClass("enclosing")?e:null},A1=function r(e,t){var a=Rr(e);if(a){var n=a.children;if(n.length){if(t==="right")return r(n[n.length-1],"right");if(t==="left")return r(n[0],"left")}}return e},bt=function(e,t){return e?(t&&(e=A1(e,t)),z1[e.classes[0]]||null):null},ge=function(e,t){var a=["nulldelimiter"].concat(e.baseSizingClasses());return I0(t.concat(a))},G=function(e,t,a){if(!e)return I0();if(Le[e.type]){var n=Le[e.type](e,t);if(a&&t.size!==a.size){n=I0(t.sizingClasses(a),[n],t);var s=t.sizeMultiplier/a.sizeMultiplier;n.height*=s,n.depth*=s}return n}else throw new z("Got group of unknown type: '"+e.type+"'")};function De(r,e){var t=I0(["base"],r,e),a=I0(["strut"]);return a.style.height=T(t.height+t.depth),t.depth&&(a.style.verticalAlign=T(-t.depth)),t.children.unshift(a),t}function yt(r,e){var t=null;r.length===1&&r[0].type==="tag"&&(t=r[0].tag,r=r[0].body);var a=a0(r,e,"root"),n;a.length===2&&a[1].hasClass("tag")&&(n=a.pop());for(var s=[],l=[],h=0;h0&&(s.push(De(l,e)),l=[]),s.push(a[h]));l.length>0&&s.push(De(l,e));var f;t?(f=De(a0(t,e,!0)),f.classes=["tag"],s.push(f)):n&&s.push(n);var v=I0(["katex-html"],s);if(v.setAttribute("aria-hidden","true"),f){var b=f.children[0];b.style.height=T(v.height+v.depth),v.depth&&(b.style.verticalAlign=T(-v.depth))}return v}function $r(r){return new Y0(r)}var s0=class{constructor(e,t,a){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=a||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){var e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=G0(this.classes));for(var a=0;a0&&(e+=' class ="'+O.escape(G0(this.classes))+'"'),e+=">";for(var a=0;a",e}toText(){return this.children.map(e=>e.toText()).join("")}},g0=class{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return O.escape(this.toText())}toText(){return this.text}},xt=class{constructor(e){this.width=void 0,this.character=void 0,this.width=e,e>=.05555&&e<=.05556?this.character="\u200A":e>=.1666&&e<=.1667?this.character="\u2009":e>=.2222&&e<=.2223?this.character="\u2005":e>=.2777&&e<=.2778?this.character="\u2005\u200A":e>=-.05556&&e<=-.05555?this.character="\u200A\u2063":e>=-.1667&&e<=-.1666?this.character="\u2009\u2063":e>=-.2223&&e<=-.2222?this.character="\u205F\u2063":e>=-.2778&&e<=-.2777?this.character="\u2005\u2063":this.character=null}toNode(){if(this.character)return document.createTextNode(this.character);var e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",T(this.width)),e}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},M={MathNode:s0,TextNode:g0,SpaceNode:xt,newDocumentFragment:$r},y0=function(e,t,a){return Y[t][e]&&Y[t][e].replace&&e.charCodeAt(0)!==55349&&!(Cr.hasOwnProperty(e)&&a&&(a.fontFamily&&a.fontFamily.slice(4,6)==="tt"||a.font&&a.font.slice(4,6)==="tt"))&&(e=Y[t][e].replace),new M.TextNode(e)},Bt=function(e){return e.length===1?e[0]:new M.MathNode("mrow",e)},Dt=function(e,t){if(t.fontFamily==="texttt")return"monospace";if(t.fontFamily==="textsf")return t.fontShape==="textit"&&t.fontWeight==="textbf"?"sans-serif-bold-italic":t.fontShape==="textit"?"sans-serif-italic":t.fontWeight==="textbf"?"bold-sans-serif":"sans-serif";if(t.fontShape==="textit"&&t.fontWeight==="textbf")return"bold-italic";if(t.fontShape==="textit")return"italic";if(t.fontWeight==="textbf")return"bold";var a=t.font;if(!a||a==="mathnormal")return null;var n=e.mode;if(a==="mathit")return"italic";if(a==="boldsymbol")return e.type==="textord"?"bold":"bold-italic";if(a==="mathbf")return"bold";if(a==="mathbb")return"double-struck";if(a==="mathsfit")return"sans-serif-italic";if(a==="mathfrak")return"fraktur";if(a==="mathscr"||a==="mathcal")return"script";if(a==="mathsf")return"sans-serif";if(a==="mathtt")return"monospace";var s=e.text;if(O.contains(["\\imath","\\jmath"],s))return null;Y[n][s]&&Y[n][s].replace&&(s=Y[n][s].replace);var l=y.fontMap[a].fontName;return Tt(s,l,n)?y.fontMap[a].variant:null};function nt(r){if(!r)return!1;if(r.type==="mi"&&r.children.length===1){var e=r.children[0];return e instanceof g0&&e.text==="."}else if(r.type==="mo"&&r.children.length===1&&r.getAttribute("separator")==="true"&&r.getAttribute("lspace")==="0em"&&r.getAttribute("rspace")==="0em"){var t=r.children[0];return t instanceof g0&&t.text===","}else return!1}var m0=function(e,t,a){if(e.length===1){var n=X(e[0],t);return a&&n instanceof s0&&n.type==="mo"&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}for(var s=[],l,h=0;h=1&&(l.type==="mn"||nt(l))){var f=c.children[0];f instanceof s0&&f.type==="mn"&&(f.children=[...l.children,...f.children],s.pop())}else if(l.type==="mi"&&l.children.length===1){var v=l.children[0];if(v instanceof g0&&v.text==="\u0338"&&(c.type==="mo"||c.type==="mi"||c.type==="mn")){var b=c.children[0];b instanceof g0&&b.text.length>0&&(b.text=b.text.slice(0,1)+"\u0338"+b.text.slice(1),s.pop())}}}s.push(c),l=c}return s},V0=function(e,t,a){return Bt(m0(e,t,a))},X=function(e,t){if(!e)return new M.MathNode("mrow");if(Fe[e.type]){var a=Fe[e.type](e,t);return a}else throw new z("Got group of unknown type: '"+e.type+"'")};function ar(r,e,t,a,n){var s=m0(r,t),l;s.length===1&&s[0]instanceof s0&&O.contains(["mrow","mtable"],s[0].type)?l=s[0]:l=new M.MathNode("mrow",s);var h=new M.MathNode("annotation",[new M.TextNode(e)]);h.setAttribute("encoding","application/x-tex");var c=new M.MathNode("semantics",[l,h]),f=new M.MathNode("math",[c]);f.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),a&&f.setAttribute("display","block");var v=n?"katex":"katex-mathml";return y.makeSpan([v],[f])}var Lr=function(e){return new Re({style:e.displayMode?E.DISPLAY:E.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},Fr=function(e,t){if(t.displayMode){var a=["katex-display"];t.leqno&&a.push("leqno"),t.fleqn&&a.push("fleqn"),e=y.makeSpan(a,[e])}return e},T1=function(e,t,a){var n=Lr(a),s;if(a.output==="mathml")return ar(e,t,n,a.displayMode,!0);if(a.output==="html"){var l=yt(e,n);s=y.makeSpan(["katex"],[l])}else{var h=ar(e,t,n,a.displayMode,!1),c=yt(e,n);s=y.makeSpan(["katex"],[h,c])}return Fr(s,a)},q1=function(e,t,a){var n=Lr(a),s=yt(e,n),l=y.makeSpan(["katex"],[s]);return Fr(l,a)},B1={widehat:"^",widecheck:"\u02C7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23DF",overbrace:"\u23DE",overgroup:"\u23E0",undergroup:"\u23E1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21D2",xRightarrow:"\u21D2",overleftharpoon:"\u21BC",xleftharpoonup:"\u21BC",overrightharpoon:"\u21C0",xrightharpoonup:"\u21C0",xLeftarrow:"\u21D0",xLeftrightarrow:"\u21D4",xhookleftarrow:"\u21A9",xhookrightarrow:"\u21AA",xmapsto:"\u21A6",xrightharpoondown:"\u21C1",xleftharpoondown:"\u21BD",xrightleftharpoons:"\u21CC",xleftrightharpoons:"\u21CB",xtwoheadleftarrow:"\u219E",xtwoheadrightarrow:"\u21A0",xlongequal:"=",xtofrom:"\u21C4",xrightleftarrows:"\u21C4",xrightequilibrium:"\u21CC",xleftequilibrium:"\u21CB","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},D1=function(e){var t=new M.MathNode("mo",[new M.TextNode(B1[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},C1={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},_1=function(e){return e.type==="ordgroup"?e.body.length:1},N1=function(e,t){function a(){var h=4e5,c=e.label.slice(1);if(O.contains(["widehat","widecheck","widetilde","utilde"],c)){var f=e,v=_1(f.base),b,x,w;if(v>5)c==="widehat"||c==="widecheck"?(b=420,h=2364,w=.42,x=c+"4"):(b=312,h=2340,w=.34,x="tilde4");else{var A=[1,1,2,2,3,3][v];c==="widehat"||c==="widecheck"?(h=[0,1062,2364,2364,2364][A],b=[0,239,300,360,420][A],w=[0,.24,.3,.3,.36,.42][A],x=c+A):(h=[0,600,1033,2339,2340][A],b=[0,260,286,306,312][A],w=[0,.26,.286,.3,.306,.34][A],x="tilde"+A)}var q=new A0(x),_=new S0([q],{width:"100%",height:T(w),viewBox:"0 0 "+h+" "+b,preserveAspectRatio:"none"});return{span:y.makeSvgSpan([],[_],t),minWidth:0,height:w}}else{var D=[],N=C1[c],[$,H,F]=N,P=F/1e3,V=$.length,j,U;if(V===1){var D0=N[3];j=["hide-tail"],U=[D0]}else if(V===2)j=["halfarrow-left","halfarrow-right"],U=["xMinYMin","xMaxYMin"];else if(V===3)j=["brace-left","brace-center","brace-right"],U=["xMinYMin","xMidYMin","xMaxYMin"];else throw new Error(`Correct katexImagesData or update code here to support + `+V+" children.");for(var i0=0;i00&&(n.style.minWidth=T(s)),n},O1=function(e,t,a,n,s){var l,h=e.height+e.depth+a+n;if(/fbox|color|angl/.test(t)){if(l=y.makeSpan(["stretchy",t],[],s),t==="fbox"){var c=s.color&&s.getColor();c&&(l.style.borderColor=c)}}else{var f=[];/^[bx]cancel$/.test(t)&&f.push(new ve({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&f.push(new ve({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var v=new S0(f,{width:"100%",height:T(h)});l=y.makeSvgSpan([],[v],s)}return l.height=h,l.style.height=T(h),l},E0={encloseSpan:O1,mathMLnode:D1,svgSpan:N1};function L(r,e){if(!r||r.type!==e)throw new Error("Expected node of type "+e+", but got "+(r?"node of type "+r.type:String(r)));return r}function Ct(r){var e=Xe(r);if(!e)throw new Error("Expected node of symbol group type, but got "+(r?"node of type "+r.type:String(r)));return e}function Xe(r){return r&&(r.type==="atom"||s1.hasOwnProperty(r.type))?r:null}var _t=(r,e)=>{var t,a,n;r&&r.type==="supsub"?(a=L(r.base,"accent"),t=a.base,r.base=t,n=n1(G(r,e)),r.base=a):(a=L(r,"accent"),t=a.base);var s=G(t,e.havingCrampedStyle()),l=a.isShifty&&O.isCharacterBox(t),h=0;if(l){var c=O.getBaseElem(t),f=G(c,e.havingCrampedStyle());h=Jt(f).skew}var v=a.label==="\\c",b=v?s.height+s.depth:Math.min(s.height,e.fontMetrics().xHeight),x;if(a.isStretchy)x=E0.svgSpan(a,e),x=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:x,wrapperClasses:["svg-align"],wrapperStyle:h>0?{width:"calc(100% - "+T(2*h)+")",marginLeft:T(2*h)}:void 0}]},e);else{var w,A;a.label==="\\vec"?(w=y.staticSvg("vec",e),A=y.svgData.vec[1]):(w=y.makeOrd({mode:a.mode,text:a.label},e,"textord"),w=Jt(w),w.italic=0,A=w.width,v&&(b+=w.depth)),x=y.makeSpan(["accent-body"],[w]);var q=a.label==="\\textcircled";q&&(x.classes.push("accent-full"),b=s.height);var _=h;q||(_-=A/2),x.style.left=T(_),a.label==="\\textcircled"&&(x.style.top=".2em"),x=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-b},{type:"elem",elem:x}]},e)}var D=y.makeSpan(["mord","accent"],[x],e);return n?(n.children[0]=D,n.height=Math.max(D.height,n.height),n.classes[0]="mord",n):D},Hr=(r,e)=>{var t=r.isStretchy?E0.mathMLnode(r.label):new M.MathNode("mo",[y0(r.label,r.mode)]),a=new M.MathNode("mover",[X(r.base,e),t]);return a.setAttribute("accent","true"),a},I1=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(r=>"\\"+r).join("|"));B({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(r,e)=>{var t=He(e[0]),a=!I1.test(r.funcName),n=!a||r.funcName==="\\widehat"||r.funcName==="\\widetilde"||r.funcName==="\\widecheck";return{type:"accent",mode:r.parser.mode,label:r.funcName,isStretchy:a,isShifty:n,base:t}},htmlBuilder:_t,mathmlBuilder:Hr});B({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(r,e)=>{var t=e[0],a=r.parser.mode;return a==="math"&&(r.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+r.funcName+" works only in text mode"),a="text"),{type:"accent",mode:a,label:r.funcName,isStretchy:!1,isShifty:!0,base:t}},htmlBuilder:_t,mathmlBuilder:Hr});B({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"accentUnder",mode:t.mode,label:a,base:n}},htmlBuilder:(r,e)=>{var t=G(r.base,e),a=E0.svgSpan(r,e),n=r.label==="\\utilde"?.12:0,s=y.makeVList({positionType:"top",positionData:t.height,children:[{type:"elem",elem:a,wrapperClasses:["svg-align"]},{type:"kern",size:n},{type:"elem",elem:t}]},e);return y.makeSpan(["mord","accentunder"],[s],e)},mathmlBuilder:(r,e)=>{var t=E0.mathMLnode(r.label),a=new M.MathNode("munder",[X(r.base,e),t]);return a.setAttribute("accentunder","true"),a}});var Ce=r=>{var e=new M.MathNode("mpadded",r?[r]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};B({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(r,e,t){var{parser:a,funcName:n}=r;return{type:"xArrow",mode:a.mode,label:n,body:e[0],below:t[0]}},htmlBuilder(r,e){var t=e.style,a=e.havingStyle(t.sup()),n=y.wrapFragment(G(r.body,a,e),e),s=r.label.slice(0,2)==="\\x"?"x":"cd";n.classes.push(s+"-arrow-pad");var l;r.below&&(a=e.havingStyle(t.sub()),l=y.wrapFragment(G(r.below,a,e),e),l.classes.push(s+"-arrow-pad"));var h=E0.svgSpan(r,e),c=-e.fontMetrics().axisHeight+.5*h.height,f=-e.fontMetrics().axisHeight-.5*h.height-.111;(n.depth>.25||r.label==="\\xleftequilibrium")&&(f-=n.depth);var v;if(l){var b=-e.fontMetrics().axisHeight+l.height+.5*h.height+.111;v=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:f},{type:"elem",elem:h,shift:c},{type:"elem",elem:l,shift:b}]},e)}else v=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:f},{type:"elem",elem:h,shift:c}]},e);return v.children[0].children[0].children[1].classes.push("svg-align"),y.makeSpan(["mrel","x-arrow"],[v],e)},mathmlBuilder(r,e){var t=E0.mathMLnode(r.label);t.setAttribute("minsize",r.label.charAt(0)==="x"?"1.75em":"3.0em");var a;if(r.body){var n=Ce(X(r.body,e));if(r.below){var s=Ce(X(r.below,e));a=new M.MathNode("munderover",[t,s,n])}else a=new M.MathNode("mover",[t,n])}else if(r.below){var l=Ce(X(r.below,e));a=new M.MathNode("munder",[t,l])}else a=Ce(),a=new M.MathNode("mover",[t,a]);return a}});var E1=y.makeSpan;function Pr(r,e){var t=a0(r.body,e,!0);return E1([r.mclass],t,e)}function Gr(r,e){var t,a=m0(r.body,e);return r.mclass==="minner"?t=new M.MathNode("mpadded",a):r.mclass==="mord"?r.isCharacterBox?(t=a[0],t.type="mi"):t=new M.MathNode("mi",a):(r.isCharacterBox?(t=a[0],t.type="mo"):t=new M.MathNode("mo",a),r.mclass==="mbin"?(t.attributes.lspace="0.22em",t.attributes.rspace="0.22em"):r.mclass==="mpunct"?(t.attributes.lspace="0em",t.attributes.rspace="0.17em"):r.mclass==="mopen"||r.mclass==="mclose"?(t.attributes.lspace="0em",t.attributes.rspace="0em"):r.mclass==="minner"&&(t.attributes.lspace="0.0556em",t.attributes.width="+0.1111em")),t}B({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"mclass",mode:t.mode,mclass:"m"+a.slice(5),body:e0(n),isCharacterBox:O.isCharacterBox(n)}},htmlBuilder:Pr,mathmlBuilder:Gr});var We=r=>{var e=r.type==="ordgroup"&&r.body.length?r.body[0]:r;return e.type==="atom"&&(e.family==="bin"||e.family==="rel")?"m"+e.family:"mord"};B({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(r,e){var{parser:t}=r;return{type:"mclass",mode:t.mode,mclass:We(e[0]),body:e0(e[1]),isCharacterBox:O.isCharacterBox(e[1])}}});B({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(r,e){var{parser:t,funcName:a}=r,n=e[1],s=e[0],l;a!=="\\stackrel"?l=We(n):l="mrel";var h={type:"op",mode:n.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:a!=="\\stackrel",body:e0(n)},c={type:"supsub",mode:s.mode,base:h,sup:a==="\\underset"?null:s,sub:a==="\\underset"?s:null};return{type:"mclass",mode:t.mode,mclass:l,body:[c],isCharacterBox:O.isCharacterBox(c)}},htmlBuilder:Pr,mathmlBuilder:Gr});B({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"pmb",mode:t.mode,mclass:We(e[0]),body:e0(e[0])}},htmlBuilder(r,e){var t=a0(r.body,e,!0),a=y.makeSpan([r.mclass],t,e);return a.style.textShadow="0.02em 0.01em 0.04px",a},mathmlBuilder(r,e){var t=m0(r.body,e),a=new M.MathNode("mstyle",t);return a.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),a}});var R1={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},nr=()=>({type:"styling",body:[],mode:"math",style:"display"}),ir=r=>r.type==="textord"&&r.text==="@",$1=(r,e)=>(r.type==="mathord"||r.type==="atom")&&r.text===e;function L1(r,e,t){var a=R1[r];switch(a){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return t.callFunction(a,[e[0]],[e[1]]);case"\\uparrow":case"\\downarrow":{var n=t.callFunction("\\\\cdleft",[e[0]],[]),s={type:"atom",text:a,mode:"math",family:"rel"},l=t.callFunction("\\Big",[s],[]),h=t.callFunction("\\\\cdright",[e[1]],[]),c={type:"ordgroup",mode:"math",body:[n,l,h]};return t.callFunction("\\\\cdparent",[c],[])}case"\\\\cdlongequal":return t.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{var f={type:"textord",text:"\\Vert",mode:"math"};return t.callFunction("\\Big",[f],[])}default:return{type:"textord",text:" ",mode:"math"}}}function F1(r){var e=[];for(r.gullet.beginGroup(),r.gullet.macros.set("\\cr","\\\\\\relax"),r.gullet.beginGroup();;){e.push(r.parseExpression(!1,"\\\\")),r.gullet.endGroup(),r.gullet.beginGroup();var t=r.fetch().text;if(t==="&"||t==="\\\\")r.consume();else if(t==="\\end"){e[e.length-1].length===0&&e.pop();break}else throw new z("Expected \\\\ or \\cr or \\end",r.nextToken)}for(var a=[],n=[a],s=0;s-1))if("<>AV".indexOf(f)>-1)for(var b=0;b<2;b++){for(var x=!0,w=c+1;wAV=|." after @',l[c]);var A=L1(f,v,r),q={type:"styling",body:[A],mode:"math",style:"display"};a.push(q),h=nr()}s%2===0?a.push(h):a.shift(),a=[],n.push(a)}r.gullet.endGroup(),r.gullet.endGroup();var _=new Array(n[0].length).fill({type:"align",align:"c",pregap:.25,postgap:.25});return{type:"array",mode:"math",body:n,arraystretch:1,addJot:!0,rowGaps:[null],cols:_,colSeparationType:"CD",hLinesBeforeRow:new Array(n.length+1).fill([])}}B({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r;return{type:"cdlabel",mode:t.mode,side:a.slice(4),label:e[0]}},htmlBuilder(r,e){var t=e.havingStyle(e.style.sup()),a=y.wrapFragment(G(r.label,t,e),e);return a.classes.push("cd-label-"+r.side),a.style.bottom=T(.8-a.depth),a.height=0,a.depth=0,a},mathmlBuilder(r,e){var t=new M.MathNode("mrow",[X(r.label,e)]);return t=new M.MathNode("mpadded",[t]),t.setAttribute("width","0"),r.side==="left"&&t.setAttribute("lspace","-1width"),t.setAttribute("voffset","0.7em"),t=new M.MathNode("mstyle",[t]),t.setAttribute("displaystyle","false"),t.setAttribute("scriptlevel","1"),t}});B({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(r,e){var{parser:t}=r;return{type:"cdlabelparent",mode:t.mode,fragment:e[0]}},htmlBuilder(r,e){var t=y.wrapFragment(G(r.fragment,e),e);return t.classes.push("cd-vert-arrow"),t},mathmlBuilder(r,e){return new M.MathNode("mrow",[X(r.fragment,e)])}});B({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(r,e){for(var{parser:t}=r,a=L(e[0],"ordgroup"),n=a.body,s="",l=0;l=1114111)throw new z("\\@char with invalid code point "+s);return c<=65535?f=String.fromCharCode(c):(c-=65536,f=String.fromCharCode((c>>10)+55296,(c&1023)+56320)),{type:"textord",mode:t.mode,text:f}}});var Vr=(r,e)=>{var t=a0(r.body,e.withColor(r.color),!1);return y.makeFragment(t)},Ur=(r,e)=>{var t=m0(r.body,e.withColor(r.color)),a=new M.MathNode("mstyle",t);return a.setAttribute("mathcolor",r.color),a};B({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(r,e){var{parser:t}=r,a=L(e[0],"color-token").color,n=e[1];return{type:"color",mode:t.mode,color:a,body:e0(n)}},htmlBuilder:Vr,mathmlBuilder:Ur});B({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(r,e){var{parser:t,breakOnTokenText:a}=r,n=L(e[0],"color-token").color;t.gullet.macros.set("\\current@color",n);var s=t.parseExpression(!0,a);return{type:"color",mode:t.mode,color:n,body:s}},htmlBuilder:Vr,mathmlBuilder:Ur});B({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(r,e,t){var{parser:a}=r,n=a.gullet.future().text==="["?a.parseSizeGroup(!0):null,s=!a.settings.displayMode||!a.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:a.mode,newLine:s,size:n&&L(n,"size").value}},htmlBuilder(r,e){var t=y.makeSpan(["mspace"],[],e);return r.newLine&&(t.classes.push("newline"),r.size&&(t.style.marginTop=T(Q(r.size,e)))),t},mathmlBuilder(r,e){var t=new M.MathNode("mspace");return r.newLine&&(t.setAttribute("linebreak","newline"),r.size&&t.setAttribute("height",T(Q(r.size,e)))),t}});var wt={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},Xr=r=>{var e=r.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(e))throw new z("Expected a control sequence",r);return e},H1=r=>{var e=r.gullet.popToken();return e.text==="="&&(e=r.gullet.popToken(),e.text===" "&&(e=r.gullet.popToken())),e},Wr=(r,e,t,a)=>{var n=r.gullet.macros.get(t.text);n==null&&(t.noexpand=!0,n={tokens:[t],numArgs:0,unexpandable:!r.gullet.isExpandable(t.text)}),r.gullet.macros.set(e,n,a)};B({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(r){var{parser:e,funcName:t}=r;e.consumeSpaces();var a=e.fetch();if(wt[a.text])return(t==="\\global"||t==="\\\\globallong")&&(a.text=wt[a.text]),L(e.parseFunction(),"internal");throw new z("Invalid token after macro prefix",a)}});B({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=e.gullet.popToken(),n=a.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(n))throw new z("Expected a control sequence",a);for(var s=0,l,h=[[]];e.gullet.future().text!=="{";)if(a=e.gullet.popToken(),a.text==="#"){if(e.gullet.future().text==="{"){l=e.gullet.future(),h[s].push("{");break}if(a=e.gullet.popToken(),!/^[1-9]$/.test(a.text))throw new z('Invalid argument number "'+a.text+'"');if(parseInt(a.text)!==s+1)throw new z('Argument number "'+a.text+'" out of order');s++,h.push([])}else{if(a.text==="EOF")throw new z("Expected a macro definition");h[s].push(a.text)}var{tokens:c}=e.gullet.consumeArg();return l&&c.unshift(l),(t==="\\edef"||t==="\\xdef")&&(c=e.gullet.expandTokens(c),c.reverse()),e.gullet.macros.set(n,{tokens:c,numArgs:s,delimiters:h},t===wt[t]),{type:"internal",mode:e.mode}}});B({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=Xr(e.gullet.popToken());e.gullet.consumeSpaces();var n=H1(e);return Wr(e,a,n,t==="\\\\globallet"),{type:"internal",mode:e.mode}}});B({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r){var{parser:e,funcName:t}=r,a=Xr(e.gullet.popToken()),n=e.gullet.popToken(),s=e.gullet.popToken();return Wr(e,a,s,t==="\\\\globalfuture"),e.gullet.pushToken(s),e.gullet.pushToken(n),{type:"internal",mode:e.mode}}});var ce=function(e,t,a){var n=Y.math[e]&&Y.math[e].replace,s=Tt(n||e,t,a);if(!s)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return s},Nt=function(e,t,a,n){var s=a.havingBaseStyle(t),l=y.makeSpan(n.concat(s.sizingClasses(a)),[e],a),h=s.sizeMultiplier/a.sizeMultiplier;return l.height*=h,l.depth*=h,l.maxFontSize=s.sizeMultiplier,l},Yr=function(e,t,a){var n=t.havingBaseStyle(a),s=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=T(s),e.height-=s,e.depth+=s},P1=function(e,t,a,n,s,l){var h=y.makeSymbol(e,"Main-Regular",s,n),c=Nt(h,t,n,l);return a&&Yr(c,n,t),c},G1=function(e,t,a,n){return y.makeSymbol(e,"Size"+t+"-Regular",a,n)},Zr=function(e,t,a,n,s,l){var h=G1(e,t,s,n),c=Nt(y.makeSpan(["delimsizing","size"+t],[h],n),E.TEXT,n,l);return a&&Yr(c,n,E.TEXT),c},it=function(e,t,a){var n;t==="Size1-Regular"?n="delim-size1":n="delim-size4";var s=y.makeSpan(["delimsizinginner",n],[y.makeSpan([],[y.makeSymbol(e,t,a)])]);return{type:"elem",elem:s}},st=function(e,t,a){var n=z0["Size4-Regular"][e.charCodeAt(0)]?z0["Size4-Regular"][e.charCodeAt(0)][4]:z0["Size1-Regular"][e.charCodeAt(0)][4],s=new A0("inner",ja(e,Math.round(1e3*t))),l=new S0([s],{width:T(n),height:T(t),style:"width:"+T(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),h=y.makeSvgSpan([],[l],a);return h.height=t,h.style.height=T(t),h.style.width=T(n),{type:"elem",elem:h}},St=.008,_e={type:"kern",size:-1*St},V1=["|","\\lvert","\\rvert","\\vert"],U1=["\\|","\\lVert","\\rVert","\\Vert"],jr=function(e,t,a,n,s,l){var h,c,f,v,b="",x=0;h=f=v=e,c=null;var w="Size1-Regular";e==="\\uparrow"?f=v="\u23D0":e==="\\Uparrow"?f=v="\u2016":e==="\\downarrow"?h=f="\u23D0":e==="\\Downarrow"?h=f="\u2016":e==="\\updownarrow"?(h="\\uparrow",f="\u23D0",v="\\downarrow"):e==="\\Updownarrow"?(h="\\Uparrow",f="\u2016",v="\\Downarrow"):O.contains(V1,e)?(f="\u2223",b="vert",x=333):O.contains(U1,e)?(f="\u2225",b="doublevert",x=556):e==="["||e==="\\lbrack"?(h="\u23A1",f="\u23A2",v="\u23A3",w="Size4-Regular",b="lbrack",x=667):e==="]"||e==="\\rbrack"?(h="\u23A4",f="\u23A5",v="\u23A6",w="Size4-Regular",b="rbrack",x=667):e==="\\lfloor"||e==="\u230A"?(f=h="\u23A2",v="\u23A3",w="Size4-Regular",b="lfloor",x=667):e==="\\lceil"||e==="\u2308"?(h="\u23A1",f=v="\u23A2",w="Size4-Regular",b="lceil",x=667):e==="\\rfloor"||e==="\u230B"?(f=h="\u23A5",v="\u23A6",w="Size4-Regular",b="rfloor",x=667):e==="\\rceil"||e==="\u2309"?(h="\u23A4",f=v="\u23A5",w="Size4-Regular",b="rceil",x=667):e==="("||e==="\\lparen"?(h="\u239B",f="\u239C",v="\u239D",w="Size4-Regular",b="lparen",x=875):e===")"||e==="\\rparen"?(h="\u239E",f="\u239F",v="\u23A0",w="Size4-Regular",b="rparen",x=875):e==="\\{"||e==="\\lbrace"?(h="\u23A7",c="\u23A8",v="\u23A9",f="\u23AA",w="Size4-Regular"):e==="\\}"||e==="\\rbrace"?(h="\u23AB",c="\u23AC",v="\u23AD",f="\u23AA",w="Size4-Regular"):e==="\\lgroup"||e==="\u27EE"?(h="\u23A7",v="\u23A9",f="\u23AA",w="Size4-Regular"):e==="\\rgroup"||e==="\u27EF"?(h="\u23AB",v="\u23AD",f="\u23AA",w="Size4-Regular"):e==="\\lmoustache"||e==="\u23B0"?(h="\u23A7",v="\u23AD",f="\u23AA",w="Size4-Regular"):(e==="\\rmoustache"||e==="\u23B1")&&(h="\u23AB",v="\u23A9",f="\u23AA",w="Size4-Regular");var A=ce(h,w,s),q=A.height+A.depth,_=ce(f,w,s),D=_.height+_.depth,N=ce(v,w,s),$=N.height+N.depth,H=0,F=1;if(c!==null){var P=ce(c,w,s);H=P.height+P.depth,F=2}var V=q+$+H,j=Math.max(0,Math.ceil((t-V)/(F*D))),U=V+j*F*D,D0=n.fontMetrics().axisHeight;a&&(D0*=n.sizeMultiplier);var i0=U/2-D0,r0=[];if(b.length>0){var X0=U-q-$,u0=Math.round(U*1e3),x0=Ka(b,Math.round(X0*1e3)),$0=new A0(b,x0),K0=(x/1e3).toFixed(3)+"em",J0=(u0/1e3).toFixed(3)+"em",je=new S0([$0],{width:K0,height:J0,viewBox:"0 0 "+x+" "+u0}),L0=y.makeSvgSpan([],[je],n);L0.height=u0/1e3,L0.style.width=K0,L0.style.height=J0,r0.push({type:"elem",elem:L0})}else{if(r0.push(it(v,w,s)),r0.push(_e),c===null){var F0=U-q-$+2*St;r0.push(st(f,F0,n))}else{var f0=(U-q-$-H)/2+2*St;r0.push(st(f,f0,n)),r0.push(_e),r0.push(it(c,w,s)),r0.push(_e),r0.push(st(f,f0,n))}r0.push(_e),r0.push(it(h,w,s))}var le=n.havingBaseStyle(E.TEXT),Ke=y.makeVList({positionType:"bottom",positionData:i0,children:r0},le);return Nt(y.makeSpan(["delimsizing","mult"],[Ke],le),E.TEXT,n,l)},ot=80,lt=.08,ut=function(e,t,a,n,s){var l=Za(e,n,a),h=new A0(e,l),c=new S0([h],{width:"400em",height:T(t),viewBox:"0 0 400000 "+a,preserveAspectRatio:"xMinYMin slice"});return y.makeSvgSpan(["hide-tail"],[c],s)},X1=function(e,t){var a=t.havingBaseSizing(),n=ea("\\surd",e*a.sizeMultiplier,Qr,a),s=a.sizeMultiplier,l=Math.max(0,t.minRuleThickness-t.fontMetrics().sqrtRuleThickness),h,c=0,f=0,v=0,b;return n.type==="small"?(v=1e3+1e3*l+ot,e<1?s=1:e<1.4&&(s=.7),c=(1+l+lt)/s,f=(1+l)/s,h=ut("sqrtMain",c,v,l,t),h.style.minWidth="0.853em",b=.833/s):n.type==="large"?(v=(1e3+ot)*me[n.size],f=(me[n.size]+l)/s,c=(me[n.size]+l+lt)/s,h=ut("sqrtSize"+n.size,c,v,l,t),h.style.minWidth="1.02em",b=1/s):(c=e+l+lt,f=e+l,v=Math.floor(1e3*e+l)+ot,h=ut("sqrtTall",c,v,l,t),h.style.minWidth="0.742em",b=1.056),h.height=f,h.style.height=T(c),{span:h,advanceWidth:b,ruleWidth:(t.fontMetrics().sqrtRuleThickness+l)*s}},Kr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","\\surd"],W1=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1"],Jr=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],me=[0,1.2,1.8,2.4,3],Y1=function(e,t,a,n,s){if(e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle"),O.contains(Kr,e)||O.contains(Jr,e))return Zr(e,t,!1,a,n,s);if(O.contains(W1,e))return jr(e,me[t],!1,a,n,s);throw new z("Illegal delimiter: '"+e+"'")},Z1=[{type:"small",style:E.SCRIPTSCRIPT},{type:"small",style:E.SCRIPT},{type:"small",style:E.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],j1=[{type:"small",style:E.SCRIPTSCRIPT},{type:"small",style:E.SCRIPT},{type:"small",style:E.TEXT},{type:"stack"}],Qr=[{type:"small",style:E.SCRIPTSCRIPT},{type:"small",style:E.SCRIPT},{type:"small",style:E.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],K1=function(e){if(e.type==="small")return"Main-Regular";if(e.type==="large")return"Size"+e.size+"-Regular";if(e.type==="stack")return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},ea=function(e,t,a,n){for(var s=Math.min(2,3-n.style.size),l=s;lt)return a[l]}return a[a.length-1]},ta=function(e,t,a,n,s,l){e==="<"||e==="\\lt"||e==="\u27E8"?e="\\langle":(e===">"||e==="\\gt"||e==="\u27E9")&&(e="\\rangle");var h;O.contains(Jr,e)?h=Z1:O.contains(Kr,e)?h=Qr:h=j1;var c=ea(e,t,h,n);return c.type==="small"?P1(e,c.style,a,n,s,l):c.type==="large"?Zr(e,c.size,a,n,s,l):jr(e,t,a,n,s,l)},J1=function(e,t,a,n,s,l){var h=n.fontMetrics().axisHeight*n.sizeMultiplier,c=901,f=5/n.fontMetrics().ptPerEm,v=Math.max(t-h,a+h),b=Math.max(v/500*c,2*v-f);return ta(e,b,!0,n,s,l)},O0={sqrtImage:X1,sizedDelim:Y1,sizeToMaxHeight:me,customSizedDelim:ta,leftRightDelim:J1},sr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},Q1=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230A","\u230B","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27E8","\\rangle","\u27E9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27EE","\u27EF","\\lmoustache","\\rmoustache","\u23B0","\u23B1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function Ye(r,e){var t=Xe(r);if(t&&O.contains(Q1,t.text))return t;throw t?new z("Invalid delimiter '"+t.text+"' after '"+e.funcName+"'",r):new z("Invalid delimiter type '"+r.type+"'",r)}B({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(r,e)=>{var t=Ye(e[0],r);return{type:"delimsizing",mode:r.parser.mode,size:sr[r.funcName].size,mclass:sr[r.funcName].mclass,delim:t.text}},htmlBuilder:(r,e)=>r.delim==="."?y.makeSpan([r.mclass]):O0.sizedDelim(r.delim,r.size,e,r.mode,[r.mclass]),mathmlBuilder:r=>{var e=[];r.delim!=="."&&e.push(y0(r.delim,r.mode));var t=new M.MathNode("mo",e);r.mclass==="mopen"||r.mclass==="mclose"?t.setAttribute("fence","true"):t.setAttribute("fence","false"),t.setAttribute("stretchy","true");var a=T(O0.sizeToMaxHeight[r.size]);return t.setAttribute("minsize",a),t.setAttribute("maxsize",a),t}});function or(r){if(!r.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}B({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=r.parser.gullet.macros.get("\\current@color");if(t&&typeof t!="string")throw new z("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:r.parser.mode,delim:Ye(e[0],r).text,color:t}}});B({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=Ye(e[0],r),a=r.parser;++a.leftrightDepth;var n=a.parseExpression(!1);--a.leftrightDepth,a.expect("\\right",!1);var s=L(a.parseFunction(),"leftright-right");return{type:"leftright",mode:a.mode,body:n,left:t.text,right:s.delim,rightColor:s.color}},htmlBuilder:(r,e)=>{or(r);for(var t=a0(r.body,e,!0,["mopen","mclose"]),a=0,n=0,s=!1,l=0;l{or(r);var t=m0(r.body,e);if(r.left!=="."){var a=new M.MathNode("mo",[y0(r.left,r.mode)]);a.setAttribute("fence","true"),t.unshift(a)}if(r.right!=="."){var n=new M.MathNode("mo",[y0(r.right,r.mode)]);n.setAttribute("fence","true"),r.rightColor&&n.setAttribute("mathcolor",r.rightColor),t.push(n)}return Bt(t)}});B({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var t=Ye(e[0],r);if(!r.parser.leftrightDepth)throw new z("\\middle without preceding \\left",t);return{type:"middle",mode:r.parser.mode,delim:t.text}},htmlBuilder:(r,e)=>{var t;if(r.delim===".")t=ge(e,[]);else{t=O0.sizedDelim(r.delim,1,e,r.mode,[]);var a={delim:r.delim,options:e};t.isMiddle=a}return t},mathmlBuilder:(r,e)=>{var t=r.delim==="\\vert"||r.delim==="|"?y0("|","text"):y0(r.delim,r.mode),a=new M.MathNode("mo",[t]);return a.setAttribute("fence","true"),a.setAttribute("lspace","0.05em"),a.setAttribute("rspace","0.05em"),a}});var Ot=(r,e)=>{var t=y.wrapFragment(G(r.body,e),e),a=r.label.slice(1),n=e.sizeMultiplier,s,l=0,h=O.isCharacterBox(r.body);if(a==="sout")s=y.makeSpan(["stretchy","sout"]),s.height=e.fontMetrics().defaultRuleThickness/n,l=-.5*e.fontMetrics().xHeight;else if(a==="phase"){var c=Q({number:.6,unit:"pt"},e),f=Q({number:.35,unit:"ex"},e),v=e.havingBaseSizing();n=n/v.sizeMultiplier;var b=t.height+t.depth+c+f;t.style.paddingLeft=T(b/2+c);var x=Math.floor(1e3*b*n),w=Wa(x),A=new S0([new A0("phase",w)],{width:"400em",height:T(x/1e3),viewBox:"0 0 400000 "+x,preserveAspectRatio:"xMinYMin slice"});s=y.makeSvgSpan(["hide-tail"],[A],e),s.style.height=T(b),l=t.depth+c+f}else{/cancel/.test(a)?h||t.classes.push("cancel-pad"):a==="angl"?t.classes.push("anglpad"):t.classes.push("boxpad");var q=0,_=0,D=0;/box/.test(a)?(D=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness),q=e.fontMetrics().fboxsep+(a==="colorbox"?0:D),_=q):a==="angl"?(D=Math.max(e.fontMetrics().defaultRuleThickness,e.minRuleThickness),q=4*D,_=Math.max(0,.25-t.depth)):(q=h?.2:0,_=q),s=E0.encloseSpan(t,a,q,_,e),/fbox|boxed|fcolorbox/.test(a)?(s.style.borderStyle="solid",s.style.borderWidth=T(D)):a==="angl"&&D!==.049&&(s.style.borderTopWidth=T(D),s.style.borderRightWidth=T(D)),l=t.depth+_,r.backgroundColor&&(s.style.backgroundColor=r.backgroundColor,r.borderColor&&(s.style.borderColor=r.borderColor))}var N;if(r.backgroundColor)N=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:s,shift:l},{type:"elem",elem:t,shift:0}]},e);else{var $=/cancel|phase/.test(a)?["svg-align"]:[];N=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:t,shift:0},{type:"elem",elem:s,shift:l,wrapperClasses:$}]},e)}return/cancel/.test(a)&&(N.height=t.height,N.depth=t.depth),/cancel/.test(a)&&!h?y.makeSpan(["mord","cancel-lap"],[N],e):y.makeSpan(["mord"],[N],e)},It=(r,e)=>{var t=0,a=new M.MathNode(r.label.indexOf("colorbox")>-1?"mpadded":"menclose",[X(r.body,e)]);switch(r.label){case"\\cancel":a.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":a.setAttribute("notation","downdiagonalstrike");break;case"\\phase":a.setAttribute("notation","phasorangle");break;case"\\sout":a.setAttribute("notation","horizontalstrike");break;case"\\fbox":a.setAttribute("notation","box");break;case"\\angl":a.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(t=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,a.setAttribute("width","+"+2*t+"pt"),a.setAttribute("height","+"+2*t+"pt"),a.setAttribute("lspace",t+"pt"),a.setAttribute("voffset",t+"pt"),r.label==="\\fcolorbox"){var n=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);a.setAttribute("style","border: "+n+"em solid "+String(r.borderColor))}break;case"\\xcancel":a.setAttribute("notation","updiagonalstrike downdiagonalstrike");break}return r.backgroundColor&&a.setAttribute("mathbackground",r.backgroundColor),a};B({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(r,e,t){var{parser:a,funcName:n}=r,s=L(e[0],"color-token").color,l=e[1];return{type:"enclose",mode:a.mode,label:n,backgroundColor:s,body:l}},htmlBuilder:Ot,mathmlBuilder:It});B({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(r,e,t){var{parser:a,funcName:n}=r,s=L(e[0],"color-token").color,l=L(e[1],"color-token").color,h=e[2];return{type:"enclose",mode:a.mode,label:n,backgroundColor:l,borderColor:s,body:h}},htmlBuilder:Ot,mathmlBuilder:It});B({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"enclose",mode:t.mode,label:"\\fbox",body:e[0]}}});B({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"enclose",mode:t.mode,label:a,body:n}},htmlBuilder:Ot,mathmlBuilder:It});B({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(r,e){var{parser:t}=r;return{type:"enclose",mode:t.mode,label:"\\angl",body:e[0]}}});var ra={};function T0(r){for(var{type:e,names:t,props:a,handler:n,htmlBuilder:s,mathmlBuilder:l}=r,h={type:e,numArgs:a.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:n},c=0;c{var e=r.parser.settings;if(!e.displayMode)throw new z("{"+r.envName+"} can be used only in display mode.")};function Et(r){if(r.indexOf("ed")===-1)return r.indexOf("*")===-1}function U0(r,e,t){var{hskipBeforeAndAfter:a,addJot:n,cols:s,arraystretch:l,colSeparationType:h,autoTag:c,singleRow:f,emptySingleRow:v,maxNumCols:b,leqno:x}=e;if(r.gullet.beginGroup(),f||r.gullet.macros.set("\\cr","\\\\\\relax"),!l){var w=r.gullet.expandMacroAsText("\\arraystretch");if(w==null)l=1;else if(l=parseFloat(w),!l||l<0)throw new z("Invalid \\arraystretch: "+w)}r.gullet.beginGroup();var A=[],q=[A],_=[],D=[],N=c!=null?[]:void 0;function $(){c&&r.gullet.macros.set("\\@eqnsw","1",!0)}function H(){N&&(r.gullet.macros.get("\\df@tag")?(N.push(r.subparse([new b0("\\df@tag")])),r.gullet.macros.set("\\df@tag",void 0,!0)):N.push(!!c&&r.gullet.macros.get("\\@eqnsw")==="1"))}for($(),D.push(lr(r));;){var F=r.parseExpression(!1,f?"\\end":"\\\\");r.gullet.endGroup(),r.gullet.beginGroup(),F={type:"ordgroup",mode:r.mode,body:F},t&&(F={type:"styling",mode:r.mode,style:t,body:[F]}),A.push(F);var P=r.fetch().text;if(P==="&"){if(b&&A.length===b){if(f||h)throw new z("Too many tab characters: &",r.nextToken);r.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}r.consume()}else if(P==="\\end"){H(),A.length===1&&F.type==="styling"&&F.body[0].body.length===0&&(q.length>1||!v)&&q.pop(),D.length0&&($+=.25),f.push({pos:$,isDashed:we[Se]})}for(H(l[0]),a=0;a0&&(i0+=N,Vwe))for(a=0;a=h)){var ee=void 0;(n>0||e.hskipBeforeAndAfter)&&(ee=O.deflt(f0.pregap,x),ee!==0&&(x0=y.makeSpan(["arraycolsep"],[]),x0.style.width=T(ee),u0.push(x0)));var te=[];for(a=0;a0){for(var Sa=y.makeLineSpan("hline",t,v),ka=y.makeLineSpan("hdashline",t,v),Je=[{type:"elem",elem:c,shift:0}];f.length>0;){var Ut=f.pop(),Xt=Ut.pos-r0;Ut.isDashed?Je.push({type:"elem",elem:ka,shift:Xt}):Je.push({type:"elem",elem:Sa,shift:Xt})}c=y.makeVList({positionType:"individualShift",children:Je},t)}if(K0.length===0)return y.makeSpan(["mord"],[c],t);var Qe=y.makeVList({positionType:"individualShift",children:K0},t);return Qe=y.makeSpan(["tag"],[Qe],t),y.makeFragment([c,Qe])},en={c:"center ",l:"left ",r:"right "},B0=function(e,t){for(var a=[],n=new M.MathNode("mtd",[],["mtr-glue"]),s=new M.MathNode("mtd",[],["mml-eqn-num"]),l=0;l0){var A=e.cols,q="",_=!1,D=0,N=A.length;A[0].type==="separator"&&(x+="top ",D=1),A[A.length-1].type==="separator"&&(x+="bottom ",N-=1);for(var $=D;$0?"left ":"",x+=j[j.length-1].length>0?"right ":"";for(var U=1;U-1?"alignat":"align",s=e.envName==="split",l=U0(e.parser,{cols:a,addJot:!0,autoTag:s?void 0:Et(e.envName),emptySingleRow:!0,colSeparationType:n,maxNumCols:s?2:void 0,leqno:e.parser.settings.leqno},"display"),h,c=0,f={type:"ordgroup",mode:e.mode,body:[]};if(t[0]&&t[0].type==="ordgroup"){for(var v="",b=0;b0&&w&&(_=1),a[A]={type:"align",align:q,pregap:_,postgap:0}}return l.colSeparationType=w?"align":"alignat",l};T0({type:"array",names:["array","darray"],props:{numArgs:1},handler(r,e){var t=Xe(e[0]),a=t?[e[0]]:L(e[0],"ordgroup").body,n=a.map(function(l){var h=Ct(l),c=h.text;if("lcr".indexOf(c)!==-1)return{type:"align",align:c};if(c==="|")return{type:"separator",separator:"|"};if(c===":")return{type:"separator",separator:":"};throw new z("Unknown column alignment: "+c,l)}),s={cols:n,hskipBeforeAndAfter:!0,maxNumCols:n.length};return U0(r.parser,s,Rt(r.envName))},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(r){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[r.envName.replace("*","")],t="c",a={hskipBeforeAndAfter:!1,cols:[{type:"align",align:t}]};if(r.envName.charAt(r.envName.length-1)==="*"){var n=r.parser;if(n.consumeSpaces(),n.fetch().text==="["){if(n.consume(),n.consumeSpaces(),t=n.fetch().text,"lcr".indexOf(t)===-1)throw new z("Expected l or c or r",n.nextToken);n.consume(),n.consumeSpaces(),n.expect("]"),n.consume(),a.cols=[{type:"align",align:t}]}}var s=U0(r.parser,a,Rt(r.envName)),l=Math.max(0,...s.body.map(h=>h.length));return s.cols=new Array(l).fill({type:"align",align:t}),e?{type:"leftright",mode:r.mode,body:[s],left:e[0],right:e[1],rightColor:void 0}:s},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(r){var e={arraystretch:.5},t=U0(r.parser,e,"script");return t.colSeparationType="small",t},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["subarray"],props:{numArgs:1},handler(r,e){var t=Xe(e[0]),a=t?[e[0]]:L(e[0],"ordgroup").body,n=a.map(function(l){var h=Ct(l),c=h.text;if("lc".indexOf(c)!==-1)return{type:"align",align:c};throw new z("Unknown column alignment: "+c,l)});if(n.length>1)throw new z("{subarray} can contain only one column");var s={cols:n,hskipBeforeAndAfter:!1,arraystretch:.5};if(s=U0(r.parser,s,"script"),s.body.length>0&&s.body[0].length>1)throw new z("{subarray} can contain only one column");return s},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(r){var e={arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},t=U0(r.parser,e,Rt(r.envName));return{type:"leftright",mode:r.mode,body:[t],left:r.envName.indexOf("r")>-1?".":"\\{",right:r.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:na,htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(r){O.contains(["gather","gather*"],r.envName)&&Ze(r);var e={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Et(r.envName),emptySingleRow:!0,leqno:r.parser.settings.leqno};return U0(r.parser,e,"display")},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:na,htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(r){Ze(r);var e={autoTag:Et(r.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:r.parser.settings.leqno};return U0(r.parser,e,"display")},htmlBuilder:q0,mathmlBuilder:B0});T0({type:"array",names:["CD"],props:{numArgs:0},handler(r){return Ze(r),F1(r.parser)},htmlBuilder:q0,mathmlBuilder:B0});m("\\nonumber","\\gdef\\@eqnsw{0}");m("\\notag","\\nonumber");B({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler(r,e){throw new z(r.funcName+" valid only within array environment")}});var ur=ra;B({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];if(n.type!=="ordgroup")throw new z("Invalid environment name",n);for(var s="",l=0;l{var t=r.font,a=e.withFont(t);return G(r.body,a)},sa=(r,e)=>{var t=r.font,a=e.withFont(t);return X(r.body,a)},hr={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};B({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathsfit","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=He(e[0]),s=a;return s in hr&&(s=hr[s]),{type:"font",mode:t.mode,font:s.slice(1),body:n}},htmlBuilder:ia,mathmlBuilder:sa});B({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(r,e)=>{var{parser:t}=r,a=e[0],n=O.isCharacterBox(a);return{type:"mclass",mode:t.mode,mclass:We(a),body:[{type:"font",mode:t.mode,font:"boldsymbol",body:a}],isCharacterBox:n}}});B({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(r,e)=>{var{parser:t,funcName:a,breakOnTokenText:n}=r,{mode:s}=t,l=t.parseExpression(!0,n),h="math"+a.slice(1);return{type:"font",mode:s,font:h,body:{type:"ordgroup",mode:t.mode,body:l}}},htmlBuilder:ia,mathmlBuilder:sa});var oa=(r,e)=>{var t=e;return r==="display"?t=t.id>=E.SCRIPT.id?t.text():E.DISPLAY:r==="text"&&t.size===E.DISPLAY.size?t=E.TEXT:r==="script"?t=E.SCRIPT:r==="scriptscript"&&(t=E.SCRIPTSCRIPT),t},$t=(r,e)=>{var t=oa(r.size,e.style),a=t.fracNum(),n=t.fracDen(),s;s=e.havingStyle(a);var l=G(r.numer,s,e);if(r.continued){var h=8.5/e.fontMetrics().ptPerEm,c=3.5/e.fontMetrics().ptPerEm;l.height=l.height0?A=3*x:A=7*x,q=e.fontMetrics().denom1):(b>0?(w=e.fontMetrics().num2,A=x):(w=e.fontMetrics().num3,A=3*x),q=e.fontMetrics().denom2);var _;if(v){var N=e.fontMetrics().axisHeight;w-l.depth-(N+.5*b){var t=new M.MathNode("mfrac",[X(r.numer,e),X(r.denom,e)]);if(!r.hasBarLine)t.setAttribute("linethickness","0px");else if(r.barSize){var a=Q(r.barSize,e);t.setAttribute("linethickness",T(a))}var n=oa(r.size,e.style);if(n.size!==e.style.size){t=new M.MathNode("mstyle",[t]);var s=n.size===E.DISPLAY.size?"true":"false";t.setAttribute("displaystyle",s),t.setAttribute("scriptlevel","0")}if(r.leftDelim!=null||r.rightDelim!=null){var l=[];if(r.leftDelim!=null){var h=new M.MathNode("mo",[new M.TextNode(r.leftDelim.replace("\\",""))]);h.setAttribute("fence","true"),l.push(h)}if(l.push(t),r.rightDelim!=null){var c=new M.MathNode("mo",[new M.TextNode(r.rightDelim.replace("\\",""))]);c.setAttribute("fence","true"),l.push(c)}return Bt(l)}return t};B({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=e[1],l,h=null,c=null,f="auto";switch(a){case"\\dfrac":case"\\frac":case"\\tfrac":l=!0;break;case"\\\\atopfrac":l=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":l=!1,h="(",c=")";break;case"\\\\bracefrac":l=!1,h="\\{",c="\\}";break;case"\\\\brackfrac":l=!1,h="[",c="]";break;default:throw new Error("Unrecognized genfrac command")}switch(a){case"\\dfrac":case"\\dbinom":f="display";break;case"\\tfrac":case"\\tbinom":f="text";break}return{type:"genfrac",mode:t.mode,continued:!1,numer:n,denom:s,hasBarLine:l,leftDelim:h,rightDelim:c,size:f,barSize:null}},htmlBuilder:$t,mathmlBuilder:Lt});B({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=e[1];return{type:"genfrac",mode:t.mode,continued:!0,numer:n,denom:s,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}}});B({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(r){var{parser:e,funcName:t,token:a}=r,n;switch(t){case"\\over":n="\\frac";break;case"\\choose":n="\\binom";break;case"\\atop":n="\\\\atopfrac";break;case"\\brace":n="\\\\bracefrac";break;case"\\brack":n="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:e.mode,replaceWith:n,token:a}}});var cr=["display","text","script","scriptscript"],mr=function(e){var t=null;return e.length>0&&(t=e,t=t==="."?null:t),t};B({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(r,e){var{parser:t}=r,a=e[4],n=e[5],s=He(e[0]),l=s.type==="atom"&&s.family==="open"?mr(s.text):null,h=He(e[1]),c=h.type==="atom"&&h.family==="close"?mr(h.text):null,f=L(e[2],"size"),v,b=null;f.isBlank?v=!0:(b=f.value,v=b.number>0);var x="auto",w=e[3];if(w.type==="ordgroup"){if(w.body.length>0){var A=L(w.body[0],"textord");x=cr[Number(A.text)]}}else w=L(w,"textord"),x=cr[Number(w.text)];return{type:"genfrac",mode:t.mode,numer:a,denom:n,continued:!1,hasBarLine:v,barSize:b,leftDelim:l,rightDelim:c,size:x}},htmlBuilder:$t,mathmlBuilder:Lt});B({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(r,e){var{parser:t,funcName:a,token:n}=r;return{type:"infix",mode:t.mode,replaceWith:"\\\\abovefrac",size:L(e[0],"size").value,token:n}}});B({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0],s=_a(L(e[1],"infix").size),l=e[2],h=s.number>0;return{type:"genfrac",mode:t.mode,numer:n,denom:l,continued:!1,hasBarLine:h,barSize:s,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:$t,mathmlBuilder:Lt});var la=(r,e)=>{var t=e.style,a,n;r.type==="supsub"?(a=r.sup?G(r.sup,e.havingStyle(t.sup()),e):G(r.sub,e.havingStyle(t.sub()),e),n=L(r.base,"horizBrace")):n=L(r,"horizBrace");var s=G(n.base,e.havingBaseStyle(E.DISPLAY)),l=E0.svgSpan(n,e),h;if(n.isOver?(h=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:l}]},e),h.children[0].children[0].children[1].classes.push("svg-align")):(h=y.makeVList({positionType:"bottom",positionData:s.depth+.1+l.height,children:[{type:"elem",elem:l},{type:"kern",size:.1},{type:"elem",elem:s}]},e),h.children[0].children[0].children[0].classes.push("svg-align")),a){var c=y.makeSpan(["mord",n.isOver?"mover":"munder"],[h],e);n.isOver?h=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:c},{type:"kern",size:.2},{type:"elem",elem:a}]},e):h=y.makeVList({positionType:"bottom",positionData:c.depth+.2+a.height+a.depth,children:[{type:"elem",elem:a},{type:"kern",size:.2},{type:"elem",elem:c}]},e)}return y.makeSpan(["mord",n.isOver?"mover":"munder"],[h],e)},tn=(r,e)=>{var t=E0.mathMLnode(r.label);return new M.MathNode(r.isOver?"mover":"munder",[X(r.base,e),t])};B({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(r,e){var{parser:t,funcName:a}=r;return{type:"horizBrace",mode:t.mode,label:a,isOver:/^\\over/.test(a),base:e[0]}},htmlBuilder:la,mathmlBuilder:tn});B({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[1],n=L(e[0],"url").url;return t.settings.isTrusted({command:"\\href",url:n})?{type:"href",mode:t.mode,href:n,body:e0(a)}:t.formatUnsupportedCmd("\\href")},htmlBuilder:(r,e)=>{var t=a0(r.body,e,!1);return y.makeAnchor(r.href,[],t,e)},mathmlBuilder:(r,e)=>{var t=V0(r.body,e);return t instanceof s0||(t=new s0("mrow",[t])),t.setAttribute("href",r.href),t}});B({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=L(e[0],"url").url;if(!t.settings.isTrusted({command:"\\url",url:a}))return t.formatUnsupportedCmd("\\url");for(var n=[],s=0;s{var{parser:t,funcName:a,token:n}=r,s=L(e[0],"raw").string,l=e[1];t.settings.strict&&t.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");var h,c={};switch(a){case"\\htmlClass":c.class=s,h={command:"\\htmlClass",class:s};break;case"\\htmlId":c.id=s,h={command:"\\htmlId",id:s};break;case"\\htmlStyle":c.style=s,h={command:"\\htmlStyle",style:s};break;case"\\htmlData":{for(var f=s.split(","),v=0;v{var t=a0(r.body,e,!1),a=["enclosing"];r.attributes.class&&a.push(...r.attributes.class.trim().split(/\s+/));var n=y.makeSpan(a,t,e);for(var s in r.attributes)s!=="class"&&r.attributes.hasOwnProperty(s)&&n.setAttribute(s,r.attributes[s]);return n},mathmlBuilder:(r,e)=>V0(r.body,e)});B({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r;return{type:"htmlmathml",mode:t.mode,html:e0(e[0]),mathml:e0(e[1])}},htmlBuilder:(r,e)=>{var t=a0(r.html,e,!1);return y.makeFragment(t)},mathmlBuilder:(r,e)=>V0(r.mathml,e)});var ht=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};var t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new z("Invalid size: '"+e+"' in \\includegraphics");var a={number:+(t[1]+t[2]),unit:t[3]};if(!Tr(a))throw new z("Invalid unit: '"+a.unit+"' in \\includegraphics.");return a};B({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(r,e,t)=>{var{parser:a}=r,n={number:0,unit:"em"},s={number:.9,unit:"em"},l={number:0,unit:"em"},h="";if(t[0])for(var c=L(t[0],"raw").string,f=c.split(","),v=0;v{var t=Q(r.height,e),a=0;r.totalheight.number>0&&(a=Q(r.totalheight,e)-t);var n=0;r.width.number>0&&(n=Q(r.width,e));var s={height:T(t+a)};n>0&&(s.width=T(n)),a>0&&(s.verticalAlign=T(-a));var l=new vt(r.src,r.alt,s);return l.height=t,l.depth=a,l},mathmlBuilder:(r,e)=>{var t=new M.MathNode("mglyph",[]);t.setAttribute("alt",r.alt);var a=Q(r.height,e),n=0;if(r.totalheight.number>0&&(n=Q(r.totalheight,e)-a,t.setAttribute("valign",T(-n))),t.setAttribute("height",T(a+n)),r.width.number>0){var s=Q(r.width,e);t.setAttribute("width",T(s))}return t.setAttribute("src",r.src),t}});B({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(r,e){var{parser:t,funcName:a}=r,n=L(e[0],"size");if(t.settings.strict){var s=a[1]==="m",l=n.value.unit==="mu";s?(l||t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" supports only mu units, "+("not "+n.value.unit+" units")),t.mode!=="math"&&t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" works only in math mode")):l&&t.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" doesn't support mu units")}return{type:"kern",mode:t.mode,dimension:n.value}},htmlBuilder(r,e){return y.makeGlue(r.dimension,e)},mathmlBuilder(r,e){var t=Q(r.dimension,e);return new M.SpaceNode(t)}});B({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"lap",mode:t.mode,alignment:a.slice(5),body:n}},htmlBuilder:(r,e)=>{var t;r.alignment==="clap"?(t=y.makeSpan([],[G(r.body,e)]),t=y.makeSpan(["inner"],[t],e)):t=y.makeSpan(["inner"],[G(r.body,e)]);var a=y.makeSpan(["fix"],[]),n=y.makeSpan([r.alignment],[t,a],e),s=y.makeSpan(["strut"]);return s.style.height=T(n.height+n.depth),n.depth&&(s.style.verticalAlign=T(-n.depth)),n.children.unshift(s),n=y.makeSpan(["thinbox"],[n],e),y.makeSpan(["mord","vbox"],[n],e)},mathmlBuilder:(r,e)=>{var t=new M.MathNode("mpadded",[X(r.body,e)]);if(r.alignment!=="rlap"){var a=r.alignment==="llap"?"-1":"-0.5";t.setAttribute("lspace",a+"width")}return t.setAttribute("width","0px"),t}});B({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(r,e){var{funcName:t,parser:a}=r,n=a.mode;a.switchMode("math");var s=t==="\\("?"\\)":"$",l=a.parseExpression(!1,s);return a.expect(s),a.switchMode(n),{type:"styling",mode:a.mode,style:"text",body:l}}});B({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(r,e){throw new z("Mismatched "+r.funcName)}});var dr=(r,e)=>{switch(e.style.size){case E.DISPLAY.size:return r.display;case E.TEXT.size:return r.text;case E.SCRIPT.size:return r.script;case E.SCRIPTSCRIPT.size:return r.scriptscript;default:return r.text}};B({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(r,e)=>{var{parser:t}=r;return{type:"mathchoice",mode:t.mode,display:e0(e[0]),text:e0(e[1]),script:e0(e[2]),scriptscript:e0(e[3])}},htmlBuilder:(r,e)=>{var t=dr(r,e),a=a0(t,e,!1);return y.makeFragment(a)},mathmlBuilder:(r,e)=>{var t=dr(r,e);return V0(t,e)}});var ua=(r,e,t,a,n,s,l)=>{r=y.makeSpan([],[r]);var h=t&&O.isCharacterBox(t),c,f;if(e){var v=G(e,a.havingStyle(n.sup()),a);f={elem:v,kern:Math.max(a.fontMetrics().bigOpSpacing1,a.fontMetrics().bigOpSpacing3-v.depth)}}if(t){var b=G(t,a.havingStyle(n.sub()),a);c={elem:b,kern:Math.max(a.fontMetrics().bigOpSpacing2,a.fontMetrics().bigOpSpacing4-b.height)}}var x;if(f&&c){var w=a.fontMetrics().bigOpSpacing5+c.elem.height+c.elem.depth+c.kern+r.depth+l;x=y.makeVList({positionType:"bottom",positionData:w,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:c.elem,marginLeft:T(-s)},{type:"kern",size:c.kern},{type:"elem",elem:r},{type:"kern",size:f.kern},{type:"elem",elem:f.elem,marginLeft:T(s)},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else if(c){var A=r.height-l;x=y.makeVList({positionType:"top",positionData:A,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:c.elem,marginLeft:T(-s)},{type:"kern",size:c.kern},{type:"elem",elem:r}]},a)}else if(f){var q=r.depth+l;x=y.makeVList({positionType:"bottom",positionData:q,children:[{type:"elem",elem:r},{type:"kern",size:f.kern},{type:"elem",elem:f.elem,marginLeft:T(s)},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else return r;var _=[x];if(c&&s!==0&&!h){var D=y.makeSpan(["mspace"],[],a);D.style.marginRight=T(s),_.unshift(D)}return y.makeSpan(["mop","op-limits"],_,a)},ha=["\\smallint"],se=(r,e)=>{var t,a,n=!1,s;r.type==="supsub"?(t=r.sup,a=r.sub,s=L(r.base,"op"),n=!0):s=L(r,"op");var l=e.style,h=!1;l.size===E.DISPLAY.size&&s.symbol&&!O.contains(ha,s.name)&&(h=!0);var c;if(s.symbol){var f=h?"Size2-Regular":"Size1-Regular",v="";if((s.name==="\\oiint"||s.name==="\\oiiint")&&(v=s.name.slice(1),s.name=v==="oiint"?"\\iint":"\\iiint"),c=y.makeSymbol(s.name,f,"math",e,["mop","op-symbol",h?"large-op":"small-op"]),v.length>0){var b=c.italic,x=y.staticSvg(v+"Size"+(h?"2":"1"),e);c=y.makeVList({positionType:"individualShift",children:[{type:"elem",elem:c,shift:0},{type:"elem",elem:x,shift:h?.08:0}]},e),s.name="\\"+v,c.classes.unshift("mop"),c.italic=b}}else if(s.body){var w=a0(s.body,e,!0);w.length===1&&w[0]instanceof c0?(c=w[0],c.classes[0]="mop"):c=y.makeSpan(["mop"],w,e)}else{for(var A=[],q=1;q{var t;if(r.symbol)t=new s0("mo",[y0(r.name,r.mode)]),O.contains(ha,r.name)&&t.setAttribute("largeop","false");else if(r.body)t=new s0("mo",m0(r.body,e));else{t=new s0("mi",[new g0(r.name.slice(1))]);var a=new s0("mo",[y0("\u2061","text")]);r.parentIsSupSub?t=new s0("mrow",[t,a]):t=$r([t,a])}return t},rn={"\u220F":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22C0":"\\bigwedge","\u22C1":"\\bigvee","\u22C2":"\\bigcap","\u22C3":"\\bigcup","\u2A00":"\\bigodot","\u2A01":"\\bigoplus","\u2A02":"\\bigotimes","\u2A04":"\\biguplus","\u2A06":"\\bigsqcup"};B({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220F","\u2210","\u2211","\u22C0","\u22C1","\u22C2","\u22C3","\u2A00","\u2A01","\u2A02","\u2A04","\u2A06"],props:{numArgs:0},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=a;return n.length===1&&(n=rn[n]),{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:se,mathmlBuilder:be});B({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:e0(a)}},htmlBuilder:se,mathmlBuilder:be});var an={"\u222B":"\\int","\u222C":"\\iint","\u222D":"\\iiint","\u222E":"\\oint","\u222F":"\\oiint","\u2230":"\\oiiint"};B({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:t}},htmlBuilder:se,mathmlBuilder:be});B({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:t}},htmlBuilder:se,mathmlBuilder:be});B({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222B","\u222C","\u222D","\u222E","\u222F","\u2230"],props:{numArgs:0},handler(r){var{parser:e,funcName:t}=r,a=t;return a.length===1&&(a=an[a]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:a}},htmlBuilder:se,mathmlBuilder:be});var ca=(r,e)=>{var t,a,n=!1,s;r.type==="supsub"?(t=r.sup,a=r.sub,s=L(r.base,"operatorname"),n=!0):s=L(r,"operatorname");var l;if(s.body.length>0){for(var h=s.body.map(b=>{var x=b.text;return typeof x=="string"?{type:"textord",mode:b.mode,text:x}:b}),c=a0(h,e.withFont("mathrm"),!0),f=0;f{for(var t=m0(r.body,e.withFont("mathrm")),a=!0,n=0;nv.toText()).join("");t=[new M.TextNode(h)]}var c=new M.MathNode("mi",t);c.setAttribute("mathvariant","normal");var f=new M.MathNode("mo",[y0("\u2061","text")]);return r.parentIsSupSub?new M.MathNode("mrow",[c,f]):M.newDocumentFragment([c,f])};B({type:"operatorname",names:["\\operatorname@","\\operatornamewithlimits"],props:{numArgs:1},handler:(r,e)=>{var{parser:t,funcName:a}=r,n=e[0];return{type:"operatorname",mode:t.mode,body:e0(n),alwaysHandleSupSub:a==="\\operatornamewithlimits",limits:!1,parentIsSupSub:!1}},htmlBuilder:ca,mathmlBuilder:nn});m("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@");j0({type:"ordgroup",htmlBuilder(r,e){return r.semisimple?y.makeFragment(a0(r.body,e,!1)):y.makeSpan(["mord"],a0(r.body,e,!0),e)},mathmlBuilder(r,e){return V0(r.body,e,!0)}});B({type:"overline",names:["\\overline"],props:{numArgs:1},handler(r,e){var{parser:t}=r,a=e[0];return{type:"overline",mode:t.mode,body:a}},htmlBuilder(r,e){var t=G(r.body,e.havingCrampedStyle()),a=y.makeLineSpan("overline-line",e),n=e.fontMetrics().defaultRuleThickness,s=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:t},{type:"kern",size:3*n},{type:"elem",elem:a},{type:"kern",size:n}]},e);return y.makeSpan(["mord","overline"],[s],e)},mathmlBuilder(r,e){var t=new M.MathNode("mo",[new M.TextNode("\u203E")]);t.setAttribute("stretchy","true");var a=new M.MathNode("mover",[X(r.body,e),t]);return a.setAttribute("accent","true"),a}});B({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"phantom",mode:t.mode,body:e0(a)}},htmlBuilder:(r,e)=>{var t=a0(r.body,e.withPhantom(),!1);return y.makeFragment(t)},mathmlBuilder:(r,e)=>{var t=m0(r.body,e);return new M.MathNode("mphantom",t)}});B({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"hphantom",mode:t.mode,body:a}},htmlBuilder:(r,e)=>{var t=y.makeSpan([],[G(r.body,e.withPhantom())]);if(t.height=0,t.depth=0,t.children)for(var a=0;a{var t=m0(e0(r.body),e),a=new M.MathNode("mphantom",t),n=new M.MathNode("mpadded",[a]);return n.setAttribute("height","0px"),n.setAttribute("depth","0px"),n}});B({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(r,e)=>{var{parser:t}=r,a=e[0];return{type:"vphantom",mode:t.mode,body:a}},htmlBuilder:(r,e)=>{var t=y.makeSpan(["inner"],[G(r.body,e.withPhantom())]),a=y.makeSpan(["fix"],[]);return y.makeSpan(["mord","rlap"],[t,a],e)},mathmlBuilder:(r,e)=>{var t=m0(e0(r.body),e),a=new M.MathNode("mphantom",t),n=new M.MathNode("mpadded",[a]);return n.setAttribute("width","0px"),n}});B({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(r,e){var{parser:t}=r,a=L(e[0],"size").value,n=e[1];return{type:"raisebox",mode:t.mode,dy:a,body:n}},htmlBuilder(r,e){var t=G(r.body,e),a=Q(r.dy,e);return y.makeVList({positionType:"shift",positionData:-a,children:[{type:"elem",elem:t}]},e)},mathmlBuilder(r,e){var t=new M.MathNode("mpadded",[X(r.body,e)]),a=r.dy.number+r.dy.unit;return t.setAttribute("voffset",a),t}});B({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0,allowedInArgument:!0},handler(r){var{parser:e}=r;return{type:"internal",mode:e.mode}}});B({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["size","size","size"]},handler(r,e,t){var{parser:a}=r,n=t[0],s=L(e[0],"size"),l=L(e[1],"size");return{type:"rule",mode:a.mode,shift:n&&L(n,"size").value,width:s.value,height:l.value}},htmlBuilder(r,e){var t=y.makeSpan(["mord","rule"],[],e),a=Q(r.width,e),n=Q(r.height,e),s=r.shift?Q(r.shift,e):0;return t.style.borderRightWidth=T(a),t.style.borderTopWidth=T(n),t.style.bottom=T(s),t.width=a,t.height=n+s,t.depth=-s,t.maxFontSize=n*1.125*e.sizeMultiplier,t},mathmlBuilder(r,e){var t=Q(r.width,e),a=Q(r.height,e),n=r.shift?Q(r.shift,e):0,s=e.color&&e.getColor()||"black",l=new M.MathNode("mspace");l.setAttribute("mathbackground",s),l.setAttribute("width",T(t)),l.setAttribute("height",T(a));var h=new M.MathNode("mpadded",[l]);return n>=0?h.setAttribute("height",T(n)):(h.setAttribute("height",T(n)),h.setAttribute("depth",T(-n))),h.setAttribute("voffset",T(n)),h}});function ma(r,e,t){for(var a=a0(r,e,!1),n=e.sizeMultiplier/t.sizeMultiplier,s=0;s{var t=e.havingSize(r.size);return ma(r.body,t,e)};B({type:"sizing",names:pr,props:{numArgs:0,allowedInText:!0},handler:(r,e)=>{var{breakOnTokenText:t,funcName:a,parser:n}=r,s=n.parseExpression(!1,t);return{type:"sizing",mode:n.mode,size:pr.indexOf(a)+1,body:s}},htmlBuilder:sn,mathmlBuilder:(r,e)=>{var t=e.havingSize(r.size),a=m0(r.body,t),n=new M.MathNode("mstyle",a);return n.setAttribute("mathsize",T(t.sizeMultiplier)),n}});B({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(r,e,t)=>{var{parser:a}=r,n=!1,s=!1,l=t[0]&&L(t[0],"ordgroup");if(l)for(var h="",c=0;c{var t=y.makeSpan([],[G(r.body,e)]);if(!r.smashHeight&&!r.smashDepth)return t;if(r.smashHeight&&(t.height=0,t.children))for(var a=0;a{var t=new M.MathNode("mpadded",[X(r.body,e)]);return r.smashHeight&&t.setAttribute("height","0px"),r.smashDepth&&t.setAttribute("depth","0px"),t}});B({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(r,e,t){var{parser:a}=r,n=t[0],s=e[0];return{type:"sqrt",mode:a.mode,body:s,index:n}},htmlBuilder(r,e){var t=G(r.body,e.havingCrampedStyle());t.height===0&&(t.height=e.fontMetrics().xHeight),t=y.wrapFragment(t,e);var a=e.fontMetrics(),n=a.defaultRuleThickness,s=n;e.style.idt.height+t.depth+l&&(l=(l+b-t.height-t.depth)/2);var x=c.height-t.height-l-f;t.style.paddingLeft=T(v);var w=y.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:t,wrapperClasses:["svg-align"]},{type:"kern",size:-(t.height+x)},{type:"elem",elem:c},{type:"kern",size:f}]},e);if(r.index){var A=e.havingStyle(E.SCRIPTSCRIPT),q=G(r.index,A,e),_=.6*(w.height-w.depth),D=y.makeVList({positionType:"shift",positionData:-_,children:[{type:"elem",elem:q}]},e),N=y.makeSpan(["root"],[D]);return y.makeSpan(["mord","sqrt"],[N,w],e)}else return y.makeSpan(["mord","sqrt"],[w],e)},mathmlBuilder(r,e){var{body:t,index:a}=r;return a?new M.MathNode("mroot",[X(t,e),X(a,e)]):new M.MathNode("msqrt",[X(t,e)])}});var fr={display:E.DISPLAY,text:E.TEXT,script:E.SCRIPT,scriptscript:E.SCRIPTSCRIPT};B({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(r,e){var{breakOnTokenText:t,funcName:a,parser:n}=r,s=n.parseExpression(!0,t),l=a.slice(1,a.length-5);return{type:"styling",mode:n.mode,style:l,body:s}},htmlBuilder(r,e){var t=fr[r.style],a=e.havingStyle(t).withFont("");return ma(r.body,a,e)},mathmlBuilder(r,e){var t=fr[r.style],a=e.havingStyle(t),n=m0(r.body,a),s=new M.MathNode("mstyle",n),l={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]},h=l[r.style];return s.setAttribute("scriptlevel",h[0]),s.setAttribute("displaystyle",h[1]),s}});var on=function(e,t){var a=e.base;if(a)if(a.type==="op"){var n=a.limits&&(t.style.size===E.DISPLAY.size||a.alwaysHandleSupSub);return n?se:null}else if(a.type==="operatorname"){var s=a.alwaysHandleSupSub&&(t.style.size===E.DISPLAY.size||a.limits);return s?ca:null}else{if(a.type==="accent")return O.isCharacterBox(a.base)?_t:null;if(a.type==="horizBrace"){var l=!e.sub;return l===a.isOver?la:null}else return null}else return null};j0({type:"supsub",htmlBuilder(r,e){var t=on(r,e);if(t)return t(r,e);var{base:a,sup:n,sub:s}=r,l=G(a,e),h,c,f=e.fontMetrics(),v=0,b=0,x=a&&O.isCharacterBox(a);if(n){var w=e.havingStyle(e.style.sup());h=G(n,w,e),x||(v=l.height-w.fontMetrics().supDrop*w.sizeMultiplier/e.sizeMultiplier)}if(s){var A=e.havingStyle(e.style.sub());c=G(s,A,e),x||(b=l.depth+A.fontMetrics().subDrop*A.sizeMultiplier/e.sizeMultiplier)}var q;e.style===E.DISPLAY?q=f.sup1:e.style.cramped?q=f.sup3:q=f.sup2;var _=e.sizeMultiplier,D=T(.5/f.ptPerEm/_),N=null;if(c){var $=r.base&&r.base.type==="op"&&r.base.name&&(r.base.name==="\\oiint"||r.base.name==="\\oiiint");(l instanceof c0||$)&&(N=T(-l.italic))}var H;if(h&&c){v=Math.max(v,q,h.depth+.25*f.xHeight),b=Math.max(b,f.sub2);var F=f.defaultRuleThickness,P=4*F;if(v-h.depth-(c.height-b)0&&(v+=V,b-=V)}var j=[{type:"elem",elem:c,shift:b,marginRight:D,marginLeft:N},{type:"elem",elem:h,shift:-v,marginRight:D}];H=y.makeVList({positionType:"individualShift",children:j},e)}else if(c){b=Math.max(b,f.sub1,c.height-.8*f.xHeight);var U=[{type:"elem",elem:c,marginLeft:N,marginRight:D}];H=y.makeVList({positionType:"shift",positionData:b,children:U},e)}else if(h)v=Math.max(v,q,h.depth+.25*f.xHeight),H=y.makeVList({positionType:"shift",positionData:-v,children:[{type:"elem",elem:h,marginRight:D}]},e);else throw new Error("supsub must have either sup or sub.");var D0=bt(l,"right")||"mord";return y.makeSpan([D0],[l,y.makeSpan(["msupsub"],[H])],e)},mathmlBuilder(r,e){var t=!1,a,n;r.base&&r.base.type==="horizBrace"&&(n=!!r.sup,n===r.base.isOver&&(t=!0,a=r.base.isOver)),r.base&&(r.base.type==="op"||r.base.type==="operatorname")&&(r.base.parentIsSupSub=!0);var s=[X(r.base,e)];r.sub&&s.push(X(r.sub,e)),r.sup&&s.push(X(r.sup,e));var l;if(t)l=a?"mover":"munder";else if(r.sub)if(r.sup){var f=r.base;f&&f.type==="op"&&f.limits&&e.style===E.DISPLAY||f&&f.type==="operatorname"&&f.alwaysHandleSupSub&&(e.style===E.DISPLAY||f.limits)?l="munderover":l="msubsup"}else{var c=r.base;c&&c.type==="op"&&c.limits&&(e.style===E.DISPLAY||c.alwaysHandleSupSub)||c&&c.type==="operatorname"&&c.alwaysHandleSupSub&&(c.limits||e.style===E.DISPLAY)?l="munder":l="msub"}else{var h=r.base;h&&h.type==="op"&&h.limits&&(e.style===E.DISPLAY||h.alwaysHandleSupSub)||h&&h.type==="operatorname"&&h.alwaysHandleSupSub&&(h.limits||e.style===E.DISPLAY)?l="mover":l="msup"}return new M.MathNode(l,s)}});j0({type:"atom",htmlBuilder(r,e){return y.mathsym(r.text,r.mode,e,["m"+r.family])},mathmlBuilder(r,e){var t=new M.MathNode("mo",[y0(r.text,r.mode)]);if(r.family==="bin"){var a=Dt(r,e);a==="bold-italic"&&t.setAttribute("mathvariant",a)}else r.family==="punct"?t.setAttribute("separator","true"):(r.family==="open"||r.family==="close")&&t.setAttribute("stretchy","false");return t}});var da={mi:"italic",mn:"normal",mtext:"normal"};j0({type:"mathord",htmlBuilder(r,e){return y.makeOrd(r,e,"mathord")},mathmlBuilder(r,e){var t=new M.MathNode("mi",[y0(r.text,r.mode,e)]),a=Dt(r,e)||"italic";return a!==da[t.type]&&t.setAttribute("mathvariant",a),t}});j0({type:"textord",htmlBuilder(r,e){return y.makeOrd(r,e,"textord")},mathmlBuilder(r,e){var t=y0(r.text,r.mode,e),a=Dt(r,e)||"normal",n;return r.mode==="text"?n=new M.MathNode("mtext",[t]):/[0-9]/.test(r.text)?n=new M.MathNode("mn",[t]):r.text==="\\prime"?n=new M.MathNode("mo",[t]):n=new M.MathNode("mi",[t]),a!==da[n.type]&&n.setAttribute("mathvariant",a),n}});var ct={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},mt={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};j0({type:"spacing",htmlBuilder(r,e){if(mt.hasOwnProperty(r.text)){var t=mt[r.text].className||"";if(r.mode==="text"){var a=y.makeOrd(r,e,"textord");return a.classes.push(t),a}else return y.makeSpan(["mspace",t],[y.mathsym(r.text,r.mode,e)],e)}else{if(ct.hasOwnProperty(r.text))return y.makeSpan(["mspace",ct[r.text]],[],e);throw new z('Unknown type of space "'+r.text+'"')}},mathmlBuilder(r,e){var t;if(mt.hasOwnProperty(r.text))t=new M.MathNode("mtext",[new M.TextNode("\xA0")]);else{if(ct.hasOwnProperty(r.text))return new M.MathNode("mspace");throw new z('Unknown type of space "'+r.text+'"')}return t}});var vr=()=>{var r=new M.MathNode("mtd",[]);return r.setAttribute("width","50%"),r};j0({type:"tag",mathmlBuilder(r,e){var t=new M.MathNode("mtable",[new M.MathNode("mtr",[vr(),new M.MathNode("mtd",[V0(r.body,e)]),vr(),new M.MathNode("mtd",[V0(r.tag,e)])])]);return t.setAttribute("width","100%"),t}});var gr={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},br={"\\textbf":"textbf","\\textmd":"textmd"},ln={"\\textit":"textit","\\textup":"textup"},yr=(r,e)=>{var t=r.font;if(t){if(gr[t])return e.withTextFontFamily(gr[t]);if(br[t])return e.withTextFontWeight(br[t]);if(t==="\\emph")return e.fontShape==="textit"?e.withTextFontShape("textup"):e.withTextFontShape("textit")}else return e;return e.withTextFontShape(ln[t])};B({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup","\\emph"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(r,e){var{parser:t,funcName:a}=r,n=e[0];return{type:"text",mode:t.mode,body:e0(n),font:a}},htmlBuilder(r,e){var t=yr(r,e),a=a0(r.body,t,!0);return y.makeSpan(["mord","text"],a,t)},mathmlBuilder(r,e){var t=yr(r,e);return V0(r.body,t)}});B({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(r,e){var{parser:t}=r;return{type:"underline",mode:t.mode,body:e[0]}},htmlBuilder(r,e){var t=G(r.body,e),a=y.makeLineSpan("underline-line",e),n=e.fontMetrics().defaultRuleThickness,s=y.makeVList({positionType:"top",positionData:t.height,children:[{type:"kern",size:n},{type:"elem",elem:a},{type:"kern",size:3*n},{type:"elem",elem:t}]},e);return y.makeSpan(["mord","underline"],[s],e)},mathmlBuilder(r,e){var t=new M.MathNode("mo",[new M.TextNode("\u203E")]);t.setAttribute("stretchy","true");var a=new M.MathNode("munder",[X(r.body,e),t]);return a.setAttribute("accentunder","true"),a}});B({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(r,e){var{parser:t}=r;return{type:"vcenter",mode:t.mode,body:e[0]}},htmlBuilder(r,e){var t=G(r.body,e),a=e.fontMetrics().axisHeight,n=.5*(t.height-a-(t.depth+a));return y.makeVList({positionType:"shift",positionData:n,children:[{type:"elem",elem:t}]},e)},mathmlBuilder(r,e){return new M.MathNode("mpadded",[X(r.body,e)],["vcenter"])}});B({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(r,e,t){throw new z("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(r,e){for(var t=xr(r),a=[],n=e.havingStyle(e.style.text()),s=0;sr.body.replace(/ /g,r.star?"\u2423":"\xA0"),P0=Er,pa=`[ \r + ]`,un="\\\\[a-zA-Z@]+",hn="\\\\[^\uD800-\uDFFF]",cn="("+un+")"+pa+"*",mn=`\\\\( |[ \r ]+ -?)[ \r ]*`,yt="[\u0300-\u036F]",l4=new RegExp(yt+"+$"),o4="("+ha+"+)|"+(s4+"|")+"([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]"+(yt+"*")+"|[\uD800-\uDBFF][\uDC00-\uDFFF]"+(yt+"*")+"|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5"+("|"+i4)+("|"+n4+")"),Le=class{constructor(e,t){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=t,this.tokenRegex=new RegExp(o4,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){var e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new p0("EOF",new m0(this,t,t));var a=this.tokenRegex.exec(e);if(a===null||a.index!==t)throw new M("Unexpected character: '"+e[t]+"'",new p0(e[t],new m0(this,t,t+1)));var n=a[6]||a[3]||(a[2]?"\\ ":" ");if(this.catcodes[n]===14){var s=e.indexOf(` -`,this.tokenRegex.lastIndex);return s===-1?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=s+1,this.lex()}return new p0(n,new m0(this,t,this.tokenRegex.lastIndex))}},xt=class{constructor(e,t){e===void 0&&(e={}),t===void 0&&(t={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(this.undefStack.length===0)throw new M("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var e=this.undefStack.pop();for(var t in e)e.hasOwnProperty(t)&&(e[t]==null?delete this.current[t]:this.current[t]=e[t])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,a){if(a===void 0&&(a=!1),a){for(var n=0;n0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{var s=this.undefStack[this.undefStack.length-1];s&&!s.hasOwnProperty(e)&&(s[e]=this.current[e])}t==null?delete this.current[e]:this.current[e]=t}},u4=_r;m("\\noexpand",function(r){var e=r.popToken();return r.isExpandable(e.text)&&(e.noexpand=!0,e.treatAsRelax=!0),{tokens:[e],numArgs:0}});m("\\expandafter",function(r){var e=r.popToken();return r.expandOnce(!0),{tokens:[e],numArgs:0}});m("\\@firstoftwo",function(r){var e=r.consumeArgs(2);return{tokens:e[0],numArgs:0}});m("\\@secondoftwo",function(r){var e=r.consumeArgs(2);return{tokens:e[1],numArgs:0}});m("\\@ifnextchar",function(r){var e=r.consumeArgs(3);r.consumeSpaces();var t=r.future();return e[0].length===1&&e[0][0].text===t.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}});m("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}");m("\\TextOrMath",function(r){var e=r.consumeArgs(2);return r.mode==="text"?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});var gr={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};m("\\char",function(r){var e=r.popToken(),t,a="";if(e.text==="'")t=8,e=r.popToken();else if(e.text==='"')t=16,e=r.popToken();else if(e.text==="`")if(e=r.popToken(),e.text[0]==="\\")a=e.text.charCodeAt(1);else{if(e.text==="EOF")throw new M("\\char` missing argument");a=e.text.charCodeAt(0)}else t=10;if(t){if(a=gr[e.text],a==null||a>=t)throw new M("Invalid base-"+t+" digit "+e.text);for(var n;(n=gr[r.future().text])!=null&&n{var a=r.consumeArg().tokens;if(a.length!==1)throw new M("\\newcommand's first argument must be a macro name");var n=a[0].text,s=r.isDefined(n);if(s&&!e)throw new M("\\newcommand{"+n+"} attempting to redefine "+(n+"; use \\renewcommand"));if(!s&&!t)throw new M("\\renewcommand{"+n+"} when command "+n+" does not yet exist; use \\newcommand");var o=0;if(a=r.consumeArg().tokens,a.length===1&&a[0].text==="["){for(var h="",c=r.expandNextToken();c.text!=="]"&&c.text!=="EOF";)h+=c.text,c=r.expandNextToken();if(!h.match(/^\s*[0-9]+\s*$/))throw new M("Invalid number of arguments: "+h);o=parseInt(h),a=r.consumeArg().tokens}return r.macros.set(n,{tokens:a,numArgs:o}),""};m("\\newcommand",r=>Ot(r,!1,!0));m("\\renewcommand",r=>Ot(r,!0,!1));m("\\providecommand",r=>Ot(r,!0,!0));m("\\message",r=>{var e=r.consumeArgs(1)[0];return console.log(e.reverse().map(t=>t.text).join("")),""});m("\\errmessage",r=>{var e=r.consumeArgs(1)[0];return console.error(e.reverse().map(t=>t.text).join("")),""});m("\\show",r=>{var e=r.popToken(),t=e.text;return console.log(e,r.macros.get(t),L0[t],X.math[t],X.text[t]),""});m("\\bgroup","{");m("\\egroup","}");m("~","\\nobreakspace");m("\\lq","`");m("\\rq","'");m("\\aa","\\r a");m("\\AA","\\r A");m("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xA9}");m("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}");m("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xAE}");m("\u212C","\\mathscr{B}");m("\u2130","\\mathscr{E}");m("\u2131","\\mathscr{F}");m("\u210B","\\mathscr{H}");m("\u2110","\\mathscr{I}");m("\u2112","\\mathscr{L}");m("\u2133","\\mathscr{M}");m("\u211B","\\mathscr{R}");m("\u212D","\\mathfrak{C}");m("\u210C","\\mathfrak{H}");m("\u2128","\\mathfrak{Z}");m("\\Bbbk","\\Bbb{k}");m("\xB7","\\cdotp");m("\\llap","\\mathllap{\\textrm{#1}}");m("\\rlap","\\mathrlap{\\textrm{#1}}");m("\\clap","\\mathclap{\\textrm{#1}}");m("\\mathstrut","\\vphantom{(}");m("\\underbar","\\underline{\\text{#1}}");m("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}');m("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}");m("\\ne","\\neq");m("\u2260","\\neq");m("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}");m("\u2209","\\notin");m("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}");m("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}");m("\u225A","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225A}}");m("\u225B","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225B}}");m("\u225D","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225D}}");m("\u225E","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225E}}");m("\u225F","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225F}}");m("\u27C2","\\perp");m("\u203C","\\mathclose{!\\mkern-0.8mu!}");m("\u220C","\\notni");m("\u231C","\\ulcorner");m("\u231D","\\urcorner");m("\u231E","\\llcorner");m("\u231F","\\lrcorner");m("\xA9","\\copyright");m("\xAE","\\textregistered");m("\uFE0F","\\textregistered");m("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}');m("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}');m("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}');m("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}');m("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}");m("\u22EE","\\vdots");m("\\varGamma","\\mathit{\\Gamma}");m("\\varDelta","\\mathit{\\Delta}");m("\\varTheta","\\mathit{\\Theta}");m("\\varLambda","\\mathit{\\Lambda}");m("\\varXi","\\mathit{\\Xi}");m("\\varPi","\\mathit{\\Pi}");m("\\varSigma","\\mathit{\\Sigma}");m("\\varUpsilon","\\mathit{\\Upsilon}");m("\\varPhi","\\mathit{\\Phi}");m("\\varPsi","\\mathit{\\Psi}");m("\\varOmega","\\mathit{\\Omega}");m("\\substack","\\begin{subarray}{c}#1\\end{subarray}");m("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax");m("\\boxed","\\fbox{$\\displaystyle{#1}$}");m("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;");m("\\implies","\\DOTSB\\;\\Longrightarrow\\;");m("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");var br={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};m("\\dots",function(r){var e="\\dotso",t=r.expandAfterFuture().text;return t in br?e=br[t]:(t.slice(0,4)==="\\not"||t in X.math&&N.contains(["bin","rel"],X.math[t].group))&&(e="\\dotsb"),e});var Ht={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};m("\\dotso",function(r){var e=r.future().text;return e in Ht?"\\ldots\\,":"\\ldots"});m("\\dotsc",function(r){var e=r.future().text;return e in Ht&&e!==","?"\\ldots\\,":"\\ldots"});m("\\cdots",function(r){var e=r.future().text;return e in Ht?"\\@cdots\\,":"\\@cdots"});m("\\dotsb","\\cdots");m("\\dotsm","\\cdots");m("\\dotsi","\\!\\cdots");m("\\dotsx","\\ldots\\,");m("\\DOTSI","\\relax");m("\\DOTSB","\\relax");m("\\DOTSX","\\relax");m("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");m("\\,","\\tmspace+{3mu}{.1667em}");m("\\thinspace","\\,");m("\\>","\\mskip{4mu}");m("\\:","\\tmspace+{4mu}{.2222em}");m("\\medspace","\\:");m("\\;","\\tmspace+{5mu}{.2777em}");m("\\thickspace","\\;");m("\\!","\\tmspace-{3mu}{.1667em}");m("\\negthinspace","\\!");m("\\negmedspace","\\tmspace-{4mu}{.2222em}");m("\\negthickspace","\\tmspace-{5mu}{.277em}");m("\\enspace","\\kern.5em ");m("\\enskip","\\hskip.5em\\relax");m("\\quad","\\hskip1em\\relax");m("\\qquad","\\hskip2em\\relax");m("\\tag","\\@ifstar\\tag@literal\\tag@paren");m("\\tag@paren","\\tag@literal{({#1})}");m("\\tag@literal",r=>{if(r.macros.get("\\df@tag"))throw new M("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"});m("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}");m("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)");m("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}");m("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1");m("\\newline","\\\\\\relax");m("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");var ma=A(k0["Main-Regular"][84][1]-.7*k0["Main-Regular"][65][1]);m("\\LaTeX","\\textrm{\\html@mathml{"+("L\\kern-.36em\\raisebox{"+ma+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{LaTeX}}");m("\\KaTeX","\\textrm{\\html@mathml{"+("K\\kern-.17em\\raisebox{"+ma+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{KaTeX}}");m("\\hspace","\\@ifstar\\@hspacer\\@hspace");m("\\@hspace","\\hskip #1\\relax");m("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax");m("\\ordinarycolon",":");m("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}");m("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}');m("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}');m("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}');m("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}');m("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}');m("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}');m("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}');m("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}');m("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}');m("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}');m("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}');m("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}');m("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}');m("\u2237","\\dblcolon");m("\u2239","\\eqcolon");m("\u2254","\\coloneqq");m("\u2255","\\eqqcolon");m("\u2A74","\\Coloneqq");m("\\ratio","\\vcentcolon");m("\\coloncolon","\\dblcolon");m("\\colonequals","\\coloneqq");m("\\coloncolonequals","\\Coloneqq");m("\\equalscolon","\\eqqcolon");m("\\equalscoloncolon","\\Eqqcolon");m("\\colonminus","\\coloneq");m("\\coloncolonminus","\\Coloneq");m("\\minuscolon","\\eqcolon");m("\\minuscoloncolon","\\Eqcolon");m("\\coloncolonapprox","\\Colonapprox");m("\\coloncolonsim","\\Colonsim");m("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}");m("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}");m("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}");m("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}");m("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220C}}");m("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}");m("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}");m("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}");m("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}");m("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}");m("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}");m("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}");m("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}");m("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}");m("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}");m("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}");m("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}");m("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}");m("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}");m("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}");m("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}");m("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}");m("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}");m("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228A}");m("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2ACB}");m("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228B}");m("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2ACC}");m("\\imath","\\html@mathml{\\@imath}{\u0131}");m("\\jmath","\\html@mathml{\\@jmath}{\u0237}");m("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27E6}}");m("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27E7}}");m("\u27E6","\\llbracket");m("\u27E7","\\rrbracket");m("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}");m("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}");m("\u2983","\\lBrace");m("\u2984","\\rBrace");m("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29B5}}");m("\u29B5","\\minuso");m("\\darr","\\downarrow");m("\\dArr","\\Downarrow");m("\\Darr","\\Downarrow");m("\\lang","\\langle");m("\\rang","\\rangle");m("\\uarr","\\uparrow");m("\\uArr","\\Uparrow");m("\\Uarr","\\Uparrow");m("\\N","\\mathbb{N}");m("\\R","\\mathbb{R}");m("\\Z","\\mathbb{Z}");m("\\alef","\\aleph");m("\\alefsym","\\aleph");m("\\Alpha","\\mathrm{A}");m("\\Beta","\\mathrm{B}");m("\\bull","\\bullet");m("\\Chi","\\mathrm{X}");m("\\clubs","\\clubsuit");m("\\cnums","\\mathbb{C}");m("\\Complex","\\mathbb{C}");m("\\Dagger","\\ddagger");m("\\diamonds","\\diamondsuit");m("\\empty","\\emptyset");m("\\Epsilon","\\mathrm{E}");m("\\Eta","\\mathrm{H}");m("\\exist","\\exists");m("\\harr","\\leftrightarrow");m("\\hArr","\\Leftrightarrow");m("\\Harr","\\Leftrightarrow");m("\\hearts","\\heartsuit");m("\\image","\\Im");m("\\infin","\\infty");m("\\Iota","\\mathrm{I}");m("\\isin","\\in");m("\\Kappa","\\mathrm{K}");m("\\larr","\\leftarrow");m("\\lArr","\\Leftarrow");m("\\Larr","\\Leftarrow");m("\\lrarr","\\leftrightarrow");m("\\lrArr","\\Leftrightarrow");m("\\Lrarr","\\Leftrightarrow");m("\\Mu","\\mathrm{M}");m("\\natnums","\\mathbb{N}");m("\\Nu","\\mathrm{N}");m("\\Omicron","\\mathrm{O}");m("\\plusmn","\\pm");m("\\rarr","\\rightarrow");m("\\rArr","\\Rightarrow");m("\\Rarr","\\Rightarrow");m("\\real","\\Re");m("\\reals","\\mathbb{R}");m("\\Reals","\\mathbb{R}");m("\\Rho","\\mathrm{P}");m("\\sdot","\\cdot");m("\\sect","\\S");m("\\spades","\\spadesuit");m("\\sub","\\subset");m("\\sube","\\subseteq");m("\\supe","\\supseteq");m("\\Tau","\\mathrm{T}");m("\\thetasym","\\vartheta");m("\\weierp","\\wp");m("\\Zeta","\\mathrm{Z}");m("\\argmin","\\DOTSB\\operatorname*{arg\\,min}");m("\\argmax","\\DOTSB\\operatorname*{arg\\,max}");m("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits");m("\\bra","\\mathinner{\\langle{#1}|}");m("\\ket","\\mathinner{|{#1}\\rangle}");m("\\braket","\\mathinner{\\langle{#1}\\rangle}");m("\\Bra","\\left\\langle#1\\right|");m("\\Ket","\\left|#1\\right\\rangle");var ca=r=>e=>{var t=e.consumeArg().tokens,a=e.consumeArg().tokens,n=e.consumeArg().tokens,s=e.consumeArg().tokens,o=e.macros.get("|"),h=e.macros.get("\\|");e.macros.beginGroup();var c=b=>x=>{r&&(x.macros.set("|",o),n.length&&x.macros.set("\\|",h));var w=b;if(!b&&n.length){var z=x.future();z.text==="|"&&(x.popToken(),w=!0)}return{tokens:w?n:a,numArgs:0}};e.macros.set("|",c(!1)),n.length&&e.macros.set("\\|",c(!0));var p=e.consumeArg().tokens,g=e.expandTokens([...s,...p,...t]);return e.macros.endGroup(),{tokens:g.reverse(),numArgs:0}};m("\\bra@ket",ca(!1));m("\\bra@set",ca(!0));m("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}");m("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}");m("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}");m("\\angln","{\\angl n}");m("\\blue","\\textcolor{##6495ed}{#1}");m("\\orange","\\textcolor{##ffa500}{#1}");m("\\pink","\\textcolor{##ff00af}{#1}");m("\\red","\\textcolor{##df0030}{#1}");m("\\green","\\textcolor{##28ae7b}{#1}");m("\\gray","\\textcolor{gray}{#1}");m("\\purple","\\textcolor{##9d38bd}{#1}");m("\\blueA","\\textcolor{##ccfaff}{#1}");m("\\blueB","\\textcolor{##80f6ff}{#1}");m("\\blueC","\\textcolor{##63d9ea}{#1}");m("\\blueD","\\textcolor{##11accd}{#1}");m("\\blueE","\\textcolor{##0c7f99}{#1}");m("\\tealA","\\textcolor{##94fff5}{#1}");m("\\tealB","\\textcolor{##26edd5}{#1}");m("\\tealC","\\textcolor{##01d1c1}{#1}");m("\\tealD","\\textcolor{##01a995}{#1}");m("\\tealE","\\textcolor{##208170}{#1}");m("\\greenA","\\textcolor{##b6ffb0}{#1}");m("\\greenB","\\textcolor{##8af281}{#1}");m("\\greenC","\\textcolor{##74cf70}{#1}");m("\\greenD","\\textcolor{##1fab54}{#1}");m("\\greenE","\\textcolor{##0d923f}{#1}");m("\\goldA","\\textcolor{##ffd0a9}{#1}");m("\\goldB","\\textcolor{##ffbb71}{#1}");m("\\goldC","\\textcolor{##ff9c39}{#1}");m("\\goldD","\\textcolor{##e07d10}{#1}");m("\\goldE","\\textcolor{##a75a05}{#1}");m("\\redA","\\textcolor{##fca9a9}{#1}");m("\\redB","\\textcolor{##ff8482}{#1}");m("\\redC","\\textcolor{##f9685d}{#1}");m("\\redD","\\textcolor{##e84d39}{#1}");m("\\redE","\\textcolor{##bc2612}{#1}");m("\\maroonA","\\textcolor{##ffbde0}{#1}");m("\\maroonB","\\textcolor{##ff92c6}{#1}");m("\\maroonC","\\textcolor{##ed5fa6}{#1}");m("\\maroonD","\\textcolor{##ca337c}{#1}");m("\\maroonE","\\textcolor{##9e034e}{#1}");m("\\purpleA","\\textcolor{##ddd7ff}{#1}");m("\\purpleB","\\textcolor{##c6b9fc}{#1}");m("\\purpleC","\\textcolor{##aa87ff}{#1}");m("\\purpleD","\\textcolor{##7854ab}{#1}");m("\\purpleE","\\textcolor{##543b78}{#1}");m("\\mintA","\\textcolor{##f5f9e8}{#1}");m("\\mintB","\\textcolor{##edf2df}{#1}");m("\\mintC","\\textcolor{##e0e5cc}{#1}");m("\\grayA","\\textcolor{##f6f7f7}{#1}");m("\\grayB","\\textcolor{##f0f1f2}{#1}");m("\\grayC","\\textcolor{##e3e5e6}{#1}");m("\\grayD","\\textcolor{##d6d8da}{#1}");m("\\grayE","\\textcolor{##babec2}{#1}");m("\\grayF","\\textcolor{##888d93}{#1}");m("\\grayG","\\textcolor{##626569}{#1}");m("\\grayH","\\textcolor{##3b3e40}{#1}");m("\\grayI","\\textcolor{##21242c}{#1}");m("\\kaBlue","\\textcolor{##314453}{#1}");m("\\kaGreen","\\textcolor{##71B307}{#1}");var da={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},wt=class{constructor(e,t,a){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new xt(u4,t.macros),this.mode=a,this.stack=[]}feed(e){this.lexer=new Le(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return this.stack.length===0&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){var t,a,n;if(e){if(this.consumeSpaces(),this.future().text!=="[")return null;t=this.popToken(),{tokens:n,end:a}=this.consumeArg(["]"])}else({tokens:n,start:t,end:a}=this.consumeArg());return this.pushToken(new p0("EOF",a.loc)),this.pushTokens(n),t.range(a,"")}consumeSpaces(){for(;;){var e=this.future();if(e.text===" ")this.stack.pop();else break}}consumeArg(e){var t=[],a=e&&e.length>0;a||this.consumeSpaces();var n=this.future(),s,o=0,h=0;do{if(s=this.popToken(),t.push(s),s.text==="{")++o;else if(s.text==="}"){if(--o,o===-1)throw new M("Extra }",s)}else if(s.text==="EOF")throw new M("Unexpected end of input in a macro argument, expected '"+(e&&a?e[h]:"}")+"'",s);if(e&&a)if((o===0||o===1&&e[h]==="{")&&s.text===e[h]){if(++h,h===e.length){t.splice(-h,h);break}}else h=0}while(o!==0||a);return n.text==="{"&&t[t.length-1].text==="}"&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:n,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new M("The length of delimiters doesn't match the number of args!");for(var a=t[0],n=0;nthis.settings.maxExpand)throw new M("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){var t=this.popToken(),a=t.text,n=t.noexpand?null:this._getExpansion(a);if(n==null||e&&n.unexpandable){if(e&&n==null&&a[0]==="\\"&&!this.isDefined(a))throw new M("Undefined control sequence: "+a);return this.pushToken(t),!1}this.countExpansion(1);var s=n.tokens,o=this.consumeArgs(n.numArgs,n.delimiters);if(n.numArgs){s=s.slice();for(var h=s.length-1;h>=0;--h){var c=s[h];if(c.text==="#"){if(h===0)throw new M("Incomplete placeholder at end of macro body",c);if(c=s[--h],c.text==="#")s.splice(h+1,1);else if(/^[1-9]$/.test(c.text))s.splice(h,2,...o[+c.text-1]);else throw new M("Not a valid argument number",c)}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(this.expandOnce()===!1){var e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new p0(e)]):void 0}expandTokens(e){var t=[],a=this.stack.length;for(this.pushTokens(e);this.stack.length>a;)if(this.expandOnce(!0)===!1){var n=this.stack.pop();n.treatAsRelax&&(n.noexpand=!1,n.treatAsRelax=!1),t.push(n)}return this.countExpansion(t.length),t}expandMacroAsText(e){var t=this.expandMacro(e);return t&&t.map(a=>a.text).join("")}_getExpansion(e){var t=this.macros.get(e);if(t==null)return t;if(e.length===1){var a=this.lexer.catcodes[e];if(a!=null&&a!==13)return}var n=typeof t=="function"?t(this):t;if(typeof n=="string"){var s=0;if(n.indexOf("#")!==-1)for(var o=n.replace(/##/g,"");o.indexOf("#"+(s+1))!==-1;)++s;for(var h=new Le(n,this.settings),c=[],p=h.lex();p.text!=="EOF";)c.push(p),p=h.lex();c.reverse();var g={tokens:c,numArgs:s};return g}return n}isDefined(e){return this.macros.has(e)||L0.hasOwnProperty(e)||X.math.hasOwnProperty(e)||X.text.hasOwnProperty(e)||da.hasOwnProperty(e)}isExpandable(e){var t=this.macros.get(e);return t!=null?typeof t=="string"||typeof t=="function"||!t.unexpandable:L0.hasOwnProperty(e)&&!L0[e].primitive}},yr=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,Ce=Object.freeze({"\u208A":"+","\u208B":"-","\u208C":"=","\u208D":"(","\u208E":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1D62":"i","\u2C7C":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209A":"p","\u1D63":"r","\u209B":"s","\u209C":"t","\u1D64":"u","\u1D65":"v","\u2093":"x","\u1D66":"\u03B2","\u1D67":"\u03B3","\u1D68":"\u03C1","\u1D69":"\u03D5","\u1D6A":"\u03C7","\u207A":"+","\u207B":"-","\u207C":"=","\u207D":"(","\u207E":")","\u2070":"0","\xB9":"1","\xB2":"2","\xB3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1D2C":"A","\u1D2E":"B","\u1D30":"D","\u1D31":"E","\u1D33":"G","\u1D34":"H","\u1D35":"I","\u1D36":"J","\u1D37":"K","\u1D38":"L","\u1D39":"M","\u1D3A":"N","\u1D3C":"O","\u1D3E":"P","\u1D3F":"R","\u1D40":"T","\u1D41":"U","\u2C7D":"V","\u1D42":"W","\u1D43":"a","\u1D47":"b","\u1D9C":"c","\u1D48":"d","\u1D49":"e","\u1DA0":"f","\u1D4D":"g",\u02B0:"h","\u2071":"i",\u02B2:"j","\u1D4F":"k",\u02E1:"l","\u1D50":"m",\u207F:"n","\u1D52":"o","\u1D56":"p",\u02B3:"r",\u02E2:"s","\u1D57":"t","\u1D58":"u","\u1D5B":"v",\u02B7:"w",\u02E3:"x",\u02B8:"y","\u1DBB":"z","\u1D5D":"\u03B2","\u1D5E":"\u03B3","\u1D5F":"\u03B4","\u1D60":"\u03D5","\u1D61":"\u03C7","\u1DBF":"\u03B8"}),ut={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030C":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030A":{text:"\\r",math:"\\mathring"},"\u030B":{text:"\\H"},"\u0327":{text:"\\c"}},xr={\u00E1:"a\u0301",\u00E0:"a\u0300",\u00E4:"a\u0308",\u01DF:"a\u0308\u0304",\u00E3:"a\u0303",\u0101:"a\u0304",\u0103:"a\u0306",\u1EAF:"a\u0306\u0301",\u1EB1:"a\u0306\u0300",\u1EB5:"a\u0306\u0303",\u01CE:"a\u030C",\u00E2:"a\u0302",\u1EA5:"a\u0302\u0301",\u1EA7:"a\u0302\u0300",\u1EAB:"a\u0302\u0303",\u0227:"a\u0307",\u01E1:"a\u0307\u0304",\u00E5:"a\u030A",\u01FB:"a\u030A\u0301",\u1E03:"b\u0307",\u0107:"c\u0301",\u1E09:"c\u0327\u0301",\u010D:"c\u030C",\u0109:"c\u0302",\u010B:"c\u0307",\u00E7:"c\u0327",\u010F:"d\u030C",\u1E0B:"d\u0307",\u1E11:"d\u0327",\u00E9:"e\u0301",\u00E8:"e\u0300",\u00EB:"e\u0308",\u1EBD:"e\u0303",\u0113:"e\u0304",\u1E17:"e\u0304\u0301",\u1E15:"e\u0304\u0300",\u0115:"e\u0306",\u1E1D:"e\u0327\u0306",\u011B:"e\u030C",\u00EA:"e\u0302",\u1EBF:"e\u0302\u0301",\u1EC1:"e\u0302\u0300",\u1EC5:"e\u0302\u0303",\u0117:"e\u0307",\u0229:"e\u0327",\u1E1F:"f\u0307",\u01F5:"g\u0301",\u1E21:"g\u0304",\u011F:"g\u0306",\u01E7:"g\u030C",\u011D:"g\u0302",\u0121:"g\u0307",\u0123:"g\u0327",\u1E27:"h\u0308",\u021F:"h\u030C",\u0125:"h\u0302",\u1E23:"h\u0307",\u1E29:"h\u0327",\u00ED:"i\u0301",\u00EC:"i\u0300",\u00EF:"i\u0308",\u1E2F:"i\u0308\u0301",\u0129:"i\u0303",\u012B:"i\u0304",\u012D:"i\u0306",\u01D0:"i\u030C",\u00EE:"i\u0302",\u01F0:"j\u030C",\u0135:"j\u0302",\u1E31:"k\u0301",\u01E9:"k\u030C",\u0137:"k\u0327",\u013A:"l\u0301",\u013E:"l\u030C",\u013C:"l\u0327",\u1E3F:"m\u0301",\u1E41:"m\u0307",\u0144:"n\u0301",\u01F9:"n\u0300",\u00F1:"n\u0303",\u0148:"n\u030C",\u1E45:"n\u0307",\u0146:"n\u0327",\u00F3:"o\u0301",\u00F2:"o\u0300",\u00F6:"o\u0308",\u022B:"o\u0308\u0304",\u00F5:"o\u0303",\u1E4D:"o\u0303\u0301",\u1E4F:"o\u0303\u0308",\u022D:"o\u0303\u0304",\u014D:"o\u0304",\u1E53:"o\u0304\u0301",\u1E51:"o\u0304\u0300",\u014F:"o\u0306",\u01D2:"o\u030C",\u00F4:"o\u0302",\u1ED1:"o\u0302\u0301",\u1ED3:"o\u0302\u0300",\u1ED7:"o\u0302\u0303",\u022F:"o\u0307",\u0231:"o\u0307\u0304",\u0151:"o\u030B",\u1E55:"p\u0301",\u1E57:"p\u0307",\u0155:"r\u0301",\u0159:"r\u030C",\u1E59:"r\u0307",\u0157:"r\u0327",\u015B:"s\u0301",\u1E65:"s\u0301\u0307",\u0161:"s\u030C",\u1E67:"s\u030C\u0307",\u015D:"s\u0302",\u1E61:"s\u0307",\u015F:"s\u0327",\u1E97:"t\u0308",\u0165:"t\u030C",\u1E6B:"t\u0307",\u0163:"t\u0327",\u00FA:"u\u0301",\u00F9:"u\u0300",\u00FC:"u\u0308",\u01D8:"u\u0308\u0301",\u01DC:"u\u0308\u0300",\u01D6:"u\u0308\u0304",\u01DA:"u\u0308\u030C",\u0169:"u\u0303",\u1E79:"u\u0303\u0301",\u016B:"u\u0304",\u1E7B:"u\u0304\u0308",\u016D:"u\u0306",\u01D4:"u\u030C",\u00FB:"u\u0302",\u016F:"u\u030A",\u0171:"u\u030B",\u1E7D:"v\u0303",\u1E83:"w\u0301",\u1E81:"w\u0300",\u1E85:"w\u0308",\u0175:"w\u0302",\u1E87:"w\u0307",\u1E98:"w\u030A",\u1E8D:"x\u0308",\u1E8B:"x\u0307",\u00FD:"y\u0301",\u1EF3:"y\u0300",\u00FF:"y\u0308",\u1EF9:"y\u0303",\u0233:"y\u0304",\u0177:"y\u0302",\u1E8F:"y\u0307",\u1E99:"y\u030A",\u017A:"z\u0301",\u017E:"z\u030C",\u1E91:"z\u0302",\u017C:"z\u0307",\u00C1:"A\u0301",\u00C0:"A\u0300",\u00C4:"A\u0308",\u01DE:"A\u0308\u0304",\u00C3:"A\u0303",\u0100:"A\u0304",\u0102:"A\u0306",\u1EAE:"A\u0306\u0301",\u1EB0:"A\u0306\u0300",\u1EB4:"A\u0306\u0303",\u01CD:"A\u030C",\u00C2:"A\u0302",\u1EA4:"A\u0302\u0301",\u1EA6:"A\u0302\u0300",\u1EAA:"A\u0302\u0303",\u0226:"A\u0307",\u01E0:"A\u0307\u0304",\u00C5:"A\u030A",\u01FA:"A\u030A\u0301",\u1E02:"B\u0307",\u0106:"C\u0301",\u1E08:"C\u0327\u0301",\u010C:"C\u030C",\u0108:"C\u0302",\u010A:"C\u0307",\u00C7:"C\u0327",\u010E:"D\u030C",\u1E0A:"D\u0307",\u1E10:"D\u0327",\u00C9:"E\u0301",\u00C8:"E\u0300",\u00CB:"E\u0308",\u1EBC:"E\u0303",\u0112:"E\u0304",\u1E16:"E\u0304\u0301",\u1E14:"E\u0304\u0300",\u0114:"E\u0306",\u1E1C:"E\u0327\u0306",\u011A:"E\u030C",\u00CA:"E\u0302",\u1EBE:"E\u0302\u0301",\u1EC0:"E\u0302\u0300",\u1EC4:"E\u0302\u0303",\u0116:"E\u0307",\u0228:"E\u0327",\u1E1E:"F\u0307",\u01F4:"G\u0301",\u1E20:"G\u0304",\u011E:"G\u0306",\u01E6:"G\u030C",\u011C:"G\u0302",\u0120:"G\u0307",\u0122:"G\u0327",\u1E26:"H\u0308",\u021E:"H\u030C",\u0124:"H\u0302",\u1E22:"H\u0307",\u1E28:"H\u0327",\u00CD:"I\u0301",\u00CC:"I\u0300",\u00CF:"I\u0308",\u1E2E:"I\u0308\u0301",\u0128:"I\u0303",\u012A:"I\u0304",\u012C:"I\u0306",\u01CF:"I\u030C",\u00CE:"I\u0302",\u0130:"I\u0307",\u0134:"J\u0302",\u1E30:"K\u0301",\u01E8:"K\u030C",\u0136:"K\u0327",\u0139:"L\u0301",\u013D:"L\u030C",\u013B:"L\u0327",\u1E3E:"M\u0301",\u1E40:"M\u0307",\u0143:"N\u0301",\u01F8:"N\u0300",\u00D1:"N\u0303",\u0147:"N\u030C",\u1E44:"N\u0307",\u0145:"N\u0327",\u00D3:"O\u0301",\u00D2:"O\u0300",\u00D6:"O\u0308",\u022A:"O\u0308\u0304",\u00D5:"O\u0303",\u1E4C:"O\u0303\u0301",\u1E4E:"O\u0303\u0308",\u022C:"O\u0303\u0304",\u014C:"O\u0304",\u1E52:"O\u0304\u0301",\u1E50:"O\u0304\u0300",\u014E:"O\u0306",\u01D1:"O\u030C",\u00D4:"O\u0302",\u1ED0:"O\u0302\u0301",\u1ED2:"O\u0302\u0300",\u1ED6:"O\u0302\u0303",\u022E:"O\u0307",\u0230:"O\u0307\u0304",\u0150:"O\u030B",\u1E54:"P\u0301",\u1E56:"P\u0307",\u0154:"R\u0301",\u0158:"R\u030C",\u1E58:"R\u0307",\u0156:"R\u0327",\u015A:"S\u0301",\u1E64:"S\u0301\u0307",\u0160:"S\u030C",\u1E66:"S\u030C\u0307",\u015C:"S\u0302",\u1E60:"S\u0307",\u015E:"S\u0327",\u0164:"T\u030C",\u1E6A:"T\u0307",\u0162:"T\u0327",\u00DA:"U\u0301",\u00D9:"U\u0300",\u00DC:"U\u0308",\u01D7:"U\u0308\u0301",\u01DB:"U\u0308\u0300",\u01D5:"U\u0308\u0304",\u01D9:"U\u0308\u030C",\u0168:"U\u0303",\u1E78:"U\u0303\u0301",\u016A:"U\u0304",\u1E7A:"U\u0304\u0308",\u016C:"U\u0306",\u01D3:"U\u030C",\u00DB:"U\u0302",\u016E:"U\u030A",\u0170:"U\u030B",\u1E7C:"V\u0303",\u1E82:"W\u0301",\u1E80:"W\u0300",\u1E84:"W\u0308",\u0174:"W\u0302",\u1E86:"W\u0307",\u1E8C:"X\u0308",\u1E8A:"X\u0307",\u00DD:"Y\u0301",\u1EF2:"Y\u0300",\u0178:"Y\u0308",\u1EF8:"Y\u0303",\u0232:"Y\u0304",\u0176:"Y\u0302",\u1E8E:"Y\u0307",\u0179:"Z\u0301",\u017D:"Z\u030C",\u1E90:"Z\u0302",\u017B:"Z\u0307",\u03AC:"\u03B1\u0301",\u1F70:"\u03B1\u0300",\u1FB1:"\u03B1\u0304",\u1FB0:"\u03B1\u0306",\u03AD:"\u03B5\u0301",\u1F72:"\u03B5\u0300",\u03AE:"\u03B7\u0301",\u1F74:"\u03B7\u0300",\u03AF:"\u03B9\u0301",\u1F76:"\u03B9\u0300",\u03CA:"\u03B9\u0308",\u0390:"\u03B9\u0308\u0301",\u1FD2:"\u03B9\u0308\u0300",\u1FD1:"\u03B9\u0304",\u1FD0:"\u03B9\u0306",\u03CC:"\u03BF\u0301",\u1F78:"\u03BF\u0300",\u03CD:"\u03C5\u0301",\u1F7A:"\u03C5\u0300",\u03CB:"\u03C5\u0308",\u03B0:"\u03C5\u0308\u0301",\u1FE2:"\u03C5\u0308\u0300",\u1FE1:"\u03C5\u0304",\u1FE0:"\u03C5\u0306",\u03CE:"\u03C9\u0301",\u1F7C:"\u03C9\u0300",\u038E:"\u03A5\u0301",\u1FEA:"\u03A5\u0300",\u03AB:"\u03A5\u0308",\u1FE9:"\u03A5\u0304",\u1FE8:"\u03A5\u0306",\u038F:"\u03A9\u0301",\u1FFA:"\u03A9\u0300"},Pe=class r{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new wt(e,t,this.mode),this.settings=t,this.leftrightDepth=0}expect(e,t){if(t===void 0&&(t=!0),this.fetch().text!==e)throw new M("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return this.nextToken==null&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{var e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){var t=this.nextToken;this.consume(),this.gullet.pushToken(new p0("}")),this.gullet.pushTokens(e);var a=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,a}parseExpression(e,t){for(var a=[];;){this.mode==="math"&&this.consumeSpaces();var n=this.fetch();if(r.endOfExpression.indexOf(n.text)!==-1||t&&n.text===t||e&&L0[n.text]&&L0[n.text].infix)break;var s=this.parseAtom(t);if(s){if(s.type==="internal")continue}else break;a.push(s)}return this.mode==="text"&&this.formLigatures(a),this.handleInfixNodes(a)}handleInfixNodes(e){for(var t=-1,a,n=0;n=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+t[0]+'" used in math mode',e);var h=X[this.mode][t].group,c=m0.range(e),p;if(e1.hasOwnProperty(h)){var g=h;p={type:"atom",mode:this.mode,family:g,loc:c,text:t}}else p={type:h,mode:this.mode,loc:c,text:t};o=p}else if(t.charCodeAt(0)>=128)this.settings.strict&&(kr(t.charCodeAt(0))?this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'"'+(" ("+t.charCodeAt(0)+")"),e)),o={type:"textord",mode:"text",loc:m0.range(e),text:t};else return null;if(this.consume(),s)for(var b=0;b0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,a){if(a===void 0&&(a=!1),a){for(var n=0;n0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{var s=this.undefStack[this.undefStack.length-1];s&&!s.hasOwnProperty(e)&&(s[e]=this.current[e])}t==null?delete this.current[e]:this.current[e]=t}},fn=aa;m("\\noexpand",function(r){var e=r.popToken();return r.isExpandable(e.text)&&(e.noexpand=!0,e.treatAsRelax=!0),{tokens:[e],numArgs:0}});m("\\expandafter",function(r){var e=r.popToken();return r.expandOnce(!0),{tokens:[e],numArgs:0}});m("\\@firstoftwo",function(r){var e=r.consumeArgs(2);return{tokens:e[0],numArgs:0}});m("\\@secondoftwo",function(r){var e=r.consumeArgs(2);return{tokens:e[1],numArgs:0}});m("\\@ifnextchar",function(r){var e=r.consumeArgs(3);r.consumeSpaces();var t=r.future();return e[0].length===1&&e[0][0].text===t.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}});m("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}");m("\\TextOrMath",function(r){var e=r.consumeArgs(2);return r.mode==="text"?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});var wr={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};m("\\char",function(r){var e=r.popToken(),t,a="";if(e.text==="'")t=8,e=r.popToken();else if(e.text==='"')t=16,e=r.popToken();else if(e.text==="`")if(e=r.popToken(),e.text[0]==="\\")a=e.text.charCodeAt(1);else{if(e.text==="EOF")throw new z("\\char` missing argument");a=e.text.charCodeAt(0)}else t=10;if(t){if(a=wr[e.text],a==null||a>=t)throw new z("Invalid base-"+t+" digit "+e.text);for(var n;(n=wr[r.future().text])!=null&&n{var n=r.consumeArg().tokens;if(n.length!==1)throw new z("\\newcommand's first argument must be a macro name");var s=n[0].text,l=r.isDefined(s);if(l&&!e)throw new z("\\newcommand{"+s+"} attempting to redefine "+(s+"; use \\renewcommand"));if(!l&&!t)throw new z("\\renewcommand{"+s+"} when command "+s+" does not yet exist; use \\newcommand");var h=0;if(n=r.consumeArg().tokens,n.length===1&&n[0].text==="["){for(var c="",f=r.expandNextToken();f.text!=="]"&&f.text!=="EOF";)c+=f.text,f=r.expandNextToken();if(!c.match(/^\s*[0-9]+\s*$/))throw new z("Invalid number of arguments: "+c);h=parseInt(c),n=r.consumeArg().tokens}return l&&a||r.macros.set(s,{tokens:n,numArgs:h}),""};m("\\newcommand",r=>Ft(r,!1,!0,!1));m("\\renewcommand",r=>Ft(r,!0,!1,!1));m("\\providecommand",r=>Ft(r,!0,!0,!0));m("\\message",r=>{var e=r.consumeArgs(1)[0];return console.log(e.reverse().map(t=>t.text).join("")),""});m("\\errmessage",r=>{var e=r.consumeArgs(1)[0];return console.error(e.reverse().map(t=>t.text).join("")),""});m("\\show",r=>{var e=r.popToken(),t=e.text;return console.log(e,r.macros.get(t),P0[t],Y.math[t],Y.text[t]),""});m("\\bgroup","{");m("\\egroup","}");m("~","\\nobreakspace");m("\\lq","`");m("\\rq","'");m("\\aa","\\r a");m("\\AA","\\r A");m("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xA9}");m("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}");m("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xAE}");m("\u212C","\\mathscr{B}");m("\u2130","\\mathscr{E}");m("\u2131","\\mathscr{F}");m("\u210B","\\mathscr{H}");m("\u2110","\\mathscr{I}");m("\u2112","\\mathscr{L}");m("\u2133","\\mathscr{M}");m("\u211B","\\mathscr{R}");m("\u212D","\\mathfrak{C}");m("\u210C","\\mathfrak{H}");m("\u2128","\\mathfrak{Z}");m("\\Bbbk","\\Bbb{k}");m("\xB7","\\cdotp");m("\\llap","\\mathllap{\\textrm{#1}}");m("\\rlap","\\mathrlap{\\textrm{#1}}");m("\\clap","\\mathclap{\\textrm{#1}}");m("\\mathstrut","\\vphantom{(}");m("\\underbar","\\underline{\\text{#1}}");m("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}');m("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}");m("\\ne","\\neq");m("\u2260","\\neq");m("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}");m("\u2209","\\notin");m("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}");m("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}");m("\u225A","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225A}}");m("\u225B","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225B}}");m("\u225D","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225D}}");m("\u225E","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225E}}");m("\u225F","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225F}}");m("\u27C2","\\perp");m("\u203C","\\mathclose{!\\mkern-0.8mu!}");m("\u220C","\\notni");m("\u231C","\\ulcorner");m("\u231D","\\urcorner");m("\u231E","\\llcorner");m("\u231F","\\lrcorner");m("\xA9","\\copyright");m("\xAE","\\textregistered");m("\uFE0F","\\textregistered");m("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}');m("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}');m("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}');m("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}');m("\\vdots","{\\varvdots\\rule{0pt}{15pt}}");m("\u22EE","\\vdots");m("\\varGamma","\\mathit{\\Gamma}");m("\\varDelta","\\mathit{\\Delta}");m("\\varTheta","\\mathit{\\Theta}");m("\\varLambda","\\mathit{\\Lambda}");m("\\varXi","\\mathit{\\Xi}");m("\\varPi","\\mathit{\\Pi}");m("\\varSigma","\\mathit{\\Sigma}");m("\\varUpsilon","\\mathit{\\Upsilon}");m("\\varPhi","\\mathit{\\Phi}");m("\\varPsi","\\mathit{\\Psi}");m("\\varOmega","\\mathit{\\Omega}");m("\\substack","\\begin{subarray}{c}#1\\end{subarray}");m("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax");m("\\boxed","\\fbox{$\\displaystyle{#1}$}");m("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;");m("\\implies","\\DOTSB\\;\\Longrightarrow\\;");m("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");m("\\dddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}");m("\\ddddot","{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}");var Sr={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};m("\\dots",function(r){var e="\\dotso",t=r.expandAfterFuture().text;return t in Sr?e=Sr[t]:(t.slice(0,4)==="\\not"||t in Y.math&&O.contains(["bin","rel"],Y.math[t].group))&&(e="\\dotsb"),e});var Ht={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};m("\\dotso",function(r){var e=r.future().text;return e in Ht?"\\ldots\\,":"\\ldots"});m("\\dotsc",function(r){var e=r.future().text;return e in Ht&&e!==","?"\\ldots\\,":"\\ldots"});m("\\cdots",function(r){var e=r.future().text;return e in Ht?"\\@cdots\\,":"\\@cdots"});m("\\dotsb","\\cdots");m("\\dotsm","\\cdots");m("\\dotsi","\\!\\cdots");m("\\dotsx","\\ldots\\,");m("\\DOTSI","\\relax");m("\\DOTSB","\\relax");m("\\DOTSX","\\relax");m("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");m("\\,","\\tmspace+{3mu}{.1667em}");m("\\thinspace","\\,");m("\\>","\\mskip{4mu}");m("\\:","\\tmspace+{4mu}{.2222em}");m("\\medspace","\\:");m("\\;","\\tmspace+{5mu}{.2777em}");m("\\thickspace","\\;");m("\\!","\\tmspace-{3mu}{.1667em}");m("\\negthinspace","\\!");m("\\negmedspace","\\tmspace-{4mu}{.2222em}");m("\\negthickspace","\\tmspace-{5mu}{.277em}");m("\\enspace","\\kern.5em ");m("\\enskip","\\hskip.5em\\relax");m("\\quad","\\hskip1em\\relax");m("\\qquad","\\hskip2em\\relax");m("\\tag","\\@ifstar\\tag@literal\\tag@paren");m("\\tag@paren","\\tag@literal{({#1})}");m("\\tag@literal",r=>{if(r.macros.get("\\df@tag"))throw new z("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"});m("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}");m("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)");m("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}");m("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1");m("\\newline","\\\\\\relax");m("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");var fa=T(z0["Main-Regular"][84][1]-.7*z0["Main-Regular"][65][1]);m("\\LaTeX","\\textrm{\\html@mathml{"+("L\\kern-.36em\\raisebox{"+fa+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{LaTeX}}");m("\\KaTeX","\\textrm{\\html@mathml{"+("K\\kern-.17em\\raisebox{"+fa+"}{\\scriptstyle A}")+"\\kern-.15em\\TeX}{KaTeX}}");m("\\hspace","\\@ifstar\\@hspacer\\@hspace");m("\\@hspace","\\hskip #1\\relax");m("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax");m("\\ordinarycolon",":");m("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}");m("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}');m("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}');m("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}');m("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}');m("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}');m("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}');m("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}');m("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}');m("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}');m("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}');m("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}');m("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}');m("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}');m("\u2237","\\dblcolon");m("\u2239","\\eqcolon");m("\u2254","\\coloneqq");m("\u2255","\\eqqcolon");m("\u2A74","\\Coloneqq");m("\\ratio","\\vcentcolon");m("\\coloncolon","\\dblcolon");m("\\colonequals","\\coloneqq");m("\\coloncolonequals","\\Coloneqq");m("\\equalscolon","\\eqqcolon");m("\\equalscoloncolon","\\Eqqcolon");m("\\colonminus","\\coloneq");m("\\coloncolonminus","\\Coloneq");m("\\minuscolon","\\eqcolon");m("\\minuscoloncolon","\\Eqcolon");m("\\coloncolonapprox","\\Colonapprox");m("\\coloncolonsim","\\Colonsim");m("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}");m("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}");m("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}");m("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}");m("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220C}}");m("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}");m("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}");m("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}");m("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}");m("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}");m("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}");m("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}");m("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}");m("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}");m("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}");m("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}");m("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}");m("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}");m("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}");m("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}");m("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}");m("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}");m("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}");m("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228A}");m("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2ACB}");m("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228B}");m("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2ACC}");m("\\imath","\\html@mathml{\\@imath}{\u0131}");m("\\jmath","\\html@mathml{\\@jmath}{\u0237}");m("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27E6}}");m("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27E7}}");m("\u27E6","\\llbracket");m("\u27E7","\\rrbracket");m("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}");m("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}");m("\u2983","\\lBrace");m("\u2984","\\rBrace");m("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29B5}}");m("\u29B5","\\minuso");m("\\darr","\\downarrow");m("\\dArr","\\Downarrow");m("\\Darr","\\Downarrow");m("\\lang","\\langle");m("\\rang","\\rangle");m("\\uarr","\\uparrow");m("\\uArr","\\Uparrow");m("\\Uarr","\\Uparrow");m("\\N","\\mathbb{N}");m("\\R","\\mathbb{R}");m("\\Z","\\mathbb{Z}");m("\\alef","\\aleph");m("\\alefsym","\\aleph");m("\\Alpha","\\mathrm{A}");m("\\Beta","\\mathrm{B}");m("\\bull","\\bullet");m("\\Chi","\\mathrm{X}");m("\\clubs","\\clubsuit");m("\\cnums","\\mathbb{C}");m("\\Complex","\\mathbb{C}");m("\\Dagger","\\ddagger");m("\\diamonds","\\diamondsuit");m("\\empty","\\emptyset");m("\\Epsilon","\\mathrm{E}");m("\\Eta","\\mathrm{H}");m("\\exist","\\exists");m("\\harr","\\leftrightarrow");m("\\hArr","\\Leftrightarrow");m("\\Harr","\\Leftrightarrow");m("\\hearts","\\heartsuit");m("\\image","\\Im");m("\\infin","\\infty");m("\\Iota","\\mathrm{I}");m("\\isin","\\in");m("\\Kappa","\\mathrm{K}");m("\\larr","\\leftarrow");m("\\lArr","\\Leftarrow");m("\\Larr","\\Leftarrow");m("\\lrarr","\\leftrightarrow");m("\\lrArr","\\Leftrightarrow");m("\\Lrarr","\\Leftrightarrow");m("\\Mu","\\mathrm{M}");m("\\natnums","\\mathbb{N}");m("\\Nu","\\mathrm{N}");m("\\Omicron","\\mathrm{O}");m("\\plusmn","\\pm");m("\\rarr","\\rightarrow");m("\\rArr","\\Rightarrow");m("\\Rarr","\\Rightarrow");m("\\real","\\Re");m("\\reals","\\mathbb{R}");m("\\Reals","\\mathbb{R}");m("\\Rho","\\mathrm{P}");m("\\sdot","\\cdot");m("\\sect","\\S");m("\\spades","\\spadesuit");m("\\sub","\\subset");m("\\sube","\\subseteq");m("\\supe","\\supseteq");m("\\Tau","\\mathrm{T}");m("\\thetasym","\\vartheta");m("\\weierp","\\wp");m("\\Zeta","\\mathrm{Z}");m("\\argmin","\\DOTSB\\operatorname*{arg\\,min}");m("\\argmax","\\DOTSB\\operatorname*{arg\\,max}");m("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits");m("\\bra","\\mathinner{\\langle{#1}|}");m("\\ket","\\mathinner{|{#1}\\rangle}");m("\\braket","\\mathinner{\\langle{#1}\\rangle}");m("\\Bra","\\left\\langle#1\\right|");m("\\Ket","\\left|#1\\right\\rangle");var va=r=>e=>{var t=e.consumeArg().tokens,a=e.consumeArg().tokens,n=e.consumeArg().tokens,s=e.consumeArg().tokens,l=e.macros.get("|"),h=e.macros.get("\\|");e.macros.beginGroup();var c=b=>x=>{r&&(x.macros.set("|",l),n.length&&x.macros.set("\\|",h));var w=b;if(!b&&n.length){var A=x.future();A.text==="|"&&(x.popToken(),w=!0)}return{tokens:w?n:a,numArgs:0}};e.macros.set("|",c(!1)),n.length&&e.macros.set("\\|",c(!0));var f=e.consumeArg().tokens,v=e.expandTokens([...s,...f,...t]);return e.macros.endGroup(),{tokens:v.reverse(),numArgs:0}};m("\\bra@ket",va(!1));m("\\bra@set",va(!0));m("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}");m("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}");m("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}");m("\\angln","{\\angl n}");m("\\blue","\\textcolor{##6495ed}{#1}");m("\\orange","\\textcolor{##ffa500}{#1}");m("\\pink","\\textcolor{##ff00af}{#1}");m("\\red","\\textcolor{##df0030}{#1}");m("\\green","\\textcolor{##28ae7b}{#1}");m("\\gray","\\textcolor{gray}{#1}");m("\\purple","\\textcolor{##9d38bd}{#1}");m("\\blueA","\\textcolor{##ccfaff}{#1}");m("\\blueB","\\textcolor{##80f6ff}{#1}");m("\\blueC","\\textcolor{##63d9ea}{#1}");m("\\blueD","\\textcolor{##11accd}{#1}");m("\\blueE","\\textcolor{##0c7f99}{#1}");m("\\tealA","\\textcolor{##94fff5}{#1}");m("\\tealB","\\textcolor{##26edd5}{#1}");m("\\tealC","\\textcolor{##01d1c1}{#1}");m("\\tealD","\\textcolor{##01a995}{#1}");m("\\tealE","\\textcolor{##208170}{#1}");m("\\greenA","\\textcolor{##b6ffb0}{#1}");m("\\greenB","\\textcolor{##8af281}{#1}");m("\\greenC","\\textcolor{##74cf70}{#1}");m("\\greenD","\\textcolor{##1fab54}{#1}");m("\\greenE","\\textcolor{##0d923f}{#1}");m("\\goldA","\\textcolor{##ffd0a9}{#1}");m("\\goldB","\\textcolor{##ffbb71}{#1}");m("\\goldC","\\textcolor{##ff9c39}{#1}");m("\\goldD","\\textcolor{##e07d10}{#1}");m("\\goldE","\\textcolor{##a75a05}{#1}");m("\\redA","\\textcolor{##fca9a9}{#1}");m("\\redB","\\textcolor{##ff8482}{#1}");m("\\redC","\\textcolor{##f9685d}{#1}");m("\\redD","\\textcolor{##e84d39}{#1}");m("\\redE","\\textcolor{##bc2612}{#1}");m("\\maroonA","\\textcolor{##ffbde0}{#1}");m("\\maroonB","\\textcolor{##ff92c6}{#1}");m("\\maroonC","\\textcolor{##ed5fa6}{#1}");m("\\maroonD","\\textcolor{##ca337c}{#1}");m("\\maroonE","\\textcolor{##9e034e}{#1}");m("\\purpleA","\\textcolor{##ddd7ff}{#1}");m("\\purpleB","\\textcolor{##c6b9fc}{#1}");m("\\purpleC","\\textcolor{##aa87ff}{#1}");m("\\purpleD","\\textcolor{##7854ab}{#1}");m("\\purpleE","\\textcolor{##543b78}{#1}");m("\\mintA","\\textcolor{##f5f9e8}{#1}");m("\\mintB","\\textcolor{##edf2df}{#1}");m("\\mintC","\\textcolor{##e0e5cc}{#1}");m("\\grayA","\\textcolor{##f6f7f7}{#1}");m("\\grayB","\\textcolor{##f0f1f2}{#1}");m("\\grayC","\\textcolor{##e3e5e6}{#1}");m("\\grayD","\\textcolor{##d6d8da}{#1}");m("\\grayE","\\textcolor{##babec2}{#1}");m("\\grayF","\\textcolor{##888d93}{#1}");m("\\grayG","\\textcolor{##626569}{#1}");m("\\grayH","\\textcolor{##3b3e40}{#1}");m("\\grayI","\\textcolor{##21242c}{#1}");m("\\kaBlue","\\textcolor{##314453}{#1}");m("\\kaGreen","\\textcolor{##71B307}{#1}");var ga={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},zt=class{constructor(e,t,a){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new Mt(fn,t.macros),this.mode=a,this.stack=[]}feed(e){this.lexer=new Pe(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return this.stack.length===0&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){var t,a,n;if(e){if(this.consumeSpaces(),this.future().text!=="[")return null;t=this.popToken(),{tokens:n,end:a}=this.consumeArg(["]"])}else({tokens:n,start:t,end:a}=this.consumeArg());return this.pushToken(new b0("EOF",a.loc)),this.pushTokens(n),t.range(a,"")}consumeSpaces(){for(;;){var e=this.future();if(e.text===" ")this.stack.pop();else break}}consumeArg(e){var t=[],a=e&&e.length>0;a||this.consumeSpaces();var n=this.future(),s,l=0,h=0;do{if(s=this.popToken(),t.push(s),s.text==="{")++l;else if(s.text==="}"){if(--l,l===-1)throw new z("Extra }",s)}else if(s.text==="EOF")throw new z("Unexpected end of input in a macro argument, expected '"+(e&&a?e[h]:"}")+"'",s);if(e&&a)if((l===0||l===1&&e[h]==="{")&&s.text===e[h]){if(++h,h===e.length){t.splice(-h,h);break}}else h=0}while(l!==0||a);return n.text==="{"&&t[t.length-1].text==="}"&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:n,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new z("The length of delimiters doesn't match the number of args!");for(var a=t[0],n=0;nthis.settings.maxExpand)throw new z("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){var t=this.popToken(),a=t.text,n=t.noexpand?null:this._getExpansion(a);if(n==null||e&&n.unexpandable){if(e&&n==null&&a[0]==="\\"&&!this.isDefined(a))throw new z("Undefined control sequence: "+a);return this.pushToken(t),!1}this.countExpansion(1);var s=n.tokens,l=this.consumeArgs(n.numArgs,n.delimiters);if(n.numArgs){s=s.slice();for(var h=s.length-1;h>=0;--h){var c=s[h];if(c.text==="#"){if(h===0)throw new z("Incomplete placeholder at end of macro body",c);if(c=s[--h],c.text==="#")s.splice(h+1,1);else if(/^[1-9]$/.test(c.text))s.splice(h,2,...l[+c.text-1]);else throw new z("Not a valid argument number",c)}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(this.expandOnce()===!1){var e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new b0(e)]):void 0}expandTokens(e){var t=[],a=this.stack.length;for(this.pushTokens(e);this.stack.length>a;)if(this.expandOnce(!0)===!1){var n=this.stack.pop();n.treatAsRelax&&(n.noexpand=!1,n.treatAsRelax=!1),t.push(n)}return this.countExpansion(t.length),t}expandMacroAsText(e){var t=this.expandMacro(e);return t&&t.map(a=>a.text).join("")}_getExpansion(e){var t=this.macros.get(e);if(t==null)return t;if(e.length===1){var a=this.lexer.catcodes[e];if(a!=null&&a!==13)return}var n=typeof t=="function"?t(this):t;if(typeof n=="string"){var s=0;if(n.indexOf("#")!==-1)for(var l=n.replace(/##/g,"");l.indexOf("#"+(s+1))!==-1;)++s;for(var h=new Pe(n,this.settings),c=[],f=h.lex();f.text!=="EOF";)c.push(f),f=h.lex();c.reverse();var v={tokens:c,numArgs:s};return v}return n}isDefined(e){return this.macros.has(e)||P0.hasOwnProperty(e)||Y.math.hasOwnProperty(e)||Y.text.hasOwnProperty(e)||ga.hasOwnProperty(e)}isExpandable(e){var t=this.macros.get(e);return t!=null?typeof t=="string"||typeof t=="function"||!t.unexpandable:P0.hasOwnProperty(e)&&!P0[e].primitive}},kr=/^[₊₋₌₍₎₀₁₂₃₄₅₆₇₈₉ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓᵦᵧᵨᵩᵪ]/,Ne=Object.freeze({"\u208A":"+","\u208B":"-","\u208C":"=","\u208D":"(","\u208E":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1D62":"i","\u2C7C":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209A":"p","\u1D63":"r","\u209B":"s","\u209C":"t","\u1D64":"u","\u1D65":"v","\u2093":"x","\u1D66":"\u03B2","\u1D67":"\u03B3","\u1D68":"\u03C1","\u1D69":"\u03D5","\u1D6A":"\u03C7","\u207A":"+","\u207B":"-","\u207C":"=","\u207D":"(","\u207E":")","\u2070":"0","\xB9":"1","\xB2":"2","\xB3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1D2C":"A","\u1D2E":"B","\u1D30":"D","\u1D31":"E","\u1D33":"G","\u1D34":"H","\u1D35":"I","\u1D36":"J","\u1D37":"K","\u1D38":"L","\u1D39":"M","\u1D3A":"N","\u1D3C":"O","\u1D3E":"P","\u1D3F":"R","\u1D40":"T","\u1D41":"U","\u2C7D":"V","\u1D42":"W","\u1D43":"a","\u1D47":"b","\u1D9C":"c","\u1D48":"d","\u1D49":"e","\u1DA0":"f","\u1D4D":"g",\u02B0:"h","\u2071":"i",\u02B2:"j","\u1D4F":"k",\u02E1:"l","\u1D50":"m",\u207F:"n","\u1D52":"o","\u1D56":"p",\u02B3:"r",\u02E2:"s","\u1D57":"t","\u1D58":"u","\u1D5B":"v",\u02B7:"w",\u02E3:"x",\u02B8:"y","\u1DBB":"z","\u1D5D":"\u03B2","\u1D5E":"\u03B3","\u1D5F":"\u03B4","\u1D60":"\u03D5","\u1D61":"\u03C7","\u1DBF":"\u03B8"}),dt={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030C":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030A":{text:"\\r",math:"\\mathring"},"\u030B":{text:"\\H"},"\u0327":{text:"\\c"}},Mr={\u00E1:"a\u0301",\u00E0:"a\u0300",\u00E4:"a\u0308",\u01DF:"a\u0308\u0304",\u00E3:"a\u0303",\u0101:"a\u0304",\u0103:"a\u0306",\u1EAF:"a\u0306\u0301",\u1EB1:"a\u0306\u0300",\u1EB5:"a\u0306\u0303",\u01CE:"a\u030C",\u00E2:"a\u0302",\u1EA5:"a\u0302\u0301",\u1EA7:"a\u0302\u0300",\u1EAB:"a\u0302\u0303",\u0227:"a\u0307",\u01E1:"a\u0307\u0304",\u00E5:"a\u030A",\u01FB:"a\u030A\u0301",\u1E03:"b\u0307",\u0107:"c\u0301",\u1E09:"c\u0327\u0301",\u010D:"c\u030C",\u0109:"c\u0302",\u010B:"c\u0307",\u00E7:"c\u0327",\u010F:"d\u030C",\u1E0B:"d\u0307",\u1E11:"d\u0327",\u00E9:"e\u0301",\u00E8:"e\u0300",\u00EB:"e\u0308",\u1EBD:"e\u0303",\u0113:"e\u0304",\u1E17:"e\u0304\u0301",\u1E15:"e\u0304\u0300",\u0115:"e\u0306",\u1E1D:"e\u0327\u0306",\u011B:"e\u030C",\u00EA:"e\u0302",\u1EBF:"e\u0302\u0301",\u1EC1:"e\u0302\u0300",\u1EC5:"e\u0302\u0303",\u0117:"e\u0307",\u0229:"e\u0327",\u1E1F:"f\u0307",\u01F5:"g\u0301",\u1E21:"g\u0304",\u011F:"g\u0306",\u01E7:"g\u030C",\u011D:"g\u0302",\u0121:"g\u0307",\u0123:"g\u0327",\u1E27:"h\u0308",\u021F:"h\u030C",\u0125:"h\u0302",\u1E23:"h\u0307",\u1E29:"h\u0327",\u00ED:"i\u0301",\u00EC:"i\u0300",\u00EF:"i\u0308",\u1E2F:"i\u0308\u0301",\u0129:"i\u0303",\u012B:"i\u0304",\u012D:"i\u0306",\u01D0:"i\u030C",\u00EE:"i\u0302",\u01F0:"j\u030C",\u0135:"j\u0302",\u1E31:"k\u0301",\u01E9:"k\u030C",\u0137:"k\u0327",\u013A:"l\u0301",\u013E:"l\u030C",\u013C:"l\u0327",\u1E3F:"m\u0301",\u1E41:"m\u0307",\u0144:"n\u0301",\u01F9:"n\u0300",\u00F1:"n\u0303",\u0148:"n\u030C",\u1E45:"n\u0307",\u0146:"n\u0327",\u00F3:"o\u0301",\u00F2:"o\u0300",\u00F6:"o\u0308",\u022B:"o\u0308\u0304",\u00F5:"o\u0303",\u1E4D:"o\u0303\u0301",\u1E4F:"o\u0303\u0308",\u022D:"o\u0303\u0304",\u014D:"o\u0304",\u1E53:"o\u0304\u0301",\u1E51:"o\u0304\u0300",\u014F:"o\u0306",\u01D2:"o\u030C",\u00F4:"o\u0302",\u1ED1:"o\u0302\u0301",\u1ED3:"o\u0302\u0300",\u1ED7:"o\u0302\u0303",\u022F:"o\u0307",\u0231:"o\u0307\u0304",\u0151:"o\u030B",\u1E55:"p\u0301",\u1E57:"p\u0307",\u0155:"r\u0301",\u0159:"r\u030C",\u1E59:"r\u0307",\u0157:"r\u0327",\u015B:"s\u0301",\u1E65:"s\u0301\u0307",\u0161:"s\u030C",\u1E67:"s\u030C\u0307",\u015D:"s\u0302",\u1E61:"s\u0307",\u015F:"s\u0327",\u1E97:"t\u0308",\u0165:"t\u030C",\u1E6B:"t\u0307",\u0163:"t\u0327",\u00FA:"u\u0301",\u00F9:"u\u0300",\u00FC:"u\u0308",\u01D8:"u\u0308\u0301",\u01DC:"u\u0308\u0300",\u01D6:"u\u0308\u0304",\u01DA:"u\u0308\u030C",\u0169:"u\u0303",\u1E79:"u\u0303\u0301",\u016B:"u\u0304",\u1E7B:"u\u0304\u0308",\u016D:"u\u0306",\u01D4:"u\u030C",\u00FB:"u\u0302",\u016F:"u\u030A",\u0171:"u\u030B",\u1E7D:"v\u0303",\u1E83:"w\u0301",\u1E81:"w\u0300",\u1E85:"w\u0308",\u0175:"w\u0302",\u1E87:"w\u0307",\u1E98:"w\u030A",\u1E8D:"x\u0308",\u1E8B:"x\u0307",\u00FD:"y\u0301",\u1EF3:"y\u0300",\u00FF:"y\u0308",\u1EF9:"y\u0303",\u0233:"y\u0304",\u0177:"y\u0302",\u1E8F:"y\u0307",\u1E99:"y\u030A",\u017A:"z\u0301",\u017E:"z\u030C",\u1E91:"z\u0302",\u017C:"z\u0307",\u00C1:"A\u0301",\u00C0:"A\u0300",\u00C4:"A\u0308",\u01DE:"A\u0308\u0304",\u00C3:"A\u0303",\u0100:"A\u0304",\u0102:"A\u0306",\u1EAE:"A\u0306\u0301",\u1EB0:"A\u0306\u0300",\u1EB4:"A\u0306\u0303",\u01CD:"A\u030C",\u00C2:"A\u0302",\u1EA4:"A\u0302\u0301",\u1EA6:"A\u0302\u0300",\u1EAA:"A\u0302\u0303",\u0226:"A\u0307",\u01E0:"A\u0307\u0304",\u00C5:"A\u030A",\u01FA:"A\u030A\u0301",\u1E02:"B\u0307",\u0106:"C\u0301",\u1E08:"C\u0327\u0301",\u010C:"C\u030C",\u0108:"C\u0302",\u010A:"C\u0307",\u00C7:"C\u0327",\u010E:"D\u030C",\u1E0A:"D\u0307",\u1E10:"D\u0327",\u00C9:"E\u0301",\u00C8:"E\u0300",\u00CB:"E\u0308",\u1EBC:"E\u0303",\u0112:"E\u0304",\u1E16:"E\u0304\u0301",\u1E14:"E\u0304\u0300",\u0114:"E\u0306",\u1E1C:"E\u0327\u0306",\u011A:"E\u030C",\u00CA:"E\u0302",\u1EBE:"E\u0302\u0301",\u1EC0:"E\u0302\u0300",\u1EC4:"E\u0302\u0303",\u0116:"E\u0307",\u0228:"E\u0327",\u1E1E:"F\u0307",\u01F4:"G\u0301",\u1E20:"G\u0304",\u011E:"G\u0306",\u01E6:"G\u030C",\u011C:"G\u0302",\u0120:"G\u0307",\u0122:"G\u0327",\u1E26:"H\u0308",\u021E:"H\u030C",\u0124:"H\u0302",\u1E22:"H\u0307",\u1E28:"H\u0327",\u00CD:"I\u0301",\u00CC:"I\u0300",\u00CF:"I\u0308",\u1E2E:"I\u0308\u0301",\u0128:"I\u0303",\u012A:"I\u0304",\u012C:"I\u0306",\u01CF:"I\u030C",\u00CE:"I\u0302",\u0130:"I\u0307",\u0134:"J\u0302",\u1E30:"K\u0301",\u01E8:"K\u030C",\u0136:"K\u0327",\u0139:"L\u0301",\u013D:"L\u030C",\u013B:"L\u0327",\u1E3E:"M\u0301",\u1E40:"M\u0307",\u0143:"N\u0301",\u01F8:"N\u0300",\u00D1:"N\u0303",\u0147:"N\u030C",\u1E44:"N\u0307",\u0145:"N\u0327",\u00D3:"O\u0301",\u00D2:"O\u0300",\u00D6:"O\u0308",\u022A:"O\u0308\u0304",\u00D5:"O\u0303",\u1E4C:"O\u0303\u0301",\u1E4E:"O\u0303\u0308",\u022C:"O\u0303\u0304",\u014C:"O\u0304",\u1E52:"O\u0304\u0301",\u1E50:"O\u0304\u0300",\u014E:"O\u0306",\u01D1:"O\u030C",\u00D4:"O\u0302",\u1ED0:"O\u0302\u0301",\u1ED2:"O\u0302\u0300",\u1ED6:"O\u0302\u0303",\u022E:"O\u0307",\u0230:"O\u0307\u0304",\u0150:"O\u030B",\u1E54:"P\u0301",\u1E56:"P\u0307",\u0154:"R\u0301",\u0158:"R\u030C",\u1E58:"R\u0307",\u0156:"R\u0327",\u015A:"S\u0301",\u1E64:"S\u0301\u0307",\u0160:"S\u030C",\u1E66:"S\u030C\u0307",\u015C:"S\u0302",\u1E60:"S\u0307",\u015E:"S\u0327",\u0164:"T\u030C",\u1E6A:"T\u0307",\u0162:"T\u0327",\u00DA:"U\u0301",\u00D9:"U\u0300",\u00DC:"U\u0308",\u01D7:"U\u0308\u0301",\u01DB:"U\u0308\u0300",\u01D5:"U\u0308\u0304",\u01D9:"U\u0308\u030C",\u0168:"U\u0303",\u1E78:"U\u0303\u0301",\u016A:"U\u0304",\u1E7A:"U\u0304\u0308",\u016C:"U\u0306",\u01D3:"U\u030C",\u00DB:"U\u0302",\u016E:"U\u030A",\u0170:"U\u030B",\u1E7C:"V\u0303",\u1E82:"W\u0301",\u1E80:"W\u0300",\u1E84:"W\u0308",\u0174:"W\u0302",\u1E86:"W\u0307",\u1E8C:"X\u0308",\u1E8A:"X\u0307",\u00DD:"Y\u0301",\u1EF2:"Y\u0300",\u0178:"Y\u0308",\u1EF8:"Y\u0303",\u0232:"Y\u0304",\u0176:"Y\u0302",\u1E8E:"Y\u0307",\u0179:"Z\u0301",\u017D:"Z\u030C",\u1E90:"Z\u0302",\u017B:"Z\u0307",\u03AC:"\u03B1\u0301",\u1F70:"\u03B1\u0300",\u1FB1:"\u03B1\u0304",\u1FB0:"\u03B1\u0306",\u03AD:"\u03B5\u0301",\u1F72:"\u03B5\u0300",\u03AE:"\u03B7\u0301",\u1F74:"\u03B7\u0300",\u03AF:"\u03B9\u0301",\u1F76:"\u03B9\u0300",\u03CA:"\u03B9\u0308",\u0390:"\u03B9\u0308\u0301",\u1FD2:"\u03B9\u0308\u0300",\u1FD1:"\u03B9\u0304",\u1FD0:"\u03B9\u0306",\u03CC:"\u03BF\u0301",\u1F78:"\u03BF\u0300",\u03CD:"\u03C5\u0301",\u1F7A:"\u03C5\u0300",\u03CB:"\u03C5\u0308",\u03B0:"\u03C5\u0308\u0301",\u1FE2:"\u03C5\u0308\u0300",\u1FE1:"\u03C5\u0304",\u1FE0:"\u03C5\u0306",\u03CE:"\u03C9\u0301",\u1F7C:"\u03C9\u0300",\u038E:"\u03A5\u0301",\u1FEA:"\u03A5\u0300",\u03AB:"\u03A5\u0308",\u1FE9:"\u03A5\u0304",\u1FE8:"\u03A5\u0306",\u038F:"\u03A9\u0301",\u1FFA:"\u03A9\u0300"},Ge=class r{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new zt(e,t,this.mode),this.settings=t,this.leftrightDepth=0}expect(e,t){if(t===void 0&&(t=!0),this.fetch().text!==e)throw new z("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return this.nextToken==null&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{var e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){var t=this.nextToken;this.consume(),this.gullet.pushToken(new b0("}")),this.gullet.pushTokens(e);var a=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,a}parseExpression(e,t){for(var a=[];;){this.mode==="math"&&this.consumeSpaces();var n=this.fetch();if(r.endOfExpression.indexOf(n.text)!==-1||t&&n.text===t||e&&P0[n.text]&&P0[n.text].infix)break;var s=this.parseAtom(t);if(s){if(s.type==="internal")continue}else break;a.push(s)}return this.mode==="text"&&this.formLigatures(a),this.handleInfixNodes(a)}handleInfixNodes(e){for(var t=-1,a,n=0;n=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+t[0]+'" used in math mode',e);var h=Y[this.mode][t].group,c=d0.range(e),f;if(i1.hasOwnProperty(h)){var v=h;f={type:"atom",mode:this.mode,family:v,loc:c,text:t}}else f={type:h,mode:this.mode,loc:c,text:t};l=f}else if(t.charCodeAt(0)>=128)this.settings.strict&&(Ar(t.charCodeAt(0))?this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'"'+(" ("+t.charCodeAt(0)+")"),e)),l={type:"textord",mode:"text",loc:d0.range(e),text:t};else return null;if(this.consume(),s)for(var b=0;b=0;n--)r[n].loc.start>a&&(t+=" ",a=r[n].loc.start),t+=r[n].text,a+=r[n].text.length;var s=W.go(S.go(t,e));return s},S={go:function(r,e){if(!r)return[];e===void 0&&(e="ce");var t="0",a={};a.parenthesisLevel=0,r=r.replace(/\n/g," "),r=r.replace(/[\u2212\u2013\u2014\u2010]/g,"-"),r=r.replace(/[\u2026]/g,"...");for(var n,s=10,l=[];;){n!==r?(s=10,n=r):s--;var h=S.stateMachines[e],c=h.transitions[t]||h.transitions["*"];e:for(var f=0;f0){if(b.revisit||(r=v.remainder),!b.toContinue)break e}else return l}}if(s<=0)throw["MhchemBugU","mhchem bug U. Please report."]}},concatArray:function(r,e){if(e)if(Array.isArray(e))for(var t=0;t":/^[=<>]/,"#":/^[#\u2261]/,"+":/^\+/,"-$":/^-(?=[\s_},;\]/]|$|\([a-z]+\))/,"-9":/^-(?=[0-9])/,"- orbital overlap":/^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,"-":/^-/,"pm-operator":/^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,operator:/^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,arrowUpDown:/^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,"\\bond{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\bond{","","","}")},"->":/^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,CMT:/^[CMT](?=\[)/,"[(...)]":function(r){return S.patterns.findObserveGroups(r,"[","","","]")},"1st-level escape":/^(&|\\\\|\\hline)\s*/,"\\,":/^(?:\\[,\ ;:])/,"\\x{}{}":function(r){return S.patterns.findObserveGroups(r,"",/^\\[a-zA-Z]+\{/,"}","","","{","}","",!0)},"\\x{}":function(r){return S.patterns.findObserveGroups(r,"",/^\\[a-zA-Z]+\{/,"}","")},"\\ca":/^\\ca(?:\s+|(?![a-zA-Z]))/,"\\x":/^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,orbital:/^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/,others:/^[\/~|]/,"\\frac{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\frac{","","","}","{","","","}")},"\\overset{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\overset{","","","}","{","","","}")},"\\underset{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\underset{","","","}","{","","","}")},"\\underbrace{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\underbrace{","","","}_","{","","","}")},"\\color{(...)}0":function(r){return S.patterns.findObserveGroups(r,"\\color{","","","}")},"\\color{(...)}{(...)}1":function(r){return S.patterns.findObserveGroups(r,"\\color{","","","}","{","","","}")},"\\color(...){(...)}2":function(r){return S.patterns.findObserveGroups(r,"\\color","\\","",/^(?=\{)/,"{","","","}")},"\\ce{(...)}":function(r){return S.patterns.findObserveGroups(r,"\\ce{","","","}")},oxidation$:/^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"d-oxidation$":/^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,"roman numeral":/^[IVX]+/,"1/2$":/^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,amount:function(r){var e;if(e=r.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/),e)return{match_:e[0],remainder:r.substr(e[0].length)};var t=S.patterns.findObserveGroups(r,"","$","$","");return t&&(e=t.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/),e)?{match_:e[0],remainder:r.substr(e[0].length)}:null},amount2:function(r){return this.amount(r)},"(KV letters),":/^(?:[A-Z][a-z]{0,2}|i)(?=,)/,formula$:function(r){if(r.match(/^\([a-z]+\)$/))return null;var e=r.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);return e?{match_:e[0],remainder:r.substr(e[0].length)}:null},uprightEntities:/^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,"/":/^\s*(\/)\s*/,"//":/^\s*(\/\/)\s*/,"*":/^\s*[*.]\s*/},findObserveGroups:function(r,e,t,a,n,s,l,h,c,f){var v=function(D,N){if(typeof N=="string")return D.indexOf(N)!==0?null:N;var $=D.match(N);return $?$[0]:null},b=function(D,N,$){for(var H=0;N0,null},x=v(r,e);if(x===null||(r=r.substr(x.length),x=v(r,t),x===null))return null;var w=b(r,x.length,a||n);if(w===null)return null;var A=r.substring(0,a?w.endMatchEnd:w.endMatchBegin);if(s||l){var q=this.findObserveGroups(r.substr(w.endMatchEnd),s,l,h,c);if(q===null)return null;var _=[A,q.match_];return{match_:f?_.join(""):_,remainder:q.remainder}}else return{match_:A,remainder:r.substr(w.endMatchEnd)}},match_:function(r,e){var t=S.patterns.patterns[r];if(t===void 0)throw["MhchemBugP","mhchem bug P. Please report. ("+r+")"];if(typeof t=="function")return S.patterns.patterns[r](e);var a=e.match(t);if(a){var n;return a[2]?n=[a[1],a[2]]:a[1]?n=a[1]:n=a[0],{match_:n,remainder:e.substr(a[0].length)}}return null}},actions:{"a=":function(r,e){r.a=(r.a||"")+e},"b=":function(r,e){r.b=(r.b||"")+e},"p=":function(r,e){r.p=(r.p||"")+e},"o=":function(r,e){r.o=(r.o||"")+e},"q=":function(r,e){r.q=(r.q||"")+e},"d=":function(r,e){r.d=(r.d||"")+e},"rm=":function(r,e){r.rm=(r.rm||"")+e},"text=":function(r,e){r.text_=(r.text_||"")+e},insert:function(r,e,t){return{type_:t}},"insert+p1":function(r,e,t){return{type_:t,p1:e}},"insert+p1+p2":function(r,e,t){return{type_:t,p1:e[0],p2:e[1]}},copy:function(r,e){return e},rm:function(r,e){return{type_:"rm",p1:e||""}},text:function(r,e){return S.go(e,"text")},"{text}":function(r,e){var t=["{"];return S.concatArray(t,S.go(e,"text")),t.push("}"),t},"tex-math":function(r,e){return S.go(e,"tex-math")},"tex-math tight":function(r,e){return S.go(e,"tex-math tight")},bond:function(r,e,t){return{type_:"bond",kind_:t||e}},"color0-output":function(r,e){return{type_:"color0",color:e[0]}},ce:function(r,e){return S.go(e)},"1/2":function(r,e){var t=[];e.match(/^[+\-]/)&&(t.push(e.substr(0,1)),e=e.substr(1));var a=e.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);return a[1]=a[1].replace(/\$/g,""),t.push({type_:"frac",p1:a[1],p2:a[2]}),a[3]&&(a[3]=a[3].replace(/\$/g,""),t.push({type_:"tex-math",p1:a[3]})),t},"9,9":function(r,e){return S.go(e,"9,9")}},createTransitions:function(r){var e,t,a,n,s={};for(e in r)for(t in r[e])for(a=t.split("|"),r[e][t].stateArray=a,n=0;n":{"0|1|2|3":{action_:"r=",nextState:"r"},"a|as":{action_:["output","r="],nextState:"r"},"*":{action_:["output","r="],nextState:"r"}},"+":{o:{action_:"d= kv",nextState:"d"},"d|D":{action_:"d=",nextState:"d"},q:{action_:"d=",nextState:"qd"},"qd|qD":{action_:"d=",nextState:"qd"},dq:{action_:["output","d="],nextState:"d"},3:{action_:["sb=false","output","operator"],nextState:"0"}},amount:{"0|2":{action_:"a=",nextState:"a"}},"pm-operator":{"0|1|2|a|as":{action_:["sb=false","output",{type_:"operator",option:"\\pm"}],nextState:"0"}},operator:{"0|1|2|a|as":{action_:["sb=false","output","operator"],nextState:"0"}},"-$":{"o|q":{action_:["charge or bond","output"],nextState:"qd"},d:{action_:"d=",nextState:"d"},D:{action_:["output",{type_:"bond",option:"-"}],nextState:"3"},q:{action_:"d=",nextState:"qd"},qd:{action_:"d=",nextState:"qd"},"qD|dq":{action_:["output",{type_:"bond",option:"-"}],nextState:"3"}},"-9":{"3|o":{action_:["output",{type_:"insert",option:"hyphen"}],nextState:"3"}},"- orbital overlap":{o:{action_:["output",{type_:"insert",option:"hyphen"}],nextState:"2"},d:{action_:["output",{type_:"insert",option:"hyphen"}],nextState:"2"}},"-":{"0|1|2":{action_:[{type_:"output",option:1},"beginsWithBond=true",{type_:"bond",option:"-"}],nextState:"3"},3:{action_:{type_:"bond",option:"-"}},a:{action_:["output",{type_:"insert",option:"hyphen"}],nextState:"2"},as:{action_:[{type_:"output",option:2},{type_:"bond",option:"-"}],nextState:"3"},b:{action_:"b="},o:{action_:{type_:"- after o/d",option:!1},nextState:"2"},q:{action_:{type_:"- after o/d",option:!1},nextState:"2"},"d|qd|dq":{action_:{type_:"- after o/d",option:!0},nextState:"2"},"D|qD|p":{action_:["output",{type_:"bond",option:"-"}],nextState:"3"}},amount2:{"1|3":{action_:"a=",nextState:"a"}},letters:{"0|1|2|3|a|as|b|p|bp|o":{action_:"o=",nextState:"o"},"q|dq":{action_:["output","o="],nextState:"o"},"d|D|qd|qD":{action_:"o after d",nextState:"o"}},digits:{o:{action_:"q=",nextState:"q"},"d|D":{action_:"q=",nextState:"dq"},q:{action_:["output","o="],nextState:"o"},a:{action_:"o=",nextState:"o"}},"space A":{"b|p|bp":{}},space:{a:{nextState:"as"},0:{action_:"sb=false"},"1|2":{action_:"sb=true"},"r|rt|rd|rdt|rdq":{action_:"output",nextState:"0"},"*":{action_:["output","sb=true"],nextState:"1"}},"1st-level escape":{"1|2":{action_:["output",{type_:"insert+p1",option:"1st-level escape"}]},"*":{action_:["output",{type_:"insert+p1",option:"1st-level escape"}],nextState:"0"}},"[(...)]":{"r|rt":{action_:"rd=",nextState:"rd"},"rd|rdt":{action_:"rq=",nextState:"rdq"}},"...":{"o|d|D|dq|qd|qD":{action_:["output",{type_:"bond",option:"..."}],nextState:"3"},"*":{action_:[{type_:"output",option:1},{type_:"insert",option:"ellipsis"}],nextState:"1"}},". |* ":{"*":{action_:["output",{type_:"insert",option:"addition compound"}],nextState:"1"}},"state of aggregation $":{"*":{action_:["output","state of aggregation"],nextState:"1"}},"{[(":{"a|as|o":{action_:["o=","output","parenthesisLevel++"],nextState:"2"},"0|1|2|3":{action_:["o=","output","parenthesisLevel++"],nextState:"2"},"*":{action_:["output","o=","output","parenthesisLevel++"],nextState:"2"}},")]}":{"0|1|2|3|b|p|bp|o":{action_:["o=","parenthesisLevel--"],nextState:"o"},"a|as|d|D|q|qd|qD|dq":{action_:["output","o=","parenthesisLevel--"],nextState:"o"}},", ":{"*":{action_:["output","comma"],nextState:"0"}},"^_":{"*":{}},"^{(...)}|^($...$)":{"0|1|2|as":{action_:"b=",nextState:"b"},p:{action_:"b=",nextState:"bp"},"3|o":{action_:"d= kv",nextState:"D"},q:{action_:"d=",nextState:"qD"},"d|D|qd|qD|dq":{action_:["output","d="],nextState:"D"}},"^a|^\\x{}{}|^\\x{}|^\\x|'":{"0|1|2|as":{action_:"b=",nextState:"b"},p:{action_:"b=",nextState:"bp"},"3|o":{action_:"d= kv",nextState:"d"},q:{action_:"d=",nextState:"qd"},"d|qd|D|qD":{action_:"d="},dq:{action_:["output","d="],nextState:"d"}},"_{(state of aggregation)}$":{"d|D|q|qd|qD|dq":{action_:["output","q="],nextState:"q"}},"_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x":{"0|1|2|as":{action_:"p=",nextState:"p"},b:{action_:"p=",nextState:"bp"},"3|o":{action_:"q=",nextState:"q"},"d|D":{action_:"q=",nextState:"dq"},"q|qd|qD|dq":{action_:["output","q="],nextState:"q"}},"=<>":{"0|1|2|3|a|as|o|q|d|D|qd|qD|dq":{action_:[{type_:"output",option:2},"bond"],nextState:"3"}},"#":{"0|1|2|3|a|as|o":{action_:[{type_:"output",option:2},{type_:"bond",option:"#"}],nextState:"3"}},"{}":{"*":{action_:{type_:"output",option:1},nextState:"1"}},"{...}":{"0|1|2|3|a|as|b|p|bp":{action_:"o=",nextState:"o"},"o|d|D|q|qd|qD|dq":{action_:["output","o="],nextState:"o"}},"$...$":{a:{action_:"a="},"0|1|2|3|as|b|p|bp|o":{action_:"o=",nextState:"o"},"as|o":{action_:"o="},"q|d|D|qd|qD|dq":{action_:["output","o="],nextState:"o"}},"\\bond{(...)}":{"*":{action_:[{type_:"output",option:2},"bond"],nextState:"3"}},"\\frac{(...)}":{"*":{action_:[{type_:"output",option:1},"frac-output"],nextState:"3"}},"\\overset{(...)}":{"*":{action_:[{type_:"output",option:2},"overset-output"],nextState:"3"}},"\\underset{(...)}":{"*":{action_:[{type_:"output",option:2},"underset-output"],nextState:"3"}},"\\underbrace{(...)}":{"*":{action_:[{type_:"output",option:2},"underbrace-output"],nextState:"3"}},"\\color{(...)}{(...)}1|\\color(...){(...)}2":{"*":{action_:[{type_:"output",option:2},"color-output"],nextState:"3"}},"\\color{(...)}0":{"*":{action_:[{type_:"output",option:2},"color0-output"]}},"\\ce{(...)}":{"*":{action_:[{type_:"output",option:2},"ce"],nextState:"3"}},"\\,":{"*":{action_:[{type_:"output",option:1},"copy"],nextState:"1"}},"\\x{}{}|\\x{}|\\x":{"0|1|2|3|a|as|b|p|bp|o|c0":{action_:["o=","output"],nextState:"3"},"*":{action_:["output","o=","output"],nextState:"3"}},others:{"*":{action_:[{type_:"output",option:1},"copy"],nextState:"3"}},else2:{a:{action_:"a to o",nextState:"o",revisit:!0},as:{action_:["output","sb=true"],nextState:"1",revisit:!0},"r|rt|rd|rdt|rdq":{action_:["output"],nextState:"0",revisit:!0},"*":{action_:["output","copy"],nextState:"3"}}}),actions:{"o after d":function(r,e){var t;if((r.d||"").match(/^[0-9]+$/)){var a=r.d;r.d=void 0,t=this.output(r),r.b=a}else t=this.output(r);return S.actions["o="](r,e),t},"d= kv":function(r,e){r.d=e,r.dType="kv"},"charge or bond":function(r,e){if(r.beginsWithBond){var t=[];return S.concatArray(t,this.output(r)),S.concatArray(t,S.actions.bond(r,e,"-")),t}else r.d=e},"- after o/d":function(r,e,t){var a=S.patterns.match_("orbital",r.o||""),n=S.patterns.match_("one lowercase greek letter $",r.o||""),s=S.patterns.match_("one lowercase latin letter $",r.o||""),l=S.patterns.match_("$one lowercase latin letter$ $",r.o||""),h=e==="-"&&(a&&a.remainder===""||n||s||l);h&&!r.a&&!r.b&&!r.p&&!r.d&&!r.q&&!a&&s&&(r.o="$"+r.o+"$");var c=[];return h?(S.concatArray(c,this.output(r)),c.push({type_:"hyphen"})):(a=S.patterns.match_("digits",r.d||""),t&&a&&a.remainder===""?(S.concatArray(c,S.actions["d="](r,e)),S.concatArray(c,this.output(r))):(S.concatArray(c,this.output(r)),S.concatArray(c,S.actions.bond(r,e,"-")))),c},"a to o":function(r){r.o=r.a,r.a=void 0},"sb=true":function(r){r.sb=!0},"sb=false":function(r){r.sb=!1},"beginsWithBond=true":function(r){r.beginsWithBond=!0},"beginsWithBond=false":function(r){r.beginsWithBond=!1},"parenthesisLevel++":function(r){r.parenthesisLevel++},"parenthesisLevel--":function(r){r.parenthesisLevel--},"state of aggregation":function(r,e){return{type_:"state of aggregation",p1:S.go(e,"o")}},comma:function(r,e){var t=e.replace(/\s*$/,""),a=t!==e;return a&&r.parenthesisLevel===0?{type_:"comma enumeration L",p1:t}:{type_:"comma enumeration M",p1:t}},output:function(r,e,t){var a;if(!r.r)a=[],!r.a&&!r.b&&!r.p&&!r.o&&!r.q&&!r.d&&!t||(r.sb&&a.push({type_:"entitySkip"}),!r.o&&!r.q&&!r.d&&!r.b&&!r.p&&t!==2?(r.o=r.a,r.a=void 0):!r.o&&!r.q&&!r.d&&(r.b||r.p)?(r.o=r.a,r.d=r.b,r.q=r.p,r.a=r.b=r.p=void 0):r.o&&r.dType==="kv"&&S.patterns.match_("d-oxidation$",r.d||"")?r.dType="oxidation":r.o&&r.dType==="kv"&&!r.q&&(r.dType=void 0),a.push({type_:"chemfive",a:S.go(r.a,"a"),b:S.go(r.b,"bd"),p:S.go(r.p,"pq"),o:S.go(r.o,"o"),q:S.go(r.q,"pq"),d:S.go(r.d,r.dType==="oxidation"?"oxidation":"bd"),dType:r.dType}));else{var n;r.rdt==="M"?n=S.go(r.rd,"tex-math"):r.rdt==="T"?n=[{type_:"text",p1:r.rd||""}]:n=S.go(r.rd);var s;r.rqt==="M"?s=S.go(r.rq,"tex-math"):r.rqt==="T"?s=[{type_:"text",p1:r.rq||""}]:s=S.go(r.rq),a={type_:"arrow",r:r.r,rd:n,rq:s}}for(var l in r)l!=="parenthesisLevel"&&l!=="beginsWithBond"&&delete r[l];return a},"oxidation-output":function(r,e){var t=["{"];return S.concatArray(t,S.go(e,"oxidation")),t.push("}"),t},"frac-output":function(r,e){return{type_:"frac-ce",p1:S.go(e[0]),p2:S.go(e[1])}},"overset-output":function(r,e){return{type_:"overset",p1:S.go(e[0]),p2:S.go(e[1])}},"underset-output":function(r,e){return{type_:"underset",p1:S.go(e[0]),p2:S.go(e[1])}},"underbrace-output":function(r,e){return{type_:"underbrace",p1:S.go(e[0]),p2:S.go(e[1])}},"color-output":function(r,e){return{type_:"color",color1:e[0],color2:S.go(e[1])}},"r=":function(r,e){r.r=e},"rdt=":function(r,e){r.rdt=e},"rd=":function(r,e){r.rd=e},"rqt=":function(r,e){r.rqt=e},"rq=":function(r,e){r.rq=e},operator:function(r,e,t){return{type_:"operator",kind_:t||e}}}},a:{transitions:S.createTransitions({empty:{"*":{}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"1",revisit:!0}},"$(...)$":{"*":{action_:"tex-math tight",nextState:"1"}},",":{"*":{action_:{type_:"insert",option:"commaDecimal"}}},else2:{"*":{action_:"copy"}}}),actions:{}},o:{transitions:S.createTransitions({empty:{"*":{}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"1",revisit:!0}},letters:{"*":{action_:"rm"}},"\\ca":{"*":{action_:{type_:"insert",option:"circa"}}},"\\x{}{}|\\x{}|\\x":{"*":{action_:"copy"}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},"{(...)}":{"*":{action_:"{text}"}},else2:{"*":{action_:"copy"}}}),actions:{}},text:{transitions:S.createTransitions({empty:{"*":{action_:"output"}},"{...}":{"*":{action_:"text="}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},"\\greek":{"*":{action_:["output","rm"]}},"\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:["output","copy"]}},else:{"*":{action_:"text="}}}),actions:{output:function(r){if(r.text_){var e={type_:"text",p1:r.text_};for(var t in r)delete r[t];return e}}}},pq:{transitions:S.createTransitions({empty:{"*":{}},"state of aggregation $":{"*":{action_:"state of aggregation"}},i$:{0:{nextState:"!f",revisit:!0}},"(KV letters),":{0:{action_:"rm",nextState:"0"}},formula$:{0:{nextState:"f",revisit:!0}},"1/2$":{0:{action_:"1/2"}},else:{0:{nextState:"!f",revisit:!0}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},"{(...)}":{"*":{action_:"text"}},"a-z":{f:{action_:"tex-math"}},letters:{"*":{action_:"rm"}},"-9.,9":{"*":{action_:"9,9"}},",":{"*":{action_:{type_:"insert+p1",option:"comma enumeration S"}}},"\\color{(...)}{(...)}1|\\color(...){(...)}2":{"*":{action_:"color-output"}},"\\color{(...)}0":{"*":{action_:"color0-output"}},"\\ce{(...)}":{"*":{action_:"ce"}},"\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"copy"}},else2:{"*":{action_:"copy"}}}),actions:{"state of aggregation":function(r,e){return{type_:"state of aggregation subscript",p1:S.go(e,"o")}},"color-output":function(r,e){return{type_:"color",color1:e[0],color2:S.go(e[1],"pq")}}}},bd:{transitions:S.createTransitions({empty:{"*":{}},x$:{0:{nextState:"!f",revisit:!0}},formula$:{0:{nextState:"f",revisit:!0}},else:{0:{nextState:"!f",revisit:!0}},"-9.,9 no missing 0":{"*":{action_:"9,9"}},".":{"*":{action_:{type_:"insert",option:"electron dot"}}},"a-z":{f:{action_:"tex-math"}},x:{"*":{action_:{type_:"insert",option:"KV x"}}},letters:{"*":{action_:"rm"}},"'":{"*":{action_:{type_:"insert",option:"prime"}}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},"{(...)}":{"*":{action_:"text"}},"\\color{(...)}{(...)}1|\\color(...){(...)}2":{"*":{action_:"color-output"}},"\\color{(...)}0":{"*":{action_:"color0-output"}},"\\ce{(...)}":{"*":{action_:"ce"}},"\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"copy"}},else2:{"*":{action_:"copy"}}}),actions:{"color-output":function(r,e){return{type_:"color",color1:e[0],color2:S.go(e[1],"bd")}}}},oxidation:{transitions:S.createTransitions({empty:{"*":{}},"roman numeral":{"*":{action_:"roman-numeral"}},"${(...)}$|$(...)$":{"*":{action_:"tex-math"}},else:{"*":{action_:"copy"}}}),actions:{"roman-numeral":function(r,e){return{type_:"roman numeral",p1:e||""}}}},"tex-math":{transitions:S.createTransitions({empty:{"*":{action_:"output"}},"\\ce{(...)}":{"*":{action_:["output","ce"]}},"{...}|\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"o="}},else:{"*":{action_:"o="}}}),actions:{output:function(r){if(r.o){var e={type_:"tex-math",p1:r.o};for(var t in r)delete r[t];return e}}}},"tex-math tight":{transitions:S.createTransitions({empty:{"*":{action_:"output"}},"\\ce{(...)}":{"*":{action_:["output","ce"]}},"{...}|\\,|\\x{}{}|\\x{}|\\x":{"*":{action_:"o="}},"-|+":{"*":{action_:"tight operator"}},else:{"*":{action_:"o="}}}),actions:{"tight operator":function(r,e){r.o=(r.o||"")+"{"+e+"}"},output:function(r){if(r.o){var e={type_:"tex-math",p1:r.o};for(var t in r)delete r[t];return e}}}},"9,9":{transitions:S.createTransitions({empty:{"*":{}},",":{"*":{action_:"comma"}},else:{"*":{action_:"copy"}}}),actions:{comma:function(){return{type_:"commaDecimal"}}}},pu:{transitions:S.createTransitions({empty:{"*":{action_:"output"}},space$:{"*":{action_:["output","space"]}},"{[(|)]}":{"0|a":{action_:"copy"}},"(-)(9)^(-9)":{0:{action_:"number^",nextState:"a"}},"(-)(9.,9)(e)(99)":{0:{action_:"enumber",nextState:"a"}},space:{"0|a":{}},"pm-operator":{"0|a":{action_:{type_:"operator",option:"\\pm"},nextState:"0"}},operator:{"0|a":{action_:"copy",nextState:"0"}},"//":{d:{action_:"o=",nextState:"/"}},"/":{d:{action_:"o=",nextState:"/"}},"{...}|else":{"0|d":{action_:"d=",nextState:"d"},a:{action_:["space","d="],nextState:"d"},"/|q":{action_:"q=",nextState:"q"}}}),actions:{enumber:function(r,e){var t=[];return e[0]==="+-"||e[0]==="+/-"?t.push("\\pm "):e[0]&&t.push(e[0]),e[1]&&(S.concatArray(t,S.go(e[1],"pu-9,9")),e[2]&&(e[2].match(/[,.]/)?S.concatArray(t,S.go(e[2],"pu-9,9")):t.push(e[2])),e[3]=e[4]||e[3],e[3]&&(e[3]=e[3].trim(),e[3]==="e"||e[3].substr(0,1)==="*"?t.push({type_:"cdot"}):t.push({type_:"times"}))),e[3]&&t.push("10^{"+e[5]+"}"),t},"number^":function(r,e){var t=[];return e[0]==="+-"||e[0]==="+/-"?t.push("\\pm "):e[0]&&t.push(e[0]),S.concatArray(t,S.go(e[1],"pu-9,9")),t.push("^{"+e[2]+"}"),t},operator:function(r,e,t){return{type_:"operator",kind_:t||e}},space:function(){return{type_:"pu-space-1"}},output:function(r){var e,t=S.patterns.match_("{(...)}",r.d||"");t&&t.remainder===""&&(r.d=t.match_);var a=S.patterns.match_("{(...)}",r.q||"");if(a&&a.remainder===""&&(r.q=a.match_),r.d&&(r.d=r.d.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),r.d=r.d.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F")),r.q){r.q=r.q.replace(/\u00B0C|\^oC|\^{o}C/g,"{}^{\\circ}C"),r.q=r.q.replace(/\u00B0F|\^oF|\^{o}F/g,"{}^{\\circ}F");var n={d:S.go(r.d,"pu"),q:S.go(r.q,"pu")};r.o==="//"?e={type_:"pu-frac",p1:n.d,p2:n.q}:(e=n.d,n.d.length>1||n.q.length>1?e.push({type_:" / "}):e.push({type_:"/"}),S.concatArray(e,n.q))}else e=S.go(r.d,"pu-2");for(var s in r)delete r[s];return e}}},"pu-2":{transitions:S.createTransitions({empty:{"*":{action_:"output"}},"*":{"*":{action_:["output","cdot"],nextState:"0"}},"\\x":{"*":{action_:"rm="}},space:{"*":{action_:["output","space"],nextState:"0"}},"^{(...)}|^(-1)":{1:{action_:"^(-1)"}},"-9.,9":{0:{action_:"rm=",nextState:"0"},1:{action_:"^(-1)",nextState:"0"}},"{...}|else":{"*":{action_:"rm=",nextState:"1"}}}),actions:{cdot:function(){return{type_:"tight cdot"}},"^(-1)":function(r,e){r.rm+="^{"+e+"}"},space:function(){return{type_:"pu-space-2"}},output:function(r){var e=[];if(r.rm){var t=S.patterns.match_("{(...)}",r.rm||"");t&&t.remainder===""?e=S.go(t.match_,"pu"):e={type_:"rm",p1:r.rm}}for(var a in r)delete r[a];return e}}},"pu-9,9":{transitions:S.createTransitions({empty:{0:{action_:"output-0"},o:{action_:"output-o"}},",":{0:{action_:["output-0","comma"],nextState:"o"}},".":{0:{action_:["output-0","copy"],nextState:"o"}},else:{"*":{action_:"text="}}}),actions:{comma:function(){return{type_:"commaDecimal"}},"output-0":function(r){var e=[];if(r.text_=r.text_||"",r.text_.length>4){var t=r.text_.length%3;t===0&&(t=3);for(var a=r.text_.length-3;a>0;a-=3)e.push(r.text_.substr(a,3)),e.push({type_:"1000 separator"});e.push(r.text_.substr(0,t)),e.reverse()}else e.push(r.text_);for(var n in r)delete r[n];return e},"output-o":function(r){var e=[];if(r.text_=r.text_||"",r.text_.length>4){for(var t=r.text_.length-3,a=0;a":return"rightarrow";case"\u2192":return"rightarrow";case"\u27F6":return"rightarrow";case"<-":return"leftarrow";case"<->":return"leftrightarrow";case"<-->":return"rightleftarrows";case"<=>":return"rightleftharpoons";case"\u21CC":return"rightleftharpoons";case"<=>>":return"rightequilibrium";case"<<=>":return"leftequilibrium";default:throw["MhchemBugT","mhchem bug T. Please report."]}},_getBond:function(r){switch(r){case"-":return"{-}";case"1":return"{-}";case"=":return"{=}";case"2":return"{=}";case"#":return"{\\equiv}";case"3":return"{\\equiv}";case"~":return"{\\tripledash}";case"~-":return"{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}";case"~=":return"{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";case"~--":return"{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";case"-~-":return"{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}";case"...":return"{{\\cdot}{\\cdot}{\\cdot}}";case"....":return"{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";case"->":return"{\\rightarrow}";case"<-":return"{\\leftarrow}";case"<":return"{<}";case">":return"{>}";default:throw["MhchemBugT","mhchem bug T. Please report."]}},_getOperator:function(r){switch(r){case"+":return" {}+{} ";case"-":return" {}-{} ";case"=":return" {}={} ";case"<":return" {}<{} ";case">":return" {}>{} ";case"<<":return" {}\\ll{} ";case">>":return" {}\\gg{} ";case"\\pm":return" {}\\pm{} ";case"\\approx":return" {}\\approx{} ";case"$\\approx$":return" {}\\approx{} ";case"v":return" \\downarrow{} ";case"(v)":return" \\downarrow{} ";case"^":return" \\uparrow{} ";case"(^)":return" \\uparrow{} ";default:throw["MhchemBugT","mhchem bug T. Please report."]}}};var wn=function(r){let e=r.data,t=e.expression,a=e.options,n=r.header;n.warnings=[],a.strict=="warn"&&(a.strict=(l,h)=>{n.warnings.push(`katex: LaTeX-incompatible input and strict mode is set to 'warn': ${h} [${l}]`)});let s=oe.renderToString(t,a);et({header:n,data:{output:s}})};Wt(wn);})(); diff --git a/internal/warpc/js/renderkatex.js b/internal/warpc/js/renderkatex.js index 8e94098ef..7c8ac25ee 100644 --- a/internal/warpc/js/renderkatex.js +++ b/internal/warpc/js/renderkatex.js @@ -1,11 +1,22 @@ import { readInput, writeOutput } from './common'; import katex from 'katex'; +import 'katex/contrib/mhchem/mhchem.js'; const render = function (input) { const data = input.data; const expression = data.expression; const options = data.options; const header = input.header; + header.warnings = []; + + if (options.strict == 'warn') { + // By default, KaTeX will write to console.warn, that's a little hard to handle. + options.strict = (errorCode, errorMsg) => { + header.warnings.push( + `katex: LaTeX-incompatible input and strict mode is set to 'warn': ${errorMsg} [${errorCode}]`, + ); + }; + } // Any error thrown here will be caught by the common.js readInput function. const output = katex.renderToString(expression, options); writeOutput({ header: header, data: { output: output } }); diff --git a/internal/warpc/katex.go b/internal/warpc/katex.go index 23ca726ac..75c20117f 100644 --- a/internal/warpc/katex.go +++ b/internal/warpc/katex.go @@ -45,7 +45,7 @@ type KatexOptions struct { // A color string given in the format "#XXX" or "#XXXXXX" ErrorColor string `json:"errorColor"` - // A collection of custom macros. + // A collection of custom macros. Macros map[string]string `json:"macros,omitempty"` // Specifies a minimum thickness, in ems, for fraction lines. @@ -53,6 +53,22 @@ type KatexOptions struct { // If true, KaTeX will throw a ParseError when it encounters an unsupported command. ThrowOnError bool `json:"throwOnError"` + + // Controls how KaTeX handles LaTeX features that offer convenience but + // aren't officially supported, one of error (default), ignore, or warn. + // + // - error: Throws an error when convenient, unsupported LaTeX features + // are encountered. + // - ignore: Allows convenient, unsupported LaTeX features without any + // feedback. + // - warn: Emits a warning when convenient, unsupported LaTeX features are + // encountered. + // + // The "newLineInDisplayMode" error code, which flags the use of \\ + // or \newline in display mode outside an array or tabular environment, is + // intentionally designed not to throw an error, despite this behavior + // being questionable. + Strict string `json:"strict"` } type KatexOutput struct { diff --git a/internal/warpc/warpc.go b/internal/warpc/warpc.go index 1159944a4..e21fefa8a 100644 --- a/internal/warpc/warpc.go +++ b/internal/warpc/warpc.go @@ -51,6 +51,9 @@ type Header struct { // Set in the response if there was an error. Err string `json:"err"` + + // Warnings is a list of warnings that may be returned in the response. + Warnings []string `json:"warnings,omitempty"` } type Message[T any] struct { @@ -155,6 +158,7 @@ func (p *dispatcherPool[Q, R]) Execute(ctx context.Context, q Message[Q]) (Messa } resp, err := call.response, p.Err() + if err == nil && resp.Header.Err != "" { err = errors.New(resp.Header.Err) } @@ -270,6 +274,8 @@ type Options struct { Infof func(format string, v ...any) + Warnf func(format string, v ...any) + // E.g. quickjs wasm. May be omitted if not needed. Runtime Binary @@ -325,6 +331,7 @@ type dispatcherPool[Q, R any] struct { counter atomic.Uint32 dispatchers []*dispatcher[Q, R] close func() error + opts Options errc chan error donec chan struct{} @@ -355,6 +362,11 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { // noop } } + if opts.Warnf == nil { + opts.Warnf = func(format string, v ...any) { + // noop + } + } if opts.Memory <= 0 { // 32 MiB @@ -384,7 +396,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { } inOuts := make([]*inOut, opts.PoolSize) - for i := 0; i < opts.PoolSize; i++ { + for i := range opts.PoolSize { var stdin, stdout hugio.ReadWriteCloser stdin = hugio.NewPipeReadWriteCloser() @@ -466,6 +478,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { dp := &dispatcherPool[Q, R]{ dispatchers: make([]*dispatcher[Q, R], len(inOuts)), + opts: opts, errc: make(chan error, 10), donec: make(chan struct{}), @@ -478,7 +491,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) { close(dp.donec) }() - for i := 0; i < len(inOuts); i++ { + for i := range inOuts { d := &dispatcher[Q, R]{ pending: make(map[uint32]*call[Q, R]), inOut: inOuts[i], diff --git a/internal/warpc/warpc_test.go b/internal/warpc/warpc_test.go index a76c46ac1..2ee4c3de5 100644 --- a/internal/warpc/warpc_test.go +++ b/internal/warpc/warpc_test.go @@ -77,6 +77,13 @@ func TestKatex(t *testing.T) { c.Assert(result.GetID(), qt.Equals, id) }) + c.Run("Chemistry", func(c *qt.C) { + id := uint32(32) + result, err := runExpression(c, id, "C_p[\\ce{H2O(l)}] = \\pu{75.3 J // mol K}") + c.Assert(err, qt.IsNil) + c.Assert(result.GetID(), qt.Equals, id) + }) + c.Run("Invalid expression", func(c *qt.C) { id := uint32(32) result, err := runExpression(c, id, "c & \\foo\\") @@ -94,7 +101,7 @@ func TestGreet(t *testing.T) { Infof: t.Logf, } - for i := 0; i < 2; i++ { + for range 2 { func() { d, err := Start[person, greeting](opts) if err != nil { @@ -116,7 +123,7 @@ func TestGreet(t *testing.T) { }, } - for j := 0; j < 20; j++ { + for j := range 20 { inputMessage.Header.ID = uint32(j + 1) g, err := d.Execute(ctx, inputMessage) if err != nil { @@ -156,7 +163,7 @@ func TestGreetParallel(t *testing.T) { ctx := context.Background() - for j := 0; j < 5; j++ { + for j := range 5 { base := i * 100 id := uint32(base + j) @@ -210,7 +217,7 @@ func TestKatexParallel(t *testing.T) { ctx := context.Background() - for j := 0; j < 1; j++ { + for j := range 1 { base := i * 100 id := uint32(base + j) diff --git a/internal/warpc/wasm/greet.wasm b/internal/warpc/wasm/greet.wasm index fb16a2c23..944199b40 100644 Binary files a/internal/warpc/wasm/greet.wasm and b/internal/warpc/wasm/greet.wasm differ diff --git a/internal/warpc/wasm/renderkatex.wasm b/internal/warpc/wasm/renderkatex.wasm index 1db68b815..b8b21c16b 100644 Binary files a/internal/warpc/wasm/renderkatex.wasm and b/internal/warpc/wasm/renderkatex.wasm differ diff --git a/langs/i18n/i18n_test.go b/langs/i18n/i18n_test.go index 8d34e069d..a23cee539 100644 --- a/langs/i18n/i18n_test.go +++ b/langs/i18n/i18n_test.go @@ -23,8 +23,6 @@ import ( "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/config/testconfig" - "github.com/gohugoio/hugo/tpl/tplimpl" - "github.com/gohugoio/hugo/resources/page" "github.com/spf13/afero" @@ -472,7 +470,6 @@ func prepareTranslationProvider(t testing.TB, test i18nTest, cfg config.Provider func prepareDeps(afs afero.Fs, cfg config.Provider) (*deps.Deps, *TranslationProvider) { d := testconfig.GetTestDeps(afs, cfg) translationProvider := NewTranslationProvider() - d.TemplateProvider = tplimpl.DefaultTemplateProvider d.TranslationProvider = translationProvider d.Site = page.NewDummyHugoSite(d.Conf) if err := d.Compile(nil); err != nil { diff --git a/langs/language_test.go b/langs/language_test.go index 543f4a133..33240f3f4 100644 --- a/langs/language_test.go +++ b/langs/language_test.go @@ -29,13 +29,13 @@ func TestCollator(t *testing.T) { coll := &Collator{c: collate.New(language.English, collate.Loose)} - for i := 0; i < 10; i++ { + for range 10 { wg.Add(1) go func() { coll.Lock() defer coll.Unlock() defer wg.Done() - for j := 0; j < 10; j++ { + for range 10 { k := coll.CompareStrings("abc", "def") c.Assert(k, qt.Equals, -1) } @@ -48,7 +48,7 @@ func BenchmarkCollator(b *testing.B) { s := []string{"foo", "bar", "éntre", "baz", "qux", "quux", "corge", "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud"} doWork := func(coll *Collator) { - for i := 0; i < len(s); i++ { + for i := range s { for j := i + 1; j < len(s); j++ { _ = coll.CompareStrings(s[i], s[j]) } diff --git a/lazy/init.go b/lazy/init.go index 7b88a5351..bef3867a9 100644 --- a/lazy/init.go +++ b/lazy/init.go @@ -36,7 +36,7 @@ type Init struct { prev *Init children []*Init - init onceMore + init OnceMore out any err error f func(context.Context) (any, error) diff --git a/lazy/init_test.go b/lazy/init_test.go index 96a959494..94736fab8 100644 --- a/lazy/init_test.go +++ b/lazy/init_test.go @@ -79,7 +79,7 @@ func TestInit(t *testing.T) { // Add some concurrency and randomness to verify thread safety and // init order. - for i := 0; i < 100; i++ { + for i := range 100 { wg.Add(1) go func(i int) { defer wg.Done() diff --git a/lazy/once.go b/lazy/once.go index c6abcd884..dac689df3 100644 --- a/lazy/once.go +++ b/lazy/once.go @@ -18,19 +18,19 @@ import ( "sync/atomic" ) -// onceMore is similar to sync.Once. +// OnceMore is similar to sync.Once. // // Additional features are: // * it can be reset, so the action can be repeated if needed // * it has methods to check if it's done or in progress -type onceMore struct { +type OnceMore struct { mu sync.Mutex lock uint32 done uint32 } -func (t *onceMore) Do(f func()) { +func (t *OnceMore) Do(f func()) { if atomic.LoadUint32(&t.done) == 1 { return } @@ -53,15 +53,15 @@ func (t *onceMore) Do(f func()) { f() } -func (t *onceMore) InProgress() bool { +func (t *OnceMore) InProgress() bool { return atomic.LoadUint32(&t.lock) == 1 } -func (t *onceMore) Done() bool { +func (t *OnceMore) Done() bool { return atomic.LoadUint32(&t.done) == 1 } -func (t *onceMore) ResetWithLock() *sync.Mutex { +func (t *OnceMore) ResetWithLock() *sync.Mutex { t.mu.Lock() defer atomic.StoreUint32(&t.done, 0) return &t.mu diff --git a/magefile.go b/magefile.go index 142b9160a..120e23666 100644 --- a/magefile.go +++ b/magefile.go @@ -1,11 +1,9 @@ //go:build mage -// +build mage package main import ( "bytes" - "errors" "fmt" "os" "path" @@ -36,10 +34,6 @@ func init() { if exe := os.Getenv("GOEXE"); exe != "" { goexe = exe } - - // We want to use Go 1.11 modules even if the source lives inside GOPATH. - // The default is "auto". - os.Setenv("GO111MODULE", "on") } func runWith(env map[string]string, cmd string, inArgs ...any) error { @@ -122,10 +116,10 @@ func HugoNoGitInfo() error { return Hugo() } -var docker = sh.RunCmd("docker") - // Build hugo Docker container func Docker() error { + docker := sh.RunCmd("docker") + if err := docker("build", "-t", "hugo", "."); err != nil { return err } @@ -148,7 +142,7 @@ func Check() { fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS) } - if isCi() && isDarwin() { + if isCI() && isDarwin() { // Skip on macOS in CI (disk space issues) } else { mg.Deps(Fmt, Vet) @@ -200,56 +194,19 @@ func Fmt() error { return nil } -var ( - pkgPrefixLen = len("github.com/gohugoio/hugo") - pkgs []string - pkgsInit sync.Once -) +const pkgPrefixLen = len("github.com/gohugoio/hugo") -func hugoPackages() ([]string, error) { - var err error - pkgsInit.Do(func() { - var s string - s, err = sh.Output(goexe, "list", "./...") - if err != nil { - return - } - pkgs = strings.Split(s, "\n") - for i := range pkgs { - pkgs[i] = "." + pkgs[i][pkgPrefixLen:] - } - }) - return pkgs, err -} - -// Run golint linter -func Lint() error { - pkgs, err := hugoPackages() +var hugoPackages = sync.OnceValues(func() ([]string, error) { + s, err := sh.Output(goexe, "list", "./...") if err != nil { - return err + return nil, err } - failed := false - for _, pkg := range pkgs { - // We don't actually want to fail this target if we find golint errors, - // so we don't pass -set_exit_status, but we still print out any failures. - if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil { - fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err) - failed = true - } + pkgs := strings.Split(s, "\n") + for i := range pkgs { + pkgs[i] = "." + pkgs[i][pkgPrefixLen:] } - if failed { - return errors.New("errors running golint") - } - return nil -} - -func isCi() bool { - return os.Getenv("CI") != "" -} - -func isDarwin() bool { - return runtime.GOOS == "darwin" -} + return pkgs, nil +}) // Run go vet linter func Vet() error { @@ -270,7 +227,7 @@ func TestCoverHTML() error { return err } defer f.Close() - if _, err := f.Write([]byte("mode: count")); err != nil { + if _, err := f.WriteString("mode: count"); err != nil { return err } pkgs, err := hugoPackages() @@ -320,6 +277,10 @@ func isUnix() bool { return runtime.GOOS != "windows" } +func isDarwin() bool { + return runtime.GOOS == "darwin" +} + func isCI() bool { return os.Getenv("CI") != "" } @@ -334,7 +295,7 @@ func buildFlags() []string { func buildTags() string { // To build the extended Hugo SCSS/SASS enabled version, build with // HUGO_BUILD_TAGS=extended mage install etc. - // To build without `hugo deploy` for smaller binary, use HUGO_BUILD_TAGS=nodeploy + // To build with `hugo deploy`, use HUGO_BUILD_TAGS=withdeploy if envtags := os.Getenv("HUGO_BUILD_TAGS"); envtags != "" { return envtags } diff --git a/main_test.go b/main_test.go index 9dd2734d2..683defb1a 100644 --- a/main_test.go +++ b/main_test.go @@ -56,19 +56,16 @@ func TestUnfinished(t *testing.T) { } func TestMain(m *testing.M) { - os.Exit( - testscript.RunMain(m, map[string]func() int{ - // The main program. - "hugo": func() int { - err := commands.Execute(os.Args[1:]) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 - } - return 0 - }, - }), - ) + testscript.Main(m, map[string]func(){ + // The main program. + "hugo": func() { + err := commands.Execute(os.Args[1:]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + }, + }) } var commonTestScriptsParam = testscript.Params{ diff --git a/main_withdeploy_off_test.go b/main_withdeploy_off_test.go new file mode 100644 index 000000000..968c0c957 --- /dev/null +++ b/main_withdeploy_off_test.go @@ -0,0 +1,28 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !withdeploy + +package main + +import ( + "testing" + + "github.com/rogpeppe/go-internal/testscript" +) + +func TestWithdeploy(t *testing.T) { + p := commonTestScriptsParam + p.Dir = "testscripts/withdeploy-off" + testscript.Run(t, p) +} diff --git a/main_withdeploy_test.go b/main_withdeploy_test.go new file mode 100644 index 000000000..004893bdf --- /dev/null +++ b/main_withdeploy_test.go @@ -0,0 +1,28 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build withdeploy + +package main + +import ( + "testing" + + "github.com/rogpeppe/go-internal/testscript" +) + +func TestWithdeploy(t *testing.T) { + p := commonTestScriptsParam + p.Dir = "testscripts/withdeploy" + testscript.Run(t, p) +} diff --git a/markup/asciidocext/convert_test.go b/markup/asciidocext/convert_test.go index b3f63b4d8..e93cab00b 100644 --- a/markup/asciidocext/convert_test.go +++ b/markup/asciidocext/convert_test.go @@ -313,7 +313,7 @@ allow = ['asciidoctor'] converter.ProviderConfig{ Logger: loggers.NewDefault(), Conf: conf, - Exec: hexec.New(securityConfig, ""), + Exec: hexec.New(securityConfig, "", loggers.NewDefault()), }, ) c.Assert(err, qt.IsNil) diff --git a/markup/blackfriday/anchors.go b/markup/blackfriday/anchors.go index 7b0b41854..e00c24c9a 100644 --- a/markup/blackfriday/anchors.go +++ b/markup/blackfriday/anchors.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package blackfriday holds some compability functions for the old Blackfriday v1 Markdown engine. +// Package blackfriday holds some compatibility functions for the old Blackfriday v1 Markdown engine. package blackfriday import "unicode" diff --git a/markup/goldmark/autoid.go b/markup/goldmark/autoid.go index e1fdfacb4..89259d33a 100644 --- a/markup/goldmark/autoid.go +++ b/markup/goldmark/autoid.go @@ -26,6 +26,7 @@ import ( "github.com/gohugoio/hugo/common/text" "github.com/yuin/goldmark/ast" + east "github.com/yuin/goldmark/extension/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/util" @@ -43,11 +44,11 @@ func sanitizeAnchorName(b []byte, idType string) []byte { func sanitizeAnchorNameWithHook(b []byte, idType string, hook func(buf *bytes.Buffer)) []byte { buf := bp.GetBuffer() - if idType == goldmark_config.AutoHeadingIDTypeBlackfriday { + if idType == goldmark_config.AutoIDTypeBlackfriday { // TODO(bep) make it more efficient. buf.WriteString(blackfriday.SanitizedAnchorName(string(b))) } else { - asciiOnly := idType == goldmark_config.AutoHeadingIDTypeGitHubAscii + asciiOnly := idType == goldmark_config.AutoIDTypeGitHubAscii if asciiOnly { // Normalize it to preserve accents if possible. @@ -90,8 +91,9 @@ func isAlphaNumeric(r rune) bool { var _ parser.IDs = (*idFactory)(nil) type idFactory struct { - idType string - vals map[string]struct{} + idType string + vals map[string]struct{} + duplicates []string } func newIDFactory(idType string) *idFactory { @@ -101,11 +103,28 @@ func newIDFactory(idType string) *idFactory { } } +type stringValuesProvider interface { + StringValues() []string +} + +var _ stringValuesProvider = (*idFactory)(nil) + +func (ids *idFactory) StringValues() []string { + values := make([]string, 0, len(ids.vals)) + for k := range ids.vals { + values = append(values, k) + } + values = append(values, ids.duplicates...) + return values +} + func (ids *idFactory) Generate(value []byte, kind ast.NodeKind) []byte { return sanitizeAnchorNameWithHook(value, ids.idType, func(buf *bytes.Buffer) { if buf.Len() == 0 { if kind == ast.KindHeading { buf.WriteString("heading") + } else if kind == east.KindDefinitionTerm { + buf.WriteString("term") } else { buf.WriteString("id") } @@ -123,11 +142,18 @@ func (ids *idFactory) Generate(value []byte, kind ast.NodeKind) []byte { buf.Truncate(pos) } } - - ids.vals[buf.String()] = struct{}{} + ids.put(buf.String()) }) } -func (ids *idFactory) Put(value []byte) { - ids.vals[util.BytesToReadOnlyString(value)] = struct{}{} +func (ids *idFactory) put(s string) { + if _, found := ids.vals[s]; found { + ids.duplicates = append(ids.duplicates, s) + } else { + ids.vals[s] = struct{}{} + } +} + +func (ids *idFactory) Put(value []byte) { + ids.put(string(value)) } diff --git a/markup/goldmark/autoid_test.go b/markup/goldmark/autoid_test.go index 0bdb63c12..e0770d86c 100644 --- a/markup/goldmark/autoid_test.go +++ b/markup/goldmark/autoid_test.go @@ -78,9 +78,9 @@ tabspace expect := expectlines[i] c.Run(input, func(c *qt.C) { b := []byte(input) - got := string(sanitizeAnchorName(b, goldmark_config.AutoHeadingIDTypeGitHub)) + got := string(sanitizeAnchorName(b, goldmark_config.AutoIDTypeGitHub)) c.Assert(got, qt.Equals, expect) - c.Assert(sanitizeAnchorNameString(input, goldmark_config.AutoHeadingIDTypeGitHub), qt.Equals, expect) + c.Assert(sanitizeAnchorNameString(input, goldmark_config.AutoIDTypeGitHub), qt.Equals, expect) c.Assert(string(b), qt.Equals, input) }) } @@ -89,20 +89,20 @@ tabspace func TestSanitizeAnchorNameAsciiOnly(t *testing.T) { c := qt.New(t) - c.Assert(sanitizeAnchorNameString("god is神真美好 good", goldmark_config.AutoHeadingIDTypeGitHubAscii), qt.Equals, "god-is-good") - c.Assert(sanitizeAnchorNameString("Resumé", goldmark_config.AutoHeadingIDTypeGitHubAscii), qt.Equals, "resume") + c.Assert(sanitizeAnchorNameString("god is神真美好 good", goldmark_config.AutoIDTypeGitHubAscii), qt.Equals, "god-is-good") + c.Assert(sanitizeAnchorNameString("Resumé", goldmark_config.AutoIDTypeGitHubAscii), qt.Equals, "resume") } func TestSanitizeAnchorNameBlackfriday(t *testing.T) { c := qt.New(t) - c.Assert(sanitizeAnchorNameString("Let's try this, shall we?", goldmark_config.AutoHeadingIDTypeBlackfriday), qt.Equals, "let-s-try-this-shall-we") + c.Assert(sanitizeAnchorNameString("Let's try this, shall we?", goldmark_config.AutoIDTypeBlackfriday), qt.Equals, "let-s-try-this-shall-we") } func BenchmarkSanitizeAnchorName(b *testing.B) { input := []byte("God is good: 神真美好") b.ResetTimer() for i := 0; i < b.N; i++ { - result := sanitizeAnchorName(input, goldmark_config.AutoHeadingIDTypeGitHub) + result := sanitizeAnchorName(input, goldmark_config.AutoIDTypeGitHub) if len(result) != 24 { b.Fatalf("got %d", len(result)) } @@ -113,7 +113,7 @@ func BenchmarkSanitizeAnchorNameAsciiOnly(b *testing.B) { input := []byte("God is good: 神真美好") b.ResetTimer() for i := 0; i < b.N; i++ { - result := sanitizeAnchorName(input, goldmark_config.AutoHeadingIDTypeGitHubAscii) + result := sanitizeAnchorName(input, goldmark_config.AutoIDTypeGitHubAscii) if len(result) != 12 { b.Fatalf("got %d", len(result)) } @@ -124,7 +124,7 @@ func BenchmarkSanitizeAnchorNameBlackfriday(b *testing.B) { input := []byte("God is good: 神真美好") b.ResetTimer() for i := 0; i < b.N; i++ { - result := sanitizeAnchorName(input, goldmark_config.AutoHeadingIDTypeBlackfriday) + result := sanitizeAnchorName(input, goldmark_config.AutoIDTypeBlackfriday) if len(result) != 24 { b.Fatalf("got %d", len(result)) } @@ -135,7 +135,7 @@ func BenchmarkSanitizeAnchorNameString(b *testing.B) { input := "God is good: 神真美好" b.ResetTimer() for i := 0; i < b.N; i++ { - result := sanitizeAnchorNameString(input, goldmark_config.AutoHeadingIDTypeGitHub) + result := sanitizeAnchorNameString(input, goldmark_config.AutoIDTypeGitHub) if len(result) != 24 { b.Fatalf("got %d", len(result)) } diff --git a/markup/goldmark/blockquotes/blockquotes.go b/markup/goldmark/blockquotes/blockquotes.go index f9d518850..539cd1875 100644 --- a/markup/goldmark/blockquotes/blockquotes.go +++ b/markup/goldmark/blockquotes/blockquotes.go @@ -69,12 +69,12 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N return ast.WalkContinue, nil } - text := ctx.PopRenderedString() + text := strings.TrimSpace(ctx.PopRenderedString()) ordinal := ctx.GetAndIncrementOrdinal(ast.KindBlockquote) typ := typeRegular - alert := resolveBlockQuoteAlert(string(text)) + alert := resolveBlockQuoteAlert(text) if alert.typ != "" { typ = typeAlert } @@ -85,10 +85,21 @@ func (r *htmlRenderer) renderBlockquote(w util.BufWriter, src []byte, node ast.N } if typ == typeAlert { - // Trim preamble:

    [!NOTE]
    \n but preserve leading paragraph. - // We could possibly complicate this by moving this to the parser, but - // keep it simple for now. - text = "

    " + text[strings.Index(text, "\n")+1:] + // Parse the blockquote content to determine the alert text. The alert + // text begins after the first newline, but we need to add an opening p + // tag if the first line of the blockquote content does not have a + // closing p tag. At some point we might want to move this to the + // parser. + before, after, found := strings.Cut(text, "\n") + if found { + if strings.HasSuffix(before, "

    ") { + text = after + } else { + text = "

    " + after + } + } else { + text = "" + } } bqctx := &blockquoteContext{ @@ -165,7 +176,7 @@ func resolveBlockQuoteAlert(s string) blockQuoteAlert { m := blockQuoteAlertRe.FindStringSubmatch(s) if len(m) == 4 { title := strings.TrimSpace(m[3]) - title = strings.TrimRight(title, "

    ") + title = strings.TrimSuffix(title, "

    ") return blockQuoteAlert{ typ: strings.ToLower(m[1]), sign: m[2], diff --git a/markup/goldmark/blockquotes/blockquotes_integration_test.go b/markup/goldmark/blockquotes/blockquotes_integration_test.go index 1f671df2b..6f7914d07 100644 --- a/markup/goldmark/blockquotes/blockquotes_integration_test.go +++ b/markup/goldmark/blockquotes/blockquotes_integration_test.go @@ -48,10 +48,9 @@ Content: {{ .Content }} title: "p1" --- -> [!NOTE] +> [!NOTE] > This is a note with some whitespace after the alert type. - > [!TIP] > This is a tip. @@ -64,29 +63,26 @@ title: "p1" > This is a tip with attributes. {class="foo bar" id="baz"} -> [!NOTE] +> [!NOTE] > Note triggering showing the position. {showpos="true"} - -> [!nOtE] +> [!nOtE] > Mixed case alert type. - - ` b := hugolib.Test(t, files) b.AssertFileContentExact("public/p1/index.html", - "Blockquote Alert: |

    This is a note with some whitespace after the alert type.

    \n|alert|", + "Blockquote Alert: |

    This is a note with some whitespace after the alert type.

    |alert|", "Blockquote Alert: |

    This is a tip.

    ", - "Blockquote Alert: |

    This is a caution with some whitespace before the alert type.

    \n|alert|", - "Blockquote: |

    A regular blockquote.

    \n|regular|", - "Blockquote Alert Attributes: |

    This is a tip with attributes.

    \n|map[class:foo bar id:baz]|", - filepath.FromSlash("/content/p1.md:20:3"), - "Blockquote Alert Page: |

    This is a tip with attributes.

    \n|p1|p1|", + "Blockquote Alert: |

    This is a caution with some whitespace before the alert type.

    |alert|", + "Blockquote: |

    A regular blockquote.

    |regular|", + "Blockquote Alert Attributes: |

    This is a tip with attributes.

    |map[class:foo bar id:baz]|", + filepath.FromSlash("/content/p1.md:19:3"), + "Blockquote Alert Page: |

    This is a tip with attributes.

    |p1|p1|", // Issue 12767. - "Blockquote Alert: |

    Mixed case alert type.

    \n|alert", + "Blockquote Alert: |

    Mixed case alert type.

    |alert", ) } @@ -142,17 +138,116 @@ title: "Home" {{ .Content }} -- layouts/_default/_markup/render-blockquote.html -- AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|AlertSign: {{ .AlertSign | safeHTML }}|Text: {{ .Text }}| - + ` b := hugolib.Test(t, files) b.AssertFileContentExact("public/index.html", "AlertType: tip|AlertTitle: Callouts can have custom titles|AlertSign: |", "AlertType: tip|AlertTitle: Title-only callout|AlertSign: |", - "AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text:

    Yes! In a foldable callout, the contents are hidden when the callout is collapsed

    \n|", - "AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|Text:

    Yes! In a foldable callout, the contents are hidden when the callout is collapsed

    \n|", - "AlertType: danger|AlertTitle: |AlertSign: |Text:

    Do not approach or handle without protective gear.

    \n|", + "AlertType: faq|AlertTitle: Foldable negated callout|AlertSign: -|Text:

    Yes! In a foldable callout, the contents are hidden when the callout is collapsed

    |", + "AlertType: faq|AlertTitle: Foldable callout|AlertSign: +|Text:

    Yes! In a foldable callout, the contents are hidden when the callout is collapsed

    |", + "AlertType: danger|AlertTitle: |AlertSign: |Text:

    Do not approach or handle without protective gear.

    |", "AlertTitle: Can callouts be nested?|", "AlertTitle: You can even use multiple layers of nesting.|", ) } + +// Issue 12913 +// Issue 13119 +// Issue 13302 +func TestBlockquoteRenderHookTextParsing(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ .Content }} +-- layouts/_default/_markup/render-blockquote.html -- +AlertType: {{ .AlertType }}|AlertTitle: {{ .AlertTitle }}|Text: {{ .Text }}| +-- content/_index.md -- +--- +title: home +--- + +> [!one] + +> [!two] title + +> [!three] +> line 1 + +> [!four] title +> line 1 + +> [!five] +> line 1 +> line 2 + +> [!six] title +> line 1 +> line 2 + +> [!seven] +> - list item + +> [!eight] title +> - list item + +> [!nine] +> line 1 +> - list item + +> [!ten] title +> line 1 +> - list item + +> [!eleven] +> line 1 +> - list item +> +> line 2 + +> [!twelve] title +> line 1 +> - list item +> +> line 2 + +> [!thirteen] +> ![alt](a.jpg) + +> [!fourteen] title +> ![alt](a.jpg) + +> [!fifteen] _title_ + +> [!sixteen] _title_ +> line one + +> seventeen +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/index.html", + "AlertType: one|AlertTitle: |Text: |", + "AlertType: two|AlertTitle: title|Text: |", + "AlertType: three|AlertTitle: |Text:

    line 1

    |", + "AlertType: four|AlertTitle: title|Text:

    line 1

    |", + "AlertType: five|AlertTitle: |Text:

    line 1\nline 2

    |", + "AlertType: six|AlertTitle: title|Text:

    line 1\nline 2

    |", + "AlertType: seven|AlertTitle: |Text:
      \n
    • list item
    • \n
    |", + "AlertType: eight|AlertTitle: title|Text:
      \n
    • list item
    • \n
    |", + "AlertType: nine|AlertTitle: |Text:

    line 1

    \n
      \n
    • list item
    • \n
    |", + "AlertType: ten|AlertTitle: title|Text:

    line 1

    \n
      \n
    • list item
    • \n
    |", + "AlertType: eleven|AlertTitle: |Text:

    line 1

    \n
      \n
    • list item
    • \n
    \n

    line 2

    |", + "AlertType: twelve|AlertTitle: title|Text:

    line 1

    \n
      \n
    • list item
    • \n
    \n

    line 2

    |", + "AlertType: thirteen|AlertTitle: |Text:

    \"alt\"

    |", + "AlertType: fourteen|AlertTitle: title|Text:

    \"alt\"

    |", + "AlertType: fifteen|AlertTitle: title|Text: |", + "AlertType: sixteen|AlertTitle: title|Text:

    line one

    |", + "AlertType: |AlertTitle: |Text:

    seventeen

    |", + ) +} diff --git a/markup/goldmark/codeblocks/codeblocks_integration_test.go b/markup/goldmark/codeblocks/codeblocks_integration_test.go index 8ed691302..97f4eb384 100644 --- a/markup/goldmark/codeblocks/codeblocks_integration_test.go +++ b/markup/goldmark/codeblocks/codeblocks_integration_test.go @@ -69,7 +69,7 @@ fmt.Println("Hello, World!"); ## Golang Code -§§§golang +§§§go fmt.Println("Hello, Golang!"); §§§ @@ -97,14 +97,14 @@ Go Language: go| Go Code: fmt.Println("Hello, World!"); Go Code: fmt.Println("Hello, Golang!"); -Go Language: golang| +Go Language: go| `, "Goat SVG:Go Code\nGo Code: fmt.Println(\"Hello, World!\");\n|\nGo Language: go|", - "

    Golang Code

    \nGo Code: fmt.Println(\"Hello, Golang!\");\n|\nGo Language: golang|", + "

    Golang Code

    \nGo Code: fmt.Println(\"Hello, Golang!\");\n|\nGo Language: go|", "

    Bash Code

    \n
    32echo "l1";\n33",
     	)
     }
    diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go
    index 4164f0e0a..c29632b90 100644
    --- a/markup/goldmark/codeblocks/render.go
    +++ b/markup/goldmark/codeblocks/render.go
    @@ -77,7 +77,7 @@ func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.No
     	var buff bytes.Buffer
     
     	l := n.Lines().Len()
    -	for i := 0; i < l; i++ {
    +	for i := range l {
     		line := n.Lines().At(i)
     		buff.Write(line.Value(src))
     	}
    diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go
    index 5c31eee40..ceacd150e 100644
    --- a/markup/goldmark/convert.go
    +++ b/markup/goldmark/convert.go
    @@ -43,6 +43,7 @@ import (
     )
     
     const (
    +	// Don't change this. This pattern is lso used in the image render hooks.
     	internalAttrPrefix = "_h__"
     )
     
    @@ -60,7 +61,7 @@ func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) {
     			cfg: cfg,
     			md:  md,
     			sanitizeAnchorName: func(s string) string {
    -				return sanitizeAnchorNameString(s, cfg.MarkupConfig().Goldmark.Parser.AutoHeadingIDType)
    +				return sanitizeAnchorNameString(s, cfg.MarkupConfig().Goldmark.Parser.AutoIDType)
     			},
     		}, nil
     	}), nil
    @@ -106,7 +107,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
     		renderer.WithNodeRenderers(util.Prioritized(emoji.NewHTMLRenderer(), 200)))
     	var (
     		extensions = []goldmark.Extender{
    -			hugocontext.New(),
    +			hugocontext.New(pcfg.Logger),
     			newLinks(cfg),
     			newTocExtension(tocRendererOptions),
     			blockquotes.New(),
    @@ -187,16 +188,12 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
     		extensions = append(extensions, emoji.Emoji)
     	}
     
    -	if cfg.Parser.AutoHeadingID {
    -		parserOptions = append(parserOptions, parser.WithAutoHeadingID())
    -	}
    -
     	if cfg.Parser.Attribute.Title {
     		parserOptions = append(parserOptions, parser.WithAttribute())
     	}
     
    -	if cfg.Parser.Attribute.Block {
    -		extensions = append(extensions, attributes.New())
    +	if cfg.Parser.Attribute.Block || cfg.Parser.AutoHeadingID || cfg.Parser.AutoDefinitionTermID {
    +		extensions = append(extensions, attributes.New(cfg.Parser))
     	}
     
     	md := goldmark.New(
    @@ -294,7 +291,7 @@ func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (converter.Resu
     }
     
     func (c *goldmarkConverter) newParserContext(rctx converter.RenderContext) *parserContext {
    -	ctx := parser.NewContext(parser.WithIDs(newIDFactory(c.cfg.MarkupConfig().Goldmark.Parser.AutoHeadingIDType)))
    +	ctx := parser.NewContext(parser.WithIDs(newIDFactory(c.cfg.MarkupConfig().Goldmark.Parser.AutoIDType)))
     	ctx.Set(tocEnableKey, rctx.RenderTOC)
     	return &parserContext{
     		Context: ctx,
    diff --git a/markup/goldmark/convert_test.go b/markup/goldmark/convert_test.go
    index 6048bce39..a35441aff 100644
    --- a/markup/goldmark/convert_test.go
    +++ b/markup/goldmark/convert_test.go
    @@ -208,8 +208,8 @@ unsafe = true
     
     	toc, ok := b.(converter.TableOfContentsProvider)
     	c.Assert(ok, qt.Equals, true)
    -	tocString := string(toc.TableOfContents().ToHTML(1, 2, false))
    -	c.Assert(tocString, qt.Contains, "TableOfContents")
    +	tocHTML, _ := toc.TableOfContents().ToHTML(1, 2, false)
    +	c.Assert(string(tocHTML), qt.Contains, "TableOfContents")
     }
     
     func TestConvertAutoIDAsciiOnly(t *testing.T) {
    diff --git a/markup/goldmark/goldmark_config/config.go b/markup/goldmark/goldmark_config/config.go
    index c6e0bcd3d..04eb371d9 100644
    --- a/markup/goldmark/goldmark_config/config.go
    +++ b/markup/goldmark/goldmark_config/config.go
    @@ -15,9 +15,9 @@
     package goldmark_config
     
     const (
    -	AutoHeadingIDTypeGitHub      = "github"
    -	AutoHeadingIDTypeGitHubAscii = "github-ascii"
    -	AutoHeadingIDTypeBlackfriday = "blackfriday"
    +	AutoIDTypeGitHub      = "github"
    +	AutoIDTypeGitHubAscii = "github-ascii"
    +	AutoIDTypeBlackfriday = "blackfriday"
     )
     
     // Default holds the default Goldmark configuration.
    @@ -79,7 +79,8 @@ var Default = Config{
     	},
     	Parser: Parser{
     		AutoHeadingID:                      true,
    -		AutoHeadingIDType:                  AutoHeadingIDTypeGitHub,
    +		AutoDefinitionTermID:               false,
    +		AutoIDType:                         AutoIDTypeGitHub,
     		WrapStandAloneImageWithinParagraph: true,
     		Attribute: ParserAttribute{
     			Title: true,
    @@ -97,6 +98,16 @@ type Config struct {
     	RenderHooks            RenderHooks
     }
     
    +func (c *Config) Init() error {
    +	if err := c.Parser.Init(); err != nil {
    +		return err
    +	}
    +	if c.Parser.AutoDefinitionTermID && !c.Extensions.DefinitionList {
    +		c.Parser.AutoDefinitionTermID = false
    +	}
    +	return nil
    +}
    +
     // RenderHooks contains configuration for Goldmark render hooks.
     type RenderHooks struct {
     	Image ImageRenderHook
    @@ -250,16 +261,30 @@ type Parser struct {
     	// auto generated heading ids.
     	AutoHeadingID bool
     
    -	// The strategy to use when generating heading IDs.
    -	// Available options are "github", "github-ascii".
    +	// Enables auto definition term ids.
    +	AutoDefinitionTermID bool
    +
    +	// The strategy to use when generating IDs.
    +	// Available options are "github", "github-ascii", and "blackfriday".
     	// Default is "github", which will create GitHub-compatible anchor names.
    -	AutoHeadingIDType string
    +	AutoIDType string
     
     	// Enables custom attributes.
     	Attribute ParserAttribute
     
     	// Whether to wrap stand-alone images within a paragraph or not.
     	WrapStandAloneImageWithinParagraph bool
    +
    +	// Renamed to AutoIDType in 0.144.0.
    +	AutoHeadingIDType string `json:"-"`
    +}
    +
    +func (p *Parser) Init() error {
    +	// Renamed from AutoHeadingIDType to AutoIDType in 0.144.0.
    +	if p.AutoHeadingIDType != "" {
    +		p.AutoIDType = p.AutoHeadingIDType
    +	}
    +	return nil
     }
     
     type ParserAttribute struct {
    diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go
    index c691435ee..a65600951 100644
    --- a/markup/goldmark/goldmark_integration_test.go
    +++ b/markup/goldmark/goldmark_integration_test.go
    @@ -157,7 +157,7 @@ title: "p1"
     	b := hugolib.Test(t, files)
     
     	b.AssertFileContent("public/p1/index.html",
    -		"

    \n Hello Test\n\n #\n

    ", + "

    \n Hello Test\n\n #\n

    ", ) } @@ -527,6 +527,39 @@ a c ) } +// Issue 13286 +func TestImageAltApostrophesWithTypographer(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[markup.goldmark.extensions.typographer] +disable = false + [markup.goldmark.renderHooks.image] + enableDefault = true +-- content/p1.md -- +--- +title: "p1" +--- + +## Image + +![A's is > than B's](some-image.png) + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + + b.AssertFileContentExact("public/p1/index.html", + // Note that this markup is slightly different than the one produced by the default Goldmark renderer, + // see issue 13292. + "alt=\"A’s is > than B’s\">", + ) +} + // Issue #7332 // Issue #11587 func TestGoldmarkEmojiExtension(t *testing.T) { @@ -802,3 +835,129 @@ H~2~0 "

    1st

    ", ) } + +// Issue 12997. +func TestGoldmarkRawHTMLWarningBlocks(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +markup.goldmark.renderer.unsafe = false +-- content/p1.md -- +--- +title: "p1" +--- +
    Some raw HTML
    +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/p1/index.html", "") + b.AssertLogContains("WARN Raw HTML omitted while rendering \"/content/p1.md\"; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-goldmark-raw-html']") + + b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) + b.AssertFileContent("public/p1/index.html", "! ") + b.AssertLogContains("! WARN") +} + +func TestGoldmarkRawHTMLWarningInline(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +markup.goldmark.renderer.unsafe = false +-- content/p1.md -- +--- +title: "p1" +--- +raw HTML +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/p1/index.html", "") + b.AssertLogContains("WARN Raw HTML omitted while rendering \"/content/p1.md\"; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe\nYou can suppress this warning by adding the following to your site configuration:\nignoreLogs = ['warning-goldmark-raw-html']") + + b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) + b.AssertFileContent("public/p1/index.html", "! ") + b.AssertLogContains("! WARN") +} + +// See https://github.com/gohugoio/hugo/issues/13278#issuecomment-2603280548 +func TestGoldmarkRawHTMLCommentNoWarning(t *testing.T) { + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +markup.goldmark.renderer.unsafe = false +-- content/p1.md -- +--- +title: "p1" +--- +# HTML comments + +## Simple + + + + + **Hello**_world_. +## With HTML + + + +## With HTML and JS + + + +## With Block + + + +## XSS + + + + +## More + +This is a word. + +This is a word. + +This is a word. + +This is a word. + +This is a word. + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files, hugolib.TestOptWarn()) + + b.AssertFileContent("public/p1/index.html", + "! ", + "! ", + "! ", + "! script", + ) + b.AssertLogContains("! WARN") + + b = hugolib.Test(t, strings.ReplaceAll(files, "markup.goldmark.renderer.unsafe = false", "markup.goldmark.renderer.unsafe = true"), hugolib.TestOptWarn()) + b.AssertFileContent("public/p1/index.html", + "! ", + "", + "", + ) + b.AssertLogContains("! WARN") +} diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go index b9c548dac..7a556083c 100644 --- a/markup/goldmark/hugocontext/hugocontext.go +++ b/markup/goldmark/hugocontext/hugocontext.go @@ -16,20 +16,24 @@ package hugocontext import ( "bytes" "fmt" + "regexp" "strconv" "github.com/gohugoio/hugo/bufferpool" + "github.com/gohugoio/hugo/common/constants" + "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/markup/goldmark/internal/render" "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer" + "github.com/yuin/goldmark/renderer/html" "github.com/yuin/goldmark/text" "github.com/yuin/goldmark/util" ) -func New() goldmark.Extender { - return &hugoContextExtension{} +func New(logger loggers.Logger) goldmark.Extender { + return &hugoContextExtension{logger: logger} } // Wrap wraps the given byte slice in a Hugo context that used to determine the correct Page @@ -37,14 +41,19 @@ func New() goldmark.Extender { func Wrap(b []byte, pid uint64) string { buf := bufferpool.GetBuffer() defer bufferpool.PutBuffer(buf) - buf.Write(prefix) + buf.Write(hugoCtxPrefix) buf.WriteString(" pid=") buf.WriteString(strconv.FormatUint(pid, 10)) - buf.Write(endDelim) + buf.Write(hugoCtxEndDelim) buf.WriteByte('\n') buf.Write(b) - buf.Write(prefix) - buf.Write(closingDelimAndNewline) + // To make sure that we're able to parse it, make sure it ends with a newline. + if len(b) > 0 && b[len(b)-1] != '\n' { + buf.WriteByte('\n') + } + buf.Write(hugoCtxPrefix) + buf.Write(hugoCtxClosingDelim) + buf.WriteByte('\n') return buf.String() } @@ -89,45 +98,147 @@ func (h *HugoContext) Kind() ast.NodeKind { } var ( - prefix = []byte("{{__hugo_ctx") - endDelim = []byte("}}") - closingDelimAndNewline = []byte("/}}\n") + hugoCtxPrefix = []byte("{{__hugo_ctx") + hugoCtxEndDelim = []byte("}}") + hugoCtxClosingDelim = []byte("/}}") + hugoCtxRe = regexp.MustCompile(`{{__hugo_ctx( pid=\d+)?/?}}\n?`) ) var _ parser.InlineParser = (*hugoContextParser)(nil) type hugoContextParser struct{} -func (s *hugoContextParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { - line, _ := block.PeekLine() - if !bytes.HasPrefix(line, prefix) { +func (a *hugoContextParser) Trigger() []byte { + return []byte{'{'} +} + +func (s *hugoContextParser) Parse(parent ast.Node, reader text.Reader, pc parser.Context) ast.Node { + line, _ := reader.PeekLine() + if !bytes.HasPrefix(line, hugoCtxPrefix) { return nil } - end := bytes.Index(line, endDelim) + end := bytes.Index(line, hugoCtxEndDelim) if end == -1 { return nil } - block.Advance(end + len(endDelim) + 1) // +1 for the newline + reader.Advance(end + len(hugoCtxEndDelim) + 1) // +1 for the newline if line[end-1] == '/' { return &HugoContext{Closing: true} } - attrBytes := line[len(prefix)+1 : end] + attrBytes := line[len(hugoCtxPrefix)+1 : end] h := &HugoContext{} h.parseAttrs(attrBytes) return h } -func (a *hugoContextParser) Trigger() []byte { - return []byte{'{'} +type hugoContextRenderer struct { + logger loggers.Logger + html.Config } -type hugoContextRenderer struct{} +func (r *hugoContextRenderer) SetOption(name renderer.OptionName, value any) { + r.Config.SetOption(name, value) +} func (r *hugoContextRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(kindHugoContext, r.handleHugoContext) + reg.Register(ast.KindRawHTML, r.renderRawHTML) + reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock) +} + +func (r *hugoContextRenderer) stripHugoCtx(b []byte) ([]byte, bool) { + if !bytes.Contains(b, hugoCtxPrefix) { + return b, false + } + return hugoCtxRe.ReplaceAll(b, nil), true +} + +func (r *hugoContextRenderer) logRawHTMLEmittedWarn(w util.BufWriter) { + r.logger.Warnidf(constants.WarnGoldmarkRawHTML, "Raw HTML omitted while rendering %q; see https://gohugo.io/getting-started/configuration-markup/#rendererunsafe", r.getPage(w)) +} + +func (r *hugoContextRenderer) getPage(w util.BufWriter) any { + var p any + ctx, ok := w.(*render.Context) + if ok { + p, _ = render.GetPageAndPageInner(ctx) + } + return p +} + +func (r *hugoContextRenderer) isHTMLComment(b []byte) bool { + return len(b) > 4 && b[0] == '<' && b[1] == '!' && b[2] == '-' && b[3] == '-' +} + +// HTML rendering based on Goldmark implementation. +func (r *hugoContextRenderer) renderHTMLBlock( + w util.BufWriter, source []byte, node ast.Node, entering bool, +) (ast.WalkStatus, error) { + n := node.(*ast.HTMLBlock) + + if entering { + if r.Unsafe { + l := n.Lines().Len() + for i := range l { + line := n.Lines().At(i) + linev := line.Value(source) + var stripped bool + linev, stripped = r.stripHugoCtx(linev) + if stripped { + r.logger.Warnidf(constants.WarnRenderShortcodesInHTML, ".RenderShortcodes detected inside HTML block in %q; this may not be what you intended, see https://gohugo.io/methods/page/rendershortcodes/#limitations", r.getPage(w)) + } + r.Writer.SecureWrite(w, linev) + } + } else { + l := n.Lines().At(0) + v := l.Value(source) + if !r.isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("\n") + } + } + } else { + if n.HasClosure() { + if r.Unsafe { + closure := n.ClosureLine + r.Writer.SecureWrite(w, closure.Value(source)) + } else { + l := n.Lines().At(0) + v := l.Value(source) + if !r.isHTMLComment(v) { + _, _ = w.WriteString("\n") + } + } + } + } + return ast.WalkContinue, nil +} + +func (r *hugoContextRenderer) renderRawHTML( + w util.BufWriter, source []byte, node ast.Node, entering bool, +) (ast.WalkStatus, error) { + if !entering { + return ast.WalkSkipChildren, nil + } + n := node.(*ast.RawHTML) + l := n.Segments.Len() + if r.Unsafe { + for i := range l { + segment := n.Segments.At(i) + _, _ = w.Write(segment.Value(source)) + } + return ast.WalkSkipChildren, nil + } + segment := n.Segments.At(0) + v := segment.Value(source) + if !r.isHTMLComment(v) { + r.logRawHTMLEmittedWarn(w) + _, _ = w.WriteString("") + } + return ast.WalkSkipChildren, nil } func (r *hugoContextRenderer) handleHugoContext(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { @@ -148,18 +259,59 @@ func (r *hugoContextRenderer) handleHugoContext(w util.BufWriter, source []byte, return ast.WalkContinue, nil } -type hugoContextExtension struct{} +type hugoContextTransformer struct{} + +var _ parser.ASTTransformer = (*hugoContextTransformer)(nil) + +func (a *hugoContextTransformer) Transform(n *ast.Document, reader text.Reader, pc parser.Context) { + ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) { + s := ast.WalkContinue + if !entering || n.Kind() != kindHugoContext { + return s, nil + } + + if p, ok := n.Parent().(*ast.Paragraph); ok { + if p.ChildCount() == 1 { + // Avoid empty paragraphs. + p.Parent().ReplaceChild(p.Parent(), p, n) + } else { + if t, ok := n.PreviousSibling().(*ast.Text); ok { + // Remove the newline produced by the Hugo context markers. + if t.SoftLineBreak() { + if t.Segment.Len() == 0 { + p.RemoveChild(p, t) + } else { + t.SetSoftLineBreak(false) + } + } + } + } + } + + return s, nil + }) +} + +type hugoContextExtension struct { + logger loggers.Logger +} func (a *hugoContextExtension) Extend(m goldmark.Markdown) { m.Parser().AddOptions( parser.WithInlineParsers( util.Prioritized(&hugoContextParser{}, 50), ), + parser.WithASTTransformers(util.Prioritized(&hugoContextTransformer{}, 10)), ) m.Renderer().AddOptions( renderer.WithNodeRenderers( - util.Prioritized(&hugoContextRenderer{}, 50), + util.Prioritized(&hugoContextRenderer{ + logger: a.logger, + Config: html.Config{ + Writer: html.DefaultWriter, + }, + }, 50), ), ) } diff --git a/markup/goldmark/hugocontext/hugocontext_test.go b/markup/goldmark/hugocontext/hugocontext_test.go index 4a6eb80f5..62769f4d0 100644 --- a/markup/goldmark/hugocontext/hugocontext_test.go +++ b/markup/goldmark/hugocontext/hugocontext_test.go @@ -24,7 +24,7 @@ func TestWrap(t *testing.T) { b := []byte("test") - c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest{{__hugo_ctx/}}\n") + c.Assert(Wrap(b, 42), qt.Equals, "{{__hugo_ctx pid=42}}\ntest\n{{__hugo_ctx/}}\n") } func BenchmarkWrap(b *testing.B) { diff --git a/markup/goldmark/internal/extensions/attributes/attributes.go b/markup/goldmark/internal/extensions/attributes/attributes.go index feb3d915b..50ccb2ed4 100644 --- a/markup/goldmark/internal/extensions/attributes/attributes.go +++ b/markup/goldmark/internal/extensions/attributes/attributes.go @@ -1,8 +1,13 @@ package attributes import ( + "strings" + + "github.com/gohugoio/hugo/markup/goldmark/goldmark_config" + "github.com/gohugoio/hugo/markup/goldmark/internal/render" "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" + east "github.com/yuin/goldmark/extension/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/text" "github.com/yuin/goldmark/util" @@ -14,24 +19,29 @@ import ( var ( kindAttributesBlock = ast.NewNodeKind("AttributesBlock") + attrNameID = []byte("id") - defaultParser = new(attrParser) - defaultTransformer = new(transformer) - attributes goldmark.Extender = new(attrExtension) + defaultParser = new(attrParser) ) -func New() goldmark.Extender { - return attributes +func New(cfg goldmark_config.Parser) goldmark.Extender { + return &attrExtension{cfg: cfg} } -type attrExtension struct{} +type attrExtension struct { + cfg goldmark_config.Parser +} func (a *attrExtension) Extend(m goldmark.Markdown) { + if a.cfg.Attribute.Block { + m.Parser().AddOptions( + parser.WithBlockParsers( + util.Prioritized(defaultParser, 100)), + ) + } m.Parser().AddOptions( - parser.WithBlockParsers( - util.Prioritized(defaultParser, 100)), parser.WithASTTransformers( - util.Prioritized(defaultTransformer, 100), + util.Prioritized(&transformer{cfg: a.cfg}, 100), ), ) } @@ -92,18 +102,47 @@ func (a *attributesBlock) Kind() ast.NodeKind { return kindAttributesBlock } -type transformer struct{} +type transformer struct { + cfg goldmark_config.Parser +} + +func (a *transformer) isFragmentNode(n ast.Node) bool { + switch n.Kind() { + case east.KindDefinitionTerm, ast.KindHeading: + return true + default: + return false + } +} func (a *transformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { - attributes := make([]ast.Node, 0, 500) + var attributes []ast.Node + var solitaryAttributeNodes []ast.Node + if a.cfg.Attribute.Block { + attributes = make([]ast.Node, 0, 100) + } ast.Walk(node, func(node ast.Node, entering bool) (ast.WalkStatus, error) { - if entering && node.Kind() == kindAttributesBlock { + if !entering { + return ast.WalkContinue, nil + } + + if a.isFragmentNode(node) { + if id, found := node.Attribute(attrNameID); !found { + a.generateAutoID(node, reader, pc) + } else { + pc.IDs().Put(id.([]byte)) + } + } + + if a.cfg.Attribute.Block && node.Kind() == kindAttributesBlock { // Attributes for fenced code blocks are handled in their own extension, // but note that we currently only support code block attributes when // CodeFences=true. if node.PreviousSibling() != nil && node.PreviousSibling().Kind() != ast.KindFencedCodeBlock && !node.HasBlankPreviousLines() { attributes = append(attributes, node) return ast.WalkSkipChildren, nil + } else { + solitaryAttributeNodes = append(solitaryAttributeNodes, node) } } @@ -122,4 +161,44 @@ func (a *transformer) Transform(node *ast.Document, reader text.Reader, pc parse // remove attributes node attr.Parent().RemoveChild(attr.Parent(), attr) } + + // Remove any solitary attribute nodes. + for _, n := range solitaryAttributeNodes { + n.Parent().RemoveChild(n.Parent(), n) + } +} + +func (a *transformer) generateAutoID(n ast.Node, reader text.Reader, pc parser.Context) { + var text []byte + switch n := n.(type) { + case *ast.Heading: + if a.cfg.AutoHeadingID { + text = textHeadingID(n, reader) + } + case *east.DefinitionTerm: + if a.cfg.AutoDefinitionTermID { + text = []byte(render.TextPlain(n, reader.Source())) + } + } + + if len(text) > 0 { + headingID := pc.IDs().Generate(text, n.Kind()) + n.SetAttribute(attrNameID, headingID) + } +} + +// Markdown settext headers can have multiple lines, use the last line for the ID. +func textHeadingID(n *ast.Heading, reader text.Reader) []byte { + text := render.TextPlain(n, reader.Source()) + if n.Lines().Len() > 1 { + + // For multiline headings, Goldmark's extension for headings returns the last line. + // We have a slightly different approach, but in most cases the end result should be the same. + // Instead of looking at the text segments in Lines (see #13405 for issues with that), + // we split the text above and use the last line. + parts := strings.Split(text, "\n") + text = parts[len(parts)-1] + } + + return []byte(text) } diff --git a/markup/goldmark/internal/extensions/attributes/attributes_integration_test.go b/markup/goldmark/internal/extensions/attributes/attributes_integration_test.go new file mode 100644 index 000000000..e56c52550 --- /dev/null +++ b/markup/goldmark/internal/extensions/attributes/attributes_integration_test.go @@ -0,0 +1,110 @@ +package attributes_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestDescriptionListAutoID(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[markup.goldmark.parser] +autoHeadingID = true +autoDefinitionTermID = true +autoIDType = 'github-ascii' +-- content/p1.md -- +--- +title: "Title" +--- + +## Title with id set {#title-with-id} + +## Title with id set duplicate {#title-with-id} + +## My Title + +Base Name +: Base name of the file. + +Base Name +: Duplicate term name. + +My Title +: Term with same name as title. + +Foo@Bar +: The foo bar. + +foo [something](/a/b/) bar +: A foo bar. + +良善天父 +: The good father. + +Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď +: Testing accents. + +Multiline set text header +Second line +--------------- + +## Example [hyperlink](https://example.com/) in a header + +-- layouts/_default/single.html -- +{{ .Content }}|Identifiers: {{ .Fragments.Identifiers }}| +` + + b := hugolib.Test(t, files) + + b.AssertFileContent("public/p1/index.html", + `
    Base Name
    `, + `
    Base Name
    `, + `
    Foo@Bar
    `, + `

    My Title

    `, + `
    foo something bar
    `, + `

    Title with id set

    `, + `

    Title with id set duplicate

    `, + `
    My Title
    `, + `
    良善天父
    `, + `
    Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď
    `, + `

    `, + `