1
0
Fork 0

Merging upstream version 5.3.0+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 07:08:19 +01:00
parent a87fc4fcc7
commit e92720b6a7
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
605 changed files with 15320 additions and 9495 deletions

View file

@ -2,43 +2,43 @@
"files": [
{
"path": "./dist/css/bootstrap-grid.css",
"maxSize": "7.5 kB"
"maxSize": "6.5 kB"
},
{
"path": "./dist/css/bootstrap-grid.min.css",
"maxSize": "6.55 kB"
"maxSize": "6.0 kB"
},
{
"path": "./dist/css/bootstrap-reboot.css",
"maxSize": "2.75 kB"
"maxSize": "3.5 kB"
},
{
"path": "./dist/css/bootstrap-reboot.min.css",
"maxSize": "2.5 kB"
"maxSize": "3.25 kB"
},
{
"path": "./dist/css/bootstrap-utilities.css",
"maxSize": "9.25 kB"
"maxSize": "11.75 kB"
},
{
"path": "./dist/css/bootstrap-utilities.min.css",
"maxSize": "8.5 kB"
"maxSize": "10.75 kB"
},
{
"path": "./dist/css/bootstrap.css",
"maxSize": "28.75 kB"
"maxSize": "32.5 kB"
},
{
"path": "./dist/css/bootstrap.min.css",
"maxSize": "26.75 kB"
"maxSize": "30.25 kB"
},
{
"path": "./dist/js/bootstrap.bundle.js",
"maxSize": "43.25 kB"
"maxSize": "43.0 kB"
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
"maxSize": "22.75 kB"
"maxSize": "23.0 kB"
},
{
"path": "./dist/js/bootstrap.esm.js",
@ -46,7 +46,7 @@
},
{
"path": "./dist/js/bootstrap.esm.min.js",
"maxSize": "18.5 kB"
"maxSize": "18.25 kB"
},
{
"path": "./dist/js/bootstrap.js",
@ -54,7 +54,7 @@
},
{
"path": "./dist/js/bootstrap.min.js",
"maxSize": "16.25 kB"
"maxSize": "16.0 kB"
}
],
"ci": {

View file

@ -8,6 +8,7 @@
"autohiding",
"autoplay",
"autoplays",
"autoplaying",
"blazingly",
"Blockquotes",
"Bootstrappers",
@ -107,6 +108,7 @@
"unstyled",
"Uppercased",
"urlize",
"urlquery",
"vbtn",
"viewports",
"Vite",

View file

@ -3,6 +3,5 @@
**/vendor/
/_site/
/js/coverage/
/js/tests/integration/
/site/static/sw.js
/site/layouts/
/site/layouts/partials/

View file

@ -14,6 +14,35 @@
"error",
"never"
],
"import/extensions": [
"error",
"ignorePackages",
{
"js": "always"
}
],
"import/first": "error",
"import/newline-after-import": "error",
"import/no-absolute-path": "error",
"import/no-amd": "error",
"import/no-cycle": [
"error",
{
"ignoreExternal": true
}
],
"import/no-duplicates": "error",
"import/no-extraneous-dependencies": "error",
"import/no-mutable-exports": "error",
"import/no-named-as-default": "error",
"import/no-named-as-default-member": "error",
"import/no-named-default": "error",
"import/no-self-import": "error",
"import/no-unassigned-import": [
"error"
],
"import/no-useless-path-segments": "error",
"import/order": "error",
"indent": [
"error",
2,
@ -46,20 +75,146 @@
"error",
"after"
],
"prefer-template": "error",
"semi": [
"error",
"never"
],
"strict": "error",
"unicorn/explicit-length-check": "off",
"unicorn/filename-case": "off",
"unicorn/no-array-callback-reference": "off",
"unicorn/no-array-method-this-argument": "off",
"unicorn/no-null": "off",
"unicorn/no-typeof-undefined": "off",
"unicorn/no-unused-properties": "error",
"unicorn/numeric-separators-style": "off",
"unicorn/prefer-array-flat": "off",
"unicorn/prefer-at": "off",
"unicorn/prefer-dom-node-dataset": "off",
"unicorn/prefer-module": "off",
"unicorn/prefer-query-selector": "off",
"unicorn/prefer-spread": "off",
"unicorn/prefer-string-replace-all": "off",
"unicorn/prevent-abbreviations": "off"
}
},
"overrides": [
{
"files": [
"build/**"
],
"env": {
"browser": false,
"node": true
},
"parserOptions": {
"sourceType": "script"
},
"rules": {
"no-console": "off",
"unicorn/prefer-top-level-await": "off"
}
},
{
"files": [
"js/**"
],
"parserOptions": {
"sourceType": "module"
}
},
{
"files": [
"js/tests/*.js",
"js/tests/integration/rollup*.js"
],
"env": {
"node": true
},
"parserOptions": {
"sourceType": "script"
}
},
{
"files": [
"js/tests/unit/**"
],
"env": {
"jasmine": true
},
"rules": {
"no-console": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/no-useless-undefined": "off",
"unicorn/prefer-add-event-listener": "off"
}
},
{
"files": [
"js/tests/visual/**"
],
"plugins": [
"html"
],
"settings": {
"html/html-extensions": [
".html"
]
},
"rules": {
"no-console": "off",
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
"files": [
"scss/tests/**"
],
"env": {
"node": true
},
"parserOptions": {
"sourceType": "script"
}
},
{
"files": [
"site/**"
],
"env": {
"browser": true,
"node": false
},
"parserOptions": {
"sourceType": "script",
"ecmaVersion": 2019
},
"rules": {
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
},
{
"files": [
"**/*.md"
],
"plugins": [
"markdown"
],
"processor": "markdown/markdown"
},
{
"files": [
"**/*.md/*.js"
],
"extends": "plugin:markdown/recommended",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"unicorn/prefer-node-protocol": "off"
}
}
]
}

