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
[](https://github.com/gohugoio/hugo/actions?query=workflow%3ATest)
[](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
-
+
-
+
+
+
+## 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
[](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" }}
+
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 */}}
-
- {{- 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. */}}
-
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 }}
-
@@ -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 }}
+
+{{ 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 }}
+
+{{ 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 }}
-
-{{ 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 }}
-
-{{ 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 `
{{ end }}
-{{< /code >}}
+```
For multilingual sites, add the language key to the file name:
@@ -38,16 +34,16 @@ Your production server redirects the browser to the 404 page when a page is not
Host|Capabilities and configuration
:--|:--
-Amazon CloudFront|See [details](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GeneratingCustomErrorResponses.html).
-Amazon S3|See [details](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CustomErrorDocSupport.html).
-Apache|See [details](https://httpd.apache.org/docs/2.4/custom-error.html).
-Azure Static Web Apps|See [details](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#response-overrides).
-Azure Storage|See [details](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website#setting-up-a-static-website).
-Caddy|See [details](https://caddyserver.com/docs/caddyfile/directives/handle_errors).
-Cloudflare Pages|See [details](https://developers.cloudflare.com/pages/configuration/serving-pages/#not-found-behavior).
-DigitalOcean App Platform|See [details](https://docs.digitalocean.com/products/app-platform/how-to/manage-static-sites/#configure-a-static-site).
-Firebase|See [details](https://firebase.google.com/docs/hosting/full-config#404).
+Amazon CloudFront|See [details](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GeneratingCustomErrorResponses.html).
+Amazon S3|See [details](https://docs.aws.amazon.com/AmazonS3/latest/userguide/CustomErrorDocSupport.html).
+Apache|See [details](https://httpd.apache.org/docs/2.4/custom-error.html).
+Azure Static Web Apps|See [details](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#response-overrides).
+Azure Storage|See [details](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website#setting-up-a-static-website).
+Caddy|See [details](https://caddyserver.com/docs/caddyfile/directives/handle_errors).
+Cloudflare Pages|See [details](https://developers.cloudflare.com/pages/configuration/serving-pages/#not-found-behavior).
+DigitalOcean App Platform|See [details](https://docs.digitalocean.com/products/app-platform/how-to/manage-static-sites/#configure-a-static-site).
+Firebase|See [details](https://firebase.google.com/docs/hosting/full-config#404).
GitHub Pages|Redirection to is automatic and not configurable.
-GitLab Pages|See [details](https://docs.gitlab.com/ee/user/project/pages/introduction.html#custom-error-codes-pages).
-NGINX|See [details](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page).
-Netlify|See [details](https://docs.netlify.com/routing/redirects/redirect-options/).
+GitLab Pages|See [details](https://docs.gitlab.com/ee/user/project/pages/introduction.html#custom-error-codes-pages).
+NGINX|See [details](https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page).
+Netlify|See [details](https://docs.netlify.com/routing/redirects/redirect-options/).
diff --git a/docs/content/en/templates/_common/_index.md b/docs/content/en/templates/_common/_index.md
deleted file mode 100644
index 4328d4d14..000000000
--- a/docs/content/en/templates/_common/_index.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-cascade:
- _build:
- list: never
- publishResources: false
- render: never
----
-
-
diff --git a/docs/content/en/templates/_common/filter-sort-group.md b/docs/content/en/templates/_common/filter-sort-group.md
deleted file mode 100644
index 89cc095f5..000000000
--- a/docs/content/en/templates/_common/filter-sort-group.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-# Do not remove front matter.
----
-
-{{% note %}}
-The [page collections quick reference guide] describes methods and functions to filter, sort, and group page collections.
-
-[page collections quick reference guide]: /quick-reference/page-collections/
-{{% /note %}}
diff --git a/docs/content/en/templates/_index.md b/docs/content/en/templates/_index.md
index 23b2a3eaf..ea293e8e1 100644
--- a/docs/content/en/templates/_index.md
+++ b/docs/content/en/templates/_index.md
@@ -1,16 +1,8 @@
---
title: Templates
-linkTitle: In this section
-description: Go templating, template types and lookup order, shortcodes, and data.
+description: Create templates to render your content, resources, and data.
categories: []
keywords: []
-menu:
- docs:
- identifier: templates-in-this-section
- parent: templates
- weight: 10
weight: 10
aliases: [/templates/overview/,/templates/content]
---
-
-A template is an HTML file with [template actions](/getting-started/glossary/#template-action), located within the layouts directory of a project, theme, or module. Visit the topics below, in the order presented, to understand template selection and creation.
diff --git a/docs/content/en/templates/base.md b/docs/content/en/templates/base.md
index 45320e3cf..bb6a25b1e 100644
--- a/docs/content/en/templates/base.md
+++ b/docs/content/en/templates/base.md
@@ -1,14 +1,9 @@
---
title: Base templates
description: The base and block construct allows you to define the outer shell of your master templates (i.e., the chrome of the page).
-categories: [templates,fundamentals]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 50
-weight: 50
-toc: true
+weight: 40
aliases: [/templates/blocks/,/templates/base-templates-and-blocks/]
---
@@ -26,7 +21,7 @@ See [Template Lookup Order](/templates/lookup-order/) for details and examples.
The following defines a simple base template at `_default/baseof.html`. As a default template, it is the shell from which all your pages will be rendered unless you specify another `*baseof.html` closer to the beginning of the lookup order.
-{{< code file=layouts/_default/baseof.html >}}
+```go-html-template {file="layouts/_default/baseof.html"}
@@ -46,13 +41,13 @@ The following defines a simple base template at `_default/baseof.html`. As a def
{{ end }}
-{{< /code >}}
+```
## Override the base template
The default list template will inherit all of the code defined above and can then implement its own `"main"` block from:
-{{< code file=layouts/_default/list.html >}}
+```go-html-template {file="layouts/_default/list.html"}
{{ define "main" }}
Posts
{{ range .Pages }}
@@ -62,25 +57,25 @@ The default list template will inherit all of the code defined above and can the
{{ end }}
{{ end }}
-{{< /code >}}
-
-This replaces the contents of our (basically empty) "main" block with something useful for the list template. In this case, we didn't define a `"title"` block, so the contents from our base template remain unchanged in lists.
-
-{{% note %}}
-Code that you put outside the block definitions *can* break your layout. This even includes HTML comments. For example:
-
-```go-html-template
-
-{{ define "main" }}
-...your code here
-{{ end }}
```
-[See this thread from the Hugo discussion forums.](https://discourse.gohugo.io/t/baseof-html-block-templates-and-list-types-results-in-empty-pages/5612/6)
-{{% /note %}}
-The following shows how you can override both the `"main"` and `"title"` block areas from the base template with code unique to your default [single template]:
+This replaces the contents of our (basically empty) `main` block with something useful for the list template. In this case, we didn't define a `title` block, so the contents from our base template remain unchanged in lists.
-{{< code file=layouts/_default/single.html >}}
+> [!warning]
+> Only [template comments] are allowed outside a block's `define` and `end` statements. Avoid placing any other text, including HTML comments, outside these boundaries. Doing so will cause rendering issues, potentially resulting in a blank page. See the example below.
+
+```go-html-template {file="layouts/_default/do-not-do-this.html"}
+
This div element broke your template.
+{{ define "main" }}
+
{{ .Title }}
+ {{ .Content }}
+{{ end }}
+
+```
+
+The following shows how you can override both the `main` and `title` block areas from the base template with code unique to your default [single template]:
+
+```go-html-template {file="layouts/_default/single.html"}
{{ define "title" }}
{{ .Title }} – {{ .Site.Title }}
@@ -89,6 +84,7 @@ The following shows how you can override both the `"main"` and `"title"` block a
{{ .Title }}
{{ .Content }}
{{ end }}
-{{< /code >}}
+```
[single template]: /templates/types/#single
+[template comments]: /templates/introduction/#comments
diff --git a/docs/content/en/templates/content-view.md b/docs/content/en/templates/content-view.md
index be4d7116a..f001e400e 100644
--- a/docs/content/en/templates/content-view.md
+++ b/docs/content/en/templates/content-view.md
@@ -1,36 +1,31 @@
---
title: Content view templates
description: Hugo can render alternative views of your content, useful in list and summary views.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 120
-weight: 120
-toc: true
+weight: 110
aliases: [/templates/views/]
---
The following are common use cases for content views:
-* You want content of every type to be shown on the home page but only with limited [summary views][summaries].
-* You only want a bulleted list of your content in a [taxonomy template]. Views make this very straightforward by delegating the rendering of each different type of content to the content itself.
+- You want content of every type to be shown on the home page but only with limited [summary views][summaries].
+- You only want a bulleted list of your content in a [taxonomy template]. Views make this very straightforward by delegating the rendering of each different type of content to the content itself.
## Create a content view
To create a new view, create a template in each of your different content type directories with the view name. The following example contains an "li" view and a "summary" view for the `posts` and `project` content types. As you can see, these sit next to the [single template], `single.html`. You can even provide a specific view for a given type and continue to use the `_default/single.html` for the primary view.
```txt
- ▾ layouts/
- ▾ posts/
- li.html
- single.html
- summary.html
- ▾ project/
- li.html
- single.html
- summary.html
+layouts/
+├── posts/
+│ ├── li.html
+│ ├── single.html
+│ └── summary.html
+├── project/
+│ ├── li.html
+│ └── single.html
+└── summary.html
```
## Which template will be rendered?
@@ -46,11 +41,11 @@ The following is the lookup order for content views ordered by specificity.
## Example: content view inside a list
-### `list.html`
+### list.html
In this example, `.Render` is passed into the template to call the [render function][render]. `.Render` is a special function that instructs content to render itself with the view template provided as the first argument. In this case, the template is going to render the `summary.html` view that follows:
-{{< code file=layouts/_default/list.html >}}
+```go-html-template {file="layouts/_default/list.html"}
{{ .Title }}
@@ -59,13 +54,13 @@ In this example, `.Render` is passed into the template to call the [render funct
{{ end }}
-{{< /code >}}
+```
-### `summary.html`
+### summary.html
Hugo passes the `Page` object to the following `summary.html` view template.
-{{< code file=layouts/_default/summary.html >}}
+```go-html-template {file="layouts/_default/summary.html"}
@@ -76,18 +71,18 @@ Hugo passes the `Page` object to the following `summary.html` view template.
Read more »
-{{< /code >}}
+```
-### `li.html`
+### li.html
Continuing on the previous example, we can change our render function to use a smaller `li.html` view by changing the argument in the call to the `.Render` function (i.e., `{{ .Render "li" }}`).
-{{< code file=layouts/_default/li.html >}}
+```go-html-template {file="layouts/_default/li.html"}
-{{< /code >}}
+```
[render]: /methods/page/render/
[single template]: /templates/types/#single
diff --git a/docs/content/en/templates/embedded.md b/docs/content/en/templates/embedded.md
index 888f4f342..ecfd90514 100644
--- a/docs/content/en/templates/embedded.md
+++ b/docs/content/en/templates/embedded.md
@@ -1,44 +1,32 @@
---
-title: Embedded templates
-description: Hugo provides embedded templates for common use cases.
-categories: [templates]
+title: Embedded partial templates
+description: Hugo provides embedded partial templates for common use cases.
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 200
-weight: 200
-toc: true
+weight: 170
aliases: [/templates/internal]
---
## Disqus
-{{% note %}}
-To override Hugo's embedded Disqus template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "disqus.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl disqus %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded Disqus template, copy the [source code]({{% eturl disqus %}}) to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "disqus.html" . }}`
Hugo includes an embedded template for [Disqus], a popular commenting system for both static and dynamic websites. To effectively use Disqus, secure a Disqus "shortname" by [signing up] for the free service.
-[Disqus]: https://disqus.com
-[signing up]: https://disqus.com/profile/signup/
-
To include the embedded template:
```go-html-template
{{ template "_internal/disqus.html" . }}
```
-### Configure Disqus
+### Configuration {#configuration-disqus}
To use Hugo's Disqus template, first set up a single configuration value:
-{{< code-toggle file="hugo" >}}
+{{< code-toggle file=hugo >}}
[services.disqus]
shortname = 'your-disqus-shortname'
{{ code-toggle >}}
@@ -55,48 +43,59 @@ You can also set the following in the front matter for a given piece of content:
- `disqus_title`
- `disqus_url`
+### Privacy {#privacy-disqus}
+
+Adjust the relevant privacy settings in your site configuration.
+
+{{< code-toggle config=privacy.disqus />}}
+
+disable
+: (`bool`) Whether to disable the template. Default is `false`.
+
## Google Analytics
-{{% note %}}
-To override Hugo's embedded Google Analytics template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "google_analytics.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl google_analytics %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded Google Analytics template, copy the [source code]({{% eturl google_analytics %}}) to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "google_analytics.html" . }}`
Hugo includes an embedded template supporting [Google Analytics 4].
-[Google Analytics 4]: https://support.google.com/analytics/answer/10089681
-
To include the embedded template:
```go-html-template
{{ template "_internal/google_analytics.html" . }}
```
-### Configure Google Analytics
+### Configuration {#configuration-google-analytics}
Provide your tracking ID in your configuration file:
{{< code-toggle file=hugo >}}
[services.googleAnalytics]
-ID = "G-MEASUREMENT_ID"
+id = "G-MEASUREMENT_ID"
{{ code-toggle >}}
To use this value in your own template, access the configured ID with `{{ site.Config.Services.GoogleAnalytics.ID }}`.
+### Privacy {#privacy-google-analytics}
+
+Adjust the relevant privacy settings in your site configuration.
+
+{{< code-toggle config=privacy.googleAnalytics />}}
+
+disable
+: (`bool`) Whether to disable the template. Default is `false`.
+
+respectDoNotTrack
+: (`bool`) Whether to respect the browser's "do not track" setting. Default is `false`.
+
## Open Graph
-{{% note %}}
-To override Hugo's embedded Open Graph template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "opengraph.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl opengraph %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded Open Graph template, copy the [source code]({{% eturl opengraph %}}) to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "opengraph.html" . }}`
Hugo includes an embedded template for the [Open Graph protocol](https://ogp.me/), metadata that enables a page to become a rich object in a social graph.
This format is used for Facebook and some other sites.
@@ -107,7 +106,7 @@ To include the embedded template:
{{ template "_internal/opengraph.html" . }}
```
-### Configure Open Graph
+### Configuration {#configuration-open-graph}
Hugo's Open Graph template is configured using a mix of configuration settings and [front matter](/content-management/front-matter/) on individual pages.
@@ -146,21 +145,19 @@ Various optional metadata can also be set:
If using YouTube this will produce a og:video tag like ``. Use the `https://youtu.be/` format with YouTube videos (example: `https://youtu.be/qtIqKaDlqXo`).
+## Pagination
+
+See [details](/templates/pagination/).
+
## Schema
-{{% note %}}
-To override Hugo's embedded Schema template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "schema.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl schema %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded Schema template, copy the [source code]({{% eturl schema %}}) to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "schema.html" . }}`
Hugo includes an embedded template to render [microdata] `meta` elements within the `head` element of your templates.
-[microdata]: https://html.spec.whatwg.org/multipage/microdata.html#microdata
-
To include the embedded template:
```go-html-template
@@ -169,14 +166,10 @@ To include the embedded template:
## X (Twitter) Cards
-{{% note %}}
-To override Hugo's embedded Twitter Cards template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "twitter_cards.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl twitter_cards %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded Twitter Cards template, copy the [source code]({{% eturl twitter_cards %}}) to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "twitter_cards.html" . }}`
Hugo includes an embedded template for [X (Twitter) Cards](https://developer.x.com/en/docs/twitter-for-websites/cards/overview/abouts-cards),
metadata used to attach rich media to Tweets linking to your site.
@@ -187,7 +180,7 @@ To include the embedded template:
{{ template "_internal/twitter_cards.html" . }}
```
-### Configure X (Twitter) Cards
+### Configuration {#configuration-x-cards}
Hugo's X (Twitter) Card template is configured using a mix of configuration settings and [front-matter](/content-management/front-matter/) values on individual pages.
@@ -197,21 +190,21 @@ Hugo's X (Twitter) Card template is configured using a mix of configuration sett
description = "Text about my cool site"
{{ code-toggle >}}
-{{< code-toggle file=content/blog/my-post.md >}}
+{{< code-toggle file=content/blog/my-post.md fm=true >}}
title = "Post title"
description = "Text about this post"
images = ["post-cover.png"]
{{ code-toggle >}}
If [page bundles](/content-management/page-bundles/) are used and the `images` array is empty or undefined, images with file names matching `*feature*`, `*cover*`, or `*thumbnail*` are used for image metadata.
-If no image resources with those names are found, the images defined in the [site config](/getting-started/configuration/) are used instead.
+If no image resources with those names are found, the images defined in the [site config](/configuration/) are used instead.
If no images are found at all, then an image-less Twitter `summary` card is used instead of `summary_large_image`.
Hugo uses the page title and description for the card's title and description fields. The page summary is used if no description is given.
Set the value of `twitter:site` in your site configuration:
-{{< code-toggle file="hugo" copy=false >}}
+{{< code-toggle file=hugo >}}
[params.social]
twitter = "GoHugoIO"
{{ code-toggle >}}
@@ -221,3 +214,9 @@ NOTE: The `@` will be added for you
```html
```
+
+[`partial`]: /functions/partials/include/
+[Disqus]: https://disqus.com
+[Google Analytics 4]: https://support.google.com/analytics/answer/10089681
+[microdata]: https://html.spec.whatwg.org/multipage/microdata.html#microdata
+[signing up]: https://disqus.com/profile/signup/
diff --git a/docs/content/en/templates/home.md b/docs/content/en/templates/home.md
index 3086791c5..937a4a5a8 100644
--- a/docs/content/en/templates/home.md
+++ b/docs/content/en/templates/home.md
@@ -1,55 +1,58 @@
---
-title: Home templates
+title: Home page templates
description: The home page of a website is often formatted differently than the other pages. For this reason, Hugo makes it easy for you to define your new site's home page as a unique template.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 60
-weight: 60
-toc: true
+weight: 50
aliases: [/layout/homepage/,/templates/homepage-template/,/templates/homepage/]
---
-The home template is the *only* required template for building a site and therefore useful when bootstrapping a new site and template. It is also the only required template if you are developing a single-page website.
+## Introduction
+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.
-{{< youtube ut1xtRZ1QOA >}}
-
-## Home template lookup order
-
-See [Template Lookup](/templates/lookup-order/).
-
-## Add content and front matter to the home page
-
-The home page accepts content and front matter from an `_index.md` file. This file should live at the root of your `content` folder (i.e., `content/_index.md`). You can then add body copy and metadata to your home page the way you would any other content file.
-
-See the home template below or [Content Organization][contentorg] for more information on the role of `_index.md` in adding content and front matter to list pages.
-
-## Example home template
-
-{{< code file=layouts/_default/home.html >}}
+```go-html-template {file="layouts/_default/home.html"}
{{ define "main" }}
-
-
-
{{ .Title }}
- {{ with .Params.subtitle }}
- {{ . }}
- {{ end }}
-
-
-
- {{ .Content }}
-
-
- {{ range first 10 .Site.RegularPages }}
- {{ .Render "summary" }}
- {{ end }}
-
+ {{ end }}
{{ end }}
-{{< /code >}}
+```
-[contentorg]: /content-management/organization/
-[lookup]: /templates/lookup-order/
+{{% include "/_common/filter-sort-group.md" %}}
+
+## Lookup order
+
+Hugo's [template lookup order] determines the template path, allowing you to create unique templates for any page.
+
+> [!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.
+
+## Content and front matter
+
+The home page template uses content and front matter from an `_index.md` file located in the root of your content directory.
+
+{{< code-toggle file=content/_index.md fm=true >}}
+---
+title: The Home Page
+date: 2025-01-30T03:36:57-08:00
+draft: false
+params:
+ subtitle: The Subtitle
+---
+{{< /code-toggle >}}
+
+The home page template below inherits the site's shell from the base template, renders the subtitle and content as defined in the `_index.md` file, then renders of list of the site's [regular pages](g).
+
+```go-html-template {file="layouts/_default/home.html"}
+{{ define "main" }}
+
{{ .Params.Subtitle }}
+ {{ .Content }}
+ {{ range site.RegularPages }}
+
+ {{ end }}
+{{ end }}
+```
+
+[template lookup order]: /templates/lookup-order/#home-templates
diff --git a/docs/content/en/templates/introduction.md b/docs/content/en/templates/introduction.md
index 0a3ff2b0f..8898ee456 100644
--- a/docs/content/en/templates/introduction.md
+++ b/docs/content/en/templates/introduction.md
@@ -1,34 +1,22 @@
---
title: Introduction to templating
linkTitle: Introduction
-description: Create templates to render your content, resources, and data.
-categories: [templates,fundamentals]
+description: An introduction to Hugo's templating syntax.
+categories: []
keywords: []
-menu:
- docs:
- identifier: templates-introduction
- parent: templates
- weight: 20
-weight: 20
-toc: true
+weight: 10
---
-A template is a file in the layouts directory of a project, theme, or module. Templates use [variables] , [functions], and [methods] to transform your content, resources, and data into a published page.
+{{% glossary-term template %}}
-[functions]: /functions/
-[methods]: /methods/
-[variables]: #variables
+Templates use [variables], [functions], and [methods] to transform your content, resources, and data into a published page.
-{{% note %}}
-Hugo uses Go's [text/template] and [html/template] packages.
-
-The text/template package implements data-driven templates for generating textual output, while the html/template package implements data-driven templates for generating HTML output safe against code injection.
-
-By default, Hugo uses the html/template package when rendering HTML files.
-
-[text/template]: https://pkg.go.dev/text/template
-[html/template]: https://pkg.go.dev/html/template
-{{% /note %}}
+> [!note]
+> Hugo uses Go's [text/template] and [html/template] packages.
+>
+> The text/template package implements data-driven templates for generating textual output, while the html/template package implements data-driven templates for generating HTML output safe against code injection.
+>
+> By default, Hugo uses the html/template package when rendering HTML files.
For example, this HTML template initializes the `$v1` and `$v2` variables, then displays them and their product within an HTML paragraph.
@@ -38,16 +26,11 @@ For example, this HTML template initializes the `$v1` and `$v2` variables, then
The product of {{ $v1 }} and {{ $v2 }} is {{ mul $v1 $v2 }}.
```
-While HTML templates are the most common, you can create templates for any [output format] including CSV, JSON, RSS, and plain text.
-
-[output format]: /templates/output-formats/
+While HTML templates are the most common, you can create templates for any [output format](g) including CSV, JSON, RSS, and plain text.
## Context
-The most important concept to understand before creating a template is _context_, the data passed into each template. The data may be a simple value, or more commonly [objects] and associated [methods].
-
-[objects]: /getting-started/glossary/#object
-[methods]: /getting-started/glossary/#method
+The most important concept to understand before creating a template is _context_, the data passed into each template. The data may be a simple value, or more commonly [objects](g) and associated [methods](g).
For example, a template for a single page receives a `Page` object, and the `Page` object provides methods to return values or perform actions.
@@ -55,21 +38,15 @@ For example, a template for a single page receives a `Page` object, and the `Pag
Within a template, the dot (`.`) represents the current context.
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ .Title }}
-{{< /code >}}
+```
In the example above the dot represents the `Page` object, and we call its [`Title`] method to return the title as defined in [front matter].
-[front matter]: /content-management/front-matter/
-[`Title`]: /methods/page/title
-
The current context may change within a template. For example, at the top of a template the context might be a `Page` object, but we rebind the context to another value or object within [`range`] or [`with`] blocks.
-[`range`]: /functions/go-template/range/
-[`with`]: /functions/go-template/with/
-
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ .Title }}
{{ range slice "foo" "bar" }}
@@ -79,11 +56,9 @@ The current context may change within a template. For example, at the top of a t
{{ with "baz" }}
{{ . }}
{{ end }}
-{{< /code >}}
+```
-In the example above, the context changes as we `range` through the [slice] of values. In the first iteration the context is "foo", and in the second iteration the context is "bar". Inside of the `with` block the context is "baz". Hugo renders the above to:
-
-[slice]: /getting-started/glossary/#slice
+In the example above, the context changes as we `range` through the [slice](g) of values. In the first iteration the context is "foo", and in the second iteration the context is "bar". Inside of the `with` block the context is "baz". Hugo renders the above to:
```html
My Page Title
@@ -96,11 +71,11 @@ In the example above, the context changes as we `range` through the [slice] of v
Within a `range` or `with` block you can access the context passed into the template by prepending a dollar sign (`$`) to the dot:
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ with "foo" }}
{{ $.Title }} - {{ . }}
{{ end }}
-{{< /code >}}
+```
Hugo renders this to:
@@ -108,27 +83,21 @@ Hugo renders this to:
My Page Title - foo
```
-{{% note %}}
-Make sure that you thoroughly understand the concept of _context_ before you continue reading. The most common templating errors made by new users relate to context.
-{{% /note %}}
+> [!note]
+> Make sure that you thoroughly understand the concept of _context_ before you continue reading. The most common templating errors made by new users relate to context.
## Actions
In the examples above the paired opening and closing braces represent the beginning and end of a template action, a data evaluation or control structure within a template.
-A template action may contain literal values ([boolean], [string], [integer], and [float]), variables, functions, and methods.
+A template action may contain literal values ([boolean](g), [string](g), [integer](g), and [float](g)), variables, functions, and methods.
-[boolean]: /getting-started/glossary/#boolean
-[string]: /getting-started/glossary/#string
-[integer]: /getting-started/glossary/#integer
-[float]: /getting-started/glossary/#float
-
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ $convertToLower := true }}
{{ if $convertToLower }}
{{ strings.ToLower .Title }}
{{ end }}
-{{< /code >}}
+```
In the example above:
@@ -150,12 +119,12 @@ Hugo renders the above to:
Notice the blank lines and indentation in the previous example? Although irrelevant in production when you typically minify the output, you can remove the adjacent whitespace by using template action delimiters with hyphens:
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{- $convertToLower := true -}}
{{- if $convertToLower -}}
{{ strings.ToLower .Title }}
{{- end -}}
-{{< /code >}}
+```
Hugo renders this to:
@@ -167,9 +136,7 @@ Whitespace includes spaces, horizontal tabs, carriage returns, and newlines.
### Pipes
-Within a template action you may [pipe] a value to function or method. The piped value becomes the final argument to the function or method. For example, these are equivalent:
-
-[pipe]: /getting-started/glossary/#pipeline
+Within a template action you may [pipe](g) a value to a function or method. The piped value becomes the final argument to the function or method. For example, these are equivalent:
```go-html-template
{{ strings.ToLower "Hugo" }} → hugo
@@ -190,26 +157,23 @@ These are also equivalent:
{{ 5 | add 2 | mul 6 }} → 42
```
-{{% note %}}
-Remember that the piped value becomes the final argument to the function or method to which you are piping.
-{{% /note %}}
+> [!note]
+> Remember that the piped value becomes the final argument to the function or method to which you are piping.
### Line splitting
You can split a template action over two or more lines. For example, these are equivalent:
```go-html-template
-{{ $v := or .Site.Language.LanguageName .Site.Language.Lang }}
+{{ $v := or $arg1 $arg2 }}
{{ $v := or
- .Site.Language.LanguageName
- .Site.Language.Lang
+ $arg1
+ $arg2
}}
```
-You can also split [raw string literals] over two or more lines. For example, these are equivalent:
-
-[raw string literals]: /getting-started/glossary/#string-literal-raw
+You can also split [raw string literals](g) over two or more lines. For example, these are equivalent:
```go-html-template
{{ $msg := "This is line one.\nThis is line two." }}
@@ -221,16 +185,9 @@ This is line two.`
## Variables
-A variable is a user-defined [identifier] prepended with a dollar sign (`$`), representing a value of any data type, initialized or assigned within a template action. For example, `$foo` and `$bar` are variables.
+A variable is a user-defined [identifier](g) prepended with a dollar sign (`$`), representing a value of any data type, initialized or assigned within a template action. For example, `$foo` and `$bar` are variables.
-[identifier]: /getting-started/glossary/#identifier
-
-Variables may contain [scalars], [slices], [maps], or [objects].
-
-[scalars]: /getting-started/glossary/#scalar
-[slices]: /getting-started/glossary/#slice
-[maps]: /getting-started/glossary/#map
-[objects]: /getting-started/glossary/#object
+Variables may contain [scalars](g), [slices](g), [maps](g), or [objects](g).
Use `:=` to initialize a variable, and use `=` to assign a value to a variable that has been previously initialized. For example:
@@ -246,8 +203,6 @@ Variables initialized inside of an `if`, `range`, or `with` block are scoped to
With variables that represent a slice or map, use the [`index`] function to return the desired value.
-[`index`]: /functions/collections/indexfunction/
-
```go-html-template
{{ $slice := slice "foo" "bar" "baz" }}
{{ index $slice 2 }} → baz
@@ -256,13 +211,10 @@ With variables that represent a slice or map, use the [`index`] function to retu
{{ index $map "c" }} → baz
```
-{{% note %}}
-Slices and arrays are zero-based; element 0 is the first element.
-{{% /note %}}
+> [!note]
+> Slices and arrays are zero-based; element 0 is the first element.
-With variables that represent a map or object, [chain] identifiers to return the desired value or to access the desired method.
-
-[chain]: /getting-started/glossary/#chain
+With variables that represent a map or object, [chain](g) identifiers to return the desired value or to access the desired method.
```go-html-template
{{ $map := dict "a" "foo" "b" "bar" "c" "baz" }}
@@ -272,9 +224,8 @@ With variables that represent a map or object, [chain] identifiers to return the
{{ $homePage.Title }} → My Homepage
```
-{{% note %}}
-As seen above, object and method names are capitalized. Although not required, to avoid confusion we recommend beginning variable and map key names with a lowercase letter or underscore.
-{{% /note %}}
+> [!note]
+> As seen above, object and method names are capitalized. Although not required, to avoid confusion we recommend beginning variable and map key names with a lowercase letter or underscore.
## Functions
@@ -282,12 +233,8 @@ Used within a template action, a function takes one or more arguments and return
Go's text/template and html/template packages provide a small set of functions, operators, and statements for general use. See the [go-templates] section of the function documentation for details.
-[go-templates]: /functions/go-template/
-
Hugo provides hundreds of custom [functions] categorized by namespace. For example, the `strings` namespace includes these and other functions:
-[functions]: /functions
-
Function|Alias
:--|:--
[`strings.ToLower`](/functions/strings/tolower)|`lower`
@@ -308,49 +255,42 @@ Used within a template action and associated with an object, a method takes zero
The most commonly accessed objects are the [`Page`] and [`Site`] objects. This is a small sampling of the [methods] available to each object.
-[`Site`]: /methods/site/
-[`Page`]: /methods/page/
-[methods]: /methods/
-
Object|Method|Description
:--|:--|:--
`Page`|[`Date`](methods/page/date/)|Returns the date of the given page.
`Page`|[`Params`](methods/page/params/)|Returns a map of custom parameters as defined in the front matter of the given page.
`Page`|[`Title`](methods/page/title/)|Returns the title of the given page.
-`Site`|[`Data`](methods/site/data/)|Returns a data structure composed from the files in the data directory.
+`Site`|[`Data`](methods/site/data/)|Returns a data structure composed from the files in the `data` directory.
`Site`|[`Params`](methods/site/params/)|Returns a map of custom parameters as defined in the site configuration.
`Site`|[`Title`](methods/site/title/)|Returns the title as defined in the site configuration.
Chain the method to its object with a dot (`.`) as shown below, remembering that the leading dot represents the [current context].
-[current context]: #current-context
-
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ .Site.Title }} → My Site Title
{{ .Page.Title }} → My Page Title
-{{< /code >}}
+```
The context passed into most templates is a `Page` object, so this is equivalent to the previous example:
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ .Site.Title }} → My Site Title
{{ .Title }} → My Page Title
-{{< /code >}}
+```
Some methods take an argument. Separate the argument from the method with a space. For example:
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ $page := .Page.GetPage "/books/les-miserables" }}
{{ $page.Title }} → Les Misérables
-{{< /code >}}
+```
## Comments
-{{% note %}}
-Do not attempt to use HTML comment delimiters to comment out template code.
-
-Hugo strips HTML comments when rendering a page, but first evaluates any template code within the HTML comment delimiters. Depending on the template code within the HTML comment delimiters, this could cause unexpected results or fail the build.
-{{% /note %}}
+> [!note]
+> Do not attempt to use HTML comment delimiters to comment out template code.
+>
+> Hugo strips HTML comments when rendering a page, but first evaluates any template code within the HTML comment delimiters. Depending on the template code within the HTML comment delimiters, this could cause unexpected results or fail the build.
Template comments are similar to template actions. Paired opening and closing braces represent the beginning and end of a comment. For example:
@@ -376,8 +316,6 @@ You may not nest one comment inside of another.
To render an HTML comment, pass a string through the [`safeHTML`] template function. For example:
-[`safeHTML`]: /functions/safe/html
-
```go-html-template
{{ "" | safeHTML }}
{{ printf "" .Site.Title | safeHTML }}
@@ -387,8 +325,6 @@ To render an HTML comment, pass a string through the [`safeHTML`] template funct
Use the [`template`] function to include one or more of Hugo's [embedded templates]:
-[embedded templates]: /templates/embedded/
-
```go-html-template
{{ template "_internal/google_analytics.html" . }}
{{ template "_internal/opengraph" . }}
@@ -397,14 +333,8 @@ Use the [`template`] function to include one or more of Hugo's [embedded templat
{{ template "_internal/twitter_cards.html" . }}
```
-[`partial`]: /functions/partials/include/
-[`partialCached`]: /functions/partials/includecached/
-[`template`]: functions/go-template/template/
-
Use the [`partial`] or [`partialCached`] function to include one or more [partial templates]:
-[partial templates]: /templates/partial
-
```go-html-template
{{ partial "breadcrumbs.html" . }}
{{ partialCached "css.html" . }}
@@ -412,24 +342,17 @@ Use the [`partial`] or [`partialCached`] function to include one or more [partia
Create your partial templates in the layouts/partials directory.
-{{% note %}}
-In the examples above, note that we are passing the current context (the dot) to each of the templates.
-{{% /note %}}
+> [!note]
+> In the examples above, note that we are passing the current context (the dot) to each of the templates.
## Examples
This limited set of contrived examples demonstrates some of concepts described above. Please see the [functions], [methods], and [templates] documentation for specific examples.
-[templates]: /templates/
-
### Conditional blocks
See documentation for [`if`], [`else`], and [`end`].
-[`if`]: /functions/go-template/if/
-[`else`]: /functions/go-template/else/
-[`end`]: /functions/go-template/end/
-
```go-html-template
{{ $var := 42 }}
{{ if eq $var 6 }}
@@ -447,9 +370,6 @@ See documentation for [`if`], [`else`], and [`end`].
See documentation for [`and`] and [`or`].
-[`and`]: /functions/go-template/and
-[`or`]: /functions/go-template/or
-
```go-html-template
{{ $v1 := true }}
{{ $v2 := false }}
@@ -471,8 +391,6 @@ See documentation for [`and`] and [`or`].
See documentation for [`range`], [`else`], and [`end`].
-[`range`]: /functions/go-template/range/
-
```go-html-template
{{ $s := slice "foo" "bar" "baz" }}
{{ range $s }}
@@ -484,8 +402,6 @@ See documentation for [`range`], [`else`], and [`end`].
Use the [`seq`] function to loop a specified number of times:
-[`seq`]: /functions/collections/seq
-
```go-html-template
{{ $total := 0 }}
{{ range seq 4 }}
@@ -498,8 +414,6 @@ Use the [`seq`] function to loop a specified number of times:
See documentation for [`with`], [`else`], and [`end`].
-[`with`]: /functions/go-template/with/
-
```go-html-template
{{ $var := "foo" }}
{{ with $var }}
@@ -557,21 +471,65 @@ Access the custom site parameters by chaining the identifiers:
See documentation for the [`Params`](/methods/page/params/) method on a `Page` object.
-With this front matter:
+By way of example, consider this front matter:
-{{< code-toggle file=content/news/annual-conference.md >}}
+{{< code-toggle file=content/annual-conference.md fm=true >}}
title = 'Annual conference'
date = 2023-10-17T15:11:37-07:00
[params]
display_related = true
+key-with-hyphens = 'must use index function'
[params.author]
email = 'jsmith@example.org'
name = 'John Smith'
{{< /code-toggle >}}
-Access the custom page parameters by chaining the identifiers:
+The `title` and `date` fields are standard [front matter fields], while the other fields are user-defined.
+
+Access the custom fields by [chaining](g) the [identifiers](g) when needed:
```go-html-template
{{ .Params.display_related }} → true
+{{ .Params.author.email }} → jsmith@example.org
{{ .Params.author.name }} → John Smith
```
+
+In the template example above, each of the keys is a valid identifier. For example, none of the keys contains a hyphen. To access a key that is not a valid identifier, use the [`index`] function:
+
+```go-html-template
+{{ index .Params "key-with-hyphens" }} → must use index function
+```
+
+[`and`]: /functions/go-template/and
+[`else`]: /functions/go-template/else/
+[`end`]: /functions/go-template/end/
+[`if`]: /functions/go-template/if/
+[`index`]: /functions/collections/indexfunction/
+[`index`]: /functions/collections/indexfunction/
+[`or`]: /functions/go-template/or
+[`Page`]: /methods/page/
+[`partial`]: /functions/partials/include/
+[`partialCached`]: /functions/partials/includecached/
+[`range`]: /functions/go-template/range/
+[`range`]: /functions/go-template/range/
+[`safeHTML`]: /functions/safe/html
+[`seq`]: /functions/collections/seq
+[`Site`]: /methods/site/
+[`template`]: /functions/go-template/template/
+[`Title`]: /methods/page/title
+[`with`]: /functions/go-template/with/
+[`with`]: /functions/go-template/with/
+[current context]: #current-context
+[embedded templates]: /templates/embedded/
+[front matter]: /content-management/front-matter/
+[front matter fields]: /content-management/front-matter/#fields
+[functions]: /functions/
+[functions]: /functions
+[go-templates]: /functions/go-template/
+[html/template]: https://pkg.go.dev/html/template
+[methods]: /methods/
+[methods]: /methods/
+[partial templates]: /templates/partial
+[templates]: /templates/
+[text/template]: https://pkg.go.dev/text/template
+[variables]: #variables
diff --git a/docs/content/en/templates/lookup-order.md b/docs/content/en/templates/lookup-order.md
index 3f9a9c527..518900797 100644
--- a/docs/content/en/templates/lookup-order.md
+++ b/docs/content/en/templates/lookup-order.md
@@ -2,14 +2,9 @@
title: Template lookup order
linkTitle: Lookup order
description: Hugo uses the rules below to select a template for a given page, starting from the most specific.
-categories: [templates,fundamentals]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 40
-weight: 40
-toc: true
+weight: 20
---
## Lookup rules
@@ -23,7 +18,7 @@ Layout
: Can be set in front matter.
Output Format
-: See [Custom Output Formats](/templates/output-formats). An output format has both a `name` (e.g. `rss`, `amp`, `html`) and a `suffix` (e.g. `xml`, `html`). We prefer matches with both (e.g. `index.amp.html`), but look for less specific templates.
+: See [configure output formats](/configuration/output-formats/). An output format has both a `name` (e.g. `rss`, `amp`, `html`) and a `suffix` (e.g. `xml`, `html`). We prefer matches with both (e.g. `index.amp.html`), but look for less specific templates.
Note that if the output format's Media Type has more than one suffix defined, only the first is considered.
@@ -36,9 +31,8 @@ Type
Section
: Is relevant for `section`, `taxonomy` and `term` types.
-{{% note %}}
-Templates can live in either the project's or the themes' layout folders, and the most specific templates will be chosen. Hugo will interleave the lookups listed below, finding the most specific one either in the project or themes.
-{{% /note %}}
+> [!note]
+> Templates can live in either the project's or the themes' `layout` directories, and the most specific templates will be chosen. Hugo will interleave the lookups listed below, finding the most specific one either in the project or themes.
## Target a template
@@ -52,9 +46,7 @@ content/
└── contact.md
```
-Files in the root of the content directory have a [content type] of `page`. To render these pages with a unique template, create a matching subdirectory:
-
-[content type]: /getting-started/glossary/#content-type
+Files in the root of the `content` directory have a [content type](g) of `page`. To render these pages with a unique template, create a matching subdirectory:
```text
layouts/
@@ -64,7 +56,7 @@ layouts/
But the contact page probably has a form and requires a different template. In the front matter specify `layout`:
-{{< code-toggle file=content/contact.md >}}
+{{< code-toggle file=content/contact.md fm=true >}}
title = 'Contact'
layout = 'contact'
{{< /code-toggle >}}
@@ -80,12 +72,12 @@ layouts/
As a content type, the word `page` is vague. Perhaps `miscellaneous` would be better. Add `type` to the front matter of each page:
-{{< code-toggle file=content/about.md >}}
+{{< code-toggle file=content/about.md fm=true >}}
title = 'About'
type = 'miscellaneous'
{{< /code-toggle >}}
-{{< code-toggle file=content/contact.md >}}
+{{< code-toggle file=content/contact.md fm=true >}}
title = 'Contact'
type = 'miscellaneous'
layout = 'contact'
diff --git a/docs/content/en/templates/menu.md b/docs/content/en/templates/menu.md
index bd0bc5569..4ff423255 100644
--- a/docs/content/en/templates/menu.md
+++ b/docs/content/en/templates/menu.md
@@ -1,15 +1,9 @@
---
-title: Menus
+title: Menu templates
description: Create templates to render one or more menus.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- identifier: templates-menu
- parent: templates
- weight: 180
-weight: 180
-toc: true
+weight: 150
aliases: [/templates/menus/,/templates/menu-templates/]
---
@@ -29,7 +23,7 @@ The example below handles every combination.
This partial template recursively "walks" a menu structure, rendering a localized, accessible nested list.
-{{< code file=layouts/partials/menu.html copy=true >}}
+```go-html-template {file="layouts/partials/menu.html" copy=true}
{{- $page := .page }}
{{- $menuID := .menuID }}
@@ -72,14 +66,14 @@ This partial template recursively "walks" a menu structure, rendering a localize
{{- end }}
{{- end }}
-{{< /code >}}
+```
Call the partial above, passing a menu ID and the current page in context.
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
{{ partial "menu.html" (dict "menuID" "footer" "page" .) }}
-{{< /code >}}
+```
## Page references
@@ -87,7 +81,7 @@ Regardless of how you [define menu entries], an entry associated with a page has
This simplistic example renders a page parameter named `version` next to each entry's `name`. Code defensively using `with` or `if` to handle entries where (a) the entry points to an external resource, or (b) the `version` parameter is not defined.
-{{< code file=layouts/_default/single.html >}}
+```go-html-template {file="layouts/_default/single.html"}
{{- range site.Menus.main }}
{{ .Name }}
@@ -98,7 +92,7 @@ This simplistic example renders a page parameter named `version` next to each en
{{- end }}
{{- end }}
-{{< /code >}}
+```
## Menu entry parameters
@@ -109,13 +103,13 @@ When you define menu entries [in site configuration] or [in front matter], you c
This simplistic example renders a `class` attribute for each anchor element. Code defensively using `with` or `if` to handle entries where `params.class` is not defined.
-{{< code file=layouts/partials/menu.html >}}
+```go-html-template {file="layouts/partials/menu.html"}
{{- range site.Menus.main }}
{{ .Name }}
{{- end }}
-{{< /code >}}
+```
## Localize
@@ -127,7 +121,7 @@ Hugo provides two methods to localize your menu entries. See [multilingual].
[in front matter]: /content-management/menus/#define-in-front-matter
[in site configuration]: /content-management/menus/#define-in-site-configuration
[localize the menu entries]: /content-management/multilingual/#menus
-[menu entry defined in front matter]: /content-management/menus/#example-front-matter
-[menu entry defined in site configuration]: /content-management/menus/#example-site-configuration
+[menu entry defined in front matter]: /content-management/menus/#example
+[menu entry defined in site configuration]: /configuration/menus
[menu methods]: /methods/menu/
[multilingual]: /content-management/multilingual/#menus
diff --git a/docs/content/en/templates/output-formats.md b/docs/content/en/templates/output-formats.md
deleted file mode 100644
index c81d58488..000000000
--- a/docs/content/en/templates/output-formats.md
+++ /dev/null
@@ -1,248 +0,0 @@
----
-title: Custom output formats
-description: Hugo can output content in multiple formats, including calendar events, e-book formats, Google AMP, and JSON search indexes, or any custom text format.
-categories: [templates,fundamentals]
-keywords: []
-menu:
- docs:
- parent: templates
- weight: 210
-weight: 210
-toc: true
-aliases: [/templates/outputs/,/extras/output-formats/,/content-management/custom-outputs/]
----
-
-This page describes how to properly configure your site with the media types and output formats, as well as where to create your templates for your custom outputs.
-
-## Media types
-
-A [media type] (formerly known as a MIME type) is a two-part identifier for file formats and format contents transmitted on the internet.
-
-This is the full set of built-in media types in Hugo:
-
-{{< datatable "config" "mediaTypes" "_key" "suffixes" >}}
-
-**Note:**
-
-- It is possible to add custom media types or change the defaults; e.g., if you want to change the suffix for `text/html` to `asp`.
-- `Suffixes` are the values that will be used for URLs and file names for that media type in Hugo.
-- The `Type` is the identifier that must be used when defining new/custom `Output Formats` (see below).
-- The full set of media types will be registered in Hugo's built-in development server to make sure they are recognized by the browser.
-
-To add or modify a media type, define it in a `mediaTypes` section in your [site configuration], either for all sites or for a given language.
-
-{{< code-toggle file=hugo >}}
-[mediaTypes]
- [mediaTypes."text/enriched"]
- suffixes = ["enr"]
- [mediaTypes."text/html"]
- suffixes = ["asp"]
-{{ code-toggle >}}
-
-The above example adds one new media type, `text/enriched`, and changes the suffix for the built-in `text/html` media type.
-
-**Note:** these media types are configured for **your output formats**. If you want to redefine one of Hugo's default output formats (e.g. `HTML`), you also need to redefine the media type. So, if you want to change the suffix of the `HTML` output format from `html` (default) to `htm`:
-
-{{< code-toggle file=hugo >}}
-[mediaTypes]
- [mediaTypes."text/html"]
- suffixes = ["htm"]
-
-[outputFormats]
- [outputFormats.html]
- mediaType = "text/html"
-{{ code-toggle >}}
-
-{{% note %}}
-For the above to work, you also need to add an `outputs` definition in your site configuration.
-{{% /note %}}
-
-## Output format definitions
-
-Given a media type and some additional configuration, you get an **Output Format**.
-
-This is the full set of Hugo's built-in output formats:
-
-{{< datatable "config" "outputFormats" "_key" "baseName" "isHTML" "isPlainText" "mediaType" "noUgly" "path" "permalinkable" "protocol" "rel" >}}
-
-- A page can be output in as many output formats as you want, and you can have an infinite amount of output formats defined **as long as they resolve to a unique path on the file system**. In the above table, the best example of this is `amp` vs. `html`. `amp` has the value `amp` for `path` so it doesn't overwrite the `html` version; e.g. we can now have both `/index.html` and `/amp/index.html`.
-- The `mediaType` must match a defined media type.
-- You can define new output formats or redefine built-in output formats; e.g., if you want to put `amp` pages in a different path.
-
-To add or modify an output format, define it in an `outputFormats` section in your site's [configuration file](/getting-started/configuration/), either for all sites or for a given language.
-
-{{< code-toggle file=hugo >}}
-[outputFormats.MyEnrichedFormat]
-mediaType = "text/enriched"
-baseName = "myindex"
-isPlainText = true
-protocol = "bep://"
-{{ code-toggle >}}
-
-The above example is fictional, but if used for the home page on a site with `baseURL` `https://example.org`, it will produce a plain text home page with the URL `bep://example.org/myindex.enr`.
-
-### Configure output formats
-
-Use these parameters when configuring an output format:
-
-baseName
-: (`string`) The base name of the published file. Default is `index`.
-
-isHTML
-: (`bool`) If `true`, classifies the output format as HTML. Hugo uses this value to determine when to create alias redirects, when to inject the LiveReload script, etc. Default is `false`.
-
-isPlainText
-: (`bool`) If `true`, Hugo parses templates for this output format with Go's [text/template] package instead of the [html/template] package. Default is `false`.
-
-[html/template]: https://pkg.go.dev/html/template
-[text/template]: https://pkg.go.dev/text/template
-
-mediaType
-: (`string`) The [media type] of the published file. This must match a defined media type, either [built-in](#media-types) or custom.
-
-[media type]: https://en.wikipedia.org/wiki/Media_type
-
-notAlternative
-: (`bool`) If `true`, excludes this output format from the values returned by the [`AlternativeOutputFormats`] method on a `Page` object. Default is `false`.
-
-[`AlternativeOutputFormats`]: /methods/page/alternativeoutputformats/
-
-noUgly
-: (`bool`) If `true`, disables ugly URLs for this output format when `uglyURLs` is `true` in your site configuration. Default is `false`.
-
-path
-: (`string`) The path to the directory containing the published files, relative to the root of the publish directory.
-
-permalinkable
-: (`bool`) If `true`, the [`Permalink`] and [`RelPermalink`] methods on a `Page` object return the rendering output format rather than main output format ([see below](#link-to-output-formats)). Enabled by default for the `html` and `amp` output formats. Default is `false`.
-
-[`Permalink`]: /methods/page/permalink/
-[`RelPermalink`]: /methods/page/relpermalink/
-
-protocol
-: (`string`) The protocol (scheme) of the URL for this output format. For example, `https://` or `webcal://`. Default is the scheme of the `baseURL` parameter in your site configuration, typically `https://`.
-
-rel
-: (`string`) If provided, you can assign this value to `rel` attributes in `link` elements when iterating over output formats in your templates. Default is `alternate`.
-
-root
-: (`bool`) If `true`, files will be published to the root of the publish directory. Default is `false`.
-
-ugly
-: (`bool`) If `true`, enables uglyURLs for this output format when `uglyURLs` is `false` in your site configuration. Default is `false`.
-
-weight
-: (`int`) When set to a non-zero value, Hugo uses the `weight` as the first criteria when sorting output formats, falling back to the name of the output format. Lighter items float to the top, while heavier items sink to the bottom. Hugo renders output formats sequentially based on the sort order.
-
-## Output formats for pages
-
-A `Page` in Hugo can be rendered to multiple _output formats_ on the file
-system.
-
-### Default output formats
-
-Every `Page` has a [`Kind`] attribute, and the default Output
-Formats are set based on that.
-
-{{< code-toggle config=outputs />}}
-
-### Customizing output formats
-
-This can be changed by defining an `outputs` list of output formats in either
-the `Page` front matter or in the site configuration (either for all sites or
-per language).
-
-Example from site configuration file:
-
-{{< code-toggle file=hugo >}}
-[outputs]
- home = ["html", "amp", "rss"]
- page = ["html"]
-{{ code-toggle >}}
-
-Note that in the above examples, the _output formats_ for `section`,
-`taxonomy` and `term` will stay at their default value `['html','rss']`.
-
-* The `outputs` definition is per page [`Kind`].
-* The names (e.g. `html`, `amp`) must match the `name` of a defined output format, and can be overridden per page in front matter.
-
-The following is an example of front matter in a content file that defines output formats for the rendered `Page`:
-
-{{< code-toggle file=content/example.md fm=true >}}
-title: Example
-outputs:
-- html
-- amp
-- json
-{{< /code-toggle >}}
-
-## List output formats
-
-Each `Page` object has both an [`OutputFormats`] method (all formats, including the current) and an [`AlternativeOutputFormats`] method, the latter of which is useful for creating a `link rel` list in your site's ``:
-
-[`OutputFormats`]: /methods/page/outputformats
-[`AlternativeOutputFormats`]: /methods/page/alternativeoutputformats
-
-```go-html-template
-{{ range .AlternativeOutputFormats -}}
-
-{{ end }}
-```
-
-## Link to output formats
-
-The [`Permalink`] and [`RelPermalink`] methods on a `Page` object return the first output format defined for that page (usually `HTML` if nothing else is defined). This is regardless of the template from which they are called.
-
-[`Permalink`]: /methods/page/permalink
-[`RelPermalink`]: /methods/page/relpermalink
-
-__from `single.json.json`:__
-```go-html-template
-{{ .RelPermalink }} → /that-page/
-{{ with .OutputFormats.Get "json" }}
- {{ .RelPermalink }} → /that-page/index.json
-{{ end }}
-```
-
-In order for them to return the output format of the current template file instead, the given output format should have its `permalinkable` setting set to true.
-
-**Same template file as above with json output format's `permalinkable` set to true:**
-
-```go-html-template
-{{ .RelPermalink }} → /that-page/index.json
-{{ with .OutputFormats.Get "html" }}
- {{ .RelPermalink }} → /that-page/
-{{ end }}
-```
-
-From content files, you can use the `ref` or `relref` shortcodes:
-
-```go-html-template
-[Neat]({{* ref "blog/neat.md" "amp" */>}})
-[Who]({{* relref "about.md#who" "amp" */>}})
-```
-
-## Templates for your output formats
-
-Each output format requires a corresponding template conforming to the [template lookup order](/templates/lookup-order/). Hugo considers both output format and suffix when selecting a template.
-
-For example, to generate a JSON file for the home page, the template with highest specificity is `layouts/index.json.json`.
-
-Hugo will now also detect the media type and output format of partials, if possible, and use that information to decide if the partial should be parsed as a plain text template or not.
-
-Hugo will look for the name given, so you can name it whatever you want. But if you want it treated as plain text, you should use the file suffix and, if needed, the name of the Output Format. The pattern is as follows:
-
-```go-html-template
-[partial name].[OutputFormat].[suffix]
-```
-
-The partial below is a plain text template . The output format is `csv`, and since this is the only output format with the suffix `csv`, we don't need to include the output format `name`):
-
-```go-html-template
-{{ partial "mytextpartial.csv" . }}
-```
-
-[site configuration]: /getting-started/configuration/
-[lookup order]: /templates/lookup-order/
-[media type]: https://en.wikipedia.org/wiki/Media_type
-[`kind`]: /methods/page/kind/
diff --git a/docs/content/en/templates/pagination.md b/docs/content/en/templates/pagination.md
index 2fb338278..018ec9271 100644
--- a/docs/content/en/templates/pagination.md
+++ b/docs/content/en/templates/pagination.md
@@ -1,14 +1,9 @@
---
title: Pagination
description: Split a list page into two or more subsets.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 190
-weight: 190
-toc: true
+weight: 160
aliases: [/extras/pagination,/doc/pagination/]
---
@@ -20,14 +15,13 @@ Displaying a large page collection on a list page is not user-friendly:
Improve usability by paginating `home`, `section`, `taxonomy`, and `term` pages.
-{{% note %}}
-The most common templating mistake related to pagination is invoking pagination more than once for a given list page. See the [caching](#caching) section below.
-{{% /note %}}
+> [!note]
+> The most common templating mistake related to pagination is invoking pagination more than once for a given list page. See the [caching](#caching) section below.
## Terminology
paginate
-: To split a [list page] into two or more subsets.
+: To split a [list page](g) into two or more subsets.
pagination
: The process of paginating a list page.
@@ -38,47 +32,9 @@ pager
paginator
: A collection of pagers.
-[list page]: /getting-started/glossary/#list-page
-
## Configuration
-Control pagination behavior in your site configuration. These are the default settings:
-
-{{< code-toggle file=hugo config=pagination />}}
-
-disableAliases
-: (`bool`) Whether to disable alias generation for the first pager. Default is `false`.
-
-pagerSize
-: (`int`) The number of pages per pager. Default is `10`.
-
-path
-: (`string`) The segment of each pager URL indicating that the target page is a pager. Default is `page`.
-
-With multilingual sites you can define the pagination behavior for each language:
-
-{{< code-toggle file=hugo >}}
-[languages.en]
-contentDir = 'content/en'
-languageCode = 'en-US'
-languageDirection = 'ltr'
-languageName = 'English'
-weight = 1
-[languages.en.pagination]
-disableAliases = true
-pagerSize = 10
-path = 'page'
-[languages.de]
-contentDir = 'content/de'
-languageCode = 'de-DE'
-languageDirection = 'ltr'
-languageName = 'Deutsch'
-weight = 2
-[languages.de.pagination]
-disableAliases = true
-pagerSize = 20
-path = 'blatt'
-{{< /code-toggle >}}
+See [configure pagination](/configuration/pagination).
## Methods
@@ -95,9 +51,6 @@ The `Paginate` method is more flexible, allowing you to:
By comparison, the `Paginator` method paginates the page collection passed into the template, and you cannot override the number of pages per pager.
-[`Paginate`]: /methods/page/paginate/
-[`Paginator`]: /methods/page/paginator/
-
## Examples
To paginate a list page using the `Paginate` method:
@@ -116,11 +69,10 @@ To paginate a list page using the `Paginate` method:
In the example above, we:
1. Build a page collection
-2. Sort the page collection by title
-3. Paginate the page collection, with 7 pages per pager
-4. Range over the paginated page collection, rendering a link to each page
-5. Call the embedded pagination template to create navigation links between pagers
-
+1. Sort the page collection by title
+1. Paginate the page collection, with 7 pages per pager
+1. Range over the paginated page collection, rendering a link to each page
+1. Call the embedded pagination template to create navigation links between pagers
To paginate a list page using the `Paginator` method:
@@ -135,27 +87,22 @@ To paginate a list page using the `Paginator` method:
In the example above, we:
1. Paginate the page collection passed into the template, with the default number of pages per pager
-2. Range over the paginated page collection, rendering a link to each page
-3. Call the embedded pagination template to create navigation links between pagers
+1. Range over the paginated page collection, rendering a link to each page
+1. Call the embedded pagination template to create navigation links between pagers
## Caching
-{{% note %}}
-The most common templating mistake related to pagination is invoking pagination more than once for a given list page.
-{{% /note %}}
+> [!note]
+> The most common templating mistake related to pagination is invoking pagination more than once for a given list page.
Regardless of pagination method, the initial invocation is cached and cannot be changed. If you invoke pagination more than once for a given list page, subsequent invocations use the cached result. This means that subsequent invocations will not behave as written.
When paginating conditionally, do not use the `compare.Conditional` function due to its eager evaluation of arguments. Use an `if-else` construct instead.
-[`compare.Conditional`]: /functions/compare/conditional/
-
## Grouping
Use pagination with any of the [grouping methods]. For example:
-[grouping methods]: /quick-reference/page-collections/#group
-
```go-html-template
{{ $pages := where site.RegularPages "Type" "posts" }}
{{ $paginator := .Paginate ($pages.GroupByDate "Jan 2006") }}
@@ -170,8 +117,6 @@ Use pagination with any of the [grouping methods]. For example:
{{ template "_internal/pagination.html" . }}
```
-[grouping methods]: /quick-reference/page-collections/#group
-
## Navigation
As shown in the examples above, the easiest way to add navigation between pagers is with Hugo's embedded pagination template:
@@ -192,18 +137,14 @@ The `terse` format has fewer controls and page slots, consuming less space when
{{ template "_internal/pagination.html" (dict "page" . "format" "terse") }}
```
-{{% note %}}
-To override Hugo's embedded pagination template, copy the [source code] to a file with the same name in the layouts/partials directory, then call it from your templates using the [`partial`] function:
-
-`{{ partial "pagination.html" . }}`
-
-[`partial`]: /functions/partials/include/
-[source code]: {{% eturl pagination %}}
-{{% /note %}}
+> [!note]
+> To override Hugo's embedded pagination template, copy the [source code] to a file with the same name in the `layouts/partials` directory, then call it from your templates using the [`partial`] function:
+>
+> `{{ partial "pagination.html" . }}`
Create custom navigation components using any of the `Pager` methods:
-{{< list-pages-in-section path=/methods/pager >}}
+{{% list-pages-in-section path=/methods/pager %}}
## Structure
@@ -291,3 +232,10 @@ public/
│ └── index.html
└── index.html
```
+
+[`Paginate`]: /methods/page/paginate/
+[`Paginator`]: /methods/page/paginator/
+[`partial`]: /functions/partials/include/
+[grouping methods]: /quick-reference/page-collections/#group
+[grouping methods]: /quick-reference/page-collections/#group
+[source code]: {{% eturl pagination %}}
diff --git a/docs/content/en/templates/partial.md b/docs/content/en/templates/partial.md
index 30055e7e3..7ff2d9594 100644
--- a/docs/content/en/templates/partial.md
+++ b/docs/content/en/templates/partial.md
@@ -1,14 +1,9 @@
---
title: Partial templates
description: Partials are smaller, context-aware components in your list and page templates that can be used economically to keep your templating DRY.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 110
-weight: 110
-toc: true
+weight: 100
aliases: [/templates/partials/,/layout/chrome/]
---
@@ -27,8 +22,7 @@ layouts/
├── head/
│ ├── favicons.html
│ ├── metadata.html
- │ ├── prerender.html
- │ └── twitter.html
+ │ └── prerender.html
└── header/
├── site-header.html
└── site-nav.html
@@ -40,13 +34,11 @@ All partials are called within your templates using the following pattern:
{{ partial "/.html" . }}
```
-{{% note %}}
-One of the most common mistakes with new Hugo users is failing to pass a context to the partial call. In the pattern above, note how "the dot" (`.`) is required as the second argument to give the partial context. You can read more about "the dot" in the [Hugo templating introduction](/templates/introduction/#context).
-{{% /note %}}
+> [!note]
+> One of the most common mistakes with new Hugo users is failing to pass a context to the partial call. In the pattern above, note how "the dot" (`.`) is required as the second argument to give the partial context. You can read more about "the dot" in the [Hugo templating introduction](/templates/introduction/#context).
-{{% note %}}
-`` including `baseof` is reserved. ([#5373](https://github.com/gohugoio/hugo/issues/5373))
-{{% /note %}}
+> [!note]
+> Do not include the word "baseof" when naming partial templates. The word "baseof" is reserved for base templates.
As shown in the above example directory structure, you can nest your directories within `partials` for better source organization. You only need to call the nested partial's path relative to the `partials` directory:
@@ -57,7 +49,7 @@ As shown in the above example directory structure, you can nest your directories
### Variable scoping
-The second argument in a partial call is the variable being passed down. The above examples are passing the `.`, which tells the template receiving the partial to apply the current [context][context].
+The second argument in a partial call is the variable being passed down. The above examples are passing the dot (`.`), which tells the template receiving the partial to apply the current [context][context].
This means the partial will *only* be able to access those variables. The partial is isolated and cannot access the outer scope. From within the partial, `$.Var` is equivalent to `.Var`.
@@ -100,9 +92,8 @@ In addition to outputting markup, partials can be used to return a value of any
{{ end }}
```
-{{% note %}}
-Only one `return` statement is allowed per partial file.
-{{% /note %}}
+> [!note]
+> Only one `return` statement is allowed per partial file.
## Inline partials
@@ -127,7 +118,7 @@ The `partialCached` template function provides significant performance gains for
The following `header.html` partial template is used for [spf13.com](https://spf13.com/):
-{{< code file=layouts/partials/header.html >}}
+```go-html-template {file="layouts/partials/header.html"}
@@ -142,17 +133,16 @@ The following `header.html` partial template is used for [spf13.com](https://spf
{{ partial "head_includes.html" . }}
-{{< /code >}}
+```
-{{% note %}}
-The `header.html` example partial was built before the introduction of block templates to Hugo. Read more on [base templates and blocks](/templates/base/) for defining the outer chrome or shell of your master templates (i.e., your site's head, header, and footer). You can even combine blocks and partials for added flexibility.
-{{% /note %}}
+> [!note]
+> The `header.html` example partial was built before the introduction of block templates to Hugo. Read more on [base templates and blocks](/templates/base/) for defining the outer chrome or shell of your master templates (i.e., your site's head, header, and footer). You can even combine blocks and partials for added flexibility.
### `footer.html`
The following `footer.html` partial template is used for [spf13.com](https://spf13.com/):
-{{< code file=layouts/partials/footer.html >}}
+```go-html-template {file="layouts/partials/footer.html"}
-{{< /code >}}
+```
[context]: /templates/introduction/
-[customize]: /hugo-modules/theme-components/
-[lookup order]: /templates/lookup-order/
[partialcached]: /functions/partials/includecached/
-[themes]: /themes/
diff --git a/docs/content/en/templates/robots.md b/docs/content/en/templates/robots.md
index ee0755794..2d412d775 100644
--- a/docs/content/en/templates/robots.md
+++ b/docs/content/en/templates/robots.md
@@ -2,13 +2,9 @@
title: robots.txt template
linkTitle: robots.txt templates
description: Hugo can generate a customized robots.txt in the same way as any other template.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 170
-weight: 170
+weight: 180
aliases: [/extras/robots-txt/]
---
@@ -20,8 +16,6 @@ enableRobotsTXT = true
By default, Hugo generates robots.txt using an [embedded template].
-[embedded template]: {{% eturl robots %}}
-
```text
User-agent: *
```
@@ -33,28 +27,27 @@ Search engines that honor the Robots Exclusion Protocol will interpret this as p
You may overwrite the internal template with a custom template. Hugo selects the template using this lookup order:
1. `/layouts/robots.txt`
-2. `/themes//layouts/robots.txt`
+1. `/themes//layouts/robots.txt`
## robots.txt template example
-{{< code file=layouts/robots.txt >}}
+```text {file="layouts/robots.txt"}
User-agent: *
{{ range .Pages }}
Disallow: {{ .RelPermalink }}
{{ end }}
-{{< /code >}}
+```
This template creates a robots.txt file with a `Disallow` directive for each page on the site. Search engines that honor the Robots Exclusion Protocol will not crawl any page on the site.
-{{% note %}}
-To create a robots.txt file without using a template:
-
-1. Set `enableRobotsTXT` to `false` in the site configuration.
-2. Create a robots.txt file in the `static` directory.
-
-Remember that Hugo copies everything in the [static directory][static] to the root of `publishDir` (typically `public`) when you build your site.
+> [!note]
+> To create a robots.txt file without using a template:
+>
+> 1. Set `enableRobotsTXT` to `false` in the site configuration.
+> 1. Create a robots.txt file in the `static` directory.
+>
+> Remember that Hugo copies everything in the [`static` directory][static] to the root of `publishDir` (typically `public`) when you build your site.
+[embedded template]: {{% eturl robots %}}
+[site configuration]: /configuration/
[static]: /getting-started/directory-structure/
-{{% /note %}}
-
-[site configuration]: /getting-started/configuration/
diff --git a/docs/content/en/templates/rss.md b/docs/content/en/templates/rss.md
index 0967bb338..f387a71e3 100644
--- a/docs/content/en/templates/rss.md
+++ b/docs/content/en/templates/rss.md
@@ -1,14 +1,9 @@
---
title: RSS templates
description: Use the embedded RSS template, or create your own.
-categories: [templates]
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 150
-weight: 150
-toc: true
+weight: 140
---
## Configuration
@@ -23,9 +18,7 @@ taxonomy = ['html']
term = ['html']
{{< /code-toggle >}}
-To disable feed generation for all [page kinds]:
-
-[page kinds]: /getting-started/glossary/#page-kind
+To disable feed generation for all [page kinds](g):
{{< code-toggle file=hugo >}}
disableKinds = ['rss']
@@ -54,7 +47,7 @@ email = 'jdoe@example.org'
To include a feed reference in the `head` element of your rendered pages, place this within the `head` element of your templates:
```go-html-template
-{{ with .OutputFormats.Get "rss" -}}
+{{ with .OutputFormats.Get "rss" }}
{{ printf `` .Rel .MediaType.Type .Permalink site.Title | safeHTML }}
{{ end }}
```
@@ -69,9 +62,6 @@ Hugo will render this to:
Override Hugo's [embedded RSS template] by creating one or more of your own, following the naming conventions as shown in the [template lookup order].
-[embedded RSS template]: {{% eturl rss %}}
-[template lookup order]: /templates/lookup-order/#rss-templates
-
For example, to use different templates for home, section, taxonomy, and term pages:
```text
@@ -84,3 +74,6 @@ layouts/
```
RSS templates receive the `.Page` and `.Site` objects in context.
+
+[embedded RSS template]: {{% eturl rss %}}
+[template lookup order]: /templates/lookup-order/#rss-templates
diff --git a/docs/content/en/templates/section.md b/docs/content/en/templates/section.md
index 28d931534..8bc0f9dab 100644
--- a/docs/content/en/templates/section.md
+++ b/docs/content/en/templates/section.md
@@ -1,14 +1,9 @@
---
title: Section templates
-description: Use section templates to list members of a section.
-categories: [templates]
+description: Create a section template to list its members.
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 80
-weight: 80
-toc: true
+weight: 70
aliases: [/templates/sections/,/templates/section-templates/]
---
@@ -22,7 +17,7 @@ See [Template Lookup](/templates/lookup-order/).
## Example: creating a default section template
-{{< code file=layouts/_default/section.html >}}
+```go-html-template {file="layouts/_default/section.html"}
{{ define "main" }}
{{ .Content }}
@@ -37,7 +32,7 @@ See [Template Lookup](/templates/lookup-order/).
{{ template "_internal/pagination.html" . }}
{{ end }}
-{{< /code >}}
+```
### Example: using `.Site.GetPage`
diff --git a/docs/content/en/templates/shortcode.md b/docs/content/en/templates/shortcode.md
index 5587c7479..3ed573651 100644
--- a/docs/content/en/templates/shortcode.md
+++ b/docs/content/en/templates/shortcode.md
@@ -1,349 +1,279 @@
---
-title: Create your own shortcodes
-linkTitle: Shortcode templates
-description: You can extend Hugo's embedded shortcodes by creating your own using the same templating syntax as that for single and list pages.
-categories: [templates]
+title: Shortcode templates
+description: Create custom shortcodes to simplify and standardize content creation.
+categories: []
keywords: []
-menu:
- docs:
- parent: templates
- weight: 130
-weight: 130
+weight: 120
aliases: [/templates/shortcode-templates/]
-toc: true
---
-Shortcodes are a means to consolidate templating into small, reusable snippets that you can embed directly inside your content.
+> [!note]
+> Before creating custom shortcodes, please review the [shortcodes] page in the [content management] section. Understanding the usage details will help you design and create better templates.
-{{% note %}}
-Hugo also ships with embedded shortcodes for common use cases. (See [Content Management: Shortcodes](/content-management/shortcodes/).)
-{{% /note %}}
+## Introduction
-## Create custom shortcodes
+Hugo provides [embedded shortcodes] for many common tasks, but you'll likely need to create your own for more specific needs. Some examples of custom shortcodes you might develop include:
-Hugo's embedded shortcodes cover many common, but not all, use cases. Luckily, Hugo provides the ability to easily create custom shortcodes to meet your website's needs.
+- Audio players
+- Video players
+- Image galleries
+- Diagrams
+- Maps
+- Tables
+- And many other custom elements
-{{< youtube Eu4zSaKOY4A >}}
+## Directory structure
-### File location
+Create shortcode templates within the `layouts/shortcodes` directory, either at its root or organized into subdirectories.
-To create a shortcode, place an HTML template in the `layouts/shortcodes` directory. Consider the file name carefully since the shortcode name will mirror that of the file but without the `.html` extension. For example, `layouts/shortcodes/myshortcode.html` will be called with either `{{* myshortcode /*/>}}` or `{{%/* myshortcode /*/%}}`.
-
-You can organize your shortcodes in subdirectories, e.g. in `layouts/shortcodes/boxes`. These shortcodes would then be accessible with their relative path, e.g:
-
-```go-html-template
-{{* boxes/square */>}}
+```text
+layouts/
+└── shortcodes/
+ ├── diagrams/
+ │ ├── kroki.html
+ │ └── plotly.html
+ ├── media/
+ │ ├── audio.html
+ │ ├── gallery.html
+ │ └── video.html
+ ├── capture.html
+ ├── column.html
+ ├── include.html
+ └── row.html
```
-Note the forward slash.
+When calling a shortcode in a subdirectory, specify its path relative to the `shortcode` directory, excluding the file extension.
-### Template lookup order
+```text
+{{* media/audio path=/audio/podcast/episode-42.mp3 */>}}
+```
+
+## Lookup order
Hugo selects shortcode templates based on the shortcode name, the current output format, and the current language. The examples below are sorted by specificity in descending order. The least specific path is at the bottom of the list.
Shortcode name|Output format|Language|Template path
:--|:--|:--|:--
-foo|html|en|layouts/shortcodes/foo.en.html
-foo|html|en|layouts/shortcodes/foo.html.html
-foo|html|en|layouts/shortcodes/foo.html
-foo|html|en|layouts/shortcodes/foo.html.en.html
+foo|html|en|`layouts/shortcodes/foo.en.html`
+foo|html|en|`layouts/shortcodes/foo.html.html`
+foo|html|en|`layouts/shortcodes/foo.html`
+foo|html|en|`layouts/shortcodes/foo.html.en.html`
Shortcode name|Output format|Language|Template path
:--|:--|:--|:--
-foo|rss|en|layouts/shortcodes/foo.en.xml
-foo|rss|en|layouts/shortcodes/foo.rss.xml
-foo|rss|en|layouts/shortcodes/foo.en.html
-foo|rss|en|layouts/shortcodes/foo.rss.en.xml
-foo|rss|en|layouts/shortcodes/foo.xml
-foo|rss|en|layouts/shortcodes/foo.html.en.html
-foo|rss|en|layouts/shortcodes/foo.html.html
-foo|rss|en|layouts/shortcodes/foo.html
+foo|json|en|`layouts/shortcodes/foo.en.json`
+foo|json|en|`layouts/shortcodes/foo.json`
+foo|json|en|`layouts/shortcodes/foo.json.json`
+foo|json|en|`layouts/shortcodes/foo.json.en.json`
-Note that templates provided by a theme or module always take precedence.
+## Methods
-### Positional vs. named arguments
+Use these methods in your shortcode templates. Refer to each methods's documentation for details and examples.
-You can create shortcodes using the following types of arguments:
+{{% list-pages-in-section path=/methods/shortcode %}}
-* Positional arguments
-* Named arguments
-* Positional *or* named arguments
+## Examples
-In shortcodes with positional arguments, the order of the arguments is important. If a shortcode has a single required value, positional arguments require less typing from content authors.
+These examples range in complexity from simple to moderately advanced, with some simplified for clarity.
-For more complex layouts with multiple or optional arguments, named arguments work best. While less terse, named arguments require less memorization from a content author and can be added in a shortcode declaration in any order.
+### Insert year
-Allowing both types of arguments is useful for complex layouts where you want to set default values that can be easily overridden by users.
+Create a shortcode to insert the current year:
-### Access arguments
-
-All shortcode arguments can be accessed via the `.Get` method. Whether you pass a string or a number to the `.Get` method depends on whether you are accessing a named or positional argument, respectively.
-
-To access an argument by name, use the `.Get` method followed by the named argument as a quoted string:
-
-```go-html-template
-{{ .Get "class" }}
+```go-html-template {file="layouts/shortcodes/year.html"}
+{{- now.Format "2006" -}}
```
-To access an argument by position, use the `.Get` followed by a numeric position, keeping in mind that positional arguments are zero-indexed:
+Then call the shortcode from within your markup:
-```go-html-template
-{{ .Get 0 }}
+```text {file="content/example.md"}
+This is {{* year */>}}, and look at how far we've come.
```
-For the second position, you would just use:
+This shortcode can be used inline or as a block on its own line. If a shortcode might be used inline, remove the surrounding [whitespace] by using [template action](g) delimiters with hyphens.
-```go-html-template
-{{ .Get 1 }}
+### Insert image
+
+This example assumes the following content structure, where `content/example/index.md` is a [page bundle](g) containing one or more [page resources](g).
+
+```text
+content/
+├── example/
+│ ├── a.jpg
+│ └── index.md
+└── _index.md
```
-`with` is great when the output depends on a argument being set:
+Create a shortcode to capture an image as a page resource, resize it to the given width, convert it to the WebP format, and add an `alt` attribute:
-```go-html-template
-{{ with .Get "class" }} class="{{ . }}"{{ end }}
+```go-html-template {file="layouts/shortcodes/image.html"}
+{{- with .Page.Resources.Get (.Get "path") }}
+ {{- with .Process (printf "resize %dx wepb" ($.Get "width")) -}}
+
+ {{- end }}
+{{- end -}}
```
-`.Get` can also be used to check if a argument has been provided. This is
-most helpful when the condition depends on either of the values, or both:
+Then call the shortcode from within your markup:
-```go-html-template
-{{ if or (.Get "title") (.Get "alt") }} alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "title" }}{{ end }}"{{ end }}
+```text {file="content/example/index.md"}
+{{* image path=a.jpg width=300 alt="A white kitten" */>}}
```
-#### `.Inner`
+The example above uses:
-The `.Inner` method returns the content between the opening and closing shortcode tags. To check if `.Inner` returns anything other than whitespace:
+- The [`with`] statement to rebind the [context](g) after each successful operation
+- The [`Get`] method to retrieve arguments by name
+- The `$` to access the template context
-```go-html-template
-{{ if strings.ContainsNonSpace .Inner }}
- Inner is not empty
-{{ end }}
+> [!note]
+> Make sure that you thoroughly understand the concept of context. The most common templating errors made by new users relate to context.
+>
+> Read more about context in the [introduction to templating].
+
+### Insert image with error handling
+
+The previous example, while functional, silently fails if the image is missing, and does not gracefully exit if a required argument is missing. We'll add error handling to address these issues:
+
+```go-html-template {file="layouts/shortcodes/image.html"}
+{{- with .Get "path" }}
+ {{- with $r := $.Page.Resources.Get ($.Get "path") }}
+ {{- with $.Get "width" }}
+ {{- with $r.Process (printf "resize %dx wepb" ($.Get "width" )) }}
+ {{- $alt := or ($.Get "alt") "" -}}
+
+ {{- end }}
+ {{- else }}
+ {{- errorf "The %q shortcode requires a 'width' argument: see %s" $.Name $.Position }}
+ {{- end }}
+ {{- else }}
+ {{- warnf "The %q shortcode was unable to find %s: see %s" $.Name ($.Get "path") $.Position }}
+ {{- end }}
+{{- else }}
+ {{- errorf "The %q shortcode requires a 'path' argument: see %s" .Name .Position }}
+{{- end -}}
```
-{{% note %}}
-Any shortcode that calls the `.Inner` method must be closed or self-closed. To call a shortcode using the self-closing syntax.
+This template throws an error and gracefully fails the build if the author neglected to provide a `path` or `width` argument, and it emits a warning if it cannot find the image at the specified path. If the author does not provide an `alt` argument, the `alt` attribute is set to an empty string.
-```go-html-template
-{{* innershortcode /*/>}}
+The [`Name`] and [`Position`] methods provide helpful context for errors and warnings. For example, a missing `width` argument causes the shortcode to throw this error:
+
+```text
+ERROR The "image" shortcode requires a 'width' argument: see "/home/user/project/content/example/index.md:7:1"
```
-{{% /note %}}
+### Positional arguments
-#### `.Params`
+Shortcode arguments can be [named or positional]. We used named arguments previously; let's explore positional arguments. Here's the named argument version of our example:
-The `.Params` method in shortcodes returns the arguments passed to the shortcode for more complicated use cases. You can also access higher-scoped arguments with the following logic:
-
-$.Params
-: these are the arguments passed directly into the shortcode declaration (e.g., a YouTube video ID)
-
-$.Page.Params
-: refers to the page's parameters; the "page" in this case refers to the content file in which the shortcode is declared (e.g., a `shortcode_color` field in a content's front matter could be accessed via `$.Page.Params.shortcode_color`).
-
-$.Site.Params
-: refers to parameters defined in your site configuration.
-
-#### `.IsNamedParams`
-
-The `.IsNamedParams` method checks whether the shortcode declaration uses named arguments and returns a boolean value.
-
-For example, you could create an `image` shortcode that can take either a `src` named argument or the first positional argument, depending on the preference of the content's author. Let's assume the `image` shortcode is called as follows:
-
-```go-html-template
-{{* image src="images/my-image.jpg" */>}}
+```text {file="content/example/index.md"}
+{{* image path=a.jpg width=300 alt="A white kitten" */>}}
```
-You could then include the following as part of your shortcode templating:
+Here's how to call it with positional arguments:
-```go-html-template
-{{ if .IsNamedParams }}
-
-{{ else }}
-
-{{ end }}
+```text {file="content/example/index.md"}
+{{* image a.jpg 300 "A white kitten" */>}}
```
-See the [example Vimeo shortcode][vimeoexample] below for `.IsNamedParams` in action.
+Using the `Get` method with zero-indexed keys, we'll initialize variables with descriptive names in our template:
-{{% note %}}
-While you can create shortcode templates that accept both positional and named arguments, you *cannot* declare shortcodes in content with a mix of argument types. Therefore, a shortcode declared like `{{* image src="images/my-image.jpg" "This is my alt text" */>}}` will return an error.
-{{% /note %}}
-
-Shortcodes can also be nested. In a nested shortcode, you can access the parent shortcode context with the [`.Parent`] shortcode method. This can be very useful for inheritance from the root.
-
-### Checking for existence
-
-You can check if a specific shortcode is used on a page by calling `.HasShortcode` in that page template, providing the name of the shortcode. This is useful when you want to include specific scripts or styles in the header that are only used by that shortcode.
-
-## Custom shortcode examples
-
-The following are examples of the different types of shortcodes you can create via shortcode template files in `/layouts/shortcodes`.
-
-### Single-word example: `year`
-
-Let's assume you would like to keep mentions of your copyright year current in your content files without having to continually review your Markdown. Your goal is to be able to call the shortcode as follows:
-
-```go-html-template
-{{* year */>}}
+```go-html-template {file="layouts/shortcodes/image.html"}
+{{ $path := .Get 0 }}
+{{ $width := .Get 1 }}
+{{ $alt := .Get 2 }}
```
-{{< code file=layouts/shortcodes/year.html >}}
-{{ now.Format "2006" }}
-{{< /code >}}
+> [!note]
+> Positional arguments work well for frequently used shortcodes with one or two arguments. Since you'll use them often, the argument order will be easy to remember. For less frequently used shortcodes, or those with more than two arguments, named arguments improve readability and reduce the chance of errors.
-### Single positional example: `youtube`
+### Named and positional arguments
-Embedded videos are a common addition to Markdown content. The following is the code used by [Hugo's built-in YouTube shortcode][youtubeshortcode]:
+You can create a shortcode that will accept both named and positional arguments, but not at the same time. Use the [`IsNamedParams`] method to determine whether the shortcode call used named or positional arguments:
-```go-html-template
-{{* youtube 09jf3ow9jfw */>}}
+```go-html-template {file="layouts/shortcodes/image.html"}
+{{ $path := cond (.IsNamedParams) (.Get "path") (.Get 0) }}
+{{ $width := cond (.IsNamedParams) (.Get "width") (.Get 1) }}
+{{ $alt := cond (.IsNamedParams) (.Get "alt") (.Get 2) }}
```
-Would load the template at `/layouts/shortcodes/youtube.html`:
+This example uses the `cond` alias for the [`compare.Conditional`] function to get the argument by name if `IsNamedParams` returns `true`, otherwise get the argument by position.
-{{< code file=layouts/shortcodes/youtube.html >}}
-
-
-
+### 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"}
+{{* image path=a.jpg width=300 alt="A white kitten" */>}}
+```
+
+```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"}
+{{* image a.jpg 300 "A white kitten" */>}}
+```
+
+```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"}
+{{* contrived title="A Contrived Example" */>}}
+This is a **bold** word, and this is an _emphasized_ word.
+{{* /contrived */>}}
+```
+
+```go-html-template {file="layouts/shortcodes/contrived.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 >}}
-{{* img src="/media/spf13.jpg" title="Steve Francia" */>}}
-{{< /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") }}
{{ end }}
-
- {{ end }}
-
-
-{{< /code >}}
-
-Would be rendered as:
-
-{{< code file=img-output.html >}}
-
-
-
-
Steve Francia
-
-
-{{< /code >}}
-
-### Single flexible example: `vimeo`
-
-```go-html-template
-{{* vimeo 49718712 */>}}
-{{* vimeo id="49718712" class="flex-video" */>}}
```
-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 >}}
-{{* highlight html */>}}
-
- This HTML
-
-{{* /highlight */>}}
-{{< /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"}
{{* gallery class="content-gallery" */>}}
{{* img src="/images/one.jpg" */>}}
{{* img src="/images/two.jpg" */>}}
@@ -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"}
+{{* audio src=/audio/test.mp3 */>}}
```
-## 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
- {{* time.inline */>}}{{ now }}{{* /time.inline */>}}
- ```
-
-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
-{{* time.inline /*/>}}
+```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']
{{ code-toggle >}}
-[`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]
{{ 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
{{ 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
{{ 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
{{ 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:
{{ 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/']
---
-[](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 }}
{{ 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
{{ 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
{{ 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
{{ 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 }}
-{{< /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 >}}
-{{* audio src="audio/test.mp3" */>}}
-{{< /code >}}
+```text {file="content/example.md"}
+{{* audio src=/audio/test.mp3 */>}}
+```
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\
+`{{* shortcode */>}}` 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\
-`{{* shortcode */>}}` 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" }}
+
+
+
+{{- 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 . }}
+
+ {{- .Text -}}
+
+ {{- end }}
+
+ {{- end }}
+
+
+ {{- range .TBody }}
+
+ {{- range . }}
+
+ {{- .Text -}}
+
+ {{- end }}
+
+ {{- end }}
+
+
+
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 @@
+
+ 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.
+
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
-
+
Processed
-
+
{{- else -}}
-
+
{{- 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 . }}
+
+
+
+ {{- 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 }}
+
+ 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 }}
+
+{{ 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
\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
+---
+
+
+[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";for(var o=0;o",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='",e}},Qa={\u00EE:"\u0131\u0302",\u00EF:"\u0131\u0308",\u00ED:"\u0131\u0301",\u00EC:"\u0131\u0300"},u0=class{constructor(e,t,a,n,s,o,h,c){this.text=void 0,this.height=void 0,this.depth=void 0,this.italic=void 0,this.skew=void 0,this.width=void 0,this.maxFontSize=void 0,this.classes=void 0,this.style=void 0,this.text=e,this.height=t||0,this.depth=a||0,this.italic=n||0,this.skew=s||0,this.width=o||0,this.classes=h||[],this.style=c||{},this.maxFontSize=0;var p=Ha(this.text.charCodeAt(0));p&&this.classes.push(p+"_fallback"),/[îïíì]/.test(this.text)&&(this.text=Qa[this.text])}hasClass(e){return N.contains(this.classes,e)}toNode(){var e=document.createTextNode(this.text),t=null;this.italic>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";for(var a=0;a",e}},S0=class{constructor(e,t){this.pathName=void 0,this.alternate=void 0,this.pathName=e,this.alternate=t}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"path");return this.alternate?t.setAttribute("d",this.alternate):t.setAttribute("d",$t[this.pathName]),t}toMarkup(){return this.alternate?'':''}},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="",e}};function jt(r){if(r instanceof u0)return r;throw new Error("Expected symbolNode but got "+String(r)+".")}function _a(r){if(r instanceof W0)return r;throw new Error("Expected span 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='",e}},a1={\u00EE:"\u0131\u0302",\u00EF:"\u0131\u0308",\u00ED:"\u0131\u0301",\u00EC:"\u0131\u0300"},c0=class{constructor(e,t,a,n,s,l,h,c){this.text=void 0,this.height=void 0,this.depth=void 0,this.italic=void 0,this.skew=void 0,this.width=void 0,this.maxFontSize=void 0,this.classes=void 0,this.style=void 0,this.text=e,this.height=t||0,this.depth=a||0,this.italic=n||0,this.skew=s||0,this.width=l||0,this.classes=h||[],this.style=c||{},this.maxFontSize=0;var f=Ha(this.text.charCodeAt(0));f&&this.classes.push(f+"_fallback"),/[îïíì]/.test(this.text)&&(this.text=a1[this.text])}hasClass(e){return O.contains(this.classes,e)}toNode(){var e=document.createTextNode(this.text),t=null;this.italic>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";for(var a=0;a",e}},A0=class{constructor(e,t){this.pathName=void 0,this.alternate=void 0,this.pathName=e,this.alternate=t}toNode(){var e="http://www.w3.org/2000/svg",t=document.createElementNS(e,"path");return this.alternate?t.setAttribute("d",this.alternate):t.setAttribute("d",Yt[this.pathName]),t}toMarkup(){return this.alternate?'':''}},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="",e}};function Jt(r){if(r instanceof c0)return r;throw new Error("Expected symbolNode but got "+String(r)+".")}function n1(r){if(r instanceof Z0)return r;throw new Error("Expected span 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: |