| Click the "New issue" button. |
+| Microsoft | Edge | Blink | | Go to "Help > Send Feedback" from the browser |
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
-fits with the scope and aims of the project. It's up to *you* to make a strong
+fits with the scope and aims of the project. It's up to _you_ to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 4675f70..98e45c5 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -31,7 +31,7 @@
-* https://deploy-preview-{your pr number}--twbs-bootstrap.netlify.app/
+-
### Related issues
diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml
new file mode 100644
index 0000000..9578772
--- /dev/null
+++ b/.github/codeql/codeql-config.yml
@@ -0,0 +1,3 @@
+name: "CodeQL config"
+paths-ignore:
+ - dist
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 29135b4..f54ba89 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,20 +1,5 @@
version: 2
updates:
- - package-ecosystem: npm
- directory: "/"
- schedule:
- interval: weekly
- day: tuesday
- time: "12:00"
- timezone: Europe/Athens
- open-pull-requests-limit: 10
- reviewers:
- - XhmikosR
- labels:
- - dependencies
- - v5
- versioning-strategy: increase
- rebase-strategy: disabled
- package-ecosystem: "github-actions"
directory: "/"
schedule:
@@ -22,3 +7,17 @@ updates:
day: tuesday
time: "12:00"
timezone: Europe/Athens
+ - package-ecosystem: npm
+ directory: "/"
+ reviewers:
+ - XhmikosR
+ labels:
+ - dependencies
+ - v5
+ schedule:
+ interval: weekly
+ day: tuesday
+ time: "12:00"
+ timezone: Europe/Athens
+ versioning-strategy: increase
+ rebase-strategy: disabled
diff --git a/.github/workflows/browserstack.yml b/.github/workflows/browserstack.yml
index 425c566..e545d62 100644
--- a/.github/workflows/browserstack.yml
+++ b/.github/workflows/browserstack.yml
@@ -2,21 +2,29 @@ name: BrowserStack
on:
push:
+ branches:
+ - "**"
+ - "!dependabot/**"
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
browserstack:
runs-on: ubuntu-latest
- if: github.repository == 'twbs/bootstrap' && (!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]'))
+ if: github.repository == 'twbs/bootstrap'
timeout-minutes: 30
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
diff --git a/.github/workflows/bundlewatch.yml b/.github/workflows/bundlewatch.yml
index d1a1747..c02a37e 100644
--- a/.github/workflows/bundlewatch.yml
+++ b/.github/workflows/bundlewatch.yml
@@ -2,14 +2,17 @@ name: Bundlewatch
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
bundlewatch:
@@ -18,6 +21,8 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
diff --git a/.github/workflows/calibreapp-image-actions.yml b/.github/workflows/calibreapp-image-actions.yml
index e23f562..21df1f6 100644
--- a/.github/workflows/calibreapp-image-actions.yml
+++ b/.github/workflows/calibreapp-image-actions.yml
@@ -17,6 +17,8 @@ jobs:
steps:
- name: Checkout Repo
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Compress Images
uses: calibreapp/image-actions@1.1.0
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 70be056..b1780ee 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -7,13 +7,12 @@ on:
- v4-dev
- "!dependabot/**"
pull_request:
- # The branches below must be a subset of the branches above
branches:
- main
- v4-dev
- "!dependabot/**"
schedule:
- - cron: "0 2 * * 5"
+ - cron: "0 2 * * 4"
workflow_dispatch:
jobs:
@@ -21,18 +20,25 @@ jobs:
name: Analyze
runs-on: ubuntu-latest
permissions:
- actions: read
- contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
+ config-file: ./.github/codeql/codeql-config.yml
languages: "javascript"
+ queries: +security-and-quality
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:javascript"
diff --git a/.github/workflows/cspell.yml b/.github/workflows/cspell.yml
index 3751ad3..11788e3 100644
--- a/.github/workflows/cspell.yml
+++ b/.github/workflows/cspell.yml
@@ -2,22 +2,30 @@ name: cspell
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+
+permissions:
+ contents: read
jobs:
cspell:
+ permissions:
+ # allow streetsidesoftware/cspell-action to fetch files for commits and PRs
+ contents: read
+ pull-requests: read
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Run cspell
uses: streetsidesoftware/cspell-action@v2
diff --git a/.github/workflows/css.yml b/.github/workflows/css.yml
index 857a567..66112a9 100644
--- a/.github/workflows/css.yml
+++ b/.github/workflows/css.yml
@@ -2,14 +2,17 @@ name: CSS
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
css:
@@ -18,6 +21,8 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
@@ -30,3 +35,6 @@ jobs:
- name: Build CSS
run: npm run css
+
+ - name: Run CSS tests
+ run: npm run css-test
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index f33413e..2a684f6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -2,14 +2,17 @@ name: Docs
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
docs:
@@ -18,6 +21,8 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml
index b251cd7..b5000d8 100644
--- a/.github/workflows/issue-close-require.yml
+++ b/.github/workflows/issue-close-require.yml
@@ -4,8 +4,15 @@ on:
schedule:
- cron: "0 0 * * *"
+permissions:
+ contents: read
+
jobs:
issue-close-require:
+ permissions:
+ # allow actions-cool/issues-helper to update issues and PRs
+ issues: write
+ pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'twbs/bootstrap'
steps:
diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml
index fac5849..584879d 100644
--- a/.github/workflows/issue-labeled.yml
+++ b/.github/workflows/issue-labeled.yml
@@ -4,8 +4,15 @@ on:
issues:
types: [labeled]
+permissions:
+ contents: read
+
jobs:
issue-labeled:
+ permissions:
+ # allow actions-cool/issues-helper to update issues and PRs
+ issues: write
+ pull-requests: write
if: github.repository == 'twbs/bootstrap'
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml
index 82616c5..805b1b7 100644
--- a/.github/workflows/js.yml
+++ b/.github/workflows/js.yml
@@ -2,23 +2,32 @@ name: JS Tests
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
run:
+ permissions:
+ # allow coverallsapp/github-action to create new checks issues and fetch code
+ checks: write
+ contents: read
name: JS Tests
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
@@ -36,7 +45,7 @@ jobs:
run: npm run js-test
- name: Run Coveralls
- uses: coverallsapp/github-action@1.1.3
+ uses: coverallsapp/github-action@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
path-to-lcov: "./js/coverage/lcov.info"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 816694e..fd62b41 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -2,14 +2,17 @@ name: Lint
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
lint:
@@ -18,6 +21,8 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
diff --git a/.github/workflows/node-sass.yml b/.github/workflows/node-sass.yml
index 465cee4..c558e44 100644
--- a/.github/workflows/node-sass.yml
+++ b/.github/workflows/node-sass.yml
@@ -2,14 +2,17 @@ name: CSS (node-sass)
on:
push:
- branches-ignore:
- - "dependabot/**"
+ branches:
+ - main
pull_request:
workflow_dispatch:
env:
FORCE_COLOR: 2
- NODE: 16
+ NODE: 18
+
+permissions:
+ contents: read
jobs:
css:
@@ -18,6 +21,8 @@ jobs:
steps:
- name: Clone repository
uses: actions/checkout@v3
+ with:
+ persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v3
@@ -29,3 +34,16 @@ jobs:
npx --package node-sass@latest node-sass --version
npx --package node-sass@latest node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/ -o dist-sass/css/
ls -Al dist-sass/css
+
+ - name: Check built CSS files for Sass variables
+ shell: bash
+ run: |
+ SASS_VARS_FOUND=$(find "dist-sass/css/" -type f -name "*.css" -print0 | xargs -0 --no-run-if-empty grep -F "\$" || true)
+ if [[ -z "$SASS_VARS_FOUND" ]]; then
+ echo "All good, no Sass variables found!"
+ exit 0
+ else
+ echo "Found $(echo "$SASS_VARS_FOUND" | wc -l | bc) Sass variables:"
+ echo "$SASS_VARS_FOUND"
+ exit 1
+ fi
diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml
index bbd0a24..f620dd3 100644
--- a/.github/workflows/release-notes.yml
+++ b/.github/workflows/release-notes.yml
@@ -6,8 +6,15 @@ on:
- main
workflow_dispatch:
+permissions:
+ contents: read
+
jobs:
update_release_draft:
+ permissions:
+ # allow release-drafter/release-drafter to create GitHub releases and add labels to PRs
+ contents: write
+ pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'twbs/bootstrap'
steps:
diff --git a/.gitignore b/.gitignore
index 2215d63..0c9b6f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,5 +38,6 @@ Thumbs.db
*.komodoproject
# Folders to ignore
+/dist-sass/
/js/coverage/
/node_modules/
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..4812751
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+lockfile-version=2
diff --git a/.stylelintrc b/.stylelintrc
deleted file mode 100644
index 94c8ec1..0000000
--- a/.stylelintrc
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "extends": [
- "stylelint-config-twbs-bootstrap"
- ],
- "rules": {
- "declaration-property-value-disallowed-list": {
- "border": "none",
- "outline": "none"
- },
- "function-disallowed-list": [
- "calc",
- "lighten",
- "darken"
- ],
- "property-disallowed-list": [
- "border-radius",
- "border-top-left-radius",
- "border-top-right-radius",
- "border-bottom-right-radius",
- "border-bottom-left-radius",
- "transition"
- ],
- "scss/dollar-variable-default": [
- true,
- {
- "ignore": "local"
- }
- ],
- "scss/selector-no-union-class-name": true
- }
-}
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 0000000..589884a
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,60 @@
+{
+ "extends": [
+ "stylelint-config-twbs-bootstrap"
+ ],
+ "reportInvalidScopeDisables": true,
+ "reportNeedlessDisables": true,
+ "overrides": [
+ {
+ "files": "**/*.scss",
+ "rules": {
+ "declaration-property-value-disallowed-list": {
+ "border": "none",
+ "outline": "none"
+ },
+ "function-disallowed-list": [
+ "calc",
+ "lighten",
+ "darken"
+ ],
+ "property-disallowed-list": [
+ "border-radius",
+ "border-top-left-radius",
+ "border-top-right-radius",
+ "border-bottom-right-radius",
+ "border-bottom-left-radius",
+ "transition"
+ ],
+ "scss/dollar-variable-default": [
+ true,
+ {
+ "ignore": "local"
+ }
+ ],
+ "scss/selector-no-union-class-name": true
+ }
+ },
+ {
+ "files": "scss/**/*.{test,spec}.scss",
+ "rules": {
+ "scss/dollar-variable-default": null,
+ "declaration-no-important": null
+ }
+ },
+ {
+ "files": "site/**/*.scss",
+ "rules": {
+ "scss/dollar-variable-default": null
+ }
+ },
+ {
+ "files": "site/**/examples/**/*.css",
+ "rules": {
+ "comment-empty-line-before": null,
+ "property-no-vendor-prefix": null,
+ "selector-no-qualifying-type": null,
+ "value-no-vendor-prefix": null
+ }
+ }
+ ]
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 28fd5e8..b983cba 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall
+- Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
-* The use of sexualized language or imagery, and sexual attention or advances of
+- The use of sexualized language or imagery, and sexual attention or advances of
any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email address,
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
+- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
diff --git a/LICENSE b/LICENSE
index dda75ca..6633b55 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2011-2022 Twitter, Inc.
-Copyright (c) 2011-2022 The Bootstrap Authors
+Copyright (c) 2011-2023 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index bd40192..529b0d5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
@@ -9,7 +9,7 @@
Sleek, intuitive, and powerful front-end framework for faster and easier web development.
- Explore Bootstrap docs ยป
+ Explore Bootstrap docs ยป
Report bug
@@ -46,32 +46,32 @@ Our default branch is for development of our Bootstrap 5 release. Head to the [`
Several quick start options are available:
-- [Download the latest release](https://github.com/twbs/bootstrap/archive/v5.2.3.zip)
+- [Download the latest release](https://github.com/twbs/bootstrap/archive/v5.3.0.zip)
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`
-- Install with [npm](https://www.npmjs.com/): `npm install bootstrap@v5.2.3`
-- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@v5.2.3`
-- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.2.3`
+- Install with [npm](https://www.npmjs.com/): `npm install bootstrap@v5.3.0`
+- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@v5.3.0`
+- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.3.0`
- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass`
-Read the [Getting started page](https://getbootstrap.com/docs/5.2/getting-started/introduction/) for information on the framework contents, templates, examples, and more.
+Read the [Getting started page](https://getbootstrap.com/docs/5.3/getting-started/introduction/) for information on the framework contents, templates, examples, and more.
## Status
-[](https://github.com/twbs/bootstrap/actions?query=workflow%3AJS+Tests+branch%3Amain)
-[](https://www.npmjs.com/package/bootstrap)
-[](https://rubygems.org/gems/bootstrap)
-[](https://atmospherejs.com/twbs/bootstrap)
-[](https://packagist.org/packages/twbs/bootstrap)
-[](https://www.nuget.org/packages/bootstrap/absoluteLatest)
-[](https://coveralls.io/github/twbs/bootstrap?branch=main)
+[](https://github.com/twbs/bootstrap/actions/workflows/js.yml?query=workflow%3AJS+branch%3Amain)
+[](https://www.npmjs.com/package/bootstrap)
+[](https://rubygems.org/gems/bootstrap)
+[](https://atmospherejs.com/twbs/bootstrap)
+[](https://packagist.org/packages/twbs/bootstrap)
+[](https://www.nuget.org/packages/bootstrap/absoluteLatest)
+[](https://coveralls.io/github/twbs/bootstrap?branch=main)
[](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[](https://www.browserstack.com/automate/public-build/SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)
-[](#backers)
-[](#sponsors)
+[](#backers)
+[](#sponsors)
## What's included
@@ -144,7 +144,7 @@ Have a bug or a feature request? Please first read the [issue guidelines](https:
Bootstrap's documentation, included in this repo in the root directory, is built with [Hugo](https://gohugo.io/) and publicly hosted on GitHub Pages at . The docs may also be run locally.
-Documentation search is powered by [Algolia's DocSearch](https://docsearch.algolia.com/). Working on our search? Be sure to set `debug: true` in `site/assets/js/search.js`.
+Documentation search is powered by [Algolia's DocSearch](https://docsearch.algolia.com/).
### Running documentation locally
@@ -243,4 +243,4 @@ Thank you to all our backers! ๐ [[Become a backer](https://opencollective.com
## Copyright and license
-Code and documentation copyright 2011โ2022 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
+Code and documentation copyright 2011โ2023 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors). Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/).
diff --git a/build/.eslintrc.json b/build/.eslintrc.json
deleted file mode 100644
index dec6323..0000000
--- a/build/.eslintrc.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "env": {
- "browser": false,
- "node": true
- },
- "parserOptions": {
- "sourceType": "script"
- },
- "extends": "../.eslintrc.json",
- "rules": {
- "no-console": "off",
- "strict": "error",
- "unicorn/prefer-top-level-await": "off"
- }
-}
diff --git a/build/banner.js b/build/banner.js
index df82ff3..a022f1c 100644
--- a/build/banner.js
+++ b/build/banner.js
@@ -1,6 +1,7 @@
'use strict'
const pkg = require('../package.json')
+
const year = new Date().getFullYear()
function getBanner(pluginFilename) {
diff --git a/build/build-plugins.js b/build/build-plugins.js
index a160209..b2833a3 100644
--- a/build/build-plugins.js
+++ b/build/build-plugins.js
@@ -2,8 +2,7 @@
/*!
* Script to build our plugins to use them separately.
- * Copyright 2020-2022 The Bootstrap Authors
- * Copyright 2020-2022 Twitter, Inc.
+ * Copyright 2020-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@@ -16,7 +15,7 @@ const { babel } = require('@rollup/plugin-babel')
const banner = require('./banner.js')
const sourcePath = path.resolve(__dirname, '../js/src/').replace(/\\/g, '/')
-const jsFiles = globby.sync(sourcePath + '/**/*.js')
+const jsFiles = globby.sync(`${sourcePath}/**/*.js`)
// Array which holds the resolved plugins
const resolvedPlugins = []
@@ -27,7 +26,7 @@ const filenameToEntity = filename => filename.replace('.js', '')
for (const file of jsFiles) {
resolvedPlugins.push({
- src: file.replace('.js', ''),
+ src: file,
dist: file.replace('src', 'dist'),
fileName: path.basename(file),
className: filenameToEntity(path.basename(file))
diff --git a/build/change-version.js b/build/change-version.js
index 57c5fde..9685df5 100644
--- a/build/change-version.js
+++ b/build/change-version.js
@@ -2,8 +2,7 @@
/*!
* Script to update version number references in the project.
- * Copyright 2017-2022 The Bootstrap Authors
- * Copyright 2017-2022 Twitter, Inc.
+ * Copyright 2017-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@@ -36,9 +35,17 @@ function regExpQuoteReplacement(string) {
async function replaceRecursively(file, oldVersion, newVersion) {
const originalString = await fs.readFile(file, 'utf8')
- const newString = originalString.replace(
- new RegExp(regExpQuote(oldVersion), 'g'), regExpQuoteReplacement(newVersion)
- )
+ const newString = originalString
+ .replace(
+ new RegExp(regExpQuote(oldVersion), 'g'),
+ regExpQuoteReplacement(newVersion)
+ )
+ // Also replace the version used by the rubygem,
+ // which is using periods (`.`) instead of hyphens (`-`)
+ .replace(
+ new RegExp(regExpQuote(oldVersion.replace(/-/g, '.')), 'g'),
+ regExpQuoteReplacement(newVersion.replace(/-/g, '.'))
+ )
// No need to move any further if the strings are identical
if (originalString === newString) {
@@ -56,22 +63,35 @@ async function replaceRecursively(file, oldVersion, newVersion) {
await fs.writeFile(file, newString, 'utf8')
}
+function showUsage(args) {
+ console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
+ console.error('Got arguments:', args)
+ process.exit(1)
+}
+
async function main(args) {
let [oldVersion, newVersion] = args
if (!oldVersion || !newVersion) {
- console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
- console.error('Got arguments:', args)
- process.exit(1)
+ showUsage(args)
}
- // Strip any leading `v` from arguments because otherwise we will end up with duplicate `v`s
- [oldVersion, newVersion] = [oldVersion, newVersion].map(arg => arg.startsWith('v') ? arg.slice(1) : arg)
+ // Strip any leading `v` from arguments because
+ // otherwise we will end up with duplicate `v`s
+ [oldVersion, newVersion] = [oldVersion, newVersion].map(arg => {
+ return arg.startsWith('v') ? arg.slice(1) : arg
+ })
+
+ if (oldVersion === newVersion) {
+ showUsage(args)
+ }
try {
const files = await globby(GLOB, GLOBBY_OPTIONS)
- await Promise.all(files.map(file => replaceRecursively(file, oldVersion, newVersion)))
+ await Promise.all(
+ files.map(file => replaceRecursively(file, oldVersion, newVersion))
+ )
} catch (error) {
console.error(error)
process.exit(1)
diff --git a/build/generate-sri.js b/build/generate-sri.js
index ef1b39f..2e22924 100644
--- a/build/generate-sri.js
+++ b/build/generate-sri.js
@@ -5,8 +5,7 @@
* Remember to use the same vendor files as the CDN ones,
* otherwise the hashes won't match!
*
- * Copyright 2017-2022 The Bootstrap Authors
- * Copyright 2017-2022 Twitter, Inc.
+ * Copyright 2017-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@@ -19,11 +18,11 @@ const sh = require('shelljs')
sh.config.fatal = true
-const configFile = path.join(__dirname, '../config.yml')
+const configFile = path.join(__dirname, '../hugo.yml')
// Array of objects which holds the files to generate SRI hashes for.
// `file` is the path from the root folder
-// `configPropertyName` is the config.yml variable's name of the file
+// `configPropertyName` is the hugo.yml variable's name of the file
const files = [
{
file: 'dist/css/bootstrap.min.css',
@@ -47,8 +46,8 @@ const files = [
}
]
-for (const file of files) {
- fs.readFile(file.file, 'utf8', (error, data) => {
+for (const { file, configPropertyName } of files) {
+ fs.readFile(file, 'utf8', (error, data) => {
if (error) {
throw error
}
@@ -57,8 +56,8 @@ for (const file of files) {
const hash = crypto.createHash(algo).update(data, 'utf8').digest('base64')
const integrity = `${algo}-${hash}`
- console.log(`${file.configPropertyName}: ${integrity}`)
+ console.log(`${configPropertyName}: ${integrity}`)
- sh.sed('-i', new RegExp(`^(\\s+${file.configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
+ sh.sed('-i', new RegExp(`^(\\s+${configPropertyName}:\\s+["'])\\S*(["'])`), `$1${integrity}$2`, configFile)
})
}
diff --git a/build/rollup.config.js b/build/rollup.config.js
index 27f12ac..f01918e 100644
--- a/build/rollup.config.js
+++ b/build/rollup.config.js
@@ -40,7 +40,7 @@ if (BUNDLE) {
const rollupConfig = {
input: path.resolve(__dirname, `../js/index.${ESM ? 'esm' : 'umd'}.js`),
output: {
- banner,
+ banner: banner(),
file: path.resolve(__dirname, `../dist/js/${fileDestination}.js`),
format: ESM ? 'esm' : 'umd',
globals,
diff --git a/build/vnu-jar.js b/build/vnu-jar.js
index f29eeb7..22956cb 100644
--- a/build/vnu-jar.js
+++ b/build/vnu-jar.js
@@ -2,8 +2,7 @@
/*!
* Script to run vnu-jar if Java is available.
- * Copyright 2017-2022 The Bootstrap Authors
- * Copyright 2017-2022 Twitter, Inc.
+ * Copyright 2017-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@@ -14,10 +13,13 @@ const vnu = require('vnu-jar')
execFile('java', ['-version'], (error, stdout, stderr) => {
if (error) {
- console.error('Skipping vnu-jar test; Java is missing.')
+ console.error('Skipping vnu-jar test; Java is probably missing.')
+ console.error(error)
return
}
+ console.log('Running vnu-jar validation...')
+
const is32bitJava = !/64-Bit/.test(stderr)
// vnu-jar accepts multiple ignores joined with a `|`.
@@ -49,6 +51,8 @@ execFile('java', ['-version'], (error, stdout, stderr) => {
args.splice(0, 0, '-Xss512k')
}
+ console.log(`command used: java ${args.join(' ')}`)
+
return spawn('java', args, {
shell: true,
stdio: 'inherit'
diff --git a/build/zip-examples.js b/build/zip-examples.js
index 077901e..613376a 100644
--- a/build/zip-examples.js
+++ b/build/zip-examples.js
@@ -3,7 +3,7 @@
/*!
* Script to create the built examples zip archive;
* requires the `zip` command to be present!
- * Copyright 2020-2022 The Bootstrap Authors
+ * Copyright 2020-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
@@ -84,7 +84,7 @@ for (const file of sh.find(`${distFolder}/**/*.html`)) {
}
// create the zip file
-sh.exec(`zip -r9 "${distFolder}.zip" "${distFolder}"`)
+sh.exec(`zip -qr9 "${distFolder}.zip" "${distFolder}"`)
// remove the folder we created
sh.rm('-rf', distFolder)
diff --git a/config.yml b/hugo.yml
similarity index 69%
rename from config.yml
rename to hugo.yml
index 682f873..47da082 100644
--- a/config.yml
+++ b/hugo.yml
@@ -31,7 +31,7 @@ publishDir: "_site"
module:
mounts:
- source: dist
- target: static/docs/5.2/dist
+ target: static/docs/5.3/dist
- source: site/assets
target: assets
- source: site/content
@@ -42,9 +42,9 @@ module:
target: layouts
- source: site/static
target: static
- - source: site/static/docs/5.2/assets/img/favicons/apple-touch-icon.png
+ - source: site/static/docs/5.3/assets/img/favicons/apple-touch-icon.png
target: static/apple-touch-icon.png
- - source: site/static/docs/5.2/assets/img/favicons/favicon.ico
+ - source: site/static/docs/5.3/assets/img/favicons/favicon.ico
target: static/favicon.ico
params:
@@ -52,10 +52,10 @@ params:
description: "Powerful, extensible, and feature-packed frontend toolkit. Build and customize with Sass, utilize prebuilt grid system and components, and bring projects to life with powerful JavaScript plugins."
authors: "Mark Otto, Jacob Thornton, and Bootstrap contributors"
- current_version: "5.2.3"
- current_ruby_version: "5.2.3"
- docs_version: "5.2"
- rfs_version: "v9.0.6"
+ current_version: "5.3.0"
+ current_ruby_version: "5.3.0"
+ docs_version: "5.3"
+ rfs_version: "v10.0.0"
github_org: "https://github.com/twbs"
repo: "https://github.com/twbs/bootstrap"
twitter: "getbootstrap"
@@ -66,22 +66,23 @@ params:
swag: "https://cottonbureau.com/people/bootstrap"
download:
- source: "https://github.com/twbs/bootstrap/archive/v5.2.3.zip"
- dist: "https://github.com/twbs/bootstrap/releases/download/v5.2.3/bootstrap-5.2.3-dist.zip"
- dist_examples: "https://github.com/twbs/bootstrap/releases/download/v5.2.3/bootstrap-5.2.3-examples.zip"
+ source: "https://github.com/twbs/bootstrap/archive/v5.3.0.zip"
+ dist: "https://github.com/twbs/bootstrap/releases/download/v5.3.0/bootstrap-5.3.0-dist.zip"
+ dist_examples: "https://github.com/twbs/bootstrap/releases/download/v5.3.0/bootstrap-5.3.0-examples.zip"
cdn:
# See https://www.srihash.org for info on how to generate the hashes
- css: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
- css_hash: "sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
- css_rtl: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.rtl.min.css"
- css_rtl_hash: "sha384-DOXMLfHhQkvFFp+rWTZwVlPVqdIhpDVYT9csOnHSgWQWPX0v5MCGtjCJbY6ERspU"
- js: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"
- js_hash: "sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V"
- js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
- js_bundle_hash: "sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
- popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"
- popper_hash: "sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3"
+ css: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
+ css_hash: "sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
+ css_rtl: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.rtl.min.css"
+ css_rtl_hash: "sha384-PJsj/BTMqILvmcej7ulplguok8ag4xFTPryRq8xevL7eBYSmpXKcbNVuy+P0RMgq"
+ js: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"
+ js_hash: "sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS"
+ js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
+ js_bundle_hash: "sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
+ popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
+ popper_hash: "sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
+ popper_esm: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/esm/popper.min.js"
anchors:
min: 2
diff --git a/js/index.esm.js b/js/index.esm.js
index b837649..155d9fb 100644
--- a/js/index.esm.js
+++ b/js/index.esm.js
@@ -1,19 +1,19 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): index.esm.js
+ * Bootstrap index.esm.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-export { default as Alert } from './src/alert'
-export { default as Button } from './src/button'
-export { default as Carousel } from './src/carousel'
-export { default as Collapse } from './src/collapse'
-export { default as Dropdown } from './src/dropdown'
-export { default as Modal } from './src/modal'
-export { default as Offcanvas } from './src/offcanvas'
-export { default as Popover } from './src/popover'
-export { default as ScrollSpy } from './src/scrollspy'
-export { default as Tab } from './src/tab'
-export { default as Toast } from './src/toast'
-export { default as Tooltip } from './src/tooltip'
+export { default as Alert } from './src/alert.js'
+export { default as Button } from './src/button.js'
+export { default as Carousel } from './src/carousel.js'
+export { default as Collapse } from './src/collapse.js'
+export { default as Dropdown } from './src/dropdown.js'
+export { default as Modal } from './src/modal.js'
+export { default as Offcanvas } from './src/offcanvas.js'
+export { default as Popover } from './src/popover.js'
+export { default as ScrollSpy } from './src/scrollspy.js'
+export { default as Tab } from './src/tab.js'
+export { default as Toast } from './src/toast.js'
+export { default as Tooltip } from './src/tooltip.js'
diff --git a/js/index.umd.js b/js/index.umd.js
index 5abe8db..a33df74 100644
--- a/js/index.umd.js
+++ b/js/index.umd.js
@@ -1,22 +1,22 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): index.umd.js
+ * Bootstrap index.umd.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import Alert from './src/alert'
-import Button from './src/button'
-import Carousel from './src/carousel'
-import Collapse from './src/collapse'
-import Dropdown from './src/dropdown'
-import Modal from './src/modal'
-import Offcanvas from './src/offcanvas'
-import Popover from './src/popover'
-import ScrollSpy from './src/scrollspy'
-import Tab from './src/tab'
-import Toast from './src/toast'
-import Tooltip from './src/tooltip'
+import Alert from './src/alert.js'
+import Button from './src/button.js'
+import Carousel from './src/carousel.js'
+import Collapse from './src/collapse.js'
+import Dropdown from './src/dropdown.js'
+import Modal from './src/modal.js'
+import Offcanvas from './src/offcanvas.js'
+import Popover from './src/popover.js'
+import ScrollSpy from './src/scrollspy.js'
+import Tab from './src/tab.js'
+import Toast from './src/toast.js'
+import Tooltip from './src/tooltip.js'
export default {
Alert,
diff --git a/js/src/alert.js b/js/src/alert.js
index 59de828..88232bc 100644
--- a/js/src/alert.js
+++ b/js/src/alert.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): alert.js
+ * Bootstrap alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
-import { enableDismissTrigger } from './util/component-functions'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
diff --git a/js/src/base-component.js b/js/src/base-component.js
index 0c1a259..d13c7ab 100644
--- a/js/src/base-component.js
+++ b/js/src/base-component.js
@@ -1,20 +1,20 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): base-component.js
+ * Bootstrap base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import Data from './dom/data'
-import { executeAfterTransition, getElement } from './util/index'
-import EventHandler from './dom/event-handler'
-import Config from './util/config'
+import Data from './dom/data.js'
+import EventHandler from './dom/event-handler.js'
+import Config from './util/config.js'
+import { executeAfterTransition, getElement } from './util/index.js'
/**
* Constants
*/
-const VERSION = '5.2.3'
+const VERSION = '5.3.0'
/**
* Class definition
diff --git a/js/src/button.js b/js/src/button.js
index 03e7604..a797f50 100644
--- a/js/src/button.js
+++ b/js/src/button.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): button.js
+ * Bootstrap button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 24bbe39..68d11a3 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -1,24 +1,23 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): carousel.js
+ * Bootstrap carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
- getElementFromSelector,
getNextActiveElement,
isRTL,
isVisible,
reflow,
triggerTransitionEnd
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import Swipe from './util/swipe'
-import BaseComponent from './base-component'
+} from './util/index.js'
+import Swipe from './util/swipe.js'
/**
* Constants
@@ -330,7 +329,7 @@ class Carousel extends BaseComponent {
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
- // todo: change tests that use empty divs to avoid this check
+ // TODO: change tests that use empty divs to avoid this check
return
}
@@ -431,7 +430,7 @@ class Carousel extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
return
diff --git a/js/src/collapse.js b/js/src/collapse.js
index 204d180..9f0c60c 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -1,20 +1,18 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): collapse.js
+ * Bootstrap collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
getElement,
- getElementFromSelector,
- getSelectorFromElement,
reflow
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+} from './util/index.js'
/**
* Constants
@@ -68,7 +66,7 @@ class Collapse extends BaseComponent {
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
for (const elem of toggleList) {
- const selector = getSelectorFromElement(elem)
+ const selector = SelectorEngine.getSelectorFromElement(elem)
const filterElement = SelectorEngine.find(selector)
.filter(foundElement => foundElement === this._element)
@@ -185,7 +183,7 @@ class Collapse extends BaseComponent {
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)
for (const trigger of this._triggerArray) {
- const element = getElementFromSelector(trigger)
+ const element = SelectorEngine.getElementFromSelector(trigger)
if (element && !this._isShown(element)) {
this._addAriaAndCollapsedClass([trigger], false)
@@ -229,7 +227,7 @@ class Collapse extends BaseComponent {
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)
for (const element of children) {
- const selected = getElementFromSelector(element)
+ const selected = SelectorEngine.getElementFromSelector(element)
if (selected) {
this._addAriaAndCollapsedClass([element], this._isShown(selected))
@@ -285,10 +283,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
event.preventDefault()
}
- const selector = getSelectorFromElement(this)
- const selectorElements = SelectorEngine.find(selector)
-
- for (const element of selectorElements) {
+ for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
}
})
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
index 2c6a46e..407f67e 100644
--- a/js/src/dom/data.js
+++ b/js/src/dom/data.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): dom/data.js
+ * Bootstrap dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js
index 9876d77..561d875 100644
--- a/js/src/dom/event-handler.js
+++ b/js/src/dom/event-handler.js
@@ -1,11 +1,11 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): dom/event-handler.js
+ * Bootstrap dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { getjQuery } from '../util/index'
+import { getjQuery } from '../util/index.js'
/**
* Constants
@@ -128,7 +128,7 @@ function findHandler(events, callable, delegationSelector = null) {
function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
const isDelegated = typeof handler === 'string'
- // todo: tooltip passes `false` instead of selector, so we need to check
+ // TODO: tooltip passes `false` instead of selector, so we need to check
const callable = isDelegated ? delegationFunction : (handler || delegationFunction)
let typeEvent = getTypeEvent(originalTypeEvent)
@@ -198,9 +198,8 @@ function removeHandler(element, events, typeEvent, handler, delegationSelector)
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
const storeElementEvent = events[typeEvent] || {}
- for (const handlerKey of Object.keys(storeElementEvent)) {
+ for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
if (handlerKey.includes(namespace)) {
- const event = storeElementEvent[handlerKey]
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
}
}
@@ -248,11 +247,10 @@ const EventHandler = {
}
}
- for (const keyHandlers of Object.keys(storeElementEvent)) {
+ for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
const handlerKey = keyHandlers.replace(stripUidRegex, '')
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
- const event = storeElementEvent[keyHandlers]
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
}
}
@@ -281,8 +279,7 @@ const EventHandler = {
defaultPrevented = jQueryEvent.isDefaultPrevented()
}
- let evt = new Event(event, { bubbles, cancelable: true })
- evt = hydrateObj(evt, args)
+ const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)
if (defaultPrevented) {
evt.preventDefault()
@@ -300,8 +297,8 @@ const EventHandler = {
}
}
-function hydrateObj(obj, meta) {
- for (const [key, value] of Object.entries(meta || {})) {
+function hydrateObj(obj, meta = {}) {
+ for (const [key, value] of Object.entries(meta)) {
try {
obj[key] = value
} catch {
diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js
index 38ecfe4..dd86a9f 100644
--- a/js/src/dom/manipulator.js
+++ b/js/src/dom/manipulator.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): dom/manipulator.js
+ * Bootstrap dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index 1ba104f..3cecf6f 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -1,15 +1,36 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): dom/selector-engine.js
+ * Bootstrap dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { isDisabled, isVisible } from '../util/index'
+import { isDisabled, isVisible, parseSelector } from '../util/index.js'
-/**
- * Constants
- */
+const getSelector = element => {
+ let selector = element.getAttribute('data-bs-target')
+
+ if (!selector || selector === '#') {
+ let hrefAttribute = element.getAttribute('href')
+
+ // The only valid content that could double as a selector are IDs or classes,
+ // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
+ // `document.querySelector` will rightfully complain it is invalid.
+ // See https://github.com/twbs/bootstrap/issues/32273
+ if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
+ return null
+ }
+
+ // Just in case some CMS puts out a full URL with the anchor appended
+ if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
+ hrefAttribute = `#${hrefAttribute.split('#')[1]}`
+ }
+
+ selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
+ }
+
+ return parseSelector(selector)
+}
const SelectorEngine = {
find(selector, element = document.documentElement) {
@@ -77,6 +98,28 @@ const SelectorEngine = {
].map(selector => `${selector}:not([tabindex^="-"])`).join(',')
return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
+ },
+
+ getSelectorFromElement(element) {
+ const selector = getSelector(element)
+
+ if (selector) {
+ return SelectorEngine.findOne(selector) ? selector : null
+ }
+
+ return null
+ },
+
+ getElementFromSelector(element) {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.findOne(selector) : null
+ },
+
+ getMultipleElementsFromSelector(element) {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.find(selector) : []
}
}
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 9596baa..af5fd16 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -1,13 +1,18 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): dropdown.js
+ * Bootstrap dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
+ execute,
getElement,
getNextActiveElement,
isDisabled,
@@ -15,11 +20,7 @@ import {
isRTL,
isVisible,
noop
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+} from './util/index.js'
/**
* Constants
@@ -95,7 +96,7 @@ class Dropdown extends BaseComponent {
this._popper = null
this._parent = this._element.parentNode // dropdown wrapper
- // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||
SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||
SelectorEngine.findOne(SELECTOR_MENU, this._parent)
@@ -310,7 +311,7 @@ class Dropdown extends BaseComponent {
// Disable Popper if we have a static display or Dropdown is in Navbar
if (this._inNavbar || this._config.display === 'static') {
- Manipulator.setDataAttribute(this._menu, 'popper', 'static') // todo:v6 remove
+ Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove
defaultBsPopperConfig.modifiers = [{
name: 'applyStyles',
enabled: false
@@ -319,7 +320,7 @@ class Dropdown extends BaseComponent {
return {
...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ ...execute(this._config.popperConfig, [defaultBsPopperConfig])
}
}
@@ -408,7 +409,7 @@ class Dropdown extends BaseComponent {
event.preventDefault()
- // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?
this :
(SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||
diff --git a/js/src/modal.js b/js/src/modal.js
index 26c7e8c..b44cbb9 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -1,18 +1,18 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): modal.js
+ * Bootstrap modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
-import ScrollBarHelper from './util/scrollbar'
-import BaseComponent from './base-component'
-import Backdrop from './util/backdrop'
-import FocusTrap from './util/focustrap'
-import { enableDismissTrigger } from './util/component-functions'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import Backdrop from './util/backdrop.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import FocusTrap from './util/focustrap.js'
+import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'
+import ScrollBarHelper from './util/scrollbar.js'
/**
* Constants
@@ -139,12 +139,12 @@ class Modal extends BaseComponent {
}
dispose() {
- for (const htmlElement of [window, this._dialog]) {
- EventHandler.off(htmlElement, EVENT_KEY)
- }
+ EventHandler.off(window, EVENT_KEY)
+ EventHandler.off(this._dialog, EVENT_KEY)
this._backdrop.dispose()
this._focustrap.deactivate()
+
super.dispose()
}
@@ -208,7 +208,6 @@ class Modal extends BaseComponent {
}
if (this._config.keyboard) {
- event.preventDefault()
this.hide()
return
}
@@ -336,7 +335,7 @@ class Modal extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js
index 7dd06fd..8d1feb1 100644
--- a/js/src/offcanvas.js
+++ b/js/src/offcanvas.js
@@ -1,23 +1,22 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): offcanvas.js
+ * Bootstrap offcanvas.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import Backdrop from './util/backdrop.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import FocusTrap from './util/focustrap.js'
import {
defineJQueryPlugin,
- getElementFromSelector,
isDisabled,
isVisible
-} from './util/index'
-import ScrollBarHelper from './util/scrollbar'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
-import SelectorEngine from './dom/selector-engine'
-import Backdrop from './util/backdrop'
-import FocusTrap from './util/focustrap'
-import { enableDismissTrigger } from './util/component-functions'
+} from './util/index.js'
+import ScrollBarHelper from './util/scrollbar.js'
/**
* Constants
@@ -199,12 +198,12 @@ class Offcanvas extends BaseComponent {
return
}
- if (!this._config.keyboard) {
- EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
+ if (this._config.keyboard) {
+ this.hide()
return
}
- this.hide()
+ EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
})
}
@@ -231,7 +230,7 @@ class Offcanvas extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
diff --git a/js/src/popover.js b/js/src/popover.js
index 1b09dd4..612c521 100644
--- a/js/src/popover.js
+++ b/js/src/popover.js
@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): popover.js
+ * Bootstrap popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import Tooltip from './tooltip'
+import Tooltip from './tooltip.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index 01aba99..69de715 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): scrollspy.js
+ * Bootstrap scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index.js'
/**
* Constants
@@ -208,11 +208,11 @@ class ScrollSpy extends BaseComponent {
continue
}
- const observableSection = SelectorEngine.findOne(anchor.hash, this._element)
+ const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)
// ensure that the observableSection exists & is visible
if (isVisible(observableSection)) {
- this._targetLinks.set(anchor.hash, anchor)
+ this._targetLinks.set(decodeURI(anchor.hash), anchor)
this._observableSections.set(anchor.hash, observableSection)
}
}
diff --git a/js/src/tab.js b/js/src/tab.js
index 8dc4644..d9993d5 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): tab.js
+ * Bootstrap tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, getElementFromSelector, getNextActiveElement, isDisabled } from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
/**
* Constants
@@ -43,7 +43,7 @@ const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)'
const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'
const SELECTOR_OUTER = '.nav-item, .list-group-item'
const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`
-const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]' // todo:v6: could be only `tab`
+const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]' // TODO: could only be `tab` in v6
const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`
const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`
@@ -59,7 +59,7 @@ class Tab extends BaseComponent {
if (!this._parent) {
return
- // todo: should Throw exception on v6
+ // TODO: should throw exception in v6
// throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)
}
@@ -106,7 +106,7 @@ class Tab extends BaseComponent {
element.classList.add(CLASS_NAME_ACTIVE)
- this._activate(getElementFromSelector(element)) // Search and activate/show the proper section
+ this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section
const complete = () => {
if (element.getAttribute('role') !== 'tab') {
@@ -133,7 +133,7 @@ class Tab extends BaseComponent {
element.classList.remove(CLASS_NAME_ACTIVE)
element.blur()
- this._deactivate(getElementFromSelector(element)) // Search and deactivate the shown section too
+ this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too
const complete = () => {
if (element.getAttribute('role') !== 'tab') {
@@ -203,7 +203,7 @@ class Tab extends BaseComponent {
}
_setInitialAttributesOnTargetPanel(child) {
- const target = getElementFromSelector(child)
+ const target = SelectorEngine.getElementFromSelector(child)
if (!target) {
return
@@ -212,7 +212,7 @@ class Tab extends BaseComponent {
this._setAttributeIfNotExists(target, 'role', 'tabpanel')
if (child.id) {
- this._setAttributeIfNotExists(target, 'aria-labelledby', `#${child.id}`)
+ this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)
}
}
diff --git a/js/src/toast.js b/js/src/toast.js
index a7fe775..d5d9c0e 100644
--- a/js/src/toast.js
+++ b/js/src/toast.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): toast.js
+ * Bootstrap toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, reflow } from './util/index'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
-import { enableDismissTrigger } from './util/component-functions'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import { defineJQueryPlugin, reflow } from './util/index.js'
/**
* Constants
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index 748a0e1..1252811 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -1,17 +1,17 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): tooltip.js
+ * Bootstrap tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
-import { defineJQueryPlugin, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index'
-import { DefaultAllowlist } from './util/sanitizer'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import BaseComponent from './base-component'
-import TemplateFactory from './util/template-factory'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
+import { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index.js'
+import { DefaultAllowlist } from './util/sanitizer.js'
+import TemplateFactory from './util/template-factory.js'
/**
* Constants
@@ -62,7 +62,7 @@ const Default = {
delay: 0,
fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
- offset: [0, 0],
+ offset: [0, 6],
placement: 'top',
popperConfig: null,
sanitize: true,
@@ -197,7 +197,7 @@ class Tooltip extends BaseComponent {
return
}
- // todo v6 remove this OR make it optional
+ // TODO: v6 remove this or make it optional
this._disposePopper()
const tip = this._getTipElement()
@@ -302,13 +302,13 @@ class Tooltip extends BaseComponent {
_createTipElement(content) {
const tip = this._getTemplateFactory(content).toHtml()
- // todo: remove this check on v6
+ // TODO: remove this check in v6
if (!tip) {
return null
}
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
- // todo: on v6 the following can be achieved with CSS only
+ // TODO: v6 the following can be achieved with CSS only
tip.classList.add(`bs-${this.constructor.NAME}-auto`)
const tipId = getUID(this.constructor.NAME).toString()
@@ -370,9 +370,7 @@ class Tooltip extends BaseComponent {
}
_createPopper(tip) {
- const placement = typeof this._config.placement === 'function' ?
- this._config.placement.call(this, tip, this._element) :
- this._config.placement
+ const placement = execute(this._config.placement, [this, tip, this._element])
const attachment = AttachmentMap[placement.toUpperCase()]
return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
}
@@ -392,7 +390,7 @@ class Tooltip extends BaseComponent {
}
_resolvePossibleFunction(arg) {
- return typeof arg === 'function' ? arg.call(this._element) : arg
+ return execute(arg, [this._element])
}
_getPopperConfig(attachment) {
@@ -438,7 +436,7 @@ class Tooltip extends BaseComponent {
return {
...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ ...execute(this._config.popperConfig, [defaultBsPopperConfig])
}
}
@@ -579,9 +577,9 @@ class Tooltip extends BaseComponent {
_getDelegateConfig() {
const config = {}
- for (const key in this._config) {
- if (this.constructor.Default[key] !== this._config[key]) {
- config[key] = this._config[key]
+ for (const [key, value] of Object.entries(this._config)) {
+ if (this.constructor.Default[key] !== value) {
+ config[key] = value
}
}
diff --git a/js/src/util/backdrop.js b/js/src/util/backdrop.js
index 78279e0..0d478e9 100644
--- a/js/src/util/backdrop.js
+++ b/js/src/util/backdrop.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/backdrop.js
+ * Bootstrap util/backdrop.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import { execute, executeAfterTransition, getElement, reflow } from './index'
-import Config from './config'
+import EventHandler from '../dom/event-handler.js'
+import Config from './config.js'
+import { execute, executeAfterTransition, getElement, reflow } from './index.js'
/**
* Constants
diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js
index c2f99cc..4be828f 100644
--- a/js/src/util/component-functions.js
+++ b/js/src/util/component-functions.js
@@ -1,12 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/component-functions.js
+ * Bootstrap util/component-functions.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import { getElementFromSelector, isDisabled } from './index'
+import EventHandler from '../dom/event-handler.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import { isDisabled } from './index.js'
const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`
@@ -21,7 +22,7 @@ const enableDismissTrigger = (component, method = 'hide') => {
return
}
- const target = getElementFromSelector(this) || this.closest(`.${name}`)
+ const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)
const instance = component.getOrCreateInstance(target)
// Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
diff --git a/js/src/util/config.js b/js/src/util/config.js
index 1205905..a2b4bfb 100644
--- a/js/src/util/config.js
+++ b/js/src/util/config.js
@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/config.js
+ * Bootstrap util/config.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { isElement, toType } from './index'
-import Manipulator from '../dom/manipulator'
+import Manipulator from '../dom/manipulator.js'
+import { isElement, toType } from './index.js'
/**
* Class definition
@@ -49,8 +49,7 @@ class Config {
}
_typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
- for (const property of Object.keys(configTypes)) {
- const expectedTypes = configTypes[property]
+ for (const [property, expectedTypes] of Object.entries(configTypes)) {
const value = config[property]
const valueType = isElement(value) ? 'element' : toType(value)
diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js
index ef69166..158f3d1 100644
--- a/js/src/util/focustrap.js
+++ b/js/src/util/focustrap.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/focustrap.js
+ * Bootstrap util/focustrap.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import SelectorEngine from '../dom/selector-engine'
-import Config from './config'
+import EventHandler from '../dom/event-handler.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import Config from './config.js'
/**
* Constants
diff --git a/js/src/util/index.js b/js/src/util/index.js
index 297e571..68b8d89 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/index.js
+ * Bootstrap util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -9,6 +9,20 @@ const MAX_UID = 1_000_000
const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend'
+/**
+ * Properly escape IDs selectors to handle weird IDs
+ * @param {string} selector
+ * @returns {string}
+ */
+const parseSelector = selector => {
+ if (selector && window.CSS && window.CSS.escape) {
+ // document.querySelector needs escaping to handle IDs (html5+) containing for instance /
+ selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`)
+ }
+
+ return selector
+}
+
// Shout-out Angus Croll (https://goo.gl/pxwQGp)
const toType = object => {
if (object === null || object === undefined) {
@@ -30,47 +44,6 @@ const getUID = prefix => {
return prefix
}
-const getSelector = element => {
- let selector = element.getAttribute('data-bs-target')
-
- if (!selector || selector === '#') {
- let hrefAttribute = element.getAttribute('href')
-
- // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
- if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
- return null
- }
-
- // Just in case some CMS puts out a full URL with the anchor appended
- if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
- hrefAttribute = `#${hrefAttribute.split('#')[1]}`
- }
-
- selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
- }
-
- return selector
-}
-
-const getSelectorFromElement = element => {
- const selector = getSelector(element)
-
- if (selector) {
- return document.querySelector(selector) ? selector : null
- }
-
- return null
-}
-
-const getElementFromSelector = element => {
- const selector = getSelector(element)
-
- return selector ? document.querySelector(selector) : null
-}
-
const getTransitionDurationFromElement = element => {
if (!element) {
return 0
@@ -117,7 +90,7 @@ const getElement = object => {
}
if (typeof object === 'string' && object.length > 0) {
- return document.querySelector(object)
+ return document.querySelector(parseSelector(object))
}
return null
@@ -249,10 +222,8 @@ const defineJQueryPlugin = plugin => {
})
}
-const execute = callback => {
- if (typeof callback === 'function') {
- callback()
- }
+const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
+ return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue
}
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
@@ -318,10 +289,8 @@ export {
executeAfterTransition,
findShadowRoot,
getElement,
- getElementFromSelector,
getjQuery,
getNextActiveElement,
- getSelectorFromElement,
getTransitionDurationFromElement,
getUID,
isDisabled,
@@ -330,6 +299,7 @@ export {
isVisible,
noop,
onDOMContentLoaded,
+ parseSelector,
reflow,
triggerTransitionEnd,
toType
diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js
index 5328691..d2b0808 100644
--- a/js/src/util/sanitizer.js
+++ b/js/src/util/sanitizer.js
@@ -1,53 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/sanitizer.js
+ * Bootstrap util/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-const uriAttributes = new Set([
- 'background',
- 'cite',
- 'href',
- 'itemtype',
- 'longdesc',
- 'poster',
- 'src',
- 'xlink:href'
-])
-
+// js-docs-start allow-list
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
-/**
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
- *
- * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i
-
-/**
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
- *
- * Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i
-
-const allowedAttribute = (attribute, allowedAttributeList) => {
- const attributeName = attribute.nodeName.toLowerCase()
-
- if (allowedAttributeList.includes(attributeName)) {
- if (uriAttributes.has(attributeName)) {
- return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue))
- }
-
- return true
- }
-
- // Check if a regular expression validates the attribute.
- return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
- .some(regex => regex.test(attributeName))
-}
-
export const DefaultAllowlist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
@@ -81,6 +41,43 @@ export const DefaultAllowlist = {
u: [],
ul: []
}
+// js-docs-end allow-list
+
+const uriAttributes = new Set([
+ 'background',
+ 'cite',
+ 'href',
+ 'itemtype',
+ 'longdesc',
+ 'poster',
+ 'src',
+ 'xlink:href'
+])
+
+/**
+ * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
+ * contexts.
+ *
+ * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
+ */
+// eslint-disable-next-line unicorn/better-regex
+const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i
+
+const allowedAttribute = (attribute, allowedAttributeList) => {
+ const attributeName = attribute.nodeName.toLowerCase()
+
+ if (allowedAttributeList.includes(attributeName)) {
+ if (uriAttributes.has(attributeName)) {
+ return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))
+ }
+
+ return true
+ }
+
+ // Check if a regular expression validates the attribute.
+ return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
+ .some(regex => regex.test(attributeName))
+}
export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
if (!unsafeHtml.length) {
@@ -100,7 +97,6 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
if (!Object.keys(allowList).includes(elementName)) {
element.remove()
-
continue
}
diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js
index 5cac7b6..413f178 100644
--- a/js/src/util/scrollbar.js
+++ b/js/src/util/scrollbar.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/scrollBar.js
+ * Bootstrap util/scrollBar.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import SelectorEngine from '../dom/selector-engine'
-import Manipulator from '../dom/manipulator'
-import { isElement } from './index'
+import Manipulator from '../dom/manipulator.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import { isElement } from './index.js'
/**
* Constants
diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js
index 7126360..d2f7087 100644
--- a/js/src/util/swipe.js
+++ b/js/src/util/swipe.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/swipe.js
+ * Bootstrap util/swipe.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import Config from './config'
-import EventHandler from '../dom/event-handler'
-import { execute } from './index'
+import EventHandler from '../dom/event-handler.js'
+import Config from './config.js'
+import { execute } from './index.js'
/**
* Constants
diff --git a/js/src/util/template-factory.js b/js/src/util/template-factory.js
index cf402fa..f73589b 100644
--- a/js/src/util/template-factory.js
+++ b/js/src/util/template-factory.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.2.3): util/template-factory.js
+ * Bootstrap util/template-factory.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
-import { getElement, isElement } from '../util/index'
-import SelectorEngine from '../dom/selector-engine'
-import Config from './config'
+import SelectorEngine from '../dom/selector-engine.js'
+import Config from './config.js'
+import { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'
+import { execute, getElement, isElement } from './index.js'
/**
* Constants
@@ -143,7 +143,7 @@ class TemplateFactory extends Config {
}
_resolvePossibleFunction(arg) {
- return typeof arg === 'function' ? arg(this) : arg
+ return execute(arg, [this])
}
_putElementInTemplate(element, templateElement) {
diff --git a/js/tests/browsers.js b/js/tests/browsers.js
index 8adedc6..c515e64 100644
--- a/js/tests/browsers.js
+++ b/js/tests/browsers.js
@@ -1,6 +1,7 @@
-/* eslint-env node */
/* eslint-disable camelcase */
+'use strict'
+
const browsers = {
safariMac: {
base: 'BrowserStack',
diff --git a/js/tests/integration/bundle-modularity.js b/js/tests/integration/bundle-modularity.js
index 8546141..3c1eec9 100644
--- a/js/tests/integration/bundle-modularity.js
+++ b/js/tests/integration/bundle-modularity.js
@@ -1,3 +1,5 @@
+/* eslint-disable import/extensions, import/no-unassigned-import */
+
import Tooltip from '../../dist/tooltip'
import '../../dist/carousel'
diff --git a/js/tests/integration/rollup.bundle-modularity.js b/js/tests/integration/rollup.bundle-modularity.js
index a8670ca..63d6515 100644
--- a/js/tests/integration/rollup.bundle-modularity.js
+++ b/js/tests/integration/rollup.bundle-modularity.js
@@ -1,7 +1,7 @@
-/* eslint-env node */
+'use strict'
const commonjs = require('@rollup/plugin-commonjs')
-const configRollup = require('./rollup.bundle')
+const configRollup = require('./rollup.bundle.js')
const config = {
...configRollup,
diff --git a/js/tests/integration/rollup.bundle.js b/js/tests/integration/rollup.bundle.js
index caddcab..8b3c578 100644
--- a/js/tests/integration/rollup.bundle.js
+++ b/js/tests/integration/rollup.bundle.js
@@ -1,4 +1,4 @@
-/* eslint-env node */
+'use strict'
const { babel } = require('@rollup/plugin-babel')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
diff --git a/js/tests/karma.conf.js b/js/tests/karma.conf.js
index 6636ff1..36bf7f2 100644
--- a/js/tests/karma.conf.js
+++ b/js/tests/karma.conf.js
@@ -1,5 +1,3 @@
-/* eslint-env node */
-
'use strict'
const path = require('node:path')
@@ -8,7 +6,7 @@ const { babel } = require('@rollup/plugin-babel')
const istanbul = require('rollup-plugin-istanbul')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const replace = require('@rollup/plugin-replace')
-const { browsers } = require('./browsers')
+const { browsers } = require('./browsers.js')
const ENV = process.env
const BROWSERSTACK = Boolean(ENV.BROWSERSTACK)
@@ -105,7 +103,7 @@ if (BROWSERSTACK) {
config.browserStack = {
username: ENV.BROWSER_STACK_USERNAME,
accessKey: ENV.BROWSER_STACK_ACCESS_KEY,
- build: `bootstrap-${ENV.GITHUB_SHA ? ENV.GITHUB_SHA.slice(0, 7) + '-' : ''}${new Date().toISOString()}`,
+ build: `bootstrap-${ENV.GITHUB_SHA ? `${ENV.GITHUB_SHA.slice(0, 7)}-` : ''}${new Date().toISOString()}`,
project: 'Bootstrap',
retryLimit: 2
}
diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json
deleted file mode 100644
index 6362a1a..0000000
--- a/js/tests/unit/.eslintrc.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": [
- "../../../.eslintrc.json"
- ],
- "env": {
- "jasmine": true
- },
- "rules": {
- "unicorn/consistent-function-scoping": "off",
- "unicorn/no-useless-undefined": "off",
- "unicorn/prefer-add-event-listener": "off"
- }
-}
diff --git a/js/tests/unit/alert.spec.js b/js/tests/unit/alert.spec.js
index d3740c9..97cc3cc 100644
--- a/js/tests/unit/alert.spec.js
+++ b/js/tests/unit/alert.spec.js
@@ -1,6 +1,6 @@
-import Alert from '../../src/alert'
-import { getTransitionDurationFromElement } from '../../src/util/index'
-import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
+import Alert from '../../src/alert.js'
+import { getTransitionDurationFromElement } from '../../src/util/index.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Alert', () => {
let fixtureEl
diff --git a/js/tests/unit/base-component.spec.js b/js/tests/unit/base-component.spec.js
index b2352d6..5b7d52e 100644
--- a/js/tests/unit/base-component.spec.js
+++ b/js/tests/unit/base-component.spec.js
@@ -1,7 +1,7 @@
-import BaseComponent from '../../src/base-component'
-import { clearFixture, getFixture } from '../helpers/fixture'
-import EventHandler from '../../src/dom/event-handler'
-import { noop } from '../../src/util'
+import BaseComponent from '../../src/base-component.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { noop } from '../../src/util/index.js'
+import { clearFixture, getFixture } from '../helpers/fixture.js'
class DummyClass extends BaseComponent {
constructor(element) {
diff --git a/js/tests/unit/button.spec.js b/js/tests/unit/button.spec.js
index 09ed17e..6624fee 100644
--- a/js/tests/unit/button.spec.js
+++ b/js/tests/unit/button.spec.js
@@ -1,5 +1,5 @@
-import Button from '../../src/button'
-import { getFixture, clearFixture, jQueryMock } from '../helpers/fixture'
+import Button from '../../src/button.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Button', () => {
let fixtureEl
diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js
index d951bd5..c468b5c 100644
--- a/js/tests/unit/carousel.spec.js
+++ b/js/tests/unit/carousel.spec.js
@@ -1,8 +1,8 @@
-import Carousel from '../../src/carousel'
-import EventHandler from '../../src/dom/event-handler'
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
-import { isRTL, noop } from '../../src/util/index'
-import Swipe from '../../src/util/swipe'
+import Carousel from '../../src/carousel.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { isRTL, noop } from '../../src/util/index.js'
+import Swipe from '../../src/util/swipe.js'
+import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Carousel', () => {
const { Simulator, PointerEvent } = window
diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js
index 9c86719..58c5367 100644
--- a/js/tests/unit/collapse.spec.js
+++ b/js/tests/unit/collapse.spec.js
@@ -1,6 +1,6 @@
-import Collapse from '../../src/collapse'
-import EventHandler from '../../src/dom/event-handler'
-import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
+import Collapse from '../../src/collapse.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Collapse', () => {
let fixtureEl
@@ -277,25 +277,25 @@ describe('Collapse', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = [
'
',
- '