View file

@ -18,16 +18,16 @@ the preferred channel for [bug reports](#bug-reports), [features requests](#feat
and [submitting pull requests](#pull-requests), but please respect the following
restrictions:
* Please **do not** use the issue tracker for personal support requests. Stack Overflow ([`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag), [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions) or [IRC](/README.md#community) are better places to get help.
- Please **do not** use the issue tracker for personal support requests. Stack Overflow ([`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag), [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions) or [IRC](/README.md#community) are better places to get help.
* Please **do not** derail or troll issues. Keep the discussion on topic and
- Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
* Please **do not** post comments consisting solely of "+1" or ":thumbsup:".
- Please **do not** post comments consisting solely of "+1" or ":thumbsup:".
Use [GitHub's "reactions" feature](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/)
instead. We reserve the right to delete comments which violate this rule.
* Please **do not** open issues regarding the official themes offered on <https://themes.getbootstrap.com/>.
- Please **do not** open issues regarding the official themes offered on <https://themes.getbootstrap.com/>.
Instead, please email any questions or feedback regarding those themes to `themes AT getbootstrap DOT com`.
@ -101,16 +101,16 @@ Sometimes bugs reported to us are actually caused by bugs in the browser(s) them
| Vendor(s) | Browser(s) | Rendering engine | Bug reporting website(s) | Notes |
| ------------- | ---------------------------- | ---------------- | ------------------------------------------------------ | -------------------------------------------------------- |
| Mozilla | Firefox | Gecko | https://bugzilla.mozilla.org/enter_bug.cgi | "Core" is normally the right product option to choose. |
| Apple | Safari | WebKit | https://bugs.webkit.org/enter_bug.cgi?product=WebKit | In Apple's bug reporter, choose "Safari" as the product. |
| Google, Opera | Chrome, Chromium, Opera v15+ | Blink | https://bugs.chromium.org/p/chromium/issues/list | Click the "New issue" button. |
| Microsoft | Edge | Blink | https://developer.microsoft.com/en-us/microsoft-edge/ | Go to "Help > Send Feedback" from the browser |
| Mozilla | Firefox | Gecko | <https://bugzilla.mozilla.org/enter_bug.cgi> | "Core" is normally the right product option to choose. |
| Apple | Safari | WebKit | <https://bugs.webkit.org/enter_bug.cgi?product=WebKit> | In Apple's bug reporter, choose "Safari" as the product. |
| Google, Opera | Chrome, Chromium, Opera v15+ | Blink | <https://bugs.chromium.org/p/chromium/issues/list> | Click the "New issue" button. |
| Microsoft | Edge | Blink | <https://developer.microsoft.com/en-us/microsoft-edge/> | 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.

View file

@ -31,7 +31,7 @@
<!-- Please add direct links where your modifications can be seen in the documentation -->
* https://deploy-preview-{your pr number}--twbs-bootstrap.netlify.app/
- <https://deploy-preview-{your_pr_number}--twbs-bootstrap.netlify.app/>
### Related issues

3
.github/codeql/codeql-config.yml vendored Normal file
View file

@ -0,0 +1,3 @@
name: "CodeQL config"
paths-ignore:
- dist

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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:

1
.gitignore vendored
View file

@ -38,5 +38,6 @@ Thumbs.db
*.komodoproject
# Folders to ignore
/dist-sass/
/js/coverage/
/node_modules/

1
.npmrc Normal file
View file

@ -0,0 +1 @@
lockfile-version=2

View file

@ -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
}
}

60
.stylelintrc.json Normal file
View file

@ -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
}
}
]
}

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,6 @@
<p align="center">
<a href="https://getbootstrap.com/">
<img src="https://getbootstrap.com/docs/5.2/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap logo" width="200" height="165">
<img src="https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo-shadow.png" alt="Bootstrap logo" width="200" height="165">
</a>
</p>
@ -9,7 +9,7 @@
<p align="center">
Sleek, intuitive, and powerful front-end framework for faster and easier web development.
<br>
<a href="https://getbootstrap.com/docs/5.2/"><strong>Explore Bootstrap docs »</strong></a>
<a href="https://getbootstrap.com/docs/5.3/"><strong>Explore Bootstrap docs »</strong></a>
<br>
<br>
<a href="https://github.com/twbs/bootstrap/issues/new?assignees=-&labels=bug&template=bug_report.yml">Report bug</a>
@ -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
[![Build Status](https://img.shields.io/github/workflow/status/twbs/bootstrap/JS%20Tests/main?label=JS%20Tests&logo=github)](https://github.com/twbs/bootstrap/actions?query=workflow%3AJS+Tests+branch%3Amain)
[![npm version](https://img.shields.io/npm/v/bootstrap)](https://www.npmjs.com/package/bootstrap)
[![Gem version](https://img.shields.io/gem/v/bootstrap)](https://rubygems.org/gems/bootstrap)
[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue)](https://atmospherejs.com/twbs/bootstrap)
[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap)](https://packagist.org/packages/twbs/bootstrap)
[![NuGet](https://img.shields.io/nuget/vpre/bootstrap)](https://www.nuget.org/packages/bootstrap/absoluteLatest)
[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/main)](https://coveralls.io/github/twbs/bootstrap?branch=main)
[![Build Status](https://img.shields.io/github/actions/workflow/status/twbs/bootstrap/js.yml?branch=main&label=JS%20Tests&logo=github)](https://github.com/twbs/bootstrap/actions/workflows/js.yml?query=workflow%3AJS+branch%3Amain)
[![npm version](https://img.shields.io/npm/v/bootstrap?logo=npm&logoColor=fff)](https://www.npmjs.com/package/bootstrap)
[![Gem version](https://img.shields.io/gem/v/bootstrap?logo=rubygems&logoColor=fff)](https://rubygems.org/gems/bootstrap)
[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue?logo=meteor&logoColor=fff)](https://atmospherejs.com/twbs/bootstrap)
[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap?logo=packagist&logoColor=fff)](https://packagist.org/packages/twbs/bootstrap)
[![NuGet](https://img.shields.io/nuget/vpre/bootstrap?logo=nuget&logoColor=fff)](https://www.nuget.org/packages/bootstrap/absoluteLatest)
[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/main?logo=coveralls&logoColor=fff)](https://coveralls.io/github/twbs/bootstrap?branch=main)
[![CSS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=gzip&label=CSS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[![CSS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=brotli&label=CSS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
[![JS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=gzip&label=JS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[![JS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=brotli&label=JS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js)
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)](https://www.browserstack.com/automate/public-build/SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)
[![Backers on Open Collective](https://img.shields.io/opencollective/backers/bootstrap)](#backers)
[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/bootstrap)](#sponsors)
[![Backers on Open Collective](https://img.shields.io/opencollective/backers/bootstrap?logo=opencollective&logoColor=fff)](#backers)
[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/bootstrap?logo=opencollective&logoColor=fff)](#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 <https://getbootstrap.com/>. 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 20112022 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 20112023 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/).

View file

@ -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"
}
}

View file

@ -1,6 +1,7 @@
'use strict'
const pkg = require('../package.json')
const year = new Date().getFullYear()
function getBanner(pluginFilename) {

View file

@ -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))

View file

@ -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)

View file

@ -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)
})
}

View file

@ -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,

View file

@ -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'

View file

@ -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)

View file

@ -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

View file

@ -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'

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
}
})

View file

@ -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)
* --------------------------------------------------------------------------
*/

View file

@ -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 {

View file

@ -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)
* --------------------------------------------------------------------------
*/

View file

@ -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) : []
}
}

View file

@ -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] ||

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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)
}
}

View file

@ -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}`)
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -1,6 +1,7 @@
/* eslint-env node */
/* eslint-disable camelcase */
'use strict'
const browsers = {
safariMac: {
base: 'BrowserStack',

View file

@ -1,3 +1,5 @@
/* eslint-disable import/extensions, import/no-unassigned-import */
import Tooltip from '../../dist/tooltip'
import '../../dist/carousel'

View file

@ -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,

View file

@ -1,4 +1,4 @@
/* eslint-env node */
'use strict'
const { babel } = require('@rollup/plugin-babel')
const { nodeResolve } = require('@rollup/plugin-node-resolve')

View file

@ -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
}

View file

@ -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"
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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 = [
'<div id="parentGroup" class="accordion">',
' <div id="parentHeader" class="accordion-header">',
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
' <div class="accordion-header">',
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" class="accordion-toggle">Parent</button>',
' </div>',
' <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
' <div id="parentContent" class="accordion-collapse collapse" data-bs-parent="#parentGroup">',
' <div class="accordion-body">',
' <div id="childGroup" class="accordion">',
' <div class="accordion-item">',
' <div id="childHeader1" class="accordion-header">',
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
' <div class="accordion-header">',
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" class="accordion-toggle">Child 1</button>',
' </div>',
' <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
' <div id="childContent1" class="accordion-collapse collapse" data-bs-parent="#childGroup">',
' <div>content</div>',
' </div>',
' </div>',
' <div class="accordion-item">',
' <div id="childHeader2" class="accordion-header">',
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
' <div class="accordion-header">',
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" class="accordion-toggle">Child 2</button>',
' </div>',
' <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
' <div id="childContent2" class="accordion-collapse collapse" data-bs-parent="#childGroup">',
' <div>content</div>',
' </div>',
' </div>',
@ -338,12 +338,12 @@ describe('Collapse', () => {
fixtureEl.innerHTML = [
'<div class="accordion" id="accordionExample">',
' <div class="accordion-item">',
' <h2 class="accordion-header" id="headingOne">',
' <h2 class="accordion-header">',
' <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
' Accordion Item #1',
' </button>',
' </h2>',
' <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
' <div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">',
' <div class="accordion-body">',
' <nav>',
' <div class="nav nav-tabs" id="nav-tab" role="tablist">',
@ -640,11 +640,11 @@ describe('Collapse', () => {
'<div id="accordion">',
' <div class="item">',
' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
' <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
' <div id="collapseOne" class="collapse" role="tabpanel" data-bs-parent="#accordion"></div>',
' </div>',
' <div class="item">',
' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
' <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
' <div id="collapseTwo" class="collapse show" role="tabpanel" data-bs-parent="#accordion"></div>',
' </div>',
'</div>'
].join('')
@ -699,13 +699,13 @@ describe('Collapse', () => {
' <div class="col-lg-6">',
' <div class="item">',
' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
' <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
' <div id="collapseOne" class="collapse" role="tabpanel" data-bs-parent="#accordion"></div>',
' </div>',
' </div>',
' <div class="col-lg-6">',
' <div class="item">',
' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
' <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
' <div id="collapseTwo" class="collapse show" role="tabpanel" data-bs-parent="#accordion"></div>',
' </div>',
' </div>',
' </div>',
@ -829,18 +829,18 @@ describe('Collapse', () => {
'<div id="accordion">',
' <div class="item">',
' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
' <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">',
' <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel">',
' <div id="nestedAccordion">',
' <div class="item">',
' <a id="nestedLinkTrigger" data-bs-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>',
' <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel" aria-labelledby="headingThree"></div>',
' <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel"></div>',
' </div>',
' </div>',
' </div>',
' </div>',
' <div class="item">',
' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
' <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel" aria-labelledby="headingTwo"></div>',
' <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel"></div>',
' </div>',
'</div>'
].join('')
@ -887,17 +887,17 @@ describe('Collapse', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = [
'<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
'<a id="trigger2" role="button" data-bs-toggle="collapse" href="#test2"></a>',
'<a id="trigger2" role="button" data-bs-toggle="collapse" href="#0/my/id"></a>',
'<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
'<div id="test1" class="multi"></div>',
'<div id="test2" class="multi"></div>'
'<div id="0/my/id" class="multi"></div>'
].join('')
const trigger1 = fixtureEl.querySelector('#trigger1')
const trigger2 = fixtureEl.querySelector('#trigger2')
const trigger3 = fixtureEl.querySelector('#trigger3')
const target1 = fixtureEl.querySelector('#test1')
const target2 = fixtureEl.querySelector('#test2')
const target2 = fixtureEl.querySelector(`#${CSS.escape('0/my/id')}`)
const target2Shown = () => {
expect(trigger1).not.toHaveClass('collapsed')

View file

@ -1,5 +1,5 @@
import Data from '../../../src/dom/data'
import { getFixture, clearFixture } from '../../helpers/fixture'
import Data from '../../../src/dom/data.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Data', () => {
const TEST_KEY = 'bs.test'
@ -89,7 +89,6 @@ describe('Data', () => {
expect(Data.get(div, TEST_KEY)).toBeNull()
})
/* eslint-disable no-console */
it('should console.error a message if called with multiple keys', () => {
console.error = jasmine.createSpy('console.error')
@ -102,5 +101,4 @@ describe('Data', () => {
expect(console.error).toHaveBeenCalled()
expect(Data.get(div, UNKNOWN_KEY)).toBeNull()
})
/* eslint-enable no-console */
})

View file

@ -1,6 +1,6 @@
import EventHandler from '../../../src/dom/event-handler'
import { clearFixture, getFixture } from '../../helpers/fixture'
import { noop } from '../../../src/util'
import EventHandler from '../../../src/dom/event-handler.js'
import { noop } from '../../../src/util/index.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('EventHandler', () => {
let fixtureEl

View file

@ -1,5 +1,5 @@
import Manipulator from '../../../src/dom/manipulator'
import { clearFixture, getFixture } from '../../helpers/fixture'
import Manipulator from '../../../src/dom/manipulator.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Manipulator', () => {
let fixtureEl

View file

@ -1,5 +1,5 @@
import SelectorEngine from '../../../src/dom/selector-engine'
import { getFixture, clearFixture } from '../../helpers/fixture'
import SelectorEngine from '../../../src/dom/selector-engine.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('SelectorEngine', () => {
let fixtureEl
@ -232,5 +232,159 @@ describe('SelectorEngine', () => {
expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
})
})
})
describe('getSelectorFromElement', () => {
it('should get selector from data-bs-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-bs-target=".target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if no data-bs-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if data-bs-target equal to #', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should return null if a selector from a href is a url without an anchor', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
})
it('should return the anchor if a selector from a href is a url', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
'<div id="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('#target')
})
it('should return null if selector not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
})
it('should return null if no selector', () => {
fixtureEl.innerHTML = '<div></div>'
const testEl = fixtureEl.querySelector('div')
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
})
})
describe('getElementFromSelector', () => {
it('should get element from data-bs-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-bs-target=".target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
})
it('should get element from href if no data-bs-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
})
it('should return null if element not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
})
it('should return null if no selector', () => {
fixtureEl.innerHTML = '<div></div>'
const testEl = fixtureEl.querySelector('div')
expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
})
})
describe('getMultipleElementsFromSelector', () => {
it('should get elements from data-bs-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-bs-target=".target"></div>',
'<div class="target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should get elements in array, from href if no data-bs-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
})
it('should return empty array if elements not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
})
it('should return empty array if no selector', () => {
fixtureEl.innerHTML = '<div></div>'
const testEl = fixtureEl.querySelector('div')
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
})
})
})

