1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-08-17 02:54:01 +02:00

Merge branch 'main' into patrickhlauke-issue37428

This commit is contained in:
Patrick H. Lauke
2024-06-12 10:11:50 +01:00
committed by GitHub
514 changed files with 13199 additions and 17411 deletions

View File

@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): alert.js
* Bootstrap alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin } from './util/index.js'
import EventHandler from './dom/event-handler.js'
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.3.0-alpha1): base-component.js
* Bootstrap base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import Data from './dom/data.js'
import { executeAfterTransition, getElement } from './util/index.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.3.0-alpha1'
const VERSION = '5.3.3'
/**
* Class definition

View File

@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): button.js
* Bootstrap button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin } from './util/index.js'
import EventHandler from './dom/event-handler.js'
import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js'
import { defineJQueryPlugin } from './util/index.js'
/**
* Constants

View File

@@ -1,10 +1,14 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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,
getNextActiveElement,
@@ -13,11 +17,7 @@ import {
reflow,
triggerTransitionEnd
} from './util/index.js'
import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js'
import SelectorEngine from './dom/selector-engine.js'
import Swipe from './util/swipe.js'
import BaseComponent from './base-component.js'
/**
* Constants
@@ -329,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
}

View File

@@ -1,18 +1,18 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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,
reflow
} from './util/index.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import BaseComponent from './base-component.js'
/**
* Constants

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): dom/data.js
* Bootstrap dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): dom/event-handler.js
* Bootstrap dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -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)
@@ -279,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()

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): dom/manipulator.js
* Bootstrap dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): dom/selector-engine.js
* Bootstrap dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -29,7 +29,7 @@ const getSelector = element => {
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
}
return parseSelector(selector)
return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null
}
const SelectorEngine = {

View File

@@ -1,11 +1,15 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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,
@@ -17,10 +21,6 @@ import {
isVisible,
noop
} from './util/index.js'
import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js'
import SelectorEngine from './dom/selector-engine.js'
import BaseComponent from './base-component.js'
/**
* Constants
@@ -96,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.3/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)
@@ -224,7 +224,7 @@ class Dropdown extends BaseComponent {
_createPopper() {
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')
}
let referenceElement = this._element
@@ -311,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
@@ -409,7 +409,7 @@ class Dropdown extends BaseComponent {
event.preventDefault()
// todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/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,20 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): modal.js
* Bootstrap modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'
import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import ScrollBarHelper from './util/scrollbar.js'
import BaseComponent from './base-component.js'
import Backdrop from './util/backdrop.js'
import FocusTrap from './util/focustrap.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 +141,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()
}

View File

@@ -1,22 +1,22 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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,
isDisabled,
isVisible
} from './util/index.js'
import ScrollBarHelper from './util/scrollbar.js'
import EventHandler from './dom/event-handler.js'
import BaseComponent from './base-component.js'
import SelectorEngine from './dom/selector-engine.js'
import Backdrop from './util/backdrop.js'
import FocusTrap from './util/focustrap.js'
import { enableDismissTrigger } from './util/component-functions.js'
/**
* Constants

View File

@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): popover.js
* Bootstrap popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin } from './util/index.js'
import Tooltip from './tooltip.js'
import { defineJQueryPlugin } from './util/index.js'
/**
* Constants

View File

@@ -1,14 +1,16 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): scrollspy.js
* Bootstrap scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index.js'
import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import BaseComponent from './base-component.js'
import {
defineJQueryPlugin, getElement, isDisabled, isVisible
} from './util/index.js'
/**
* Constants
@@ -208,11 +210,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.3.0-alpha1): tab.js
* Bootstrap tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import BaseComponent from './base-component.js'
import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
/**
* Constants
@@ -30,6 +30,8 @@ const ARROW_LEFT_KEY = 'ArrowLeft'
const ARROW_RIGHT_KEY = 'ArrowRight'
const ARROW_UP_KEY = 'ArrowUp'
const ARROW_DOWN_KEY = 'ArrowDown'
const HOME_KEY = 'Home'
const END_KEY = 'End'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_FADE = 'fade'
@@ -38,12 +40,12 @@ const CLASS_DROPDOWN = 'dropdown'
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'
const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)'
const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_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 +61,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}`)
}
@@ -151,14 +153,22 @@ class Tab extends BaseComponent {
}
_keydown(event) {
if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key))) {
if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {
return
}
event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page
event.preventDefault()
const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true)
const children = this._getChildren().filter(element => !isDisabled(element))
let nextActiveElement
if ([HOME_KEY, END_KEY].includes(event.key)) {
nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]
} else {
const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
nextActiveElement = getNextActiveElement(children, event.target, isNext, true)
}
if (nextActiveElement) {
nextActiveElement.focus({ preventScroll: true })
@@ -212,7 +222,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.3.0-alpha1): toast.js
* Bootstrap toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { defineJQueryPlugin, reflow } from './util/index.js'
import EventHandler from './dom/event-handler.js'
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,16 +1,18 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): tooltip.js
* Bootstrap tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
import { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index.js'
import { DefaultAllowlist } from './util/sanitizer.js'
import BaseComponent from './base-component.js'
import EventHandler from './dom/event-handler.js'
import Manipulator from './dom/manipulator.js'
import BaseComponent from './base-component.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'
/**
@@ -62,7 +64,7 @@ const Default = {
delay: 0,
fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
offset: [0, 0],
offset: [0, 6],
placement: 'top',
popperConfig: null,
sanitize: true,
@@ -103,7 +105,7 @@ const DefaultType = {
class Tooltip extends BaseComponent {
constructor(element, config) {
if (typeof Popper === 'undefined') {
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org/docs/v2/)')
}
super(element, config)
@@ -197,7 +199,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 +304,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()

View File

@@ -1,13 +1,15 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/backdrop.js
* Bootstrap util/backdrop.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import EventHandler from '../dom/event-handler.js'
import { execute, executeAfterTransition, getElement, reflow } from './index.js'
import Config from './config.js'
import {
execute, executeAfterTransition, getElement, reflow
} from './index.js'
/**
* Constants

View File

@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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.js'
import { isDisabled } from './index.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}`

View File

@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/config.js
* Bootstrap util/config.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import { isElement, toType } from './index.js'
import Manipulator from '../dom/manipulator.js'
import { isElement, toType } from './index.js'
/**
* Class definition

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/focustrap.js
* Bootstrap util/focustrap.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/

View File

@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/index.js
* Bootstrap util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -170,7 +170,7 @@ const noop = () => {}
* @param {HTMLElement} element
* @return void
*
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
* @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
element.offsetHeight // eslint-disable-line no-unused-expressions

View File

@@ -1,53 +1,13 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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],
@@ -57,7 +17,10 @@ export const DefaultAllowlist = {
br: [],
col: [],
code: [],
dd: [],
div: [],
dl: [],
dt: [],
em: [],
hr: [],
h1: [],
@@ -81,6 +44,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 +100,6 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
if (!Object.keys(allowList).includes(elementName)) {
element.remove()
continue
}

View File

@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/scrollBar.js
* Bootstrap util/scrollBar.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import SelectorEngine from '../dom/selector-engine.js'
import Manipulator from '../dom/manipulator.js'
import SelectorEngine from '../dom/selector-engine.js'
import { isElement } from './index.js'
/**

View File

@@ -1,12 +1,12 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): util/swipe.js
* Bootstrap util/swipe.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import Config from './config.js'
import EventHandler from '../dom/event-handler.js'
import Config from './config.js'
import { execute } from './index.js'
/**

View File

@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.3.0-alpha1): 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.js'
import { execute, getElement, isElement } from './index.js'
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