View file

@ -1,7 +1,7 @@
import Dropdown from '../../src/dropdown'
import EventHandler from '../../src/dom/event-handler'
import { noop } from '../../src/util/index'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import EventHandler from '../../src/dom/event-handler.js'
import Dropdown from '../../src/dropdown.js'
import { noop } from '../../src/util/index.js'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Dropdown', () => {
let fixtureEl
@ -75,6 +75,7 @@ describe('Dropdown', () => {
resolve()
})
expect().nothing()
dropdown.show()
})
})

View file

@ -1,18 +1,18 @@
/* eslint-env jquery */
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 { clearFixture, getFixture } from '../helpers/fixture'
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'
import { clearFixture, getFixture } from '../helpers/fixture.js'
describe('jQuery', () => {
let fixtureEl

View file

@ -1,7 +1,7 @@
import Modal from '../../src/modal'
import EventHandler from '../../src/dom/event-handler'
import ScrollBarHelper from '../../src/util/scrollbar'
import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import EventHandler from '../../src/dom/event-handler.js'
import Modal from '../../src/modal.js'
import ScrollBarHelper from '../../src/util/scrollbar.js'
import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Modal', () => {
let fixtureEl

View file

@ -1,8 +1,8 @@
import Offcanvas from '../../src/offcanvas'
import EventHandler from '../../src/dom/event-handler'
import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import { isVisible } from '../../src/util/index'
import ScrollBarHelper from '../../src/util/scrollbar'
import EventHandler from '../../src/dom/event-handler.js'
import Offcanvas from '../../src/offcanvas.js'
import { isVisible } from '../../src/util/index.js'
import ScrollBarHelper from '../../src/util/scrollbar.js'
import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Offcanvas', () => {
let fixtureEl

View file

@ -1,6 +1,6 @@
import Popover from '../../src/popover'
import EventHandler from '../../src/dom/event-handler'
import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
import EventHandler from '../../src/dom/event-handler.js'
import Popover from '../../src/popover.js'
import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Popover', () => {
let fixtureEl

View file

@ -1,8 +1,6 @@
import ScrollSpy from '../../src/scrollspy'
/** Test helpers */
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import EventHandler from '../../src/dom/event-handler'
import EventHandler from '../../src/dom/event-handler.js'
import ScrollSpy from '../../src/scrollspy.js'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('ScrollSpy', () => {
let fixtureEl
@ -942,5 +940,39 @@ describe('ScrollSpy', () => {
}, 100)
link.click()
})
it('should smoothscroll to observable with anchor link that contains a french word as id', done => {
fixtureEl.innerHTML = [
'<nav id="navBar" class="navbar">',
' <ul class="nav">',
' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#présentation">div 1</a></li>',
' </ul>',
'</nav>',
'<div class="content" data-bs-target="#navBar" style="overflow-y: auto">',
' <div id="présentation">div 1</div>',
'</div>'
].join('')
const div = fixtureEl.querySelector('.content')
const link = fixtureEl.querySelector('[href="#présentation"]')
const observable = fixtureEl.querySelector('#présentation')
const clickSpy = getElementScrollSpy(div)
// eslint-disable-next-line no-new
new ScrollSpy(div, {
offset: 1,
smoothScroll: true
})
setTimeout(() => {
if (div.scrollTo) {
expect(clickSpy).toHaveBeenCalledWith({ top: observable.offsetTop - div.offsetTop, behavior: 'smooth' })
} else {
expect(clickSpy).toHaveBeenCalledWith(observable.offsetTop - div.offsetTop)
}
done()
}, 100)
link.click()
})
})
})

View file

@ -1,5 +1,5 @@
import Tab from '../../src/tab'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import Tab from '../../src/tab.js'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Tab', () => {
let fixtureEl
@ -177,6 +177,43 @@ describe('Tab', () => {
})
})
it('should work with tab id being an int', done => {
fixtureEl.innerHTML = [
'<div class="card-header d-block d-inline-block">',
' <ul class="nav nav-tabs card-header-tabs" id="page_tabs">',
' <li class="nav-item">',
' <a class="nav-link" draggable="false" data-toggle="tab" href="#tab1">',
' Working Tab 1 (#tab1)',
' </a>',
' </li>',
' <li class="nav-item">',
' <a id="trigger2" class="nav-link" draggable="false" data-toggle="tab" href="#2">',
' Tab with numeric ID should work (#2)',
' </a>',
' </li>',
' </ul>',
'</div>',
'<div class="card-body">',
' <div class="tab-content" id="page_content">',
' <div class="tab-pane fade" id="tab1">',
' Working Tab 1 (#tab1) Content Here',
' </div>',
' <div class="tab-pane fade" id="2">',
' Working Tab 2 (#2) with numeric ID',
' </div>',
'</div>'
].join('')
const profileTriggerEl = fixtureEl.querySelector('#trigger2')
const tab = new Tab(profileTriggerEl)
profileTriggerEl.addEventListener('shown.bs.tab', () => {
expect(fixtureEl.querySelector(`#${CSS.escape('2')}`)).toHaveClass('active')
done()
})
tab.show()
})
it('should not fire shown when show is prevented', () => {
return new Promise((resolve, reject) => {
fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
@ -477,7 +514,7 @@ describe('Tab', () => {
expect(tabPanel.hasAttribute('tabindex')).toBeFalse()
expect(tabPanel.hasAttribute('tabindex2')).toBeFalse()
expect(tabPanel.getAttribute('aria-labelledby')).toEqual('#foo')
expect(tabPanel.getAttribute('aria-labelledby')).toEqual('foo')
expect(tabPanel2.hasAttribute('aria-labelledby')).toBeFalse()
})
})
@ -603,19 +640,19 @@ describe('Tab', () => {
'</div>'
].join('')
const tabEl = fixtureEl.querySelector('#tab1')
const tabEl1 = fixtureEl.querySelector('#tab1')
const tabEl2 = fixtureEl.querySelector('#tab2')
const tabEl3 = fixtureEl.querySelector('#tab3')
const tabEl4 = fixtureEl.querySelector('#tab4')
const tab = new Tab(tabEl)
const tab1 = new Tab(tabEl1)
const tab2 = new Tab(tabEl2)
const tab3 = new Tab(tabEl3)
const tab4 = new Tab(tabEl4)
const spy1 = spyOn(tab, 'show').and.callThrough()
const spy1 = spyOn(tab1, 'show').and.callThrough()
const spy2 = spyOn(tab2, 'show').and.callThrough()
const spy3 = spyOn(tab3, 'show').and.callThrough()
const spy4 = spyOn(tab4, 'show').and.callThrough()
const spyFocus1 = spyOn(tabEl, 'focus').and.callThrough()
const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
@ -623,7 +660,7 @@ describe('Tab', () => {
const keydown = createEvent('keydown')
keydown.key = 'ArrowRight'
tabEl.dispatchEvent(keydown)
tabEl1.dispatchEvent(keydown)
expect(spy1).not.toHaveBeenCalled()
expect(spy2).not.toHaveBeenCalled()
expect(spy3).not.toHaveBeenCalled()
@ -644,19 +681,19 @@ describe('Tab', () => {
'</div>'
].join('')
const tabEl = fixtureEl.querySelector('#tab1')
const tabEl1 = fixtureEl.querySelector('#tab1')
const tabEl2 = fixtureEl.querySelector('#tab2')
const tabEl3 = fixtureEl.querySelector('#tab3')
const tabEl4 = fixtureEl.querySelector('#tab4')
const tab = new Tab(tabEl)
const tab1 = new Tab(tabEl1)
const tab2 = new Tab(tabEl2)
const tab3 = new Tab(tabEl3)
const tab4 = new Tab(tabEl4)
const spy1 = spyOn(tab, 'show').and.callThrough()
const spy1 = spyOn(tab1, 'show').and.callThrough()
const spy2 = spyOn(tab2, 'show').and.callThrough()
const spy3 = spyOn(tab3, 'show').and.callThrough()
const spy4 = spyOn(tab4, 'show').and.callThrough()
const spyFocus1 = spyOn(tabEl, 'focus').and.callThrough()
const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()

View file

@ -1,5 +1,5 @@
import Toast from '../../src/toast'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import Toast from '../../src/toast.js'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Toast', () => {
let fixtureEl

View file

@ -1,7 +1,7 @@
import Tooltip from '../../src/tooltip'
import EventHandler from '../../src/dom/event-handler'
import { noop } from '../../src/util/index'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
import EventHandler from '../../src/dom/event-handler.js'
import Tooltip from '../../src/tooltip.js'
import { noop } from '../../src/util/index.js'
import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Tooltip', () => {
let fixtureEl
@ -56,7 +56,7 @@ describe('Tooltip', () => {
describe('constructor', () => {
it('should take care of element either passed as a CSS selector or DOM element', () => {
fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title">'
fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title"></a>'
const tooltipEl = fixtureEl.querySelector('#tooltipEl')
const tooltipBySelector = new Tooltip('#tooltipEl')
@ -67,7 +67,7 @@ describe('Tooltip', () => {
})
it('should not take care of disallowed data attributes', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -76,7 +76,7 @@ describe('Tooltip', () => {
})
it('should convert title and content to string if numbers', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -98,7 +98,7 @@ describe('Tooltip', () => {
trigger: 'click'
})
containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipInContainerEl = containerEl.querySelector('a')
@ -114,7 +114,7 @@ describe('Tooltip', () => {
it('should create offset modifier when offset is passed as a function', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function"></a>'
const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
const tooltipEl = fixtureEl.querySelector('a')
@ -141,7 +141,7 @@ describe('Tooltip', () => {
})
it('should create offset modifier when offset option is passed in data attribute', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -150,7 +150,7 @@ describe('Tooltip', () => {
})
it('should allow to pass config to Popper with `popperConfig`', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -165,7 +165,7 @@ describe('Tooltip', () => {
})
it('should allow to pass config to Popper with `popperConfig` as a function', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const getPopperConfig = jasmine.createSpy('getPopperConfig').and.returnValue({ placement: 'left' })
@ -192,7 +192,7 @@ describe('Tooltip', () => {
describe('enable', () => {
it('should enable a tooltip', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -212,7 +212,7 @@ describe('Tooltip', () => {
describe('disable', () => {
it('should disable tooltip', () => {
return new Promise((resolve, reject) => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -235,7 +235,7 @@ describe('Tooltip', () => {
describe('toggleEnabled', () => {
it('should toggle enabled', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -251,7 +251,7 @@ describe('Tooltip', () => {
describe('toggle', () => {
it('should do nothing if disabled', () => {
return new Promise((resolve, reject) => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -273,7 +273,7 @@ describe('Tooltip', () => {
it('should show a tooltip', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -289,7 +289,7 @@ describe('Tooltip', () => {
it('should call toggle and show the tooltip when trigger is "click"', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -309,7 +309,7 @@ describe('Tooltip', () => {
it('should hide a tooltip', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -329,7 +329,7 @@ describe('Tooltip', () => {
it('should call toggle and hide the tooltip when trigger is "click"', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -354,7 +354,7 @@ describe('Tooltip', () => {
describe('dispose', () => {
it('should destroy a tooltip', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const addEventSpy = spyOn(tooltipEl, 'addEventListener').and.callThrough()
@ -381,7 +381,7 @@ describe('Tooltip', () => {
it('should destroy a tooltip after it is shown and hidden', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -402,7 +402,7 @@ describe('Tooltip', () => {
it('should destroy a tooltip and remove it from the dom', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -443,7 +443,7 @@ describe('Tooltip', () => {
describe('show', () => {
it('should show a tooltip', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -488,7 +488,7 @@ describe('Tooltip', () => {
it('should show a tooltip on mobile', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -509,7 +509,7 @@ describe('Tooltip', () => {
it('should show a tooltip relative to placement option', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -532,7 +532,7 @@ describe('Tooltip', () => {
it('should not error when trying to show a tooltip that has been removed from the dom', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -561,7 +561,7 @@ describe('Tooltip', () => {
it('should show a tooltip with a dom element container', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -579,7 +579,7 @@ describe('Tooltip', () => {
it('should show a tooltip with a jquery element container', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -600,7 +600,7 @@ describe('Tooltip', () => {
it('should show a tooltip with a selector in container', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -618,7 +618,7 @@ describe('Tooltip', () => {
it('should show a tooltip with placement as a function', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const spy = jasmine.createSpy('placement').and.returnValue('top')
const tooltipEl = fixtureEl.querySelector('a')
@ -638,7 +638,7 @@ describe('Tooltip', () => {
it('should show a tooltip without the animation', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -658,7 +658,7 @@ describe('Tooltip', () => {
})
it('should throw an error the element is not visible', () => {
fixtureEl.innerHTML = '<a href="#" style="display: none" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" style="display: none" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -672,7 +672,7 @@ describe('Tooltip', () => {
it('should not show a tooltip if show.bs.tooltip is prevented', () => {
return new Promise((resolve, reject) => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -699,7 +699,7 @@ describe('Tooltip', () => {
it('should show tooltip if leave event hasn\'t occurred before delay expires', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -723,7 +723,7 @@ describe('Tooltip', () => {
it('should not show tooltip if leave event occurs before delay expires', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -845,7 +845,7 @@ describe('Tooltip', () => {
it('should only trigger inserted event if a new tooltip element was created', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -878,7 +878,7 @@ describe('Tooltip', () => {
it('should show a tooltip with custom class provided in data attributes', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -896,7 +896,7 @@ describe('Tooltip', () => {
it('should show a tooltip with custom class provided as a string in config', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -917,7 +917,7 @@ describe('Tooltip', () => {
it('should show a tooltip with custom class provided as a function in config', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
const tooltipEl = fixtureEl.querySelector('a')
@ -956,7 +956,7 @@ describe('Tooltip', () => {
describe('hide', () => {
it('should hide a tooltip', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -974,7 +974,7 @@ describe('Tooltip', () => {
it('should hide a tooltip on mobile', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -998,7 +998,7 @@ describe('Tooltip', () => {
it('should hide a tooltip without animation', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@ -1018,7 +1018,7 @@ describe('Tooltip', () => {
it('should not hide a tooltip if hide event is prevented', () => {
return new Promise((resolve, reject) => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const assertDone = () => {
setTimeout(() => {
@ -1063,7 +1063,7 @@ describe('Tooltip', () => {
describe('update', () => {
it('should call popper update', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1082,7 +1082,7 @@ describe('Tooltip', () => {
})
it('should do nothing if the tooltip is not shown', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1094,7 +1094,7 @@ describe('Tooltip', () => {
describe('_isWithContent', () => {
it('should return true if there is content', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1103,7 +1103,7 @@ describe('Tooltip', () => {
})
it('should return false if there is no content', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title=""></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1114,7 +1114,7 @@ describe('Tooltip', () => {
describe('_getTipElement', () => {
it('should create the tip element and return it', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1126,7 +1126,7 @@ describe('Tooltip', () => {
})
it('should return the created tip element', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1145,7 +1145,7 @@ describe('Tooltip', () => {
describe('setContent', () => {
it('should set tip content', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, { animation: false })
@ -1160,7 +1160,7 @@ describe('Tooltip', () => {
})
it('should re-show tip if it was already shown', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1175,7 +1175,7 @@ describe('Tooltip', () => {
})
it('should keep tip hidden, if it was already hidden before', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1190,7 +1190,7 @@ describe('Tooltip', () => {
})
it('"setContent" should keep the initial template', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@ -1207,7 +1207,7 @@ describe('Tooltip', () => {
describe('setContent', () => {
it('should do nothing if the element is null', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)

View file

@ -1,6 +1,6 @@
import Backdrop from '../../../src/util/backdrop'
import { getTransitionDurationFromElement } from '../../../src/util/index'
import { clearFixture, getFixture } from '../../helpers/fixture'
import Backdrop from '../../../src/util/backdrop.js'
import { getTransitionDurationFromElement } from '../../../src/util/index.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
const CLASS_BACKDROP = '.modal-backdrop'
const CLASS_NAME_FADE = 'fade'

View file

@ -1,8 +1,6 @@
/* Test helpers */
import { clearFixture, createEvent, getFixture } from '../../helpers/fixture'
import { enableDismissTrigger } from '../../../src/util/component-functions'
import BaseComponent from '../../../src/base-component'
import BaseComponent from '../../../src/base-component.js'
import { enableDismissTrigger } from '../../../src/util/component-functions.js'
import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
class DummyClass2 extends BaseComponent {
static get NAME() {

View file

@ -1,5 +1,5 @@
import Config from '../../../src/util/config'
import { clearFixture, getFixture } from '../../helpers/fixture'
import Config from '../../../src/util/config.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
class DummyConfigClass extends Config {
static get NAME() {
@ -128,7 +128,7 @@ describe('Config', () => {
const obj = new DummyConfigClass()
expect(() => {
obj._typeCheckConfig(config)
}).toThrowError(TypeError, obj.constructor.NAME.toUpperCase() + ': Option "parent" provided type "number" but expected type "(string|element)".')
}).toThrowError(TypeError, `${obj.constructor.NAME.toUpperCase()}: Option "parent" provided type "number" but expected type "(string|element)".`)
})
it('should return null stringified when null is passed', () => {

View file

@ -1,7 +1,7 @@
import FocusTrap from '../../../src/util/focustrap'
import EventHandler from '../../../src/dom/event-handler'
import SelectorEngine from '../../../src/dom/selector-engine'
import { clearFixture, createEvent, getFixture } from '../../helpers/fixture'
import EventHandler from '../../../src/dom/event-handler.js'
import SelectorEngine from '../../../src/dom/selector-engine.js'
import FocusTrap from '../../../src/util/focustrap.js'
import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
describe('FocusTrap', () => {
let fixtureEl

View file

@ -1,6 +1,6 @@
import * as Util from '../../../src/util/index'
import { clearFixture, getFixture } from '../../helpers/fixture'
import { noop } from '../../../src/util/index'
import * as Util from '../../../src/util/index.js'
import { noop } from '../../../src/util/index.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Util', () => {
let fixtureEl
@ -22,119 +22,6 @@ describe('Util', () => {
})
})
describe('getSelectorFromElement', () => {
it('should get selector from data-bs-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-bs-target=".target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if no data-bs-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if data-bs-target equal to #', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should return null if a selector from a href is a url without an anchor', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toBeNull()
})
it('should return the anchor if a selector from a href is a url', () => {
fixtureEl.innerHTML = [
'<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
'<div id="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('#target')
})
it('should return null if selector not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toBeNull()
})
it('should return null if no selector', () => {
fixtureEl.innerHTML = '<div></div>'
const testEl = fixtureEl.querySelector('div')
expect(Util.getSelectorFromElement(testEl)).toBeNull()
})
})
describe('getElementFromSelector', () => {
it('should get element from data-bs-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-bs-target=".target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
})
it('should get element from href if no data-bs-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
})
it('should return null if element not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(Util.getElementFromSelector(testEl)).toBeNull()
})
it('should return null if no selector', () => {
fixtureEl.innerHTML = '<div></div>'
const testEl = fixtureEl.querySelector('div')
expect(Util.getElementFromSelector(testEl)).toBeNull()
})
})
describe('getTransitionDurationFromElement', () => {
it('should get transition from element', () => {
fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
@ -631,6 +518,25 @@ describe('Util', () => {
Util.execute(spy)
expect(spy).toHaveBeenCalled()
})
it('should execute if arg is function & return the result', () => {
const functionFoo = (num1, num2 = 10) => num1 + num2
const resultFoo = Util.execute(functionFoo, [4, 5])
expect(resultFoo).toBe(9)
const resultFoo1 = Util.execute(functionFoo, [4])
expect(resultFoo1).toBe(14)
const functionBar = () => 'foo'
const resultBar = Util.execute(functionBar)
expect(resultBar).toBe('foo')
})
it('should not execute if arg is not function & return default argument', () => {
const foo = 'bar'
expect(Util.execute(foo)).toBe('bar')
expect(Util.execute(foo, [], 4)).toBe(4)
})
})
describe('executeAfterTransition', () => {

View file

@ -1,4 +1,4 @@
import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer'
import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
describe('Sanitizer', () => {
describe('sanitizeHtml', () => {
@ -10,17 +10,75 @@ describe('Sanitizer', () => {
expect(result).toEqual(empty)
})
it('should retain tags with valid URLs', () => {
const validUrls = [
'',
'http://abc',
'HTTP://abc',
'https://abc',
'HTTPS://abc',
'ftp://abc',
'FTP://abc',
'mailto:me@example.com',
'MAILTO:me@example.com',
'tel:123-123-1234',
'TEL:123-123-1234',
'sip:me@example.com',
'SIP:me@example.com',
'#anchor',
'/page1.md',
'http://JavaScript/my.js',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', // Truncated.
'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
'unknown-scheme:abc'
]
for (const url of validUrls) {
const template = [
'<div>',
` <a href="${url}">Click me</a>`,
' <span>Some content</span>',
'</div>'
].join('')
const result = sanitizeHtml(template, DefaultAllowlist, null)
expect(result).toContain(`href="${url}"`)
}
})
it('should sanitize template by removing tags with XSS', () => {
const template = [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')
const invalidUrls = [
// eslint-disable-next-line no-script-url
'javascript:alert(7)',
// eslint-disable-next-line no-script-url
'javascript:evil()',
// eslint-disable-next-line no-script-url
'JavaScript:abc',
' javascript:abc',
' \n Java\n Script:abc',
'&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#106&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#106 &#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
'&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058',
'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A;',
'jav&#x09;ascript:alert();',
'jav\u0000ascript:alert();'
]
const result = sanitizeHtml(template, DefaultAllowlist, null)
for (const url of invalidUrls) {
const template = [
'<div>',
` <a href="${url}">Click me</a>`,
' <span>Some content</span>',
'</div>'
].join('')
expect(result).not.toContain('href="javascript:alert(7)')
const result = sanitizeHtml(template, DefaultAllowlist, null)
expect(result).not.toContain(`href="${url}"`)
}
})
it('should sanitize template and work with multiple regex', () => {

View file

@ -1,6 +1,6 @@
import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture'
import Manipulator from '../../../src/dom/manipulator'
import ScrollBarHelper from '../../../src/util/scrollbar'
import Manipulator from '../../../src/dom/manipulator.js'
import ScrollBarHelper from '../../../src/util/scrollbar.js'
import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture.js'
describe('ScrollBar', () => {
let fixtureEl

View file

@ -1,7 +1,7 @@
import { clearFixture, getFixture } from '../../helpers/fixture'
import EventHandler from '../../../src/dom/event-handler'
import Swipe from '../../../src/util/swipe'
import { noop } from '../../../src/util'
import EventHandler from '../../../src/dom/event-handler.js'
import { noop } from '../../../src/util/index.js'
import Swipe from '../../../src/util/swipe.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Swipe', () => {
const { Simulator, PointerEvent } = window

View file

@ -1,5 +1,5 @@
import { clearFixture, getFixture } from '../../helpers/fixture'
import TemplateFactory from '../../../src/util/template-factory'
import TemplateFactory from '../../../src/util/template-factory.js'
import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('TemplateFactory', () => {
let fixtureEl

View file

@ -1,19 +0,0 @@
{
"plugins": [
"html"
],
"extends": "../../../.eslintrc.json",
"parserOptions": {
"sourceType": "module"
},
"settings": {
"html/html-extensions": [
".html"
]
},
"rules": {
"no-console": "off",
"no-new": "off",
"unicorn/no-array-for-each": "off"
}
}

View file

@ -15,7 +15,7 @@
</button>
<p>For checkboxes and radio buttons, ensure that keyboard behavior is functioning correctly.</p>
<p>Navigate to the checkboxes with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>), and ensure that <kbd>SPACE</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>SPACE</kbd> toggles the checkbox again.</p>
<p>Navigate to the checkboxes with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>), and ensure that <kbd>Space</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>Space</kbd> toggles the checkbox again.</p>
<div class="btn-group" data-bs-toggle="buttons">
<label class="btn btn-primary active">
@ -29,7 +29,7 @@
</label>
</div>
<p>Navigate to the radio button group with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>TAB</kbd> or "backwards" using <kbd>SHIFT + TAB</kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd></kbd> and <kbd></kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd></kbd> and <kbd></kbd> change the selected radio button again.</p>
<p>Navigate to the radio button group with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>Tab</kbd> or "backwards" using <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd></kbd> and <kbd></kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd></kbd> and <kbd></kbd> change the selected radio button again.</p>
<div class="btn-group" data-bs-toggle="buttons">
<label class="btn btn-primary active">

Some files were not shown because too many files have changed in this diff Show more