aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlackDex <[email protected]>2022-06-08 19:46:33 +0200
committerBlackDex <[email protected]>2022-06-14 14:51:51 +0200
commit5d05ec58be9e6dcb028d69a4280a1d9e9d99f20e (patch)
treea7e1f4403a922b8624fb8e42fed5ba25533115e1
parentf95bd3bb04839ea4fa8f2700cd3867ee12b260b0 (diff)
downloadvaultwarden-5d05ec58be9e6dcb028d69a4280a1d9e9d99f20e.tar.gz
vaultwarden-5d05ec58be9e6dcb028d69a4280a1d9e9d99f20e.zip
Updated deps and misc fixes and updates
- Updated some Rust dependencies - Fixed an issue with CSP header, this was not configured correctly - Prevent sending CSP and Frame headers for the MFA connector.html files. Else some clients will fail to handle these protocols. - Add `unsafe-inline` for `script-src` only to the CSP for the Admin Interface - Updated JavaScript and CSS files for the Admin interface - Changed the layout for showing overridden settings, better visible now. - Made the version check cachable to prevent hitting the Github API rate limits - Hide the `database_url` as if it is a password in the Admin Interface Else for MariaDB/MySQL or PostgreSQL this was plain text. - Fixed an issue that pressing enter on the SMTP Test would save the config. resolves #2542 - Prevent user names larger then 50 characters resolves #2419
-rw-r--r--.github/workflows/build.yml2
-rw-r--r--.pre-commit-config.yaml8
-rw-r--r--Cargo.lock310
-rw-r--r--Cargo.toml12
-rw-r--r--src/api/admin.rs77
-rw-r--r--src/api/core/accounts.rs14
-rw-r--r--src/config.rs5
-rw-r--r--src/db/models/org_policy.rs2
-rw-r--r--src/static/scripts/bootstrap-native.js1619
-rw-r--r--src/static/scripts/bootstrap.css3496
-rw-r--r--src/static/scripts/datatables.css302
-rw-r--r--src/static/scripts/datatables.js10716
-rw-r--r--src/static/templates/admin/base.hbs11
-rw-r--r--src/static/templates/admin/settings.hbs63
-rw-r--r--src/util.rs49
15 files changed, 8890 insertions, 7796 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7857fa8a..674e37c3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,6 @@ on:
- "migrations/**"
- "Cargo.*"
- "build.rs"
- - "diesel.toml"
- "rust-toolchain"
pull_request:
paths:
@@ -17,7 +16,6 @@ on:
- "migrations/**"
- "Cargo.*"
- "build.rs"
- - "diesel.toml"
- "rust-toolchain"
jobs:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8c545317..8f31035a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.2.0
+ rev: v4.3.0
hooks:
- id: check-yaml
- id: check-json
@@ -26,7 +26,8 @@ repos:
entry: cargo test
language: system
args: ["--features", "sqlite,mysql,postgresql,enable_mimalloc", "--"]
- types: [rust]
+ types_or: [file, rust]
+ files: (Cargo.toml|Cargo.lock)
pass_filenames: false
- id: cargo-clippy
name: cargo clippy
@@ -34,5 +35,6 @@ repos:
entry: cargo clippy
language: system
args: ["--features", "sqlite,mysql,postgresql,enable_mimalloc", "--", "-D", "warnings"]
- types: [rust]
+ types_or: [file, rust]
+ files: (Cargo.toml|Cargo.lock)
pass_filenames: false
diff --git a/Cargo.lock b/Cargo.lock
index 43ffab21..007b355d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,12 +179,6 @@ dependencies = [
]
[[package]]
-name = "base-x"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74"
-
-[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -279,9 +273,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cached"
-version = "0.34.0"
+version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aadf76ddea74bab35ebeb8f1eb115b9bc04eaee42d8acc0d5f477dee6b176c9a"
+checksum = "12f5cd208ba696f870238022d81ca1d80ed9d696fd62341c747f2d8f6ecdd9fe"
dependencies = [
"async-trait",
"async_once",
@@ -371,23 +365,6 @@ dependencies = [
]
[[package]]
-name = "const_fn"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
-
-[[package]]
-name = "cookie"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
-dependencies = [
- "percent-encoding 2.1.0",
- "time 0.2.27",
- "version_check",
-]
-
-[[package]]
name = "cookie"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -407,27 +384,11 @@ dependencies = [
[[package]]
name = "cookie_store"
-version = "0.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3f7034c0932dc36f5bd8ec37368d971346809435824f277cb3b8299fc56167c"
-dependencies = [
- "cookie 0.15.1",
- "idna 0.2.3",
- "log",
- "publicsuffix",
- "serde",
- "serde_json",
- "time 0.2.27",
- "url 2.2.2",
-]
-
-[[package]]
-name = "cookie_store"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd"
dependencies = [
- "cookie 0.16.0",
+ "cookie",
"idna 0.2.3",
"log",
"publicsuffix",
@@ -695,12 +656,6 @@ dependencies = [
]
[[package]]
-name = "discard"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
-
-[[package]]
name = "dotenvy"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -717,9 +672,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "email-encoding"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75b91dddc343e7eaa27f9764e5bffe57370d957017fdd75244f5045e829a8441"
+checksum = "827e1fb86d24d558ab0454ca3fa084f8a6144ade1e3e6982f697c586bf96b41b"
dependencies = [
"base64",
"memchr",
@@ -742,9 +697,9 @@ dependencies = [
[[package]]
name = "enum-as-inner"
-version = "0.3.4"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4"
+checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73"
dependencies = [
"heck",
"proc-macro2",
@@ -970,13 +925,13 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.10.2+wasi-snapshot-preview1",
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
@@ -1033,7 +988,7 @@ dependencies = [
"indexmap",
"slab",
"tokio",
- "tokio-util 0.7.2",
+ "tokio-util 0.7.3",
"tracing",
]
@@ -1045,9 +1000,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "handlebars"
-version = "4.3.0"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d113a9853e5accd30f43003560b5563ffbb007e3f325e8b103fa0d0029c6e6df"
+checksum = "b66d0c1b6e3abfd1e72818798925e16e02ed77e1b47f6c25a95a23b377ee4299"
dependencies = [
"log",
"pest",
@@ -1125,9 +1080,9 @@ dependencies = [
[[package]]
name = "http"
-version = "0.2.7"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
+checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [
"bytes",
"fnv",
@@ -1174,7 +1129,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.4",
+ "socket2",
"tokio",
"tower-service",
"tracing",
@@ -1250,14 +1205,14 @@ dependencies = [
[[package]]
name = "ipconfig"
-version = "0.2.2"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
+checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98"
dependencies = [
- "socket2 0.3.19",
+ "socket2",
"widestring",
"winapi",
- "winreg 0.6.2",
+ "winreg 0.7.0",
]
[[package]]
@@ -1337,7 +1292,7 @@ dependencies = [
"once_cell",
"quoted_printable",
"serde",
- "socket2 0.4.4",
+ "socket2",
"tracing",
]
@@ -2194,15 +2149,15 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.10"
+version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
+checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92"
dependencies = [
"async-compression",
"base64",
"bytes",
- "cookie 0.15.1",
- "cookie_store 0.15.1",
+ "cookie",
+ "cookie_store",
"encoding_rs",
"futures-core",
"futures-util",
@@ -2226,7 +2181,8 @@ dependencies = [
"tokio",
"tokio-native-tls",
"tokio-socks",
- "tokio-util 0.6.10",
+ "tokio-util 0.7.3",
+ "tower-service",
"trust-dns-resolver",
"url 2.2.2",
"wasm-bindgen",
@@ -2314,7 +2270,7 @@ dependencies = [
"time 0.3.9",
"tokio",
"tokio-stream",
- "tokio-util 0.7.2",
+ "tokio-util 0.7.3",
"ubyte",
"version_check",
"yansi",
@@ -2342,7 +2298,7 @@ version = "0.5.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2"
dependencies = [
- "cookie 0.16.0",
+ "cookie",
"either",
"futures",
"http",
@@ -2373,15 +2329,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
-dependencies = [
- "semver",
-]
-
-[[package]]
name = "rustls"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2488,21 +2435,6 @@ dependencies = [
]
[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
-[[package]]
name = "serde"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2580,15 +2512,6 @@ dependencies = [
[[package]]
name = "sha1"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
-dependencies = [
- "sha1_smol",
-]
-
-[[package]]
-name = "sha1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
@@ -2599,12 +2522,6 @@ dependencies = [
]
[[package]]
-name = "sha1_smol"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
-
-[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2665,17 +2582,6 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "socket2"
-version = "0.3.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
-dependencies = [
- "cfg-if",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
@@ -2706,15 +2612,6 @@ dependencies = [
]
[[package]]
-name = "standback"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
-dependencies = [
- "version_check",
-]
-
-[[package]]
name = "state"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2724,55 +2621,6 @@ dependencies = [
]
[[package]]
-name = "stdweb"
-version = "0.4.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
-dependencies = [
- "discard",
- "rustc_version",
- "stdweb-derive",
- "stdweb-internal-macros",
- "stdweb-internal-runtime",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "stdweb-derive"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
-dependencies = [
- "proc-macro2",
- "quote",
- "serde",
- "serde_derive",
- "syn",
-]
-
-[[package]]
-name = "stdweb-internal-macros"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
-dependencies = [
- "base-x",
- "proc-macro2",
- "quote",
- "serde",
- "serde_derive",
- "serde_json",
- "sha1 0.6.1",
- "syn",
-]
-
-[[package]]
-name = "stdweb-internal-runtime"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
-
-[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2872,21 +2720,6 @@ dependencies = [
[[package]]
name = "time"
-version = "0.2.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
-dependencies = [
- "const_fn",
- "libc",
- "standback",
- "stdweb",
- "time-macros 0.1.1",
- "version_check",
- "winapi",
-]
-
-[[package]]
-name = "time"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
@@ -2894,17 +2727,7 @@ dependencies = [
"itoa",
"libc",
"num_threads",
- "time-macros 0.2.4",
-]
-
-[[package]]
-name = "time-macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
-dependencies = [
- "proc-macro-hack",
- "time-macros-impl",
+ "time-macros",
]
[[package]]
@@ -2914,19 +2737,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
-name = "time-macros-impl"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
-dependencies = [
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "standback",
- "syn",
-]
-
-[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2943,9 +2753,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.19.0"
+version = "1.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f392c8f16bda3456c0b00c6de39cb100449b98de55ac41c6cdd2bfcf53a1245"
+checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
dependencies = [
"bytes",
"libc",
@@ -2956,16 +2766,16 @@ dependencies = [
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
- "socket2 0.4.4",
+ "socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
+checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
@@ -3007,9 +2817,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -3044,9 +2854,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
+checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
dependencies = [
"bytes",
"futures-core",
@@ -3085,9 +2895,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
-version = "0.1.34"
+version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
+checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if",
"log",
@@ -3109,11 +2919,11 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
+checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
dependencies = [
- "lazy_static",
+ "once_cell",
"valuable",
]
@@ -3148,9 +2958,9 @@ dependencies = [
[[package]]
name = "trust-dns-proto"
-version = "0.20.4"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca94d4e9feb6a181c690c4040d7a24ef34018d8313ac5044a61d21222ae24e31"
+checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d"
dependencies = [
"async-trait",
"cfg-if",
@@ -3173,9 +2983,9 @@ dependencies = [
[[package]]
name = "trust-dns-resolver"
-version = "0.20.4"
+version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecae383baad9995efaa34ce8e57d12c3f305e545887472a492b838f4b5cfb77a"
+checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558"
dependencies = [
"cfg-if",
"futures-util",
@@ -3183,7 +2993,7 @@ dependencies = [
"lazy_static",
"log",
"lru-cache",
- "parking_lot 0.11.2",
+ "parking_lot 0.12.1",
"resolv-conf",
"smallvec",
"thiserror",
@@ -3255,9 +3065,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-normalization"
@@ -3322,9 +3132,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
-version = "1.1.1"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6d5d669b51467dcf7b2f1a796ce0f955f05f01cafda6c19d6e95f730df29238"
+checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"getrandom",
]
@@ -3344,8 +3154,8 @@ dependencies = [
"cached",
"chrono",
"chrono-tz",
- "cookie 0.16.0",
- "cookie_store 0.16.1",
+ "cookie",
+ "cookie_store",
"ctrlc",
"dashmap",
"data-encoding",
@@ -3543,9 +3353,9 @@ dependencies = [
[[package]]
name = "widestring"
-version = "0.4.3"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c"
+checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
[[package]]
name = "winapi"
@@ -3623,9 +3433,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
-version = "0.6.2"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
+checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi",
]
@@ -3657,6 +3467,6 @@ dependencies = [
"hmac",
"rand",
"reqwest",
- "sha1 0.10.1",
+ "sha1",
"threadpool",
]
diff --git a/Cargo.toml b/Cargo.toml
index 25b84a07..dbc1fc1a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,7 @@ syslog = "6.0.1" # Needs to be v4 until fern is updated
# Logging
log = "0.4.17"
fern = { version = "0.6.1", features = ["syslog-6"] }
-tracing = { version = "0.1.34", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work
+tracing = { version = "0.1.35", features = ["log"] } # Needed to have lettre and webauthn-rs trace logging to work
backtrace = "0.3.65" # Logging panics to logfile instead stderr only
@@ -61,7 +61,7 @@ dashmap = "5.3.4" # Concurrent hashmap implementation
# Async futures
futures = "0.3.21"
-tokio = { version = "1.19.0", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time"] }
+tokio = { version = "1.19.2", features = ["rt-multi-thread", "fs", "io-util", "parking_lot", "time"] }
# A generic serialization/deserialization framework
serde = { version = "1.0.137", features = ["derive"] }
@@ -79,7 +79,7 @@ rand = { version = "0.8.5", features = ["small_rng"] }
ring = "0.16.20"
# UUID generation
-uuid = { version = "1.1.1", features = ["v4"] }
+uuid = { version = "1.1.2", features = ["v4"] }
# Date and time libraries
chrono = { version = "0.4.19", features = ["clock", "serde"], default-features = false }
@@ -112,17 +112,17 @@ lettre = { version = "0.10.0-rc.7", features = ["smtp-transport", "builder", "se
percent-encoding = "2.1.0" # URL encoding library used for URL's in the emails
# Template library
-handlebars = { version = "4.3.0", features = ["dir_source"] }
+handlebars = { version = "4.3.1", features = ["dir_source"] }
# HTTP client
-reqwest = { version = "0.11.10", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] }
+reqwest = { version = "0.11.11", features = ["stream", "json", "gzip", "brotli", "socks", "cookies", "trust-dns"] }
# For favicon extraction from main website
html5gum = "0.4.0"
regex = { version = "1.5.6", features = ["std", "perf", "unicode-perl"], default-features = false }
data-url = "0.1.1"
bytes = "1.1.0"
-cached = "0.34.0"
+cached = "0.34.1"
# Used for custom short lived cookie jar during favicon extraction
cookie = "0.16.0"
diff --git a/src/api/admin.rs b/src/api/admin.rs
index 37b169ed..63edde66 100644
--- a/src/api/admin.rs
+++ b/src/api/admin.rs
@@ -491,41 +491,14 @@ async fn has_http_access() -> bool {
}
}
-#[get("/diagnostics")]
-async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResult<Html<String>> {
- use crate::util::read_file_string;
- use chrono::prelude::*;
- use std::net::ToSocketAddrs;
-
- // Get current running versions
- let web_vault_version: WebVaultVersion =
- match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "vw-version.json")) {
- Ok(s) => serde_json::from_str(&s)?,
- _ => match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "version.json")) {
- Ok(s) => serde_json::from_str(&s)?,
- _ => WebVaultVersion {
- version: String::from("Version file missing"),
- },
- },
- };
-
- // Execute some environment checks
- let running_within_docker = is_running_in_docker();
- let has_http_access = has_http_access().await;
- let uses_proxy = env::var_os("HTTP_PROXY").is_some()
- || env::var_os("http_proxy").is_some()
- || env::var_os("HTTPS_PROXY").is_some()
- || env::var_os("https_proxy").is_some();
-
- // Check if we are able to resolve DNS entries
- let dns_resolved = match ("github.com", 0).to_socket_addrs().map(|mut i| i.next()) {
- Ok(Some(a)) => a.ip().to_string(),
- _ => "Could not resolve domain name.".to_string(),
- };
-
+use cached::proc_macro::cached;
+/// Cache this function to prevent API call rate limit. Github only allows 60 requests per hour, and we use 3 here already.
+/// It will cache this function for 300 seconds (5 minutes) which should prevent the exhaustion of the rate limit.
+#[cached(time = 300, sync_writes = true)]
+async fn get_release_info(has_http_access: bool, running_within_docker: bool) -> (String, String, String) {
// If the HTTP Check failed, do not even attempt to check for new versions since we were not able to connect with github.com anyway.
- // TODO: Maybe we need to cache this using a LazyStatic or something. Github only allows 60 requests per hour, and we use 3 here already.
- let (latest_release, latest_commit, latest_web_build) = if has_http_access {
+ if has_http_access {
+ info!("Running get_release_info!!");
(
match get_github_api::<GitRelease>("https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest")
.await
@@ -558,8 +531,44 @@ async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> A
)
} else {
("-".to_string(), "-".to_string(), "-".to_string())
+ }
+}
+
+#[get("/diagnostics")]
+async fn diagnostics(_token: AdminToken, ip_header: IpHeader, conn: DbConn) -> ApiResult<Html<String>> {
+ use crate::util::read_file_string;
+ use chrono::prelude::*;
+ use std::net::ToSocketAddrs;
+
+ // Get current running versions
+ let web_vault_version: WebVaultVersion =
+ match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "vw-version.json")) {
+ Ok(s) => serde_json::from_str(&s)?,
+ _ => match read_file_string(&format!("{}/{}", CONFIG.web_vault_folder(), "version.json")) {
+ Ok(s) => serde_json::from_str(&s)?,
+ _ => WebVaultVersion {
+ version: String::from("Version file missing"),
+ },
+ },
+ };
+
+ // Execute some environment checks
+ let running_within_docker = is_running_in_docker();
+ let has_http_access = has_http_access().await;
+ let uses_proxy = env::var_os("HTTP_PROXY").is_some()
+ || env::var_os("http_proxy").is_some()
+ || env::var_os("HTTPS_PROXY").is_some()
+ || env::var_os("https_proxy").is_some();
+
+ // Check if we are able to resolve DNS entries
+ let dns_resolved = match ("github.com", 0).to_socket_addrs().map(|mut i| i.next()) {
+ Ok(Some(a)) => a.ip().to_string(),
+ _ => "Could not resolve domain name.".to_string(),
};
+ let (latest_release, latest_commit, latest_web_build) =
+ get_release_info(has_http_access, running_within_docker).await;
+
let ip_header_name = match &ip_header.0 {
Some(h) => h,
_ => "",
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
index d0a9b37a..61299de6 100644
--- a/src/api/core/accounts.rs
+++ b/src/api/core/accounts.rs
@@ -67,6 +67,14 @@ async fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
let data: RegisterData = data.into_inner().data;
let email = data.Email.to_lowercase();
+ // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden)
+ // This also prevents issues with very long usernames causing to large JWT's. See #2419
+ if let Some(ref name) = data.Name {
+ if name.len() > 50 {
+ err!("The field Name must be a string with a maximum length of 50.");
+ }
+ }
+
let mut user = match User::find_by_mail(&email, &conn).await {
Some(user) => {
if !user.password_hash.is_empty() {
@@ -176,6 +184,12 @@ async fn put_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbCo
async fn post_profile(data: JsonUpcase<ProfileData>, headers: Headers, conn: DbConn) -> JsonResult {
let data: ProfileData = data.into_inner().data;
+ // Check if the length of the username exceeds 50 characters (Same is Upstream Bitwarden)
+ // This also prevents issues with very long usernames causing to large JWT's. See #2419
+ if data.Name.len() > 50 {
+ err!("The field Name must be a string with a maximum length of 50.");
+ }
+
let mut user = headers.user;
user.name = data.Name;
diff --git a/src/config.rs b/src/config.rs
index 847382cd..a0fad9d9 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1058,12 +1058,11 @@ fn js_escape_helper<'reg, 'rc>(
_rc: &mut RenderContext<'reg, 'rc>,
out: &mut dyn Output,
) -> HelperResult {
- let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"js_escape\""))?;
+ let param = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"jsesc\""))?;
let no_quote = h.param(1).is_some();
- let value =
- param.value().as_str().ok_or_else(|| RenderError::new("Param for helper \"js_escape\" is not a String"))?;
+ let value = param.value().as_str().ok_or_else(|| RenderError::new("Param for helper \"jsesc\" is not a String"))?;
let mut escaped_value = value.replace('\\', "").replace('\'', "\\x22").replace('\"', "\\x27");
if !no_quote {
diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs
index 95045f3e..65ec0fd8 100644
--- a/src/db/models/org_policy.rs
+++ b/src/db/models/org_policy.rs
@@ -21,7 +21,7 @@ db_object! {
}
}
-#[derive(Copy, Clone, PartialEq, num_derive::FromPrimitive)]
+#[derive(Copy, Clone, Eq, PartialEq, num_derive::FromPrimitive)]
pub enum OrgPolicyType {
TwoFactorAuthentication = 0,
MasterPassword = 1,
diff --git a/src/static/scripts/bootstrap-native.js b/src/static/scripts/bootstrap-native.js
index fa266298..bf26cef8 100644
--- a/src/static/scripts/bootstrap-native.js
+++ b/src/static/scripts/bootstrap-native.js
@@ -1,5 +1,5 @@
/*!
- * Native JavaScript for Bootstrap v4.1.2 (https://thednp.github.io/bootstrap.native/)
+ * Native JavaScript for Bootstrap v4.2.0 (https://thednp.github.io/bootstrap.native/)
* Copyright 2015-2022 © dnp_theme
* Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)
*/
@@ -15,27 +15,26 @@
/**
* The global event listener.
*
- * @this {Element | HTMLElement | Window | Document}
- * @param {Event} e
- * @returns {void}
+ * @type {EventListener}
+ * @this {EventTarget}
*/
function globalListener(e) {
const that = this;
const { type } = e;
- const oneEvMap = EventRegistry[type] ? [...EventRegistry[type]] : [];
- oneEvMap.forEach((elementsMap) => {
+ [...EventRegistry[type]].forEach((elementsMap) => {
const [element, listenersMap] = elementsMap;
- [...listenersMap].forEach((listenerMap) => {
- if (element === that) {
+ /* istanbul ignore else */
+ if (element === that) {
+ [...listenersMap].forEach((listenerMap) => {
const [listener, options] = listenerMap;
listener.apply(element, [e]);
if (options && options.once) {
removeListener(element, type, listener, options);
}
- }
- });
+ });
+ }
});
}
@@ -43,10 +42,7 @@
* Register a new listener with its options and attach the `globalListener`
* to the target if this is the first listener.
*
- * @param {Element | HTMLElement | Window | Document} element
- * @param {string} eventType
- * @param {EventListenerObject['handleEvent']} listener
- * @param {AddEventListenerOptions=} options
+ * @type {Listener.ListenerAction<EventTarget>}
*/
const addListener = (element, eventType, listener, options) => {
// get element listeners first
@@ -64,9 +60,7 @@
const { size } = oneElementMap;
// register listener with its options
- if (oneElementMap) {
- oneElementMap.set(listener, options);
- }
+ oneElementMap.set(listener, options);
// add listener last
if (!size) {
@@ -78,10 +72,7 @@
* Remove a listener from registry and detach the `globalListener`
* if no listeners are found in the registry.
*
- * @param {Element | HTMLElement | Window | Document} element
- * @param {string} eventType
- * @param {EventListenerObject['handleEvent']} listener
- * @param {AddEventListenerOptions=} options
+ * @type {Listener.ListenerAction<EventTarget>}
*/
const removeListener = (element, eventType, listener, options) => {
// get listener first
@@ -100,6 +91,7 @@
if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];
// remove listener last
+ /* istanbul ignore else */
if (!oneElementMap || !oneElementMap.size) {
element.removeEventListener(eventType, globalListener, eventOptions);
}
@@ -111,7 +103,7 @@
* @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js
* @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8
*/
- const EventListener = {
+ const Listener = {
on: addListener,
off: removeListener,
globalListener,
@@ -150,34 +142,35 @@
* * If `element` parameter is not an `HTMLElement`, `getComputedStyle`
* throws a `ReferenceError`.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {string} property the css property
* @return {string} the css property value
*/
function getElementStyle(element, property) {
const computedStyle = getComputedStyle(element);
- // @ts-ignore -- must use camelcase strings,
+ // must use camelcase strings,
// or non-camelcase strings with `getPropertyValue`
- return property in computedStyle ? computedStyle[property] : '';
+ return property.includes('--')
+ ? computedStyle.getPropertyValue(property)
+ : computedStyle[property];
}
/**
* Utility to get the computed `transitionDelay`
* from Element in miliseconds.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @return {number} the value in miliseconds
*/
function getElementTransitionDelay(element) {
const propertyValue = getElementStyle(element, transitionProperty);
const delayValue = getElementStyle(element, transitionDelay);
-
- const delayScale = delayValue.includes('ms') ? 1 : 1000;
+ const delayScale = delayValue.includes('ms') ? /* istanbul ignore next */1 : 1000;
const duration = propertyValue && propertyValue !== 'none'
? parseFloat(delayValue) * delayScale : 0;
- return !Number.isNaN(duration) ? duration : 0;
+ return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0;
}
/**
@@ -190,24 +183,32 @@
* Utility to get the computed `transitionDuration`
* from Element in miliseconds.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @return {number} the value in miliseconds
*/
function getElementTransitionDuration(element) {
const propertyValue = getElementStyle(element, transitionProperty);
const durationValue = getElementStyle(element, transitionDuration);
- const durationScale = durationValue.includes('ms') ? 1 : 1000;
+ const durationScale = durationValue.includes('ms') ? /* istanbul ignore next */1 : 1000;
const duration = propertyValue && propertyValue !== 'none'
? parseFloat(durationValue) * durationScale : 0;
- return !Number.isNaN(duration) ? duration : 0;
+ return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0;
}
/**
+ * Shortcut for the `Element.dispatchEvent(Event)` method.
+ *
+ * @param {HTMLElement} element is the target
+ * @param {Event} event is the `Event` object
+ */
+ const dispatchEvent = (element, event) => element.dispatchEvent(event);
+
+ /**
* Utility to make sure callbacks are consistently
* called when transition ends.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {EventListener} handler `transitionend` callback
*/
function emulateTransitionEnd(element, handler) {
@@ -222,6 +223,7 @@
* @type {EventListener} e Event object
*/
const transitionEndWrapper = (e) => {
+ /* istanbul ignore else */
if (e.target === element) {
handler.apply(element, [e]);
element.removeEventListener(transitionEndEvent, transitionEndWrapper);
@@ -230,7 +232,8 @@
};
element.addEventListener(transitionEndEvent, transitionEndWrapper);
setTimeout(() => {
- if (!called) element.dispatchEvent(endEvent);
+ /* istanbul ignore next */
+ if (!called) dispatchEvent(element, endEvent);
}, duration + delay + 17);
} else {
handler.apply(element, [endEvent]);
@@ -238,43 +241,64 @@
}
/**
- * Returns the `document` or the `#document` element.
- * @see https://github.com/floating-ui/floating-ui
- * @param {(Node | HTMLElement | Element | globalThis)=} node
- * @returns {Document}
+ * Checks if an object is a `Node`.
+ *
+ * @param {any} node the target object
+ * @returns {boolean} the query result
*/
- function getDocument(node) {
- if (node instanceof HTMLElement) return node.ownerDocument;
- if (node instanceof Window) return node.document;
- return window.document;
- }
+ const isNode = (element) => (element && [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
+ .some((x) => +element.nodeType === x)) || false;
/**
- * A global array of possible `ParentNode`.
+ * Check if a target object is `Window`.
+ * => equivalent to `object instanceof Window`
+ *
+ * @param {any} object the target object
+ * @returns {boolean} the query result
*/
- const parentNodes = [Document, Element, HTMLElement];
+ const isWindow = (object) => (object && object.constructor.name === 'Window') || false;
/**
- * A global array with `Element` | `HTMLElement`.
+ * Checks if an object is a `Document`.
+ * @see https://dom.spec.whatwg.org/#node
+ *
+ * @param {any} object the target object
+ * @returns {boolean} the query result
*/
- const elementNodes = [Element, HTMLElement];
+ const isDocument = (object) => (object && object.nodeType === 9) || false;
+
+ /**
+ * Returns the `document` or the `#document` element.
+ * @see https://github.com/floating-ui/floating-ui
+ * @param {(Node | Window)=} node
+ * @returns {Document}
+ */
+ function getDocument(node) {
+ // node instanceof Document
+ if (isDocument(node)) return node;
+ // node instanceof Node
+ if (isNode(node)) return node.ownerDocument;
+ // node instanceof Window
+ if (isWindow(node)) return node.document;
+ // node is undefined | NULL
+ return window.document;
+ }
/**
* Utility to check if target is typeof `HTMLElement`, `Element`, `Node`
* or find one that matches a selector.
*
- * @param {HTMLElement | Element | string} selector the input selector or target element
- * @param {(HTMLElement | Element | Document)=} parent optional node to look into
- * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result
+ * @param {Node | string} selector the input selector or target element
+ * @param {ParentNode=} parent optional node to look into
+ * @return {HTMLElement?} the `HTMLElement` or `querySelector` result
*/
function querySelector(selector, parent) {
- const lookUp = parentNodes.some((x) => parent instanceof x)
- ? parent : getDocument();
+ if (isNode(selector)) {
+ return selector;
+ }
+ const lookUp = isNode(parent) ? parent : getDocument();
- // @ts-ignore
- return elementNodes.some((x) => selector instanceof x)
- // @ts-ignore
- ? selector : lookUp.querySelector(selector);
+ return lookUp.querySelector(selector);
}
/**
@@ -284,13 +308,13 @@
*
* @see https://stackoverflow.com/q/54520554/803358
*
- * @param {HTMLElement | Element} element Element to look into
+ * @param {HTMLElement} element Element to look into
* @param {string} selector the selector name
- * @return {(HTMLElement | Element)?} the query result
+ * @return {HTMLElement?} the query result
*/
function closest(element, selector) {
return element ? (element.closest(selector)
- // @ts-ignore -- break out of `ShadowRoot`
+ // break out of `ShadowRoot`
|| closest(element.getRootNode().host, selector)) : null;
}
@@ -304,7 +328,7 @@
/**
* Check class in `HTMLElement.classList`.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {string} classNAME to check
* @returns {boolean}
*/
@@ -315,7 +339,7 @@
/**
* Remove class from `HTMLElement.classList`.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {string} classNAME to remove
* @returns {void}
*/
@@ -324,14 +348,15 @@
}
/**
- * Shortcut for the `Element.dispatchEvent(Event)` method.
+ * Checks if an element is an `HTMLElement`.
+ * @see https://dom.spec.whatwg.org/#node
*
- * @param {HTMLElement | Element} element is the target
- * @param {Event} event is the `Event` object
+ * @param {any} element the target object
+ * @returns {boolean} the query result
*/
- const dispatchEvent = (element, event) => element.dispatchEvent(event);
+ const isHTMLElement = (element) => (element && element.nodeType === 1) || false;
- /** @type {Map<string, Map<HTMLElement | Element, Record<string, any>>>} */
+ /** @type {Map<string, Map<HTMLElement, Record<string, any>>>} */
const componentData = new Map();
/**
* An interface for web components background data.
@@ -340,27 +365,27 @@
const Data = {
/**
* Sets web components data.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {string} component the component's name or a unique key
* @param {Record<string, any>} instance the component instance
*/
- set: (target, component, instance) => {
- const element = querySelector(target);
- if (!element) return;
+ set: (element, component, instance) => {
+ if (!isHTMLElement(element)) return;
+ /* istanbul ignore else */
if (!componentData.has(component)) {
componentData.set(component, new Map());
}
const instanceMap = componentData.get(component);
- // @ts-ignore - not undefined, but defined right above
+ // not undefined, but defined right above
instanceMap.set(element, instance);
},
/**
* Returns all instances for specified component.
* @param {string} component the component's name or a unique key
- * @returns {Map<HTMLElement | Element, Record<string, any>>?} all the component instances
+ * @returns {Map<HTMLElement, Record<string, any>>?} all the component instances
*/
getAllFor: (component) => {
const instanceMap = componentData.get(component);
@@ -370,12 +395,12 @@
/**
* Returns the instance associated with the target.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {string} component the component's name or a unique key
* @returns {Record<string, any>?} the instance
*/
- get: (target, component) => {
- const element = querySelector(target);
+ get: (element, component) => {
+ if (!isHTMLElement(element) || !component) return null;
const allForC = Data.getAllFor(component);
const instance = element && allForC && allForC.get(element);
@@ -384,16 +409,16 @@
/**
* Removes web components data.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {string} component the component's name or a unique key
*/
- remove: (target, component) => {
- const element = querySelector(target);
+ remove: (element, component) => {
const instanceMap = componentData.get(component);
- if (!instanceMap || !element) return;
+ if (!instanceMap || !isHTMLElement(element)) return;
instanceMap.delete(element);
+ /* istanbul ignore else */
if (instanceMap.size === 0) {
componentData.delete(component);
}
@@ -402,22 +427,31 @@
/**
* An alias for `Data.get()`.
- * @type {SHORTER.getInstance<any>}
+ * @type {SHORTY.getInstance<any>}
*/
const getInstance = (target, component) => Data.get(target, component);
/**
+ * Checks if an object is an `Object`.
+ *
+ * @param {any} obj the target object
+ * @returns {boolean} the query result
+ */
+ const isObject = (obj) => (typeof obj === 'object') || false;
+
+ /**
* Returns a namespaced `CustomEvent` specific to each component.
* @param {string} EventType Event.type
* @param {Record<string, any>=} config Event.options | Event.properties
- * @returns {SHORTER.OriginalEvent} a new namespaced event
+ * @returns {SHORTY.OriginalEvent} a new namespaced event
*/
function OriginalEvent(EventType, config) {
const OriginalCustomEvent = new CustomEvent(EventType, {
cancelable: true, bubbles: true,
});
- if (config instanceof Object) {
+ /* istanbul ignore else */
+ if (isObject(config)) {
ObjectAssign(OriginalCustomEvent, config);
}
return OriginalCustomEvent;
@@ -446,7 +480,7 @@
/**
* Shortcut for `HTMLElement.getAttribute()` method.
- * @param {HTMLElement | Element} element target element
+ * @param {HTMLElement} element target element
* @param {string} attribute attribute name
* @returns {string?} attribute value
*/
@@ -465,22 +499,24 @@
* @return {niceValue} the normalized value
*/
function normalizeValue(value) {
- if (value === 'true') { // boolean
+ if (['true', true].includes(value)) { // boolean
+ // if ('true' === value) { // boolean
return true;
}
- if (value === 'false') { // boolean
+ if (['false', false].includes(value)) { // boolean
+ // if ('false' === value) { // boolean
return false;
}
- if (!Number.isNaN(+value)) { // number
- return +value;
- }
-
if (value === '' || value === 'null') { // null
return null;
}
+ if (value !== '' && !Number.isNaN(+value)) { // number
+ return +value;
+ }
+
// string / function / HTMLElement / object
return value;
}
@@ -503,14 +539,13 @@
/**
* Utility to normalize component options.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {Record<string, any>} defaultOps component default options
* @param {Record<string, any>} inputOps component instance options
* @param {string=} ns component namespace
* @return {Record<string, any>} normalized component options object
*/
function normalizeOptions(element, defaultOps, inputOps, ns) {
- // @ts-ignore -- our targets are always `HTMLElement`
const data = { ...element.dataset };
/** @type {Record<string, any>} */
const normalOps = {};
@@ -531,6 +566,7 @@
});
ObjectKeys(defaultOps).forEach((k) => {
+ /* istanbul ignore else */
if (k in inputOps) {
normalOps[k] = inputOps[k];
} else if (k in dataOps) {
@@ -545,7 +581,7 @@
return normalOps;
}
- var version = "4.1.2";
+ var version = "4.2.0";
const Version = version;
@@ -555,7 +591,7 @@
/** Returns a new `BaseComponent` instance. */
class BaseComponent {
/**
- * @param {HTMLElement | Element | string} target `Element` or selector string
+ * @param {HTMLElement | string} target `Element` or selector string
* @param {BSN.ComponentOptions=} config component instance options
*/
constructor(target, config) {
@@ -572,10 +608,11 @@
const prevInstance = Data.get(element, self.name);
if (prevInstance) prevInstance.dispose();
- /** @type {HTMLElement | Element} */
+ /** @type {HTMLElement} */
self.element = element;
- if (self.defaults && Object.keys(self.defaults).length) {
+ /* istanbul ignore else */
+ if (self.defaults && ObjectKeys(self.defaults).length) {
self.options = normalizeOptions(element, self.defaults, (config || {}), 'bs');
}
@@ -583,15 +620,17 @@
}
/* eslint-disable */
+ /* istanbul ignore next */
/** @static */
get version() { return Version; }
- /* eslint-enable */
+ /* eslint-enable */
+ /* istanbul ignore next */
/** @static */
get name() { return this.constructor.name; }
+ /* istanbul ignore next */
/** @static */
- // @ts-ignore
get defaults() { return this.constructor.defaults; }
/**
@@ -600,7 +639,6 @@
dispose() {
const self = this;
Data.remove(self.element, self.name);
- // @ts-ignore
ObjectKeys(self).forEach((prop) => { self[prop] = null; });
}
}
@@ -658,6 +696,7 @@
function toggleAlertHandler(self, add) {
const action = add ? addListener : removeListener;
const { dismiss } = self;
+ /* istanbul ignore else */
if (dismiss) action(dismiss, mouseclickEvent, self.close);
}
@@ -665,7 +704,7 @@
// ================
/** Creates a new Alert instance. */
class Alert extends BaseComponent {
- /** @param {HTMLElement | Element | string} target element or selector */
+ /** @param {HTMLElement | string} target element or selector */
constructor(target) {
super(target);
// bind
@@ -675,7 +714,7 @@
const { element } = self;
// the dismiss button
- /** @static @type {(HTMLElement | Element)?} */
+ /** @static @type {HTMLElement?} */
self.dismiss = querySelector(alertDismissSelector, element);
// add event listener
@@ -685,7 +724,6 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return alertComponent; }
/* eslint-enable */
@@ -701,12 +739,11 @@
* @this {Alert} the `Alert` instance or `EventTarget`
*/
close(e) {
- // @ts-ignore
const self = e ? getAlertInstance(closest(this, alertSelector)) : this;
- if (!self) return;
const { element } = self;
- if (hasClass(element, showClass)) {
+ /* istanbul ignore else */
+ if (element && hasClass(element, showClass)) {
dispatchEvent(element, closeAlertEvent);
if (closeAlertEvent.defaultPrevented) return;
@@ -739,7 +776,7 @@
/**
* Shortcut for `HTMLElement.setAttribute()` method.
- * @param {HTMLElement | Element} element target element
+ * @param {HTMLElement} element target element
* @param {string} attribute attribute name
* @param {string} value attribute value
* @returns {void}
@@ -749,7 +786,7 @@
/**
* Add class to `HTMLElement.classList`.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @param {string} classNAME to add
* @returns {void}
*/
@@ -811,7 +848,7 @@
/** Creates a new `Button` instance. */
class Button extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target usually a `.btn` element
+ * @param {HTMLElement | string} target usually a `.btn` element
*/
constructor(target) {
super(target);
@@ -832,7 +869,6 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return buttonComponent; }
/* eslint-enable */
@@ -845,19 +881,16 @@
*/
toggle(e) {
if (e) e.preventDefault();
- // @ts-ignore
const self = e ? getButtonInstance(this) : this;
- if (!self) return;
- const { element } = self;
+ if (!self.element) return;
+ const { element, isActive } = self;
if (hasClass(element, 'disabled')) return;
- self.isActive = hasClass(element, activeClass);
- const { isActive } = self;
const action = isActive ? removeClass : addClass;
-
action(element, activeClass);
setAttribute(element, ariaPressed, isActive ? 'false' : 'true');
+ self.isActive = hasClass(element, activeClass);
}
/** Removes the `Button` component from the target element. */
@@ -892,24 +925,6 @@
const keydownEvent = 'keydown';
/**
- * A global namespace for `touchmove` event.
- * @type {string}
- */
- const touchmoveEvent = 'touchmove';
-
- /**
- * A global namespace for `touchend` event.
- * @type {string}
- */
- const touchendEvent = 'touchend';
-
- /**
- * A global namespace for `touchstart` event.
- * @type {string}
- */
- const touchstartEvent = 'touchstart';
-
- /**
* A global namespace for `ArrowLeft` key.
* @type {string} e.which = 37 equivalent
*/
@@ -922,34 +937,31 @@
const keyArrowRight = 'ArrowRight';
/**
- * Returns the `Window` object of a target node.
- * @see https://github.com/floating-ui/floating-ui
- *
- * @param {(Node | HTMLElement | Element | Window)=} node target node
- * @returns {globalThis}
+ * A global namespace for `pointerdown` event.
+ * @type {string}
*/
- function getWindow(node) {
- if (node == null) {
- return window;
- }
+ const pointerdownEvent = 'pointerdown';
- if (!(node instanceof Window)) {
- const { ownerDocument } = node;
- return ownerDocument ? ownerDocument.defaultView || window : window;
- }
+ /**
+ * A global namespace for `pointermove` event.
+ * @type {string}
+ */
+ const pointermoveEvent = 'pointermove';
- // @ts-ignore
- return node;
- }
+ /**
+ * A global namespace for `pointerup` event.
+ * @type {string}
+ */
+ const pointerupEvent = 'pointerup';
/**
* Returns the bounding client rect of a target `HTMLElement`.
*
* @see https://github.com/floating-ui/floating-ui
*
- * @param {HTMLElement | Element} element event.target
+ * @param {HTMLElement} element event.target
* @param {boolean=} includeScale when *true*, the target scale is also computed
- * @returns {SHORTER.BoundingClientRect} the bounding client rect object
+ * @returns {SHORTY.BoundingClientRect} the bounding client rect object
*/
function getBoundingClientRect(element, includeScale) {
const {
@@ -958,10 +970,12 @@
let scaleX = 1;
let scaleY = 1;
- if (includeScale && element instanceof HTMLElement) {
+ if (includeScale && isHTMLElement(element)) {
const { offsetWidth, offsetHeight } = element;
- scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1;
- scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1;
+ scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth
+ : /* istanbul ignore next */1;
+ scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight
+ : /* istanbul ignore next */1;
}
return {
@@ -979,8 +993,8 @@
/**
* Returns the `document.documentElement` or the `<html>` element.
*
- * @param {(Node | HTMLElement | Element | globalThis)=} node
- * @returns {HTMLElement | HTMLHtmlElement}
+ * @param {(Node | Window)=} node
+ * @returns {HTMLHtmlElement}
*/
function getDocumentElement(node) {
return getDocument(node).documentElement;
@@ -990,19 +1004,20 @@
* Utility to determine if an `HTMLElement`
* is partially visible in viewport.
*
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @return {boolean} the query result
*/
const isElementInScrollRange = (element) => {
+ if (!element || !isNode(element)) return false;
+
const { top, bottom } = getBoundingClientRect(element);
const { clientHeight } = getDocumentElement(element);
- // checks bottom && top
return top <= clientHeight && bottom >= 0;
};
/**
* Checks if a page is Right To Left.
- * @param {(HTMLElement | Element)=} node the target
+ * @param {HTMLElement=} node the target
* @returns {boolean} the query result
*/
const isRTL = (node) => getDocumentElement(node).dir === 'rtl';
@@ -1011,13 +1026,11 @@
* A shortcut for `(document|Element).querySelectorAll`.
*
* @param {string} selector the input selector
- * @param {(HTMLElement | Element | Document | Node)=} parent optional node to look into
- * @return {NodeListOf<HTMLElement | Element>} the query result
+ * @param {ParentNode=} parent optional node to look into
+ * @return {NodeListOf<HTMLElement>} the query result
*/
function querySelectorAll(selector, parent) {
- const lookUp = parent && parentNodes
- .some((x) => parent instanceof x) ? parent : getDocument();
- // @ts-ignore -- `ShadowRoot` is also a node
+ const lookUp = isNode(parent) ? parent : getDocument();
return lookUp.querySelectorAll(selector);
}
@@ -1026,16 +1039,15 @@
* like `ShadowRoot` do not support `getElementsByClassName`.
*
* @param {string} selector the class name
- * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
- * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
+ * @param {ParentNode=} parent optional Element to look into
+ * @return {HTMLCollectionOf<HTMLElement>} the 'HTMLCollection'
*/
function getElementsByClassName(selector, parent) {
- const lookUp = parent && parentNodes.some((x) => parent instanceof x)
- ? parent : getDocument();
+ const lookUp = isNode(parent) ? parent : getDocument();
return lookUp.getElementsByClassName(selector);
}
- /** @type {Map<HTMLElement | Element, any>} */
+ /** @type {Map<HTMLElement, any>} */
const TimeCache = new Map();
/**
* An interface for one or more `TimerHandler`s per `Element`.
@@ -1044,17 +1056,17 @@
const Timer = {
/**
* Sets a new timeout timer for an element, or element -> key association.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {ReturnType<TimerHandler>} callback the callback
* @param {number} delay the execution delay
* @param {string=} key a unique key
*/
- set: (target, callback, delay, key) => {
- const element = querySelector(target);
-
- if (!element) return;
+ set: (element, callback, delay, key) => {
+ if (!isHTMLElement(element)) return;
+ /* istanbul ignore else */
if (key && key.length) {
+ /* istanbul ignore else */
if (!TimeCache.has(element)) {
TimeCache.set(element, new Map());
}
@@ -1067,38 +1079,35 @@
/**
* Returns the timer associated with the target.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {string=} key a unique
* @returns {number?} the timer
*/
- get: (target, key) => {
- const element = querySelector(target);
-
- if (!element) return null;
+ get: (element, key) => {
+ if (!isHTMLElement(element)) return null;
const keyTimers = TimeCache.get(element);
if (key && key.length && keyTimers && keyTimers.get) {
- return keyTimers.get(key) || null;
+ return keyTimers.get(key) || /* istanbul ignore next */null;
}
return keyTimers || null;
},
/**
* Clears the element's timer.
- * @param {HTMLElement | Element | string} target target element
+ * @param {HTMLElement} element target element
* @param {string=} key a unique key
*/
- clear: (target, key) => {
- const element = querySelector(target);
-
- if (!element) return;
+ clear: (element, key) => {
+ if (!isHTMLElement(element)) return;
if (key && key.length) {
const keyTimers = TimeCache.get(element);
-
+ /* istanbul ignore else */
if (keyTimers && keyTimers.get) {
clearTimeout(keyTimers.get(key));
keyTimers.delete(key);
+ /* istanbul ignore else */
if (keyTimers.size === 0) {
TimeCache.delete(element);
}
@@ -1113,10 +1122,9 @@
/**
* Utility to force re-paint of an `HTMLElement` target.
*
- * @param {HTMLElement | Element} element is the target
+ * @param {HTMLElement} element is the target
* @return {number} the `Element.offsetHeight` value
*/
- // @ts-ignore
const reflow = (element) => element.offsetHeight;
/**
@@ -1150,8 +1158,8 @@
* Returns the `Element` that THIS one targets
* via `data-bs-target`, `href`, `data-bs-parent` or `data-bs-container`.
*
- * @param {HTMLElement | Element} element the target element
- * @returns {(HTMLElement | Element)?} the query result
+ * @param {HTMLElement} element the target element
+ * @returns {HTMLElement?} the query result
*/
function getTargetElement(element) {
const targetAttr = [dataBsTarget, dataBsParent, dataBsContainer, 'href'];
@@ -1219,6 +1227,7 @@
} = self;
// discontinue disposed instances
+ /* istanbul ignore else */
if (self.isAnimating && getCarouselInstance(element)) {
const activeItem = getActiveIndex(self);
const orientation = direction === 'left' ? 'next' : 'prev';
@@ -1243,30 +1252,30 @@
}
/**
- * Handles the `mouseenter` / `touchstart` events when *options.pause*
+ * Handles the `mouseenter` events when *options.pause*
* is set to `hover`.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
*/
function carouselPauseHandler() {
const element = this;
const self = getCarouselInstance(element);
-
+ /* istanbul ignore else */
if (self && !self.isPaused && !Timer.get(element, pausedClass)) {
addClass(element, pausedClass);
}
}
/**
- * Handles the `mouseleave` / `touchend` events when *options.pause*
+ * Handles the `mouseleave` events when *options.pause*
* is set to `hover`.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
*/
function carouselResumeHandler() {
const element = this;
const self = getCarouselInstance(element);
-
+ /* istanbul ignore else */
if (self && self.isPaused && !Timer.get(element, pausedClass)) {
self.cycle();
}
@@ -1282,12 +1291,10 @@
e.preventDefault();
const indicator = this;
const element = closest(indicator, carouselSelector) || getTargetElement(indicator);
- if (!element) return;
const self = getCarouselInstance(element);
if (!self || self.isAnimating) return;
- // @ts-ignore
const newIndex = +getAttribute(indicator, dataBsSlideTo);
if (indicator && !hasClass(indicator, activeClass) // event target is not active
@@ -1306,10 +1313,12 @@
e.preventDefault();
const control = this;
const element = closest(control, carouselSelector) || getTargetElement(control);
- const self = element && getCarouselInstance(element);
+ const self = getCarouselInstance(element);
+
if (!self || self.isAnimating) return;
const orientation = getAttribute(control, dataBsSlide);
+ /* istanbul ignore else */
if (orientation === 'next') {
self.next();
} else if (orientation === 'prev') {
@@ -1322,16 +1331,19 @@
*
* @param {KeyboardEvent} e the `Event` object
*/
- function carouselKeyHandler({ code }) {
- const [element] = [...querySelectorAll(carouselSelector)]
+ function carouselKeyHandler({ code, target }) {
+ const doc = getDocument(target);
+ const [element] = [...querySelectorAll(carouselSelector, doc)]
.filter((x) => isElementInScrollRange(x));
-
const self = getCarouselInstance(element);
- if (!self) return;
- const RTL = isRTL();
+
+ /* istanbul ignore next */
+ if (!self || self.isAnimating || /textarea|input/i.test(target.tagName)) return;
+ const RTL = isRTL(element);
const arrowKeyNext = !RTL ? keyArrowRight : keyArrowLeft;
const arrowKeyPrev = !RTL ? keyArrowLeft : keyArrowRight;
+ /* istanbul ignore else */
if (code === arrowKeyPrev) self.prev();
else if (code === arrowKeyNext) self.next();
}
@@ -1339,80 +1351,95 @@
// CAROUSEL TOUCH HANDLERS
// =======================
/**
- * Handles the `touchdown` event for the `Carousel` element.
+ * Handles the `pointerdown` event for the `Carousel` element.
*
- * @this {HTMLElement | Element}
- * @param {TouchEvent} e the `Event` object
+ * @this {HTMLElement}
+ * @param {PointerEvent} e the `Event` object
*/
- function carouselTouchDownHandler(e) {
+ function carouselPointerDownHandler(e) {
const element = this;
+ const { target } = e;
const self = getCarouselInstance(element);
- if (!self || self.isTouch) { return; }
+ // filter pointer event on controls & indicators
+ const { controls, indicators } = self;
+ if ([...controls, ...indicators].some((el) => (el === target || el.contains(target)))) {
+ return;
+ }
+
+ if (!self || self.isAnimating || self.isTouch) { return; }
- startX = e.changedTouches[0].pageX;
+ startX = e.pageX;
- // @ts-ignore
- if (element.contains(e.target)) {
+ /* istanbul ignore else */
+ if (element.contains(target)) {
self.isTouch = true;
toggleCarouselTouchHandlers(self, true);
}
}
/**
- * Handles the `touchmove` event for the `Carousel` element.
+ * Handles the `pointermove` event for the `Carousel` element.
*
- * @this {HTMLElement | Element}
- * @param {TouchEvent} e
+ * @this {HTMLElement}
+ * @param {PointerEvent} e
*/
- function carouselTouchMoveHandler(e) {
- const { changedTouches, type } = e;
- const self = getCarouselInstance(this);
-
- if (!self || !self.isTouch) { return; }
+ function carouselPointerMoveHandler(e) {
+ // const self = getCarouselInstance(this);
- currentX = changedTouches[0].pageX;
+ // if (!self || !self.isTouch) { return; }
- // cancel touch if more than one changedTouches detected
- if (type === touchmoveEvent && changedTouches.length > 1) {
- e.preventDefault();
- }
+ currentX = e.pageX;
}
/**
- * Handles the `touchend` event for the `Carousel` element.
+ * Handles the `pointerup` event for the `Carousel` element.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
- * @param {TouchEvent} e
+ * @param {PointerEvent} e
*/
- function carouselTouchEndHandler(e) {
- const element = this;
- const self = getCarouselInstance(element);
+ function carouselPointerUpHandler(e) {
+ const { target } = e;
+ const doc = getDocument(target);
+ const self = [...querySelectorAll(carouselSelector, doc)]
+ .map((c) => getCarouselInstance(c)).find((i) => i.isTouch);
- if (!self || !self.isTouch) { return; }
+ // impossible to satisfy
+ /* istanbul ignore next */
+ if (!self) { return; }
- endX = currentX || e.changedTouches[0].pageX;
+ const { element, index } = self;
+ const RTL = isRTL(target);
- if (self.isTouch) {
- // the event target is outside the carousel OR carousel doens't include the related target
- // @ts-ignore
- if ((!element.contains(e.target) || !element.contains(e.relatedTarget))
- && Math.abs(startX - endX) < 75) { // AND swipe distance is less than 75px
- // when the above conditions are satisfied, no need to continue
- return;
- } // OR determine next index to slide to
- if (currentX < startX) {
- self.index += 1;
- } else if (currentX > startX) {
- self.index -= 1;
- }
+ self.isTouch = false;
+ toggleCarouselTouchHandlers(self);
- self.isTouch = false;
- self.to(self.index); // do the slide
+ if (doc.getSelection().toString().length) {
+ // reset pointer position
+ startX = 0; currentX = 0; endX = 0;
+ return;
+ }
- toggleCarouselTouchHandlers(self); // remove touch events handlers
+ endX = e.pageX;
+
+ // the event target is outside the carousel context
+ // OR swipe distance is less than 120px
+ /* istanbul ignore else */
+ if (!element.contains(target) || Math.abs(startX - endX) < 120) {
+ // reset pointer position
+ startX = 0; currentX = 0; endX = 0;
+ return;
}
+ // OR determine next index to slide to
+ /* istanbul ignore else */
+ if (currentX < startX) {
+ self.to(index + (RTL ? -1 : 1));
+ } else if (currentX > startX) {
+ self.to(index + (RTL ? 1 : -1));
+ }
+ // reset pointer position
+ startX = 0; currentX = 0; endX = 0;
}
// CAROUSEL PRIVATE METHODS
@@ -1426,19 +1453,20 @@
const { indicators } = self;
[...indicators].forEach((x) => removeClass(x, activeClass));
+ /* istanbul ignore else */
if (self.indicators[pageIndex]) addClass(indicators[pageIndex], activeClass);
}
/**
- * Toggles the touch event listeners for a given `Carousel` instance.
+ * Toggles the pointer event listeners for a given `Carousel` instance.
* @param {Carousel} self the `Carousel` instance
* @param {boolean=} add when `TRUE` event listeners are added
*/
function toggleCarouselTouchHandlers(self, add) {
const { element } = self;
const action = add ? addListener : removeListener;
- action(element, touchmoveEvent, carouselTouchMoveHandler, passiveHandler);
- action(element, touchendEvent, carouselTouchEndHandler, passiveHandler);
+ action(getDocument(element), pointermoveEvent, carouselPointerMoveHandler, passiveHandler);
+ action(getDocument(element), pointerupEvent, carouselPointerUpHandler, passiveHandler);
}
/**
@@ -1458,27 +1486,28 @@
if (pause && interval) {
action(element, mouseenterEvent, carouselPauseHandler);
action(element, mouseleaveEvent, carouselResumeHandler);
- action(element, touchstartEvent, carouselPauseHandler, passiveHandler);
- action(element, touchendEvent, carouselResumeHandler, passiveHandler);
}
- if (touch && slides.length > 1) {
- action(element, touchstartEvent, carouselTouchDownHandler, passiveHandler);
+ if (touch && slides.length > 2) {
+ action(element, pointerdownEvent, carouselPointerDownHandler, passiveHandler);
}
+ /* istanbul ignore else */
if (controls.length) {
controls.forEach((arrow) => {
+ /* istanbul ignore else */
if (arrow) action(arrow, mouseclickEvent, carouselControlsHandler);
});
}
+ /* istanbul ignore else */
if (indicators.length) {
indicators.forEach((indicator) => {
action(indicator, mouseclickEvent, carouselIndicatorHandler);
});
}
- // @ts-ignore
- if (keyboard) action(getWindow(element), keydownEvent, carouselKeyHandler);
+
+ if (keyboard) action(getDocument(element), keydownEvent, carouselKeyHandler);
}
/**
@@ -1489,7 +1518,6 @@
function getActiveIndex(self) {
const { slides, element } = self;
const activeItem = querySelector(`.${carouselItem}.${activeClass}`, element);
- // @ts-ignore
return [...slides].indexOf(activeItem);
}
@@ -1498,24 +1526,24 @@
/** Creates a new `Carousel` instance. */
class Carousel extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target mostly a `.carousel` element
+ * @param {HTMLElement | string} target mostly a `.carousel` element
* @param {BSN.Options.Carousel=} config instance options
*/
constructor(target, config) {
super(target, config);
// bind
const self = this;
+ // initialization element
+ const { element } = self;
// additional properties
/** @type {string} */
- self.direction = isRTL() ? 'right' : 'left';
+ self.direction = isRTL(element) ? 'right' : 'left';
/** @type {number} */
self.index = 0;
/** @type {boolean} */
self.isTouch = false;
- // initialization element
- const { element } = self;
// carousel elements
// a LIVE collection is prefferable
self.slides = getElementsByClassName(carouselItem, element);
@@ -1524,20 +1552,22 @@
// invalidate when not enough items
// no need to go further
if (slides.length < 2) { return; }
+ // external controls must be within same document context
+ const doc = getDocument(element);
self.controls = [
...querySelectorAll(`[${dataBsSlide}]`, element),
- ...querySelectorAll(`[${dataBsSlide}][${dataBsTarget}="#${element.id}"]`),
+ ...querySelectorAll(`[${dataBsSlide}][${dataBsTarget}="#${element.id}"]`, doc),
];
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.indicator = querySelector(`.${carouselString}-indicators`, element);
// a LIVE collection is prefferable
- /** @type {(HTMLElement | Element)[]} */
+ /** @type {HTMLElement[]} */
self.indicators = [
...(self.indicator ? querySelectorAll(`[${dataBsSlideTo}]`, self.indicator) : []),
- ...querySelectorAll(`[${dataBsSlideTo}][${dataBsTarget}="#${element.id}"]`),
+ ...querySelectorAll(`[${dataBsSlideTo}][${dataBsTarget}="#${element.id}"]`, doc),
];
// set JavaScript and DATA API options
@@ -1549,8 +1579,10 @@
: options.interval;
// set first slide active if none
+ /* istanbul ignore else */
if (getActiveIndex(self) < 0) {
- if (slides.length) addClass(slides[0], activeClass);
+ addClass(slides[0], activeClass);
+ /* istanbul ignore else */
if (self.indicators.length) activateCarouselIndicator(self, 0);
}
@@ -1564,12 +1596,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return carouselComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return carouselDefaults; }
/* eslint-enable */
@@ -1595,7 +1625,9 @@
/** Slide automatically through items. */
cycle() {
const self = this;
- const { element, options, isPaused } = self;
+ const {
+ element, options, isPaused, index,
+ } = self;
Timer.clear(element, carouselString);
if (isPaused) {
@@ -1604,9 +1636,12 @@
}
Timer.set(element, () => {
- if (!self.isPaused && isElementInScrollRange(element)) {
- self.index += 1;
- self.to(self.index);
+ // it's very important to check self.element
+ // where instance might have been disposed
+ /* istanbul ignore else */
+ if (self.element && !self.isPaused && !self.isTouch
+ && isElementInScrollRange(element)) {
+ self.to(index + 1);
}
}, options.interval, carouselString);
}
@@ -1615,6 +1650,7 @@
pause() {
const self = this;
const { element, options } = self;
+ /* istanbul ignore else */
if (!self.isPaused && options.interval) {
addClass(element, pausedClass);
Timer.set(element, () => {}, 1, pausedClass);
@@ -1624,13 +1660,15 @@
/** Slide to the next item. */
next() {
const self = this;
- if (!self.isAnimating) { self.index += 1; self.to(self.index); }
+ /* istanbul ignore else */
+ if (!self.isAnimating) { self.to(self.index + 1); }
}
/** Slide to the previous item. */
prev() {
const self = this;
- if (!self.isAnimating) { self.index -= 1; self.to(self.index); }
+ /* istanbul ignore else */
+ if (!self.isAnimating) { self.to(self.index - 1); }
}
/**
@@ -1643,14 +1681,16 @@
element, slides, options,
} = self;
const activeItem = getActiveIndex(self);
- const RTL = isRTL();
+ const RTL = isRTL(element);
let next = idx;
// when controled via methods, make sure to check again
// first return if we're on the same item #227
- if (self.isAnimating || activeItem === next) return;
+ // `to()` must be SPAM protected by Timer
+ if (self.isAnimating || activeItem === next || Timer.get(element, dataBsSlide)) return;
// determine transition direction
+ /* istanbul ignore else */
if ((activeItem < next) || (activeItem === 0 && next === slides.length - 1)) {
self.direction = RTL ? 'right' : 'left'; // next
} else if ((activeItem > next) || (activeItem === slides.length - 1 && next === 0)) {
@@ -1692,7 +1732,7 @@
addClass(slides[activeItem], `${carouselItem}-${directionClass}`);
emulateTransitionEnd(slides[next], () => carouselTransitionEndHandler(self));
- }, 17, dataBsSlide);
+ }, 0, dataBsSlide);
} else {
addClass(slides[next], activeClass);
removeClass(slides[activeItem], activeClass);
@@ -1700,12 +1740,13 @@
Timer.set(element, () => {
Timer.clear(element, dataBsSlide);
// check for element, might have been disposed
+ /* istanbul ignore else */
if (element && options.interval && !self.isPaused) {
self.cycle();
}
dispatchEvent(element, carouselSlidEvent);
- }, 17, dataBsSlide);
+ }, 0, dataBsSlide);
}
}
@@ -1738,6 +1779,29 @@
const ariaExpanded = 'aria-expanded';
/**
+ * Shortcut for `Object.entries()` static method.
+ * @param {Record<string, any>} obj a target object
+ * @returns {[string, any][]}
+ */
+ const ObjectEntries = (obj) => Object.entries(obj);
+
+ /**
+ * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
+ * @param {HTMLElement} element target element
+ * @param {Partial<CSSStyleDeclaration>} styles attribute value
+ */
+ const setElementStyle = (element, styles) => {
+ ObjectEntries(styles).forEach(([key, value]) => {
+ if (key.includes('--')) {
+ element.style.setProperty(key, value);
+ } else {
+ const propObject = {}; propObject[key] = value;
+ ObjectAssign(element.style, propObject);
+ }
+ });
+ };
+
+ /**
* Global namespace for most components `collapsing` class.
* As used by `Collapse` / `Tab`.
*/
@@ -1799,8 +1863,7 @@
addClass(element, collapsingClass);
removeClass(element, collapseString);
- // @ts-ignore
- element.style.height = `${element.scrollHeight}px`;
+ setElementStyle(element, { height: `${element.scrollHeight}px` });
emulateTransitionEnd(element, () => {
Timer.clear(element);
@@ -1812,8 +1875,7 @@
addClass(element, collapseString);
addClass(element, showClass);
- // @ts-ignore
- element.style.height = '';
+ setElementStyle(element, { height: '' });
dispatchEvent(element, shownCollapseEvent);
});
@@ -1825,7 +1887,6 @@
*/
function collapseContent(self) {
const {
- // @ts-ignore
element, parent, triggers,
} = self;
@@ -1836,19 +1897,18 @@
Timer.set(element, () => {}, 17);
if (parent) Timer.set(parent, () => {}, 17);
- // @ts-ignore
- element.style.height = `${element.scrollHeight}px`;
+ setElementStyle(element, { height: `${element.scrollHeight}px` });
removeClass(element, collapseString);
removeClass(element, showClass);
addClass(element, collapsingClass);
reflow(element);
- // @ts-ignore
- element.style.height = '0px';
+ setElementStyle(element, { height: '0px' });
emulateTransitionEnd(element, () => {
Timer.clear(element);
+ /* istanbul ignore else */
if (parent) Timer.clear(parent);
triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'false'));
@@ -1856,8 +1916,7 @@
removeClass(element, collapsingClass);
addClass(element, collapseString);
- // @ts-ignore
- element.style.height = '';
+ setElementStyle(element, { height: '' });
dispatchEvent(element, hiddenCollapseEvent);
});
@@ -1872,6 +1931,7 @@
const action = add ? addListener : removeListener;
const { triggers } = self;
+ /* istanbul ignore else */
if (triggers.length) {
triggers.forEach((btn) => action(btn, mouseclickEvent, collapseClickHandler));
}
@@ -1884,10 +1944,11 @@
* @param {MouseEvent} e the `Event` object
*/
function collapseClickHandler(e) {
- const { target } = e; // @ts-ignore - our target is `HTMLElement`
+ const { target } = e; // our target is `HTMLElement`
const trigger = target && closest(target, collapseToggleSelector);
const element = trigger && getTargetElement(trigger);
const self = element && getCollapseInstance(element);
+ /* istanbul ignore else */
if (self) self.toggle();
// event target is anchor link #398
@@ -1900,7 +1961,7 @@
/** Returns a new `Colapse` instance. */
class Collapse extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target and `Element` that matches the selector
+ * @param {HTMLElement | string} target and `Element` that matches the selector
* @param {BSN.Options.Collapse=} config instance options
*/
constructor(target, config) {
@@ -1910,15 +1971,17 @@
// initialization element
const { element, options } = self;
+ const doc = getDocument(element);
// set triggering elements
- /** @type {(HTMLElement | Element)[]} */
- self.triggers = [...querySelectorAll(collapseToggleSelector)]
+ /** @type {HTMLElement[]} */
+ self.triggers = [...querySelectorAll(collapseToggleSelector, doc)]
.filter((btn) => getTargetElement(btn) === element);
// set parent accordion
- /** @type {(HTMLElement | Element)?} */
- self.parent = querySelector(options.parent);
+ /** @type {HTMLElement?} */
+ self.parent = querySelector(options.parent, doc)
+ || getTargetElement(element) || null;
// add event listeners
toggleCollapseHandler(self, true);
@@ -1927,12 +1990,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return collapseComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return collapseDefaults; }
/* eslint-enable */
@@ -1953,6 +2014,7 @@
if (Timer.get(element)) return;
collapseContent(self);
+ /* istanbul ignore else */
if (triggers.length) {
triggers.forEach((btn) => addClass(btn, `${collapseString}d`));
}
@@ -1973,7 +2035,7 @@
activeCollapseInstance = activeCollapse && getCollapseInstance(activeCollapse);
}
- if ((!parent || (parent && !Timer.get(parent))) && !Timer.get(element)) {
+ if ((!parent || !Timer.get(parent)) && !Timer.get(element)) {
if (activeCollapseInstance && activeCollapse !== element) {
collapseContent(activeCollapseInstance);
activeCollapseInstance.triggers.forEach((btn) => {
@@ -1982,6 +2044,7 @@
}
expandCollapse(self);
+ /* istanbul ignore else */
if (triggers.length) {
triggers.forEach((btn) => removeClass(btn, `${collapseString}d`));
}
@@ -2047,27 +2110,36 @@
/**
* Shortcut for `HTMLElement.hasAttribute()` method.
- * @param {HTMLElement | Element} element target element
+ * @param {HTMLElement} element target element
* @param {string} attribute attribute name
* @returns {boolean} the query result
*/
const hasAttribute = (element, attribute) => element.hasAttribute(attribute);
/**
- * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
- * @param {HTMLElement | Element} element target element
- * @param {Partial<CSSStyleDeclaration>} styles attribute value
+ * Utility to focus an `HTMLElement` target.
+ *
+ * @param {HTMLElement} element is the target
*/
- // @ts-ignore
- const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
+ const focus = (element) => element.focus();
/**
- * Utility to focus an `HTMLElement` target.
+ * Returns the `Window` object of a target node.
+ * @see https://github.com/floating-ui/floating-ui
*
- * @param {HTMLElement | Element} element is the target
+ * @param {(Node | Window)=} node target node
+ * @returns {Window} the `Window` object
*/
- // @ts-ignore -- `Element`s resulted from querySelector can focus too
- const focus = (element) => element.focus();
+ function getWindow(node) {
+ // node is undefined | NULL
+ if (!node) return window;
+ // node instanceof Document
+ if (isDocument(node)) return node.defaultView;
+ // node instanceof Node
+ if (isNode(node)) return node.ownerDocument.defaultView;
+ // node is instanceof Window
+ return node;
+ }
/**
* Global namespace for `Dropdown` types / classes.
@@ -2086,16 +2158,18 @@
* Checks if an *event.target* or its parent has an `href="#"` value.
* We need to prevent jumping around onclick, don't we?
*
- * @param {HTMLElement | HTMLAnchorElement | EventTarget} element the target element
+ * @param {Node} element the target element
* @returns {boolean} the query result
*/
function isEmptyAnchor(element) {
- // @ts-ignore -- `EventTarget` must be `HTMLElement`
+ // `EventTarget` must be `HTMLElement`
const parentAnchor = closest(element, 'A');
- // @ts-ignore -- anchor href starts with #
- return element && ((hasAttribute(element, 'href') && element.href.slice(-1) === '#')
- // @ts-ignore -- OR a child of an anchor with href starts with #
- || (parentAnchor && hasAttribute(parentAnchor, 'href') && parentAnchor.href.slice(-1) === '#'));
+ return isHTMLElement(element)
+ // anchor href starts with #
+ && ((hasAttribute(element, 'href') && element.href.slice(-1) === '#')
+ // OR a child of an anchor with href starts with #
+ || (parentAnchor && hasAttribute(parentAnchor, 'href')
+ && parentAnchor.href.slice(-1) === '#'));
}
/* Native JavaScript for Bootstrap 5 | Dropdown
@@ -2127,6 +2201,7 @@
// DROPDOWN PRIVATE GC
// ===================
+ // const dropdownMenuStartClass = `${dropdownMenuClass}-start`;
const dropdownMenuEndClass = `${dropdownMenuClass}-end`;
const verticalClass = [dropdownString, dropupString];
const horizontalClass = [dropstartString, dropendString];
@@ -2159,19 +2234,22 @@
const { offset } = options;
// don't apply any style on mobile view
+ /* istanbul ignore next: this test requires a navbar */
if (getElementStyle(menu, 'position') === 'static') return;
const RTL = isRTL(element);
- const menuEnd = hasClass(parentElement, dropdownMenuEndClass);
+ // const menuStart = hasClass(menu, dropdownMenuStartClass);
+ const menuEnd = hasClass(menu, dropdownMenuEndClass);
// reset menu offset and position
const resetProps = ['margin', 'top', 'bottom', 'left', 'right'];
- // @ts-ignore
resetProps.forEach((p) => { menu.style[p] = ''; });
// set initial position class
// take into account .btn-group parent as .dropdown
- let positionClass = dropdownMenuClasses.find((c) => hasClass(parentElement, c)) || dropdownString;
+ // this requires navbar/btn-group/input-group
+ let positionClass = dropdownMenuClasses.find((c) => hasClass(parentElement, c))
+ || /* istanbul ignore next: fallback position */ dropdownString;
/** @type {Record<string, Record<string, any>>} */
let dropdownMargin = {
@@ -2187,10 +2265,10 @@
dropup: { top: 'auto', bottom: '100%' },
dropstart: RTL ? { left: '100%', right: 'auto' } : { left: 'auto', right: '100%' },
dropend: RTL ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' },
+ menuStart: RTL ? { right: 0, left: 'auto' } : { right: 'auto', left: 0 },
menuEnd: RTL ? { right: 'auto', left: 0 } : { right: 0, left: 'auto' },
};
- // @ts-ignore
const { offsetWidth: menuWidth, offsetHeight: menuHeight } = menu;
const { clientWidth, clientHeight } = getDocumentElement(element);
@@ -2232,41 +2310,47 @@
if (positionClass === dropdownString && bottomFullExceed && !topExceed) {
positionClass = dropupString;
}
+
// override position for horizontal classes
if (horizontalClass.includes(positionClass) && bottomExceed) {
ObjectAssign(dropdownPosition[positionClass], {
top: 'auto', bottom: 0,
});
}
+
// override position for vertical classes
if (verticalClass.includes(positionClass) && (leftExceed || rightExceed)) {
// don't realign when menu is wider than window
// in both RTL and non-RTL readability is KING
- if (targetLeft + targetWidth + Math.abs(menuWidth - targetWidth) + offset < clientWidth) {
- ObjectAssign(dropdownPosition[positionClass],
- leftExceed ? { left: 0, right: 'auto' } : { left: 'auto', right: 0 });
- }
+ let posAjust;
+ if (!leftExceed && rightExceed && !RTL) posAjust = { left: 'auto', right: 0 };
+ if (leftExceed && !rightExceed && RTL) posAjust = { left: 0, right: 'auto' };
+ if (posAjust) ObjectAssign(dropdownPosition[positionClass], posAjust);
}
dropdownMargin = dropdownMargin[positionClass];
- // @ts-ignore
- menu.style.margin = `${dropdownMargin.map((x) => (x ? `${x}px` : x)).join(' ')}`;
-
- setElementStyle(menu, dropdownPosition[positionClass]);
+ setElementStyle(menu, {
+ ...dropdownPosition[positionClass],
+ margin: `${dropdownMargin.map((x) => (x ? `${x}px` : x)).join(' ')}`,
+ });
- // update dropdown-menu-end
- if (hasClass(menu, dropdownMenuEndClass)) {
- setElementStyle(menu, dropdownPosition.menuEnd);
+ // override dropdown-menu-start | dropdown-menu-end
+ if (verticalClass.includes(positionClass) && menuEnd) {
+ /* istanbul ignore else */
+ if (menuEnd) {
+ const endAdjust = (!RTL && leftExceed) || (RTL && rightExceed)
+ ? 'menuStart' : /* istanbul ignore next */'menuEnd';
+ setElementStyle(menu, dropdownPosition[endAdjust]);
+ }
}
}
/**
* Returns an `Array` of focusable items in the given dropdown-menu.
- * @param {HTMLElement | Element} menu
- * @returns {(HTMLElement | Element)[]}
+ * @param {HTMLElement} menu
+ * @returns {HTMLElement[]}
*/
function getMenuItems(menu) {
- // @ts-ignore
return [...menu.children].map((c) => {
if (c && menuFocusTags.includes(c.tagName)) return c;
const { firstElementChild } = c;
@@ -2284,7 +2368,7 @@
* @param {Dropdown} self the `Dropdown` instance
*/
function toggleDropdownDismiss(self) {
- const { element } = self;
+ const { element, options } = self;
const action = self.open ? addListener : removeListener;
const doc = getDocument(element);
@@ -2293,9 +2377,9 @@
action(doc, keydownEvent, dropdownPreventScroll);
action(doc, keyupEvent, dropdownKeyHandler);
- if (self.options.display === 'dynamic') {
+ /* istanbul ignore else */
+ if (options.display === 'dynamic') {
[scrollEvent, resizeEvent].forEach((ev) => {
- // @ts-ignore
action(getWindow(element), ev, dropdownLayoutHandler, passiveHandler);
});
}
@@ -2315,16 +2399,15 @@
/**
* Returns the currently open `.dropdown` element.
*
- * @param {(Document | HTMLElement | Element | globalThis)=} element target
+ * @param {(Node | Window)=} element target
* @returns {HTMLElement?} the query result
*/
function getCurrentOpenDropdown(element) {
const currentParent = [...dropdownMenuClasses, 'btn-group', 'input-group']
- .map((c) => getElementsByClassName(`${c} ${showClass}`), getDocument(element))
+ .map((c) => getElementsByClassName(`${c} ${showClass}`, getDocument(element)))
.find((x) => x.length);
if (currentParent && currentParent.length) {
- // @ts-ignore -- HTMLElement is also Element
return [...currentParent[0].children]
.find((x) => hasAttribute(x, dataBsToggle));
}
@@ -2341,34 +2424,31 @@
*/
function dropdownDismissHandler(e) {
const { target, type } = e;
- // @ts-ignore
+
+ /* istanbul ignore next: impossible to satisfy */
if (!target || !target.closest) return; // some weird FF bug #409
- // @ts-ignore
const element = getCurrentOpenDropdown(target);
- if (!element) return;
-
const self = getDropdownInstance(element);
+
+ /* istanbul ignore next */
if (!self) return;
const { parentElement, menu } = self;
- // @ts-ignore
const hasData = closest(target, dropdownSelector) !== null;
- // @ts-ignore
const isForm = parentElement && parentElement.contains(target)
- // @ts-ignore
&& (target.tagName === 'form' || closest(target, 'form') !== null);
- // @ts-ignore
if (type === mouseclickEvent && isEmptyAnchor(target)) {
e.preventDefault();
}
- if (type === focusEvent // @ts-ignore
+ if (type === focusEvent
&& (target === element || target === menu || menu.contains(target))) {
return;
}
+ /* istanbul ignore else */
if (isForm || hasData) ; else if (self) {
self.hide();
}
@@ -2376,7 +2456,7 @@
/**
* Handles `click` event listener for `Dropdown`.
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
* @param {MouseEvent} e event object
*/
function dropdownClickHandler(e) {
@@ -2384,8 +2464,10 @@
const { target } = e;
const self = getDropdownInstance(element);
+ /* istanbul ignore else */
if (self) {
self.toggle();
+ /* istanbul ignore else */
if (target && isEmptyAnchor(target)) e.preventDefault();
}
}
@@ -2395,6 +2477,7 @@
* @param {KeyboardEvent} e event object
*/
function dropdownPreventScroll(e) {
+ /* istanbul ignore else */
if ([keyArrowDown, keyArrowUp].includes(e.code)) e.preventDefault();
}
@@ -2407,21 +2490,24 @@
const { code } = e;
const element = getCurrentOpenDropdown(this);
const self = element && getDropdownInstance(element);
- const activeItem = element && getDocument(element).activeElement;
- if (!self || !activeItem) return;
+ const { activeElement } = element && getDocument(element);
+ /* istanbul ignore next: impossible to satisfy */
+ if (!self || !activeElement) return;
const { menu, open } = self;
const menuItems = getMenuItems(menu);
// arrow up & down
if (menuItems && menuItems.length && [keyArrowDown, keyArrowUp].includes(code)) {
- let idx = menuItems.indexOf(activeItem);
- if (activeItem === element) {
+ let idx = menuItems.indexOf(activeElement);
+ /* istanbul ignore else */
+ if (activeElement === element) {
idx = 0;
} else if (code === keyArrowUp) {
idx = idx > 1 ? idx - 1 : 0;
} else if (code === keyArrowDown) {
idx = idx < menuItems.length - 1 ? idx + 1 : idx;
}
+ /* istanbul ignore else */
if (menuItems[idx]) focus(menuItems[idx]);
}
@@ -2439,6 +2525,7 @@
const element = getCurrentOpenDropdown(this);
const self = element && getDropdownInstance(element);
+ /* istanbul ignore else */
if (self && self.open) styleDropdown(self);
}
@@ -2447,7 +2534,7 @@
/** Returns a new Dropdown instance. */
class Dropdown extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target Element or string selector
+ * @param {HTMLElement | string} target Element or string selector
* @param {BSN.Options.Dropdown=} config the instance options
*/
constructor(target, config) {
@@ -2461,10 +2548,8 @@
// set targets
/** @type {(Element | HTMLElement)} */
- // @ts-ignore
self.parentElement = parentElement;
/** @type {(Element | HTMLElement)} */
- // @ts-ignore
self.menu = querySelector(`.${dropdownMenuClass}`, parentElement);
// set initial state to closed
@@ -2478,12 +2563,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return dropdownComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return dropdownDefaults; }
/* eslint-enable */
@@ -2505,12 +2588,17 @@
element, open, menu, parentElement,
} = self;
+ /* istanbul ignore next */
+ if (open) return;
+
const currentElement = getCurrentOpenDropdown(element);
const currentInstance = currentElement && getDropdownInstance(currentElement);
if (currentInstance) currentInstance.hide();
- // dispatch
- [showDropdownEvent, shownDropdownEvent].forEach((e) => { e.relatedTarget = element; });
+ // dispatch event
+ [showDropdownEvent, shownDropdownEvent].forEach((e) => {
+ e.relatedTarget = element;
+ });
dispatchEvent(parentElement, showDropdownEvent);
if (showDropdownEvent.defaultPrevented) return;
@@ -2523,11 +2611,9 @@
self.open = !open;
- setTimeout(() => {
- focus(element); // focus the element
- toggleDropdownDismiss(self);
- dispatchEvent(parentElement, shownDropdownEvent);
- }, 1);
+ focus(element); // focus the element
+ toggleDropdownDismiss(self);
+ dispatchEvent(parentElement, shownDropdownEvent);
}
/** Hides the dropdown menu from the user. */
@@ -2536,8 +2622,13 @@
const {
element, open, menu, parentElement,
} = self;
- [hideDropdownEvent, hiddenDropdownEvent].forEach((e) => { e.relatedTarget = element; });
+ /* istanbul ignore next */
+ if (!open) return;
+
+ [hideDropdownEvent, hiddenDropdownEvent].forEach((e) => {
+ e.relatedTarget = element;
+ });
dispatchEvent(parentElement, hideDropdownEvent);
if (hideDropdownEvent.defaultPrevented) return;
@@ -2546,19 +2637,15 @@
setAttribute(element, ariaExpanded, 'false');
self.open = !open;
-
// only re-attach handler if the instance is not disposed
- setTimeout(() => toggleDropdownDismiss(self), 1);
-
+ toggleDropdownDismiss(self);
dispatchEvent(parentElement, hiddenDropdownEvent);
}
/** Removes the `Dropdown` component from the target element. */
dispose() {
const self = this;
- const { parentElement } = self;
-
- if (hasClass(parentElement, showClass) && self.open) self.hide();
+ if (self.open) self.hide();
toggleDropdownHandler(self);
@@ -2586,7 +2673,7 @@
/**
* Shortcut for `HTMLElement.removeAttribute()` method.
- * @param {HTMLElement | Element} element target element
+ * @param {HTMLElement} element target element
* @param {string} attribute attribute name
* @returns {void}
*/
@@ -2595,8 +2682,8 @@
/**
* Returns the `document.body` or the `<body>` element.
*
- * @param {(Node | HTMLElement | Element | globalThis)=} node
- * @returns {HTMLElement | HTMLBodyElement}
+ * @param {(Node | Window)=} node
+ * @returns {HTMLBodyElement}
*/
function getDocumentBody(node) {
return getDocument(node).body;
@@ -2614,17 +2701,15 @@
* @param {any} element target
* @returns {boolean} the query result
*/
- const isShadowRoot = (element) => {
- const OwnElement = getWindow(element).ShadowRoot;
- return element instanceof OwnElement || element instanceof ShadowRoot;
- };
+ const isShadowRoot = (element) => (element && element.constructor.name === 'ShadowRoot')
+ || false;
/**
* Returns the `parentNode` also going through `ShadowRoot`.
* @see https://github.com/floating-ui/floating-ui
*
- * @param {Node | HTMLElement | Element} node the target node
- * @returns {Node | HTMLElement | Element} the apropriate parent node
+ * @param {Node} node the target node
+ * @returns {Node} the apropriate parent node
*/
function getParentNode(node) {
if (node.nodeName === 'HTML') {
@@ -2633,28 +2718,23 @@
// this is a quicker (but less type safe) way to save quite some bytes from the bundle
return (
- // @ts-ignore
node.assignedSlot // step into the shadow DOM of the parent of a slotted node
- || node.parentNode // @ts-ignore DOM Element detected
- || (isShadowRoot(node) ? node.host : null) // ShadowRoot detected
+ || node.parentNode // DOM Element detected
+ || (isShadowRoot(node) && node.host) // ShadowRoot detected
|| getDocumentElement(node) // fallback
);
}
/**
* Check if a target element is a `<table>`, `<td>` or `<th>`.
- * @param {any} element the target element
- * @returns {boolean} the query result
- */
- const isTableElement = (element) => ['TABLE', 'TD', 'TH'].includes(element.tagName);
-
- /**
- * Checks if an element is an `HTMLElement`.
+ * This specific check is important for determining
+ * the `offsetParent` of a given element.
*
- * @param {any} element the target object
+ * @param {any} element the target element
* @returns {boolean} the query result
*/
- const isHTMLElement = (element) => element instanceof HTMLElement;
+ const isTableElement = (element) => (element && ['TABLE', 'TD', 'TH'].includes(element.tagName))
+ || false;
/**
* Returns an `HTMLElement` to be used as default value for *options.container*
@@ -2664,9 +2744,9 @@
* offsets computation similar to **floating-ui**.
* @see https://github.com/floating-ui/floating-ui
*
- * @param {HTMLElement | Element} element the target
+ * @param {HTMLElement} element the target
* @param {boolean=} getOffset when *true* it will return an `offsetParent`
- * @returns {HTMLElement | HTMLBodyElement | Window | globalThis} the query result
+ * @returns {ParentNode | Window} the query result
*/
function getElementContainer(element, getOffset) {
const majorBlockTags = ['HTML', 'BODY'];
@@ -2675,7 +2755,6 @@
/** @type {any} */
let { offsetParent } = element;
const win = getWindow(element);
- // const { innerWidth } = getDocumentElement(element);
while (offsetParent && (isTableElement(offsetParent)
|| (isHTMLElement(offsetParent)
@@ -2684,21 +2763,21 @@
offsetParent = offsetParent.offsetParent;
}
- if (!offsetParent || (offsetParent
- && (majorBlockTags.includes(offsetParent.tagName)
- || getElementStyle(offsetParent, 'position') === 'static'))) {
+ if (!offsetParent || (majorBlockTags.includes(offsetParent.tagName)
+ || getElementStyle(offsetParent, 'position') === 'static')) {
offsetParent = win;
}
return offsetParent;
}
- /** @type {(HTMLElement)[]} */
+ /** @type {ParentNode[]} */
const containers = [];
- /** @type {any} */
+ /** @type {ParentNode} */
let { parentNode } = element;
while (parentNode && !majorBlockTags.includes(parentNode.nodeName)) {
parentNode = getParentNode(parentNode);
+ /* istanbul ignore else */
if (!(isShadowRoot(parentNode) || !!parentNode.shadowRoot
|| isTableElement(parentNode))) {
containers.push(parentNode);
@@ -2734,7 +2813,7 @@
*/
const positionStickyClass = 'position-sticky';
- /** @param {(HTMLElement | Element | Document)=} parent */
+ /** @param {(HTMLElement | Document)=} parent */
const getFixedItems = (parent) => [
...getElementsByClassName(fixedTopClass, parent),
...getElementsByClassName(fixedBottomClass, parent),
@@ -2746,7 +2825,7 @@
/**
* Removes *padding* and *overflow* from the `<body>`
* and all spacing from fixed items.
- * @param {(HTMLElement | Element)=} element the target modal/offcanvas
+ * @param {HTMLElement=} element the target modal/offcanvas
*/
function resetScrollbar(element) {
const bd = getDocumentBody(element);
@@ -2770,7 +2849,7 @@
/**
* Returns the scrollbar width if the body does overflow
* the window.
- * @param {(HTMLElement | Element)=} element
+ * @param {HTMLElement=} element
* @returns {number} the value
*/
function measureScrollbar(element) {
@@ -2783,7 +2862,7 @@
* Sets the `<body>` and fixed items style when modal / offcanvas
* is shown to the user.
*
- * @param {HTMLElement | Element} element the target modal/offcanvas
+ * @param {HTMLElement} element the target modal/offcanvas
* @param {boolean=} overflow body does overflow or not
*/
function setScrollbar(element, overflow) {
@@ -2793,20 +2872,21 @@
const sbWidth = isOpen && bodyPad ? 0 : measureScrollbar(element);
const fixedItems = getFixedItems(bd);
+ /* istanbul ignore else */
if (overflow) {
setElementStyle(bd, {
overflow: 'hidden',
paddingRight: `${bodyPad + sbWidth}px`,
});
+ /* istanbul ignore else */
if (fixedItems.length) {
fixedItems.forEach((fixed) => {
const itemPadValue = getElementStyle(fixed, 'paddingRight');
- // @ts-ignore
fixed.style.paddingRight = `${parseInt(itemPadValue, 10) + sbWidth}px`;
+ /* istanbul ignore else */
if ([stickyTopClass, positionStickyClass].some((c) => hasClass(fixed, c))) {
const itemMValue = getElementStyle(fixed, 'marginRight');
- // @ts-ignore
fixed.style.marginRight = `${parseInt(itemMValue, 10) - sbWidth}px`;
}
});
@@ -2822,9 +2902,11 @@
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
*
* @param {Record<string, string> | string} param `tagName` or object
- * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
+ * @return {HTMLElement} a new `HTMLElement` or `Element`
*/
function createElement(param) {
+ if (!param) return null;
+
if (typeof param === 'string') {
return getDocument().createElement(param);
}
@@ -2851,8 +2933,8 @@
/**
* Returns the current active modal / offcancas element.
- * @param {(HTMLElement | Element)=} element the context element
- * @returns {(HTMLElement | Element)?} the requested element
+ * @param {HTMLElement=} element the context element
+ * @returns {HTMLElement?} the requested element
*/
function getCurrentOpen(element) {
return querySelector(`${modalActiveSelector},${offcanvasActiveSelector}`, getDocument(element));
@@ -2872,7 +2954,7 @@
/**
* Append the overlay to DOM.
- * @param {HTMLElement | Element} container
+ * @param {HTMLElement} container
* @param {boolean} hasFade
* @param {boolean=} isModal
*/
@@ -2901,7 +2983,7 @@
/**
* Removes the overlay from DOM.
- * @param {(HTMLElement | Element)=} element
+ * @param {HTMLElement=} element
*/
function removeOverlay(element) {
if (!getCurrentOpen(element)) {
@@ -2912,12 +2994,12 @@
}
/**
- * @param {HTMLElement | Element} element target
+ * @param {HTMLElement} element target
* @returns {boolean}
*/
function isVisible(element) {
- return element && getElementStyle(element, 'visibility') !== 'hidden'
- // @ts-ignore
+ return isHTMLElement(element)
+ && getElementStyle(element, 'visibility') !== 'hidden'
&& element.offsetParent !== null;
}
@@ -2972,10 +3054,12 @@
const { clientHeight: modalHeight, scrollHeight: modalScrollHeight } = element;
const modalOverflow = modalHeight !== modalScrollHeight;
+ /* istanbul ignore else */
if (!modalOverflow && scrollbarWidth) {
- const pad = isRTL(element) ? 'paddingLeft' : 'paddingRight';
- // @ts-ignore -- cannot use `setElementStyle`
- element.style[pad] = `${scrollbarWidth}px`;
+ const pad = !isRTL(element) ? 'paddingRight' : /* istanbul ignore next */'paddingLeft';
+ const padStyle = {};
+ padStyle[pad] = `${scrollbarWidth}px`;
+ setElementStyle(element, padStyle);
}
setScrollbar(element, (modalOverflow || clientHeight !== scrollHeight));
}
@@ -2990,7 +3074,6 @@
const action = add ? addListener : removeListener;
const { element } = self;
action(element, mouseclickEvent, modalDismissHandler);
- // @ts-ignore
action(getWindow(element), resizeEvent, self.update, passiveHandler);
action(getDocument(element), keydownEvent, modalKeyHandler);
}
@@ -3004,6 +3087,7 @@
const action = add ? addListener : removeListener;
const { triggers } = self;
+ /* istanbul ignore else */
if (triggers.length) {
triggers.forEach((btn) => action(btn, mouseclickEvent, modalClickHandler));
}
@@ -3012,16 +3096,21 @@
/**
* Executes after a modal is hidden to the user.
* @param {Modal} self the `Modal` instance
+ * @param {Function} callback the `Modal` instance
*/
- function afterModalHide(self) {
+ function afterModalHide(self, callback) {
const { triggers, element, relatedTarget } = self;
removeOverlay(element);
- setElementStyle(element, { paddingRight: '' });
+ setElementStyle(element, { paddingRight: '', display: '' });
toggleModalDismiss(self);
const focusElement = showModalEvent.relatedTarget || triggers.find(isVisible);
+ /* istanbul ignore else */
if (focusElement) focus(focusElement);
+ /* istanbul ignore else */
+ if (callback) callback();
+
hiddenModalEvent.relatedTarget = relatedTarget;
dispatchEvent(element, hiddenModalEvent);
}
@@ -3048,6 +3137,7 @@
setElementStyle(element, { display: 'block' });
setModalScrollbar(self);
+ /* istanbul ignore else */
if (!getCurrentOpen(element)) {
setElementStyle(getDocumentBody(element), { overflow: 'hidden' });
}
@@ -3063,23 +3153,21 @@
/**
* Executes before a modal is hidden to the user.
* @param {Modal} self the `Modal` instance
- * @param {boolean=} force when `true` skip animation
+ * @param {Function=} callback when `true` skip animation
*/
- function beforeModalHide(self, force) {
+ function beforeModalHide(self, callback) {
const {
element, options, hasFade,
} = self;
- setElementStyle(element, { display: '' });
-
- // force can also be the transitionEvent object, we wanna make sure it's not
+ // callback can also be the transitionEvent object, we wanna make sure it's not
// call is not forced and overlay is visible
- if (options.backdrop && !force && hasFade && hasClass(overlay, showClass)
+ if (options.backdrop && !callback && hasFade && hasClass(overlay, showClass)
&& !getCurrentOpen(element)) { // AND no modal is visible
hideOverlay();
emulateTransitionEnd(overlay, () => afterModalHide(self));
} else {
- afterModalHide(self);
+ afterModalHide(self, callback);
}
}
@@ -3088,17 +3176,15 @@
/**
* Handles the `click` event listener for modal.
* @param {MouseEvent} e the `Event` object
- * @this {HTMLElement | Element}
*/
function modalClickHandler(e) {
const { target } = e;
- const trigger = target && closest(this, modalToggleSelector);
+ const trigger = target && closest(target, modalToggleSelector);
const element = trigger && getTargetElement(trigger);
const self = element && getModalInstance(element);
- if (!self) return;
-
+ /* istanbul ignore else */
if (trigger && trigger.tagName === 'A') e.preventDefault();
self.relatedTarget = trigger;
self.toggle();
@@ -3110,11 +3196,12 @@
*
* @param {KeyboardEvent} e the `Event` object
*/
- function modalKeyHandler({ code }) {
- const element = querySelector(modalActiveSelector);
+ function modalKeyHandler({ code, target }) {
+ const element = querySelector(modalActiveSelector, getDocument(target));
const self = element && getModalInstance(element);
- if (!self) return;
+
const { options } = self;
+ /* istanbul ignore else */
if (options.keyboard && code === keyEscape // the keyboard option is enabled and the key is 27
&& hasClass(element, showClass)) { // the modal is not visible
self.relatedTarget = null;
@@ -3125,7 +3212,7 @@
/**
* Handles the `click` event listeners that hide the modal.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
* @param {MouseEvent} e the `Event` object
*/
function modalDismissHandler(e) {
@@ -3133,19 +3220,18 @@
const self = getModalInstance(element);
// this timer is needed
+ /* istanbul ignore next: must have a filter */
if (!self || Timer.get(element)) return;
const { options, isStatic, modalDialog } = self;
const { backdrop } = options;
const { target } = e;
- // @ts-ignore
const selectedText = getDocument(element).getSelection().toString().length;
- // @ts-ignore
const targetInsideDialog = modalDialog.contains(target);
- // @ts-ignore
const dismiss = target && closest(target, modalDismissSelector);
+ /* istanbul ignore else */
if (isStatic && !targetInsideDialog) {
Timer.set(element, () => {
addClass(element, modalStaticClass);
@@ -3176,7 +3262,7 @@
/** Returns a new `Modal` instance. */
class Modal extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target usually the `.modal` element
+ * @param {HTMLElement | string} target usually the `.modal` element
* @param {BSN.Options.Modal=} config instance options
*/
constructor(target, config) {
@@ -3189,13 +3275,12 @@
const { element } = self;
// the modal-dialog
- /** @type {(HTMLElement | Element)} */
- // @ts-ignore
+ /** @type {(HTMLElement)} */
self.modalDialog = querySelector(`.${modalString}-dialog`, element);
// modal can have multiple triggering elements
- /** @type {(HTMLElement | Element)[]} */
- self.triggers = [...querySelectorAll(modalToggleSelector)]
+ /** @type {HTMLElement[]} */
+ self.triggers = [...querySelectorAll(modalToggleSelector, getDocument(element))]
.filter((btn) => getTargetElement(btn) === element);
// additional internals
@@ -3203,10 +3288,9 @@
self.isStatic = self.options.backdrop === 'static';
/** @type {boolean} */
self.hasFade = hasClass(element, fadeClass);
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.relatedTarget = null;
- /** @type {HTMLBodyElement | HTMLElement | Element} */
- // @ts-ignore
+ /** @type {HTMLBodyElement | HTMLElement} */
self.container = getElementContainer(element);
// attach event listeners
@@ -3219,12 +3303,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return modalComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return modalDefaults; }
/* eslint-enable */
@@ -3257,7 +3339,8 @@
const currentOpen = getCurrentOpen(element);
if (currentOpen && currentOpen !== element) {
const this1 = getModalInstance(currentOpen);
- const that1 = this1 || getInstance(currentOpen, 'Offcanvas');
+ const that1 = this1
+ || /* istanbul ignore next */getInstance(currentOpen, 'Offcanvas');
that1.hide();
}
@@ -3274,6 +3357,7 @@
setTimeout(() => beforeModalShow(self), overlayDelay);
} else {
beforeModalShow(self);
+ /* istanbul ignore else */
if (currentOpen && hasClass(overlay, showClass)) {
hideOverlay();
}
@@ -3282,9 +3366,9 @@
/**
* Hide the modal from the user.
- * @param {boolean=} force when `true` it will skip animation
+ * @param {Function=} callback when defined it will skip animation
*/
- hide(force) {
+ hide(callback) {
const self = this;
const {
element, hasFade, relatedTarget,
@@ -3299,28 +3383,31 @@
setAttribute(element, ariaHidden, 'true');
removeAttribute(element, ariaModal);
- if (hasFade && force !== false) {
- emulateTransitionEnd(element, () => beforeModalHide(self));
+ // if (hasFade && callback) {
+ /* istanbul ignore else */
+ if (hasFade) {
+ emulateTransitionEnd(element, () => beforeModalHide(self, callback));
} else {
- beforeModalHide(self, force);
+ beforeModalHide(self, callback);
}
}
- /** Updates the modal layout. */
+ /**
+ * Updates the modal layout.
+ * @this {Modal} the modal instance
+ */
update() {
const self = this;
-
+ /* istanbul ignore else */
if (hasClass(self.element, showClass)) setModalScrollbar(self);
}
/** Removes the `Modal` component from target element. */
dispose() {
const self = this;
- self.hide(true); // forced call
-
toggleModalHandler(self);
-
- super.dispose();
+ // use callback
+ self.hide(() => super.dispose());
}
}
@@ -3416,6 +3503,7 @@
function beforeOffcanvasShow(self) {
const { element, options } = self;
+ /* istanbul ignore else */
if (!options.scroll) {
setOffCanvasScrollbar(self);
setElementStyle(getDocumentBody(element), { overflow: 'hidden' });
@@ -3432,18 +3520,18 @@
* Executes before hiding the offcanvas.
*
* @param {Offcanvas} self the `Offcanvas` instance
+ * @param {Function=} callback the hide callback
*/
- function beforeOffcanvasHide(self) {
+ function beforeOffcanvasHide(self, callback) {
const { element, options } = self;
const currentOpen = getCurrentOpen(element);
- // @ts-ignore
element.blur();
if (!currentOpen && options.backdrop && hasClass(overlay, showClass)) {
hideOverlay();
- emulateTransitionEnd(overlay, () => hideOffcanvasComplete(self));
- } else hideOffcanvasComplete(self);
+ emulateTransitionEnd(overlay, () => hideOffcanvasComplete(self, callback));
+ } else hideOffcanvasComplete(self, callback);
}
// OFFCANVAS EVENT HANDLERS
@@ -3451,7 +3539,7 @@
/**
* Handles the `click` event listeners.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
* @param {MouseEvent} e the `Event` object
*/
function offcanvasTriggerHandler(e) {
@@ -3459,9 +3547,11 @@
const element = trigger && getTargetElement(trigger);
const self = element && getOffcanvasInstance(element);
+ /* istanbul ignore else */
if (self) {
self.relatedTarget = trigger;
self.toggle();
+ /* istanbul ignore else */
if (trigger && trigger.tagName === 'A') {
e.preventDefault();
}
@@ -3471,35 +3561,35 @@
/**
* Handles the event listeners that close the offcanvas.
*
- * @this {Document}
* @param {MouseEvent} e the `Event` object
*/
function offcanvasDismissHandler(e) {
- const element = querySelector(offcanvasActiveSelector, this);
- if (!element) return;
-
+ const { target } = e;
+ const element = querySelector(offcanvasActiveSelector, getDocument(target));
const offCanvasDismiss = querySelector(offcanvasDismissSelector, element);
const self = getOffcanvasInstance(element);
+ /* istanbul ignore next: must have a filter */
if (!self) return;
const { options, triggers } = self;
- const { target } = e;
- // @ts-ignore -- `EventTarget` is `HTMLElement`
+ const { backdrop } = options;
const trigger = closest(target, offcanvasToggleSelector);
const selection = getDocument(element).getSelection();
+ if (overlay.contains(target) && backdrop === 'static') return;
+
+ /* istanbul ignore else */
if (!(selection && selection.toString().length)
- // @ts-ignore
- && ((!element.contains(target) && options.backdrop
- && (!trigger || (trigger && !triggers.includes(trigger))))
- // @ts-ignore
+ && ((!element.contains(target) && backdrop
+ && /* istanbul ignore next */(!trigger || triggers.includes(target)))
|| (offCanvasDismiss && offCanvasDismiss.contains(target)))) {
- // @ts-ignore
self.relatedTarget = offCanvasDismiss && offCanvasDismiss.contains(target)
? offCanvasDismiss : null;
self.hide();
}
+
+ /* istanbul ignore next */
if (trigger && trigger.tagName === 'A') e.preventDefault();
}
@@ -3508,15 +3598,17 @@
* to hide it when user type the `ESC` key.
*
* @param {KeyboardEvent} e the `Event` object
- * @this {Document}
*/
- function offcanvasKeyDismissHandler({ code }) {
- const element = querySelector(offcanvasActiveSelector, this);
- if (!element) return;
+ function offcanvasKeyDismissHandler({ code, target }) {
+ const element = querySelector(offcanvasActiveSelector, getDocument(target));
const self = getOffcanvasInstance(element);
- if (self && self.options.keyboard && code === keyEscape) {
+ /* istanbul ignore next: must filter */
+ if (!self) return;
+
+ /* istanbul ignore else */
+ if (self.options.keyboard && code === keyEscape) {
self.relatedTarget = null;
self.hide();
}
@@ -3545,8 +3637,9 @@
* Handles the `transitionend` when hiding the offcanvas.
*
* @param {Offcanvas} self the `Offcanvas` instance
+ * @param {Function} callback the hide callback
*/
- function hideOffcanvasComplete(self) {
+ function hideOffcanvasComplete(self, callback) {
const { element, triggers } = self;
setAttribute(element, ariaHidden, 'true');
@@ -3555,6 +3648,7 @@
setElementStyle(element, { visibility: '' });
const visibleTrigger = showOffcanvasEvent.relatedTarget || triggers.find((x) => isVisible(x));
+ /* istanbul ignore else */
if (visibleTrigger) focus(visibleTrigger);
removeOverlay(element);
@@ -3566,6 +3660,8 @@
if (!getCurrentOpen(element)) {
toggleOffCanvasDismiss(self);
}
+ // callback
+ if (callback) callback();
}
// OFFCANVAS DEFINITION
@@ -3573,7 +3669,7 @@
/** Returns a new `Offcanvas` instance. */
class Offcanvas extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target usually an `.offcanvas` element
+ * @param {HTMLElement | string} target usually an `.offcanvas` element
* @param {BSN.Options.Offcanvas=} config instance options
*/
constructor(target, config) {
@@ -3584,15 +3680,14 @@
const { element } = self;
// all the triggering buttons
- /** @type {(HTMLElement | Element)[]} */
- self.triggers = [...querySelectorAll(offcanvasToggleSelector)]
+ /** @type {HTMLElement[]} */
+ self.triggers = [...querySelectorAll(offcanvasToggleSelector, getDocument(element))]
.filter((btn) => getTargetElement(btn) === element);
// additional instance property
- /** @type {HTMLBodyElement | HTMLElement | Element} */
- // @ts-ignore
+ /** @type {HTMLBodyElement | HTMLElement} */
self.container = getElementContainer(element);
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.relatedTarget = null;
// attach event listeners
@@ -3602,12 +3697,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return offcanvasComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return offcanvasDefaults; }
/* eslint-enable */
@@ -3640,7 +3733,8 @@
const currentOpen = getCurrentOpen(element);
if (currentOpen && currentOpen !== element) {
const this1 = getOffcanvasInstance(currentOpen);
- const that1 = this1 || getInstance(currentOpen, 'Modal');
+ const that1 = this1
+ || /* istanbul ignore next */getInstance(currentOpen, 'Modal');
that1.hide();
}
@@ -3657,6 +3751,7 @@
setTimeout(() => beforeOffcanvasShow(self), overlayDelay);
} else {
beforeOffcanvasShow(self);
+ /* istanbul ignore else */
if (currentOpen && hasClass(overlay, showClass)) {
hideOverlay();
}
@@ -3665,9 +3760,9 @@
/**
* Hides the offcanvas from the user.
- * @param {boolean=} force when `true` it will skip animation
+ * @param {Function=} callback when `true` it will skip animation
*/
- hide(force) {
+ hide(callback) {
const self = this;
const { element, relatedTarget } = self;
@@ -3681,17 +3776,16 @@
addClass(element, offcanvasTogglingClass);
removeClass(element, showClass);
- if (!force) {
- emulateTransitionEnd(element, () => beforeOffcanvasHide(self));
- } else beforeOffcanvasHide(self);
+ if (!callback) {
+ emulateTransitionEnd(element, () => beforeOffcanvasHide(self, callback));
+ } else beforeOffcanvasHide(self, callback);
}
/** Removes the `Offcanvas` from the target element. */
dispose() {
const self = this;
- self.hide(true);
toggleOffcanvasEvents(self);
- super.dispose();
+ self.hide(() => super.dispose());
}
}
@@ -3733,9 +3827,11 @@
* @param {any} element the target element
* @returns {boolean} the query result
*/
- const isMedia = (element) => element
- && [SVGElement, HTMLImageElement, HTMLVideoElement]
- .some((mediaType) => element instanceof mediaType);
+
+ const isMedia = (element) => (
+ element
+ && element.nodeType === 1
+ && ['SVG', 'Image', 'Video'].some((s) => element.constructor.name.includes(s))) || false;
/**
* Returns an `{x,y}` object with the target
@@ -3743,7 +3839,7 @@
*
* @see https://github.com/floating-ui/floating-ui
*
- * @param {HTMLElement | Element | Window} element target node / element
+ * @param {HTMLElement | Window} element target node / element
* @returns {{x: number, y: number}} the scroll tuple
*/
function getNodeScroll(element) {
@@ -3762,6 +3858,7 @@
* @returns {boolean} the query result
*/
function isScaledElement(element) {
+ if (!element || !isHTMLElement(element)) return false;
const { width, height } = getBoundingClientRect(element);
const { offsetWidth, offsetHeight } = element;
return Math.round(width) !== offsetWidth
@@ -3772,16 +3869,17 @@
* Returns the rect relative to an offset parent.
* @see https://github.com/floating-ui/floating-ui
*
- * @param {HTMLElement | Element} element target
- * @param {HTMLElement | Element | Window} offsetParent the container / offset parent
- * @param {{x: number, y: number}} scroll
- * @returns {SHORTER.OffsetRect}
+ * @param {HTMLElement} element target
+ * @param {ParentNode | Window} offsetParent the container / offset parent
+ * @param {{x: number, y: number}} scroll the offsetParent scroll position
+ * @returns {SHORTY.OffsetRect}
*/
function getRectRelativeToOffsetParent(element, offsetParent, scroll) {
- const isParentAnElement = offsetParent instanceof HTMLElement;
+ const isParentAnElement = isHTMLElement(offsetParent);
const rect = getBoundingClientRect(element, isParentAnElement && isScaledElement(offsetParent));
const offsets = { x: 0, y: 0 };
+ /* istanbul ignore next */
if (isParentAnElement) {
const offsetRect = getBoundingClientRect(offsetParent, true);
offsets.x = offsetRect.x + offsetParent.clientLeft;
@@ -3797,7 +3895,7 @@
}
/** @type {Record<string, string>} */
- var tipClassPositions = {
+ const tipClassPositions = {
top: 'top',
bottom: 'bottom',
left: 'start',
@@ -3816,27 +3914,34 @@
} = self;
const tipPositions = { ...tipClassPositions };
- // reset tooltip style (top: 0, left: 0 works best)
- setElementStyle(tooltip, { top: '0px', left: '0px', right: '' });
- // @ts-ignore
- const isPopover = self.name === popoverComponent;
- const tipWidth = tooltip.offsetWidth;
- const tipHeight = tooltip.offsetHeight;
const RTL = isRTL(element);
if (RTL) {
tipPositions.left = 'end';
tipPositions.right = 'start';
}
- const documentElement = getDocumentElement(element);
- const windowWidth = documentElement.clientWidth;
- const windowHeight = documentElement.clientHeight;
+
+ // reset tooltip style (top: 0, left: 0 works best)
+ setElementStyle(tooltip, {
+ // top: '0px', left: '0px', right: '', bottom: '',
+ top: '', left: '', right: '', bottom: '',
+ });
+ const isPopover = self.name === popoverComponent;
+ const {
+ offsetWidth: tipWidth, offsetHeight: tipHeight,
+ } = tooltip;
+ const {
+ clientWidth: htmlcw, clientHeight: htmlch,
+ } = getDocumentElement(element);
const { container } = options;
let { placement } = options;
const {
left: parentLeft, right: parentRight, top: parentTop,
} = getBoundingClientRect(container, true);
- const parentWidth = container.clientWidth;
- const scrollbarWidth = Math.abs(parentWidth - container.offsetWidth);
+ const {
+ clientWidth: parentCWidth, offsetWidth: parentOWidth,
+ } = container;
+ const scrollbarWidth = Math.abs(parentCWidth - parentOWidth);
+ // const tipAbsolute = getElementStyle(tooltip, 'position') === 'absolute';
const parentPosition = getElementStyle(container, 'position');
// const absoluteParent = parentPosition === 'absolute';
const fixedParent = parentPosition === 'fixed';
@@ -3846,8 +3951,8 @@
// const absoluteTarget = getElementStyle(element, 'position') === 'absolute';
// const stickyFixedParent = ['sticky', 'fixed'].includes(parentPosition);
const leftBoundry = RTL && fixedParent ? scrollbarWidth : 0;
- const rightBoundry = fixedParent ? parentWidth + parentLeft + (RTL ? scrollbarWidth : 0)
- : parentWidth + parentLeft + (windowWidth - parentRight) - 1;
+ const rightBoundry = fixedParent ? parentCWidth + parentLeft + (RTL ? scrollbarWidth : 0)
+ : parentCWidth + parentLeft + (htmlcw - parentRight) - 1;
const {
width: elemWidth,
height: elemHeight,
@@ -3859,7 +3964,9 @@
const scroll = getNodeScroll(offsetParent);
const { x, y } = getRectRelativeToOffsetParent(element, offsetParent, scroll);
// reset arrow style
- setElementStyle(arrow, { top: '', left: '', right: '' });
+ setElementStyle(arrow, {
+ top: '', left: '', right: '', bottom: '',
+ });
let topPosition;
let leftPosition;
let rightPosition;
@@ -3874,18 +3981,19 @@
// check placement
let topExceed = elemRectTop - tipHeight - arrowHeight < 0;
let bottomExceed = elemRectTop + tipHeight + elemHeight
- + arrowHeight >= windowHeight;
+ + arrowHeight >= htmlch;
let leftExceed = elemRectLeft - tipWidth - arrowWidth < leftBoundry;
let rightExceed = elemRectLeft + tipWidth + elemWidth
+ arrowWidth >= rightBoundry;
const horizontal = ['left', 'right'];
const vertical = ['top', 'bottom'];
+
topExceed = horizontal.includes(placement)
? elemRectTop + elemHeight / 2 - tipHeight / 2 - arrowHeight < 0
: topExceed;
bottomExceed = horizontal.includes(placement)
- ? elemRectTop + tipHeight / 2 + elemHeight / 2 + arrowHeight >= windowHeight
+ ? elemRectTop + tipHeight / 2 + elemHeight / 2 + arrowHeight >= htmlch
: bottomExceed;
leftExceed = vertical.includes(placement)
? elemRectLeft + elemWidth / 2 - tipWidth / 2 < leftBoundry
@@ -3894,9 +4002,10 @@
? elemRectLeft + tipWidth / 2 + elemWidth / 2 >= rightBoundry
: rightExceed;
- // recompute placement
- // first, when both left and right limits are exceeded, we fall back to top|bottom
+ // first remove side positions if both left and right limits are exceeded
+ // we usually fall back to top|bottom
placement = (horizontal.includes(placement)) && leftExceed && rightExceed ? 'top' : placement;
+ // second, recompute placement
placement = placement === 'top' && topExceed ? 'bottom' : placement;
placement = placement === 'bottom' && bottomExceed ? 'top' : placement;
placement = placement === 'left' && leftExceed ? 'right' : placement;
@@ -3908,6 +4017,7 @@
}
// compute tooltip / popover coordinates
+ /* istanbul ignore else */
if (horizontal.includes(placement)) { // secondary|side positions
if (placement === 'left') { // LEFT
leftPosition = x - tipWidth - (isPopover ? arrowWidth : 0);
@@ -3998,7 +4108,8 @@
});
// update arrow placement
- if (arrow instanceof HTMLElement) {
+ /* istanbul ignore else */
+ if (isHTMLElement(arrow)) {
if (arrowTop !== undefined) {
arrow.style.top = `${arrowTop}px`;
}
@@ -4027,7 +4138,7 @@
animation: true, // bool
/** @type {number} */
delay: 200, // number
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
container: null,
};
@@ -4067,6 +4178,12 @@
*/
const mousehoverEvent = 'hover';
+ /**
+ * A global namespace for `touchstart` event.
+ * @type {string}
+ */
+ const touchstartEvent = 'touchstart';
+
let elementUID = 0;
let elementMapUID = 0;
const elementIDMap = new Map();
@@ -4074,7 +4191,7 @@
/**
* Returns a unique identifier for popover, tooltip, scrollspy.
*
- * @param {HTMLElement | Element} element target element
+ * @param {HTMLElement} element target element
* @param {string=} key predefined key
* @returns {number} an existing or new unique ID
*/
@@ -4102,7 +4219,14 @@
return result;
}
- // @ts-ignore
+ /**
+ * Checks if an object is a `Function`.
+ *
+ * @param {any} fn the target object
+ * @returns {boolean} the query result
+ */
+ const isFunction = (fn) => (fn && fn.constructor.name === 'Function') || false;
+
const { userAgentData: uaDATA } = navigator;
/**
@@ -4123,8 +4247,8 @@
* A global `boolean` for Apple browsers.
* @type {boolean}
*/
- const isApple = !userAgentData ? appleBrands.test(userAgent)
- : userAgentData.brands.some((/** @type {Record<string, any>} */x) => appleBrands.test(x.brand));
+ const isApple = userAgentData ? userAgentData.brands.some((x) => appleBrands.test(x.brand))
+ : /* istanbul ignore next */appleBrands.test(userAgent);
/**
* Global namespace for `data-bs-title` attribute.
@@ -4135,46 +4259,75 @@
const tooltipComponent = 'Tooltip';
/**
+ * Checks if an object is a `NodeList`.
+ * => equivalent to `object instanceof NodeList`
+ *
+ * @param {any} object the target object
+ * @returns {boolean} the query result
+ */
+ const isNodeList = (object) => (object && object.constructor.name === 'NodeList') || false;
+
+ /**
+ * Shortcut for `typeof SOMETHING === "string"`.
+ *
+ * @param {any} str input value
+ * @returns {boolean} the query result
+ */
+ const isString = (str) => typeof str === 'string';
+
+ /**
+ * Shortcut for `Array.isArray()` static method.
+ *
+ * @param {any} arr array-like iterable object
+ * @returns {boolean} the query result
+ */
+ const isArray = (arr) => Array.isArray(arr);
+
+ /**
* Append an existing `Element` to Popover / Tooltip component or HTML
* markup string to be parsed & sanitized to be used as popover / tooltip content.
*
- * @param {HTMLElement | Element} element target
- * @param {HTMLElement | Element | string} content the `Element` to append / string
+ * @param {HTMLElement} element target
+ * @param {Node | string} content the `Element` to append / string
* @param {ReturnType<any>} sanitizeFn a function to sanitize string content
*/
function setHtml(element, content, sanitizeFn) {
- if (typeof content === 'string' && !content.length) return;
+ /* istanbul ignore next */
+ if (!isHTMLElement(element) || (isString(content) && !content.length)) return;
- if (typeof content === 'string') {
+ /* istanbul ignore else */
+ if (isString(content)) {
let dirty = content.trim(); // fixing #233
- if (typeof sanitizeFn === 'function') dirty = sanitizeFn(dirty);
+ if (isFunction(sanitizeFn)) dirty = sanitizeFn(dirty);
- const domParser = new DOMParser();
+ const win = getWindow(element);
+ const domParser = new win.DOMParser();
const tempDocument = domParser.parseFromString(dirty, 'text/html');
- const { body } = tempDocument;
- const method = body.children.length ? 'innerHTML' : 'innerText';
- // @ts-ignore
- element[method] = body[method];
- } else if (content instanceof HTMLElement) {
+ element.append(...[...tempDocument.body.childNodes]);
+ } else if (isHTMLElement(content)) {
element.append(content);
+ } else if (isNodeList(content)
+ || (isArray(content) && content.every(isNode))) {
+ element.append(...[...content]);
}
}
/**
* Creates a new tooltip / popover.
*
- * @param {BSN.Popover | BSN.Tooltip} self the `Popover` instance
+ * @param {BSN.Popover | BSN.Tooltip} self the `Tooltip` / `Popover` instance
*/
function createTip(self) {
const { id, element, options } = self;
const {
animation, customClass, sanitizeFn, placement, dismissible,
+ title, content, template, btnClose,
} = options;
- let { title, content } = options;
const isTooltip = self.name === tooltipComponent;
const tipString = isTooltip ? tooltipString : popoverString;
- const { template, btnClose } = options;
const tipPositions = { ...tipClassPositions };
+ let titleParts = [];
+ let contentParts = [];
if (isRTL(element)) {
tipPositions.left = 'end';
@@ -4185,18 +4338,18 @@
const placementClass = `bs-${tipString}-${tipPositions[placement]}`;
// load template
- /** @type {(HTMLElement | Element)?} */
- let popoverTemplate;
- if ([Element, HTMLElement].some((x) => template instanceof x)) {
- popoverTemplate = template;
+ /** @type {HTMLElement?} */
+ let tooltipTemplate;
+ if (isHTMLElement(template)) {
+ tooltipTemplate = template;
} else {
- const htmlMarkup = getDocument(element).createElement('div');
+ const htmlMarkup = createElement('div');
setHtml(htmlMarkup, template, sanitizeFn);
- popoverTemplate = htmlMarkup.firstElementChild;
+ tooltipTemplate = htmlMarkup.firstChild;
}
// set popover markup
- self.tooltip = popoverTemplate && popoverTemplate.cloneNode(true);
+ self.tooltip = isHTMLElement(tooltipTemplate) && tooltipTemplate.cloneNode(true);
const { tooltip } = self;
@@ -4210,44 +4363,79 @@
// set arrow and enable access for styleTip
self.arrow = querySelector(`.${tipString}-arrow`, tooltip);
+ const { arrow } = self;
+
+ if (isHTMLElement(title)) titleParts = [title.cloneNode(true)];
+ else {
+ const tempTitle = createElement('div');
+ setHtml(tempTitle, title, sanitizeFn);
+ titleParts = [...[...tempTitle.childNodes]];
+ }
+
+ if (isHTMLElement(content)) contentParts = [content.cloneNode(true)];
+ else {
+ const tempContent = createElement('div');
+ setHtml(tempContent, content, sanitizeFn);
+ contentParts = [...[...tempContent.childNodes]];
+ }
// set dismissible button
if (dismissible) {
if (title) {
- if (title instanceof HTMLElement) setHtml(title, btnClose, sanitizeFn);
- else title += btnClose;
+ if (isHTMLElement(btnClose)) titleParts = [...titleParts, btnClose.cloneNode(true)];
+ else {
+ const tempBtn = createElement('div');
+ setHtml(tempBtn, btnClose, sanitizeFn);
+ titleParts = [...titleParts, tempBtn.firstChild];
+ }
} else {
+ /* istanbul ignore else */
if (tooltipHeader) tooltipHeader.remove();
- if (content instanceof HTMLElement) setHtml(content, btnClose, sanitizeFn);
- else content += btnClose;
+ if (isHTMLElement(btnClose)) contentParts = [...contentParts, btnClose.cloneNode(true)];
+ else {
+ const tempBtn = createElement('div');
+ setHtml(tempBtn, btnClose, sanitizeFn);
+ contentParts = [...contentParts, tempBtn.firstChild];
+ }
}
}
// fill the template with content from options / data attributes
// also sanitize title && content
+ /* istanbul ignore else */
if (!isTooltip) {
- if (title && tooltipHeader) setHtml(tooltipHeader, title, sanitizeFn);
- if (content && tooltipBody) setHtml(tooltipBody, content, sanitizeFn);
- // @ts-ignore -- set btn
+ /* istanbul ignore else */
+ if (title && tooltipHeader) setHtml(tooltipHeader, titleParts, sanitizeFn);
+ /* istanbul ignore else */
+ if (content && tooltipBody) setHtml(tooltipBody, contentParts, sanitizeFn);
+ // set btn
self.btn = querySelector('.btn-close', tooltip);
} else if (title && tooltipBody) setHtml(tooltipBody, title, sanitizeFn);
+ // Bootstrap 5.2.x
+ addClass(tooltip, 'position-absolute');
+ addClass(arrow, 'position-absolute');
+
// set popover animation and placement
+ /* istanbul ignore else */
if (!hasClass(tooltip, tipString)) addClass(tooltip, tipString);
+ /* istanbul ignore else */
if (animation && !hasClass(tooltip, fadeClass)) addClass(tooltip, fadeClass);
+ /* istanbul ignore else */
if (customClass && !hasClass(tooltip, customClass)) {
addClass(tooltip, customClass);
}
+ /* istanbul ignore else */
if (!hasClass(tooltip, placementClass)) addClass(tooltip, placementClass);
}
/**
- * @param {(HTMLElement | Element)?} tip target
- * @param {HTMLElement | ParentNode} container parent container
+ * @param {HTMLElement} tip target
+ * @param {ParentNode} container parent container
* @returns {boolean}
*/
function isVisibleTip(tip, container) {
- return tip instanceof HTMLElement && container.contains(tip);
+ return isHTMLElement(tip) && container.contains(tip);
}
/* Native JavaScript for Bootstrap 5 | Tooltip
@@ -4289,14 +4477,18 @@
* Executes after the instance has been disposed.
*
* @param {Tooltip} self the `Tooltip` instance
+ * @param {Function=} callback the parent dispose callback
*/
- function disposeTooltipComplete(self) {
+ function disposeTooltipComplete(self, callback) {
const { element } = self;
toggleTooltipHandlers(self);
- if (element.hasAttribute(dataOriginalTitle) && self.name === tooltipString) {
+ /* istanbul ignore else */
+ if (hasAttribute(element, dataOriginalTitle) && self.name === tooltipComponent) {
toggleTooltipTitle(self);
}
+ /* istanbul ignore else */
+ if (callback) callback();
}
/**
@@ -4311,9 +4503,9 @@
action(getDocument(element), touchstartEvent, self.handleTouch, passiveHandler);
+ /* istanbul ignore else */
if (!isMedia(element)) {
[scrollEvent, resizeEvent].forEach((ev) => {
- // @ts-ignore
action(getWindow(element), ev, self.update, passiveHandler);
});
}
@@ -4337,14 +4529,16 @@
* Executes after the tooltip was hidden to the user.
*
* @param {Tooltip} self the `Tooltip` instance
+ * @param {Function=} callback the dispose callback
*/
- function tooltipHiddenAction(self) {
+ function tooltipHiddenAction(self, callback) {
const { element } = self;
const hiddenTooltipEvent = OriginalEvent(`hidden.bs.${toLowerCase(self.name)}`);
toggleTooltipAction(self);
removeTooltip(self);
dispatchEvent(element, hiddenTooltipEvent);
+ if (isFunction(callback)) callback();
Timer.clear(element, 'out');
}
@@ -4356,7 +4550,7 @@
*/
function toggleTooltipHandlers(self, add) {
const action = add ? addListener : removeListener;
- // @ts-ignore -- btn is only for dismissible popover
+ // btn is only for dismissible popover
const { element, options, btn } = self;
const { trigger, dismissible } = options;
@@ -4373,10 +4567,12 @@
}
triggerOptions.forEach((tr) => {
+ /* istanbul ignore else */
if (elemIsMedia || tr === mousehoverEvent) {
action(element, mousedownEvent, self.show);
action(element, mouseenterEvent, self.show);
+ /* istanbul ignore else */
if (dismissible && btn) {
action(btn, mouseclickEvent, self.hide);
} else {
@@ -4387,8 +4583,12 @@
action(element, tr, (!dismissible ? self.toggle : self.show));
} else if (tr === focusEvent) {
action(element, focusinEvent, self.show);
+ /* istanbul ignore else */
if (!dismissible) action(element, focusoutEvent, self.hide);
- if (isApple) action(element, mouseclickEvent, () => focus(element));
+ /* istanbul ignore else */
+ if (isApple) {
+ action(element, mouseclickEvent, () => focus(element));
+ }
}
});
}
@@ -4407,11 +4607,11 @@
const parentModal = closest(element, `.${modalString}`);
const parentOffcanvas = closest(element, `.${offcanvasString}`);
+ /* istanbul ignore else */
if (!isMedia(element)) {
const win = getWindow(element);
const overflow = offsetHeight !== scrollHeight;
const scrollTarget = overflow || offsetParent !== win ? container : win;
- // @ts-ignore
action(win, resizeEvent, self.update, passiveHandler);
action(scrollTarget, scrollEvent, self.update, passiveHandler);
}
@@ -4433,7 +4633,6 @@
const { element } = self;
setAttribute(element, titleAtt[content ? 0 : 1],
- // @ts-ignore
(content || getAttribute(element, titleAtt[0])));
removeAttribute(element, titleAtt[content ? 1 : 0]);
}
@@ -4443,7 +4642,7 @@
/** Creates a new `Tooltip` instance. */
class Tooltip extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target the target element
+ * @param {HTMLElement | string} target the target element
* @param {BSN.Options.Tooltip=} config the instance options
*/
constructor(target, config) {
@@ -4456,6 +4655,7 @@
const tipString = isTooltip ? tooltipString : popoverString;
const tipComponent = isTooltip ? tooltipComponent : popoverComponent;
+ /* istanbul ignore next: this is to set Popover too */
getTooltipInstance = (elem) => getInstance(elem, tipComponent);
// additional properties
@@ -4463,7 +4663,6 @@
self.tooltip = {};
if (!isTooltip) {
/** @type {any?} */
- // @ts-ignore
self.btn = null;
}
/** @type {any} */
@@ -4479,16 +4678,19 @@
const { options } = self;
// invalidate
- if ((!options.title && isTooltip) || (!isTooltip && !options.content)) return;
+ if ((!options.title && isTooltip) || (!isTooltip && !options.content)) {
+ // throw Error(`${this.name} Error: target has no content set.`);
+ return;
+ }
- const container = querySelector(options.container);
+ const container = querySelector(options.container, getDocument(element));
const idealContainer = getElementContainer(element);
// bypass container option when its position is static/relative
self.options.container = !container || (container
&& ['static', 'relative'].includes(getElementStyle(container, 'position')))
? idealContainer
- : container || getDocumentBody(element);
+ : /* istanbul ignore next */container || getDocumentBody(element);
// reset default options
tooltipDefaults[titleAttr] = null;
@@ -4501,7 +4703,8 @@
self.toggle = self.toggle.bind(self);
// set title attributes and add event listeners
- if (element.hasAttribute(titleAttr) && isTooltip) {
+ /* istanbul ignore else */
+ if (hasAttribute(element, titleAttr) && isTooltip) {
toggleTooltipTitle(self, options.title);
}
@@ -4515,12 +4718,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return tooltipComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return tooltipDefaults; }
/* eslint-enable */
@@ -4558,7 +4759,9 @@
self.update(e);
toggleTooltipOpenHandlers(self, true);
+ /* istanbul ignore else */
if (!hasClass(tooltip, showClass)) addClass(tooltip, showClass);
+ /* istanbul ignore else */
if (animation) emulateTransitionEnd(tooltip, () => tooltipShownAction(self));
else tooltipShownAction(self);
}, 17, 'in');
@@ -4568,15 +4771,17 @@
/**
* Hides the tooltip.
*
- * @this {Tooltip}
+ * @this {Tooltip} the Tooltip instance
+ * @param {Function=} callback the dispose callback
*/
- hide() {
+ hide(callback) {
const self = this;
const { options, tooltip, element } = self;
const { container, animation, delay } = options;
Timer.clear(element, 'in');
+ /* istanbul ignore else */
if (tooltip && isVisibleTip(tooltip, container)) {
Timer.set(element, () => {
const hideTooltipEvent = OriginalEvent(`hide.bs.${toLowerCase(self.name)}`);
@@ -4584,12 +4789,12 @@
if (hideTooltipEvent.defaultPrevented) return;
- // @ts-ignore
removeClass(tooltip, showClass);
toggleTooltipOpenHandlers(self);
- if (animation) emulateTransitionEnd(tooltip, () => tooltipHiddenAction(self));
- else tooltipHiddenAction(self);
+ /* istanbul ignore else */
+ if (animation) emulateTransitionEnd(tooltip, () => tooltipHiddenAction(self, callback));
+ else tooltipHiddenAction(self, callback);
}, delay + 17, 'out');
}
}
@@ -4601,7 +4806,6 @@
* @this {Tooltip} the `Tooltip` instance
*/
update(e) {
- // @ts-ignore
styleTip(this, e);
}
@@ -4623,6 +4827,7 @@
enable() {
const self = this;
const { enabled } = self;
+ /* istanbul ignore else */
if (!enabled) {
toggleTooltipHandlers(self, true);
self.enabled = !enabled;
@@ -4633,17 +4838,13 @@
disable() {
const self = this;
const {
- element, tooltip, options, enabled,
+ tooltip, options, enabled,
} = self;
- const { animation, container, delay } = options;
+ const { animation, container } = options;
+ /* istanbul ignore else */
if (enabled) {
if (isVisibleTip(tooltip, container) && animation) {
- self.hide();
-
- Timer.set(element, () => {
- toggleTooltipHandlers(self);
- Timer.clear(element, tooltipString);
- }, getElementTransitionDuration(tooltip) + delay + 17, tooltipString);
+ self.hide(() => toggleTooltipHandlers(self));
} else {
toggleTooltipHandlers(self);
}
@@ -4666,8 +4867,8 @@
handleTouch({ target }) {
const { tooltip, element } = this;
+ /* istanbul ignore next */
if (tooltip.contains(target) || target === element
- // @ts-ignore
|| (target && element.contains(target))) ; else {
this.hide();
}
@@ -4677,15 +4878,14 @@
dispose() {
const self = this;
const { tooltip, options } = self;
+ const callback = () => disposeTooltipComplete(self, () => super.dispose());
if (options.animation && isVisibleTip(tooltip, options.container)) {
- options.delay = 0; // reset delay
- self.hide();
- emulateTransitionEnd(tooltip, () => disposeTooltipComplete(self));
+ self.options.delay = 0; // reset delay
+ self.hide(callback);
} else {
- disposeTooltipComplete(self);
+ callback();
}
- super.dispose();
}
}
@@ -4721,7 +4921,7 @@
class Popover extends Tooltip {
/* eslint-disable -- we want to specify Popover Options */
/**
- * @param {HTMLElement | Element | string} target the target element
+ * @param {HTMLElement | string} target the target element
* @param {BSN.Options.Popover=} config the instance options
*/
constructor(target, config) {
@@ -4729,12 +4929,10 @@
}
/**
* Returns component name string.
- * @readonly @static
- */
+ */
get name() { return popoverComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return popoverDefaults; }
/* eslint-enable */
@@ -4742,8 +4940,9 @@
/* extend original `show()` */
show() {
super.show();
- // @ts-ignore -- btn only exists within dismissible popover
+ // btn only exists within dismissible popover
const { options, btn } = this;
+ /* istanbul ignore else */
if (options.dismissible && btn) setTimeout(() => focus(btn), 17);
}
}
@@ -4774,12 +4973,11 @@
* like `ShadowRoot` do not support `getElementsByTagName`.
*
* @param {string} selector the tag name
- * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
- * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
+ * @param {ParentNode=} parent optional Element to look into
+ * @return {HTMLCollectionOf<HTMLElement>} the 'HTMLCollection'
*/
function getElementsByTagName(selector, parent) {
- const lookUp = parent && parentNodes
- .some((x) => parent instanceof x) ? parent : getDocument();
+ const lookUp = isNode(parent) ? parent : getDocument();
return lookUp.getElementsByTagName(selector);
}
@@ -4792,8 +4990,6 @@
/* Native JavaScript for Bootstrap 5 | ScrollSpy
------------------------------------------------ */
- // console.log(typeof addEventListener)
-
// SCROLLSPY PRIVATE GC
// ====================
const scrollspySelector = '[data-bs-spy="scroll"]';
@@ -4832,15 +5028,15 @@
target, scrollTarget, options, itemsLength, scrollHeight, element,
} = self;
const { offset } = options;
- const isWin = scrollTarget instanceof Window;
+ const isWin = isWindow(scrollTarget);
const links = target && getElementsByTagName('A', target);
const scrollHEIGHT = scrollTarget && getScrollHeight(scrollTarget);
- // @ts-ignore
self.scrollTop = isWin ? scrollTarget.scrollY : scrollTarget.scrollTop;
// only update items/offsets once or with each mutation
+ /* istanbul ignore else */
if (links && (itemsLength !== links.length || scrollHEIGHT !== scrollHeight)) {
let href;
let targetItem;
@@ -4860,7 +5056,6 @@
if (targetItem) {
self.items.push(link);
rect = getBoundingClientRect(targetItem);
- // @ts-ignore
self.offsets.push((isWin ? rect.top + self.scrollTop : targetItem.offsetTop) - offset);
}
});
@@ -4870,12 +5065,12 @@
/**
* Returns the `scrollHeight` property of the scrolling element.
- * @param {HTMLElement | Element | Window | globalThis} scrollTarget the `ScrollSpy` instance
+ * @param {Node | Window} scrollTarget the `ScrollSpy` instance
* @return {number} `scrollTarget` height
*/
function getScrollHeight(scrollTarget) {
- return scrollTarget instanceof HTMLElement
- ? scrollTarget.scrollHeight // @ts-ignore
+ return isHTMLElement(scrollTarget)
+ ? scrollTarget.scrollHeight
: getDocumentElement(scrollTarget).scrollHeight;
}
@@ -4885,14 +5080,14 @@
* @returns {number}
*/
function getOffsetHeight({ element, scrollTarget }) {
- return (scrollTarget instanceof Window)
+ return (isWindow(scrollTarget))
? scrollTarget.innerHeight
: getBoundingClientRect(element).height;
}
/**
* Clear all items of the target.
- * @param {HTMLElement | Element} target a single item
+ * @param {HTMLElement} target a single item
*/
function clear(target) {
[...getElementsByTagName('A', target)].forEach((item) => {
@@ -4903,13 +5098,11 @@
/**
* Activates a new item.
* @param {ScrollSpy} self the `ScrollSpy` instance
- * @param {HTMLElement | Element} item a single item
+ * @param {HTMLElement} item a single item
*/
function activate(self, item) {
const { target, element } = self;
- // @ts-ignore
clear(target);
- // @ts-ignore
self.activeItem = item;
addClass(item, activeClass);
@@ -4917,15 +5110,15 @@
const parents = [];
let parentItem = item;
while (parentItem !== getDocumentBody(element)) {
- // @ts-ignore
parentItem = parentItem.parentElement;
if (hasClass(parentItem, 'nav') || hasClass(parentItem, 'dropdown-menu')) parents.push(parentItem);
}
parents.forEach((menuItem) => {
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
const parentLink = menuItem.previousElementSibling;
+ /* istanbul ignore else */
if (parentLink && !hasClass(parentLink, activeClass)) {
addClass(parentLink, activeClass);
}
@@ -4943,7 +5136,6 @@
*/
function toggleSpyHandlers(self, add) {
const action = add ? addListener : removeListener;
- // @ts-ignore
action(self.scrollTarget, scrollEvent, self.refresh, passiveHandler);
}
@@ -4952,7 +5144,7 @@
/** Returns a new `ScrollSpy` instance. */
class ScrollSpy extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target the target element
+ * @param {HTMLElement | string} target the target element
* @param {BSN.Options.ScrollSpy=} config the instance options
*/
constructor(target, config) {
@@ -4964,26 +5156,25 @@
const { element, options } = self;
// additional properties
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.target = querySelector(options.target, getDocument(element));
// invalidate
if (!self.target) return;
- const win = getWindow(element);
-
// set initial state
- /** @type {HTMLElement | Element | Window | globalThis} */
- self.scrollTarget = element.clientHeight < element.scrollHeight ? element : win;
+ /** @type {HTMLElement | Window} */
+ self.scrollTarget = element.clientHeight < element.scrollHeight
+ ? element : getWindow(element);
/** @type {number} */
self.scrollTop = 0;
/** @type {number} */
self.maxScroll = 0;
/** @type {number} */
self.scrollHeight = 0;
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.activeItem = null;
- /** @type {(HTMLElement | Element)[]} */
+ /** @type {HTMLElement[]} */
self.items = [];
/** @type {number} */
self.itemsLength = 0;
@@ -5002,12 +5193,10 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
*/
get name() { return scrollspyComponent; }
/**
* Returns component default options.
- * @readonly @static
*/
get defaults() { return scrollspyDefaults; }
/* eslint-enable */
@@ -5020,7 +5209,7 @@
const { target } = self;
// check if target is visible and invalidate
- // @ts-ignore
+ /* istanbul ignore next */
if (target.offsetHeight === 0) return;
updateSpyTargets(self);
@@ -5032,6 +5221,7 @@
if (scrollTop >= maxScroll) {
const newActiveItem = items[itemsLength - 1];
+ /* istanbul ignore else */
if (activeItem !== newActiveItem) {
activate(self, newActiveItem);
}
@@ -5042,7 +5232,6 @@
if (activeItem && scrollTop < offsets[0] && offsets[0] > 0) {
self.activeItem = null;
- // @ts-ignore
clear(target);
return;
}
@@ -5111,7 +5300,7 @@
/**
* Stores the current active tab and its content
* for a given `.nav` element.
- * @type {Map<(HTMLElement | Element), any>}
+ * @type {Map<HTMLElement, any>}
*/
const tabPrivate = new Map();
@@ -5124,12 +5313,13 @@
function triggerTabEnd(self) {
const { tabContent, nav } = self;
+ /* istanbul ignore else */
if (tabContent && hasClass(tabContent, collapsingClass)) {
- // @ts-ignore
tabContent.style.height = '';
removeClass(tabContent, collapsingClass);
}
+ /* istanbul ignore else */
if (nav) Timer.clear(nav);
}
@@ -5143,14 +5333,15 @@
} = self;
const { tab } = nav && tabPrivate.get(nav);
- if (tabContent && hasClass(nextContent, fadeClass)) { // height animation
+ /* istanbul ignore else */
+ if (tabContent && hasClass(nextContent, fadeClass)) {
const { currentHeight, nextHeight } = tabPrivate.get(element);
if (currentHeight === nextHeight) {
triggerTabEnd(self);
} else {
- setTimeout(() => { // enables height animation
- // @ts-ignore
- tabContent.style.height = `${nextHeight}px`; // height animation
+ // enables height animation
+ setTimeout(() => {
+ tabContent.style.height = `${nextHeight}px`;
reflow(tabContent);
emulateTransitionEnd(tabContent, () => triggerTabEnd(self));
}, 50);
@@ -5172,11 +5363,12 @@
const { tab, content } = nav && tabPrivate.get(nav);
let currentHeight = 0;
+ /* istanbul ignore else */
if (tabContent && hasClass(nextContent, fadeClass)) {
[content, nextContent].forEach((c) => {
addClass(c, 'overflow-hidden');
});
- currentHeight = content.scrollHeight || 0;
+ currentHeight = content.scrollHeight || /* istanbul ignore next */0;
}
// update relatedTarget and dispatch event
@@ -5188,12 +5380,12 @@
addClass(nextContent, activeClass);
removeClass(content, activeClass);
+ /* istanbul ignore else */
if (tabContent && hasClass(nextContent, fadeClass)) {
const nextHeight = nextContent.scrollHeight;
tabPrivate.set(element, { currentHeight, nextHeight });
addClass(tabContent, collapsingClass);
- // @ts-ignore -- height animation
tabContent.style.height = `${currentHeight}px`;
reflow(tabContent);
[content, nextContent].forEach((c) => {
@@ -5224,26 +5416,24 @@
function getActiveTab(self) {
const { nav } = self;
- // @ts-ignore
const activeTabs = getElementsByClassName(activeClass, nav);
- /** @type {(HTMLElement | Element)=} */
+ /** @type {(HTMLElement)=} */
let tab;
+ /* istanbul ignore else */
if (activeTabs.length === 1
- // @ts-ignore
&& !dropdownMenuClasses.some((c) => hasClass(activeTabs[0].parentElement, c))) {
[tab] = activeTabs;
} else if (activeTabs.length > 1) {
tab = activeTabs[activeTabs.length - 1];
}
const content = tab ? getTargetElement(tab) : null;
- // @ts-ignore
return { tab, content };
}
/**
* Returns a parent dropdown.
- * @param {HTMLElement | Element} element the `Tab` element
- * @returns {(HTMLElement | Element)?} the parent dropdown
+ * @param {HTMLElement} element the `Tab` element
+ * @returns {HTMLElement?} the parent dropdown
*/
function getParentDropdown(element) {
const dropdown = closest(element, `.${dropdownMenuClasses.join(',.')}`);
@@ -5264,11 +5454,12 @@
// =================
/**
* Handles the `click` event listener.
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
* @param {MouseEvent} e the `Event` object
*/
function tabClickHandler(e) {
const self = getTabInstance(this);
+ /* istanbul ignore next: must filter */
if (!self) return;
e.preventDefault();
@@ -5280,7 +5471,7 @@
/** Creates a new `Tab` instance. */
class Tab extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target the target element
+ * @param {HTMLElement | string} target the target element
*/
constructor(target) {
super(target);
@@ -5297,15 +5488,15 @@
const nav = closest(element, '.nav');
const container = closest(content, '.tab-content');
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.nav = nav;
- /** @type {HTMLElement | Element} */
+ /** @type {HTMLElement} */
self.content = content;
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.tabContent = container;
// event targets
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.dropdown = getParentDropdown(element);
// show first Tab instance of none is shown
@@ -5315,6 +5506,7 @@
const firstTab = querySelector(tabSelector, nav);
const firstTabContent = firstTab && getTargetElement(firstTab);
+ /* istanbul ignore else */
if (firstTabContent) {
addClass(firstTab, activeClass);
addClass(firstTabContent, showClass);
@@ -5330,8 +5522,7 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
- */
+ */
get name() { return tabComponent; }
/* eslint-enable */
@@ -5340,11 +5531,15 @@
/** Shows the tab to the user. */
show() {
const self = this;
- const { element, nav, dropdown } = self;
+ const {
+ element, content: nextContent, nav, dropdown,
+ } = self;
+ /* istanbul ignore else */
if (!(nav && Timer.get(nav)) && !hasClass(element, activeClass)) {
const { tab, content } = getActiveTab(self);
+ /* istanbul ignore else */
if (nav) tabPrivate.set(nav, { tab, content });
// update relatedTarget and dispatch
@@ -5361,16 +5556,21 @@
removeClass(activeDropdown, activeClass);
}
+ /* istanbul ignore else */
if (nav) {
- Timer.set(nav, () => {
+ const toggleTab = () => {
removeClass(tab, activeClass);
setAttribute(tab, ariaSelected, 'false');
if (dropdown && !hasClass(dropdown, activeClass)) addClass(dropdown, activeClass);
- }, 1);
+ };
+
+ if (hasClass(content, fadeClass) || hasClass(nextContent, fadeClass)) {
+ Timer.set(nav, toggleTab, 1);
+ } else toggleTab();
}
+ removeClass(content, showClass);
if (hasClass(content, fadeClass)) {
- removeClass(content, showClass);
emulateTransitionEnd(content, () => triggerTabHide(self));
} else {
triggerTabHide(self);
@@ -5404,6 +5604,7 @@
// ================
const toastSelector = `.${toastString}`;
const toastDismissSelector = `[${dataBsDismiss}="${toastString}"]`;
+ const toastToggleSelector = `[${dataBsToggle}="${toastString}"]`;
const showingClass = 'showing';
/** @deprecated */
const hideClass = 'hide';
@@ -5447,6 +5648,7 @@
Timer.clear(element, showingClass);
dispatchEvent(element, shownToastEvent);
+ /* istanbul ignore else */
if (options.autohide) {
Timer.set(element, () => self.hide(), options.delay, toastString);
}
@@ -5508,14 +5710,24 @@
*/
function toggleToastHandlers(self, add) {
const action = add ? addListener : removeListener;
- const { element, dismiss, options } = self;
+ const {
+ element, triggers, dismiss, options,
+ } = self;
+
+ /* istanbul ignore else */
if (dismiss) {
action(dismiss, mouseclickEvent, self.hide);
}
+
+ /* istanbul ignore else */
if (options.autohide) {
[focusinEvent, focusoutEvent, mouseenterEvent, mouseleaveEvent]
.forEach((e) => action(element, e, interactiveToastHandler));
}
+ /* istanbul ignore else */
+ if (triggers.length) {
+ triggers.forEach((btn) => action(btn, mouseclickEvent, toastClickHandler));
+ }
}
// TOAST EVENT HANDLERS
@@ -5530,17 +5742,35 @@
}
/**
+ * Handles the `click` event listener for toast.
+ * @param {MouseEvent} e the `Event` object
+ */
+ function toastClickHandler(e) {
+ const { target } = e;
+
+ const trigger = target && closest(target, toastToggleSelector);
+ const element = trigger && getTargetElement(trigger);
+ const self = element && getToastInstance(element);
+
+ /* istanbul ignore else */
+ if (trigger && trigger.tagName === 'A') e.preventDefault();
+ self.relatedTarget = trigger;
+ self.show();
+ }
+
+ /**
* Executes when user interacts with the toast without closing it,
* usually by hovering or focusing it.
*
- * @this {HTMLElement | Element}
+ * @this {HTMLElement}
* @param {MouseEvent} e the `Toast` instance
*/
function interactiveToastHandler(e) {
const element = this;
const self = getToastInstance(element);
const { type, relatedTarget } = e;
- // @ts-ignore
+
+ /* istanbul ignore next: a solid filter is required */
if (!self || (element === relatedTarget || element.contains(relatedTarget))) return;
if ([mouseenterEvent, focusinEvent].includes(type)) {
@@ -5555,7 +5785,7 @@
/** Creates a new `Toast` instance. */
class Toast extends BaseComponent {
/**
- * @param {HTMLElement | Element | string} target the target `.toast` element
+ * @param {HTMLElement | string} target the target `.toast` element
* @param {BSN.Options.Toast=} config the instance options
*/
constructor(target, config) {
@@ -5567,10 +5797,16 @@
// set fadeClass, the options.animation will override the markup
if (options.animation && !hasClass(element, fadeClass)) addClass(element, fadeClass);
else if (!options.animation && hasClass(element, fadeClass)) removeClass(element, fadeClass);
+
// dismiss button
- /** @type {(HTMLElement | Element)?} */
+ /** @type {HTMLElement?} */
self.dismiss = querySelector(toastDismissSelector, element);
+ // toast can have multiple triggering elements
+ /** @type {HTMLElement[]} */
+ self.triggers = [...querySelectorAll(toastToggleSelector, getDocument(element))]
+ .filter((btn) => getTargetElement(btn) === element);
+
// bind
self.show = self.show.bind(self);
self.hide = self.hide.bind(self);
@@ -5582,23 +5818,28 @@
/* eslint-disable */
/**
* Returns component name string.
- * @readonly @static
- */
+ */
get name() { return toastComponent; }
/**
* Returns component default options.
- * @readonly @static
- */
+ */
get defaults() { return toastDefaults; }
/* eslint-enable */
+ /**
+ * Returns *true* when toast is visible.
+ */
+ get isShown() { return hasClass(this.element, showClass); }
+
// TOAST PUBLIC METHODS
// ====================
/** Shows the toast. */
show() {
const self = this;
- const { element } = self;
- if (element && !hasClass(element, showClass)) {
+ const { element, isShown } = self;
+
+ /* istanbul ignore else */
+ if (element && !isShown) {
dispatchEvent(element, showToastEvent);
if (showToastEvent.defaultPrevented) return;
@@ -5609,9 +5850,10 @@
/** Hides the toast. */
hide() {
const self = this;
- const { element } = self;
+ const { element, isShown } = self;
- if (element && hasClass(element, showClass)) {
+ /* istanbul ignore else */
+ if (element && isShown) {
dispatchEvent(element, hideToastEvent);
if (hideToastEvent.defaultPrevented) return;
hideToast(self);
@@ -5621,9 +5863,10 @@
/** Removes the `Toast` component from the target element. */
dispose() {
const self = this;
- const { element } = self;
+ const { element, isShown } = self;
- if (hasClass(element, showClass)) {
+ /* istanbul ignore else */
+ if (isShown) {
removeClass(element, showClass);
}
@@ -5642,7 +5885,7 @@
/**
* Check if element matches a CSS selector.
*
- * @param {HTMLElement | Element} target
+ * @param {HTMLElement} target
* @param {string} selector
* @returns {boolean}
*/
@@ -5669,7 +5912,7 @@
/**
* Initialize all matched `Element`s for one component.
* @param {BSN.InitCallback<any>} callback
- * @param {NodeListOf<HTMLElement | Element> | (HTMLElement | Element)[]} collection
+ * @param {NodeList | Node[]} collection
*/
function initComponentDataAPI(callback, collection) {
[...collection].forEach((x) => callback(x));
@@ -5678,7 +5921,7 @@
/**
* Remove one component from a target container element or all in the page.
* @param {string} component the component name
- * @param {(Element | HTMLElement | Document)=} context parent `Element`
+ * @param {ParentNode} context parent `Node`
*/
function removeComponentDataAPI(component, context) {
const compData = Data.getAllFor(component);
@@ -5686,18 +5929,17 @@
if (compData) {
[...compData].forEach((x) => {
const [element, instance] = x;
- if (context && context.contains(element)) instance.dispose();
+ if (context.contains(element)) instance.dispose();
});
}
}
/**
* Initialize all BSN components for a target container.
- * @param {(Element | HTMLElement | Document)=} context parent `Element`
+ * @param {ParentNode=} context parent `Node`
*/
function initCallback(context) {
- const lookUp = context && parentNodes.some((x) => context instanceof x)
- ? context : undefined;
+ const lookUp = context && context.nodeName ? context : document;
const elemCollection = [...getElementsByTagName('*', lookUp)];
ObjectKeys(componentsList).forEach((comp) => {
@@ -5708,11 +5950,10 @@
/**
* Remove all BSN components for a target container.
- * @param {(Element | HTMLElement | Document)=} context parent `Element`
+ * @param {ParentNode=} context parent `Node`
*/
function removeDataAPI(context) {
- const lookUp = context && parentNodes.some((x) => context instanceof x)
- ? context : undefined;
+ const lookUp = context && context.nodeName ? context : document;
ObjectKeys(componentsList).forEach((comp) => {
removeComponentDataAPI(comp, lookUp);
@@ -5742,7 +5983,7 @@
initCallback,
removeDataAPI,
Version,
- EventListener,
+ EventListener: Listener,
};
return BSN;
diff --git a/src/static/scripts/bootstrap.css b/src/static/scripts/bootstrap.css
index f16c5be8..ddf519cc 100644
--- a/src/static/scripts/bootstrap.css
+++ b/src/static/scripts/bootstrap.css
@@ -1,8 +1,8 @@
@charset "UTF-8";
/*!
- * Bootstrap v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors
- * Copyright 2011-2021 Twitter, Inc.
+ * Bootstrap v5.2.0-beta1 (https://getbootstrap.com/)
+ * Copyright 2011-2022 The Bootstrap Authors
+ * Copyright 2011-2022 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root {
@@ -16,6 +16,7 @@
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
+ --bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
@@ -48,7 +49,7 @@
--bs-black-rgb: 0, 0, 0;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg-rgb: 255, 255, 255;
- --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
@@ -57,6 +58,21 @@
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-bg: #fff;
+ --bs-border-width: 1px;
+ --bs-border-style: solid;
+ --bs-border-color: #dee2e6;
+ --bs-border-color-translucent: rgba(0, 0, 0, 0.175);
+ --bs-border-radius: 0.375rem;
+ --bs-border-radius-sm: 0.25rem;
+ --bs-border-radius-lg: 0.5rem;
+ --bs-border-radius-xl: 1rem;
+ --bs-border-radius-2xl: 2rem;
+ --bs-border-radius-pill: 50rem;
+ --bs-heading-color: ;
+ --bs-link-color: #0d6efd;
+ --bs-link-hover-color: #0a58ca;
+ --bs-code-color: #d63384;
+ --bs-highlight-bg: #fff3cd;
}
*,
@@ -87,20 +103,17 @@ body {
hr {
margin: 1rem 0;
color: inherit;
- background-color: currentColor;
border: 0;
+ border-top: 1px solid;
opacity: 0.25;
}
-hr:not([size]) {
- height: 1px;
-}
-
h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
+ color: var(--bs-heading-color);
}
h1, .h1 {
@@ -152,8 +165,7 @@ p {
margin-bottom: 1rem;
}
-abbr[title],
-abbr[data-bs-original-title] {
+abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
@@ -209,8 +221,8 @@ small, .small {
}
mark, .mark {
- padding: 0.2em;
- background-color: #fcf8e3;
+ padding: 0.1875em;
+ background-color: var(--bs-highlight-bg);
}
sub,
@@ -230,11 +242,11 @@ sup {
}
a {
- color: #0d6efd;
+ color: var(--bs-link-color);
text-decoration: underline;
}
a:hover {
- color: #0a58ca;
+ color: var(--bs-link-hover-color);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
@@ -248,8 +260,6 @@ kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
- direction: ltr /* rtl:ignore */;
- unicode-bidi: bidi-override;
}
pre {
@@ -267,7 +277,7 @@ pre code {
code {
font-size: 0.875em;
- color: #d63384;
+ color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
@@ -275,16 +285,15 @@ a > code {
}
kbd {
- padding: 0.2rem 0.4rem;
+ padding: 0.1875rem 0.375rem;
font-size: 0.875em;
- color: #fff;
- background-color: #212529;
- border-radius: 0.2rem;
+ color: var(--bs-body-bg);
+ background-color: var(--bs-body-color);
+ border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
- font-weight: 700;
}
figure {
@@ -304,7 +313,7 @@ table {
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
- color: #6c757d;
+ color: rgba(var(--bs-body-color-rgb), 0.75);
text-align: left;
}
@@ -363,8 +372,8 @@ select:disabled {
opacity: 1;
}
-[list]::-webkit-calendar-picker-indicator {
- display: none;
+[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
+ display: none !important;
}
button,
@@ -450,14 +459,11 @@ legend + * {
::-webkit-file-upload-button {
font: inherit;
+ -webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-}
-
-::-webkit-file-upload-button {
- font: inherit;
-webkit-appearance: button;
}
@@ -601,8 +607,8 @@ progress {
.img-thumbnail {
padding: 0.25rem;
background-color: #fff;
- border: 1px solid #dee2e6;
- border-radius: 0.25rem;
+ border: 1px solid var(--bs-border-color);
+ border-radius: 0.375rem;
max-width: 100%;
height: auto;
}
@@ -628,9 +634,11 @@ progress {
.container-lg,
.container-md,
.container-sm {
+ --bs-gutter-x: 1.5rem;
+ --bs-gutter-y: 0;
width: 100%;
- padding-right: var(--bs-gutter-x, 0.75rem);
- padding-left: var(--bs-gutter-x, 0.75rem);
+ padding-right: calc(var(--bs-gutter-x) * 0.5);
+ padding-left: calc(var(--bs-gutter-x) * 0.5);
margin-right: auto;
margin-left: auto;
}
@@ -1952,19 +1960,21 @@ progress {
}
}
.table {
+ --bs-table-color: var(--bs-body-color);
--bs-table-bg: transparent;
+ --bs-table-border-color: var(--bs-border-color);
--bs-table-accent-bg: transparent;
- --bs-table-striped-color: #212529;
+ --bs-table-striped-color: var(--bs-body-color);
--bs-table-striped-bg: rgba(0, 0, 0, 0.05);
- --bs-table-active-color: #212529;
+ --bs-table-active-color: var(--bs-body-color);
--bs-table-active-bg: rgba(0, 0, 0, 0.1);
- --bs-table-hover-color: #212529;
+ --bs-table-hover-color: var(--bs-body-color);
--bs-table-hover-bg: rgba(0, 0, 0, 0.075);
width: 100%;
margin-bottom: 1rem;
- color: #212529;
+ color: var(--bs-table-color);
vertical-align: top;
- border-color: #dee2e6;
+ border-color: var(--bs-table-border-color);
}
.table > :not(caption) > * > * {
padding: 0.5rem 0.5rem;
@@ -1978,8 +1988,9 @@ progress {
.table > thead {
vertical-align: bottom;
}
-.table > :not(:first-child) {
- border-top: 2px solid currentColor;
+
+.table-group-divider {
+ border-top: 2px solid currentcolor;
}
.caption-top {
@@ -2009,6 +2020,11 @@ progress {
color: var(--bs-table-striped-color);
}
+.table-striped-columns > :not(caption) > tr > :nth-child(even) {
+ --bs-table-accent-bg: var(--bs-table-striped-bg);
+ color: var(--bs-table-striped-color);
+}
+
.table-active {
--bs-table-accent-bg: var(--bs-table-active-bg);
color: var(--bs-table-active-color);
@@ -2020,99 +2036,115 @@ progress {
}
.table-primary {
+ --bs-table-color: #000;
--bs-table-bg: #cfe2ff;
+ --bs-table-border-color: #bacbe6;
--bs-table-striped-bg: #c5d7f2;
--bs-table-striped-color: #000;
--bs-table-active-bg: #bacbe6;
--bs-table-active-color: #000;
--bs-table-hover-bg: #bfd1ec;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #bacbe6;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-secondary {
+ --bs-table-color: #000;
--bs-table-bg: #e2e3e5;
+ --bs-table-border-color: #cbccce;
--bs-table-striped-bg: #d7d8da;
--bs-table-striped-color: #000;
--bs-table-active-bg: #cbccce;
--bs-table-active-color: #000;
--bs-table-hover-bg: #d1d2d4;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #cbccce;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-success {
+ --bs-table-color: #000;
--bs-table-bg: #d1e7dd;
+ --bs-table-border-color: #bcd0c7;
--bs-table-striped-bg: #c7dbd2;
--bs-table-striped-color: #000;
--bs-table-active-bg: #bcd0c7;
--bs-table-active-color: #000;
--bs-table-hover-bg: #c1d6cc;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #bcd0c7;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-info {
+ --bs-table-color: #000;
--bs-table-bg: #cff4fc;
+ --bs-table-border-color: #badce3;
--bs-table-striped-bg: #c5e8ef;
--bs-table-striped-color: #000;
--bs-table-active-bg: #badce3;
--bs-table-active-color: #000;
--bs-table-hover-bg: #bfe2e9;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #badce3;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-warning {
+ --bs-table-color: #000;
--bs-table-bg: #fff3cd;
+ --bs-table-border-color: #e6dbb9;
--bs-table-striped-bg: #f2e7c3;
--bs-table-striped-color: #000;
--bs-table-active-bg: #e6dbb9;
--bs-table-active-color: #000;
--bs-table-hover-bg: #ece1be;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #e6dbb9;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-danger {
+ --bs-table-color: #000;
--bs-table-bg: #f8d7da;
+ --bs-table-border-color: #dfc2c4;
--bs-table-striped-bg: #eccccf;
--bs-table-striped-color: #000;
--bs-table-active-bg: #dfc2c4;
--bs-table-active-color: #000;
--bs-table-hover-bg: #e5c7ca;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #dfc2c4;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-light {
+ --bs-table-color: #000;
--bs-table-bg: #f8f9fa;
+ --bs-table-border-color: #dfe0e1;
--bs-table-striped-bg: #ecedee;
--bs-table-striped-color: #000;
--bs-table-active-bg: #dfe0e1;
--bs-table-active-color: #000;
--bs-table-hover-bg: #e5e6e7;
--bs-table-hover-color: #000;
- color: #000;
- border-color: #dfe0e1;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-dark {
+ --bs-table-color: #fff;
--bs-table-bg: #212529;
+ --bs-table-border-color: #373b3e;
--bs-table-striped-bg: #2c3034;
--bs-table-striped-color: #fff;
--bs-table-active-bg: #373b3e;
--bs-table-active-color: #fff;
--bs-table-hover-bg: #323539;
--bs-table-hover-color: #fff;
- color: #fff;
- border-color: #373b3e;
+ color: var(--bs-table-color);
+ border-color: var(--bs-table-border-color);
}
.table-responsive {
@@ -2177,7 +2209,7 @@ progress {
.form-text {
margin-top: 0.25rem;
font-size: 0.875em;
- color: #6c757d;
+ color: rgba(var(--bs-body-color-rgb), 0.75);
}
.form-control {
@@ -2194,7 +2226,7 @@ progress {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
@@ -2276,31 +2308,6 @@ progress {
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
background-color: #dde0e3;
}
-.form-control::-webkit-file-upload-button {
- padding: 0.375rem 0.75rem;
- margin: -0.375rem -0.75rem;
- -webkit-margin-end: 0.75rem;
- margin-inline-end: 0.75rem;
- color: #212529;
- background-color: #e9ecef;
- pointer-events: none;
- border-color: inherit;
- border-style: solid;
- border-width: 0;
- border-inline-end-width: 1px;
- border-radius: 0;
- -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
- transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
-}
-@media (prefers-reduced-motion: reduce) {
- .form-control::-webkit-file-upload-button {
- -webkit-transition: none;
- transition: none;
- }
-}
-.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
- background-color: #dde0e3;
-}
.form-control-plaintext {
display: block;
@@ -2322,7 +2329,7 @@ progress {
min-height: calc(1.5em + 0.5rem + 2px);
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
- border-radius: 0.2rem;
+ border-radius: 0.25rem;
}
.form-control-sm::-webkit-file-upload-button {
padding: 0.25rem 0.5rem;
@@ -2336,18 +2343,12 @@ progress {
-webkit-margin-end: 0.5rem;
margin-inline-end: 0.5rem;
}
-.form-control-sm::-webkit-file-upload-button {
- padding: 0.25rem 0.5rem;
- margin: -0.25rem -0.5rem;
- -webkit-margin-end: 0.5rem;
- margin-inline-end: 0.5rem;
-}
.form-control-lg {
min-height: calc(1.5em + 1rem + 2px);
padding: 0.5rem 1rem;
font-size: 1.25rem;
- border-radius: 0.3rem;
+ border-radius: 0.5rem;
}
.form-control-lg::-webkit-file-upload-button {
padding: 0.5rem 1rem;
@@ -2361,12 +2362,6 @@ progress {
-webkit-margin-end: 1rem;
margin-inline-end: 1rem;
}
-.form-control-lg::-webkit-file-upload-button {
- padding: 0.5rem 1rem;
- margin: -0.5rem -1rem;
- -webkit-margin-end: 1rem;
- margin-inline-end: 1rem;
-}
textarea.form-control {
min-height: calc(1.5em + 0.75rem + 2px);
@@ -2388,11 +2383,11 @@ textarea.form-control-lg {
}
.form-control-color::-moz-color-swatch {
height: 1.5em;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
}
.form-control-color::-webkit-color-swatch {
height: 1.5em;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
}
.form-select {
@@ -2405,12 +2400,12 @@ textarea.form-control-lg {
line-height: 1.5;
color: #212529;
background-color: #fff;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 16px 12px;
border: 1px solid #ced4da;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
-webkit-appearance: none;
-moz-appearance: none;
@@ -2443,7 +2438,7 @@ textarea.form-control-lg {
padding-bottom: 0.25rem;
padding-left: 0.5rem;
font-size: 0.875rem;
- border-radius: 0.2rem;
+ border-radius: 0.25rem;
}
.form-select-lg {
@@ -2451,7 +2446,7 @@ textarea.form-control-lg {
padding-bottom: 0.5rem;
padding-left: 1rem;
font-size: 1.25rem;
- border-radius: 0.3rem;
+ border-radius: 0.5rem;
}
.form-check {
@@ -2465,6 +2460,17 @@ textarea.form-control-lg {
margin-left: -1.5em;
}
+.form-check-reverse {
+ padding-right: 1.5em;
+ padding-left: 0;
+ text-align: right;
+}
+.form-check-reverse .form-check-input {
+ float: right;
+ margin-right: -1.5em;
+ margin-left: 0;
+}
+
.form-check-input {
width: 1em;
height: 1em;
@@ -2480,6 +2486,7 @@ textarea.form-control-lg {
appearance: none;
-webkit-print-color-adjust: exact;
color-adjust: exact;
+ print-color-adjust: exact;
}
.form-check-input[type=checkbox] {
border-radius: 0.25em;
@@ -2500,7 +2507,7 @@ textarea.form-control-lg {
border-color: #0d6efd;
}
.form-check-input:checked[type=checkbox] {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
.form-check-input:checked[type=radio] {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
@@ -2516,6 +2523,7 @@ textarea.form-control-lg {
opacity: 0.5;
}
.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {
+ cursor: default;
opacity: 0.5;
}
@@ -2542,6 +2550,14 @@ textarea.form-control-lg {
background-position: right center;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}
+.form-switch.form-check-reverse {
+ padding-right: 2.5em;
+ padding-left: 0;
+}
+.form-switch.form-check-reverse .form-check-input {
+ margin-right: -2.5em;
+ margin-left: 0;
+}
.form-check-inline {
display: inline-block;
@@ -2653,6 +2669,7 @@ textarea.form-control-lg {
position: relative;
}
.form-floating > .form-control,
+.form-floating > .form-control-plaintext,
.form-floating > .form-select {
height: calc(3.5rem + 2px);
line-height: 1.25;
@@ -2673,24 +2690,29 @@ textarea.form-control-lg {
transition: none;
}
}
-.form-floating > .form-control {
+.form-floating > .form-control,
+.form-floating > .form-control-plaintext {
padding: 1rem 0.75rem;
}
-.form-floating > .form-control::-moz-placeholder {
+.form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder {
color: transparent;
}
-.form-floating > .form-control::placeholder {
+.form-floating > .form-control::placeholder,
+.form-floating > .form-control-plaintext::placeholder {
color: transparent;
}
-.form-floating > .form-control:not(:-moz-placeholder-shown) {
+.form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) {
padding-top: 1.625rem;
padding-bottom: 0.625rem;
}
-.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) {
+.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown),
+.form-floating > .form-control-plaintext:focus,
+.form-floating > .form-control-plaintext:not(:placeholder-shown) {
padding-top: 1.625rem;
padding-bottom: 0.625rem;
}
-.form-floating > .form-control:-webkit-autofill {
+.form-floating > .form-control:-webkit-autofill,
+.form-floating > .form-control-plaintext:-webkit-autofill {
padding-top: 1.625rem;
padding-bottom: 0.625rem;
}
@@ -2704,6 +2726,7 @@ textarea.form-control-lg {
}
.form-floating > .form-control:focus ~ label,
.form-floating > .form-control:not(:placeholder-shown) ~ label,
+.form-floating > .form-control-plaintext ~ label,
.form-floating > .form-select ~ label {
opacity: 0.65;
transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
@@ -2712,6 +2735,9 @@ textarea.form-control-lg {
opacity: 0.65;
transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}
+.form-floating > .form-control-plaintext ~ label {
+ border-width: 1px 0;
+}
.input-group {
position: relative;
@@ -2751,7 +2777,7 @@ textarea.form-control-lg {
white-space: nowrap;
background-color: #e9ecef;
border: 1px solid #ced4da;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
}
.input-group-lg > .form-control,
@@ -2760,7 +2786,7 @@ textarea.form-control-lg {
.input-group-lg > .btn {
padding: 0.5rem 1rem;
font-size: 1.25rem;
- border-radius: 0.3rem;
+ border-radius: 0.5rem;
}
.input-group-sm > .form-control,
@@ -2769,7 +2795,7 @@ textarea.form-control-lg {
.input-group-sm > .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
- border-radius: 0.2rem;
+ border-radius: 0.25rem;
}
.input-group-lg > .form-select,
@@ -2812,7 +2838,7 @@ textarea.form-control-lg {
font-size: 0.875rem;
color: #fff;
background-color: rgba(25, 135, 84, 0.9);
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
}
.was-validated :valid ~ .valid-feedback,
@@ -2825,7 +2851,7 @@ textarea.form-control-lg {
.was-validated .form-control:valid, .form-control.is-valid {
border-color: #198754;
padding-right: calc(1.5em + 0.75rem);
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
@@ -2845,7 +2871,7 @@ textarea.form-control-lg {
}
.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] {
padding-right: 4.125rem;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-position: right 0.75rem center, center right 2.25rem;
background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
@@ -2854,6 +2880,10 @@ textarea.form-control-lg {
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
}
+.was-validated .form-control-color:valid, .form-control-color.is-valid {
+ width: calc(3rem + calc(1.5em + 0.75rem));
+}
+
.was-validated .form-check-input:valid, .form-check-input.is-valid {
border-color: #198754;
}
@@ -2901,7 +2931,7 @@ textarea.form-control-lg {
font-size: 0.875rem;
color: #fff;
background-color: rgba(220, 53, 69, 0.9);
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
}
.was-validated :invalid ~ .invalid-feedback,
@@ -2934,7 +2964,7 @@ textarea.form-control-lg {
}
.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] {
padding-right: 4.125rem;
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-position: right 0.75rem center, center right 2.25rem;
background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
@@ -2943,6 +2973,10 @@ textarea.form-control-lg {
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
}
+.was-validated .form-control-color:invalid, .form-control-color.is-invalid {
+ width: calc(3rem + calc(1.5em + 0.75rem));
+}
+
.was-validated .form-check-input:invalid, .form-check-input.is-invalid {
border-color: #dc3545;
}
@@ -2972,10 +3006,27 @@ textarea.form-control-lg {
}
.btn {
+ --bs-btn-padding-x: 0.75rem;
+ --bs-btn-padding-y: 0.375rem;
+ --bs-btn-font-family: ;
+ --bs-btn-font-size: 1rem;
+ --bs-btn-font-weight: 400;
+ --bs-btn-line-height: 1.5;
+ --bs-btn-color: #212529;
+ --bs-btn-bg: transparent;
+ --bs-btn-border-width: 1px;
+ --bs-btn-border-color: transparent;
+ --bs-btn-border-radius: 0.375rem;
+ --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
+ --bs-btn-disabled-opacity: 0.65;
+ --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);
display: inline-block;
- font-weight: 400;
- line-height: 1.5;
- color: #212529;
+ padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
+ font-family: var(--bs-btn-font-family);
+ font-size: var(--bs-btn-font-size);
+ font-weight: var(--bs-btn-font-weight);
+ line-height: var(--bs-btn-line-height);
+ color: var(--bs-btn-color);
text-align: center;
text-decoration: none;
vertical-align: middle;
@@ -2983,11 +3034,9 @@ textarea.form-control-lg {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
- background-color: transparent;
- border: 1px solid transparent;
- padding: 0.375rem 0.75rem;
- font-size: 1rem;
- border-radius: 0.25rem;
+ border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
+ border-radius: var(--bs-btn-border-radius);
+ background-color: var(--bs-btn-bg);
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
@@ -2996,479 +3045,322 @@ textarea.form-control-lg {
}
}
.btn:hover {
- color: #212529;
+ color: var(--bs-btn-hover-color);
+ background-color: var(--bs-btn-hover-bg);
+ border-color: var(--bs-btn-hover-border-color);
}
.btn-check:focus + .btn, .btn:focus {
+ color: var(--bs-btn-hover-color);
+ background-color: var(--bs-btn-hover-bg);
+ border-color: var(--bs-btn-hover-border-color);
outline: 0;
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+ box-shadow: var(--bs-btn-focus-box-shadow);
+}
+.btn-check:checked + .btn, .btn-check:active + .btn, .btn:active, .btn.active, .btn.show {
+ color: var(--bs-btn-active-color);
+ background-color: var(--bs-btn-active-bg);
+ border-color: var(--bs-btn-active-border-color);
+}
+.btn-check:checked + .btn:focus, .btn-check:active + .btn:focus, .btn:active:focus, .btn.active:focus, .btn.show:focus {
+ box-shadow: var(--bs-btn-focus-box-shadow);
}
.btn:disabled, .btn.disabled, fieldset:disabled .btn {
+ color: var(--bs-btn-disabled-color);
pointer-events: none;
- opacity: 0.65;
+ background-color: var(--bs-btn-disabled-bg);
+ border-color: var(--bs-btn-disabled-border-color);
+ opacity: var(--bs-btn-disabled-opacity);
}
.btn-primary {
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
-}
-.btn-primary:hover {
- color: #fff;
- background-color: #0b5ed7;
- border-color: #0a58ca;
-}
-.btn-check:focus + .btn-primary, .btn-primary:focus {
- color: #fff;
- background-color: #0b5ed7;
- border-color: #0a58ca;
- box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5);
-}
-.btn-check:checked + .btn-primary, .btn-check:active + .btn-primary, .btn-primary:active, .btn-primary.active, .show > .btn-primary.dropdown-toggle {
- color: #fff;
- background-color: #0a58ca;
- border-color: #0a53be;
-}
-.btn-check:checked + .btn-primary:focus, .btn-check:active + .btn-primary:focus, .btn-primary:active:focus, .btn-primary.active:focus, .show > .btn-primary.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5);
-}
-.btn-primary:disabled, .btn-primary.disabled {
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #0d6efd;
+ --bs-btn-border-color: #0d6efd;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #0b5ed7;
+ --bs-btn-hover-border-color: #0a58ca;
+ --bs-btn-focus-shadow-rgb: 49, 132, 253;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #0a58ca;
+ --bs-btn-active-border-color: #0a53be;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #0d6efd;
+ --bs-btn-disabled-border-color: #0d6efd;
}
.btn-secondary {
- color: #fff;
- background-color: #6c757d;
- border-color: #6c757d;
-}
-.btn-secondary:hover {
- color: #fff;
- background-color: #5c636a;
- border-color: #565e64;
-}
-.btn-check:focus + .btn-secondary, .btn-secondary:focus {
- color: #fff;
- background-color: #5c636a;
- border-color: #565e64;
- box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5);
-}
-.btn-check:checked + .btn-secondary, .btn-check:active + .btn-secondary, .btn-secondary:active, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle {
- color: #fff;
- background-color: #565e64;
- border-color: #51585e;
-}
-.btn-check:checked + .btn-secondary:focus, .btn-check:active + .btn-secondary:focus, .btn-secondary:active:focus, .btn-secondary.active:focus, .show > .btn-secondary.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5);
-}
-.btn-secondary:disabled, .btn-secondary.disabled {
- color: #fff;
- background-color: #6c757d;
- border-color: #6c757d;
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #6c757d;
+ --bs-btn-border-color: #6c757d;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #5c636a;
+ --bs-btn-hover-border-color: #565e64;
+ --bs-btn-focus-shadow-rgb: 130, 138, 145;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #565e64;
+ --bs-btn-active-border-color: #51585e;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #6c757d;
+ --bs-btn-disabled-border-color: #6c757d;
}
.btn-success {
- color: #fff;
- background-color: #198754;
- border-color: #198754;
-}
-.btn-success:hover {
- color: #fff;
- background-color: #157347;
- border-color: #146c43;
-}
-.btn-check:focus + .btn-success, .btn-success:focus {
- color: #fff;
- background-color: #157347;
- border-color: #146c43;
- box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5);
-}
-.btn-check:checked + .btn-success, .btn-check:active + .btn-success, .btn-success:active, .btn-success.active, .show > .btn-success.dropdown-toggle {
- color: #fff;
- background-color: #146c43;
- border-color: #13653f;
-}
-.btn-check:checked + .btn-success:focus, .btn-check:active + .btn-success:focus, .btn-success:active:focus, .btn-success.active:focus, .show > .btn-success.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5);
-}
-.btn-success:disabled, .btn-success.disabled {
- color: #fff;
- background-color: #198754;
- border-color: #198754;
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #198754;
+ --bs-btn-border-color: #198754;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #157347;
+ --bs-btn-hover-border-color: #146c43;
+ --bs-btn-focus-shadow-rgb: 60, 153, 110;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #146c43;
+ --bs-btn-active-border-color: #13653f;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #198754;
+ --bs-btn-disabled-border-color: #198754;
}
.btn-info {
- color: #000;
- background-color: #0dcaf0;
- border-color: #0dcaf0;
-}
-.btn-info:hover {
- color: #000;
- background-color: #31d2f2;
- border-color: #25cff2;
-}
-.btn-check:focus + .btn-info, .btn-info:focus {
- color: #000;
- background-color: #31d2f2;
- border-color: #25cff2;
- box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5);
-}
-.btn-check:checked + .btn-info, .btn-check:active + .btn-info, .btn-info:active, .btn-info.active, .show > .btn-info.dropdown-toggle {
- color: #000;
- background-color: #3dd5f3;
- border-color: #25cff2;
-}
-.btn-check:checked + .btn-info:focus, .btn-check:active + .btn-info:focus, .btn-info:active:focus, .btn-info.active:focus, .show > .btn-info.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5);
-}
-.btn-info:disabled, .btn-info.disabled {
- color: #000;
- background-color: #0dcaf0;
- border-color: #0dcaf0;
+ --bs-btn-color: #000;
+ --bs-btn-bg: #0dcaf0;
+ --bs-btn-border-color: #0dcaf0;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #31d2f2;
+ --bs-btn-hover-border-color: #25cff2;
+ --bs-btn-focus-shadow-rgb: 11, 172, 204;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #3dd5f3;
+ --bs-btn-active-border-color: #25cff2;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #0dcaf0;
+ --bs-btn-disabled-border-color: #0dcaf0;
}
.btn-warning {
- color: #000;
- background-color: #ffc107;
- border-color: #ffc107;
-}
-.btn-warning:hover {
- color: #000;
- background-color: #ffca2c;
- border-color: #ffc720;
-}
-.btn-check:focus + .btn-warning, .btn-warning:focus {
- color: #000;
- background-color: #ffca2c;
- border-color: #ffc720;
- box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5);
-}
-.btn-check:checked + .btn-warning, .btn-check:active + .btn-warning, .btn-warning:active, .btn-warning.active, .show > .btn-warning.dropdown-toggle {
- color: #000;
- background-color: #ffcd39;
- border-color: #ffc720;
-}
-.btn-check:checked + .btn-warning:focus, .btn-check:active + .btn-warning:focus, .btn-warning:active:focus, .btn-warning.active:focus, .show > .btn-warning.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5);
-}
-.btn-warning:disabled, .btn-warning.disabled {
- color: #000;
- background-color: #ffc107;
- border-color: #ffc107;
+ --bs-btn-color: #000;
+ --bs-btn-bg: #ffc107;
+ --bs-btn-border-color: #ffc107;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #ffca2c;
+ --bs-btn-hover-border-color: #ffc720;
+ --bs-btn-focus-shadow-rgb: 217, 164, 6;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #ffcd39;
+ --bs-btn-active-border-color: #ffc720;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #ffc107;
+ --bs-btn-disabled-border-color: #ffc107;
}
.btn-danger {
- color: #fff;
- background-color: #dc3545;
- border-color: #dc3545;
-}
-.btn-danger:hover {
- color: #fff;
- background-color: #bb2d3b;
- border-color: #b02a37;
-}
-.btn-check:focus + .btn-danger, .btn-danger:focus {
- color: #fff;
- background-color: #bb2d3b;
- border-color: #b02a37;
- box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5);
-}
-.btn-check:checked + .btn-danger, .btn-check:active + .btn-danger, .btn-danger:active, .btn-danger.active, .show > .btn-danger.dropdown-toggle {
- color: #fff;
- background-color: #b02a37;
- border-color: #a52834;
-}
-.btn-check:checked + .btn-danger:focus, .btn-check:active + .btn-danger:focus, .btn-danger:active:focus, .btn-danger.active:focus, .show > .btn-danger.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5);
-}
-.btn-danger:disabled, .btn-danger.disabled {
- color: #fff;
- background-color: #dc3545;
- border-color: #dc3545;
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #dc3545;
+ --bs-btn-border-color: #dc3545;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #bb2d3b;
+ --bs-btn-hover-border-color: #b02a37;
+ --bs-btn-focus-shadow-rgb: 225, 83, 97;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #b02a37;
+ --bs-btn-active-border-color: #a52834;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #dc3545;
+ --bs-btn-disabled-border-color: #dc3545;
}
.btn-light {
- color: #000;
- background-color: #f8f9fa;
- border-color: #f8f9fa;
-}
-.btn-light:hover {
- color: #000;
- background-color: #f9fafb;
- border-color: #f9fafb;
-}
-.btn-check:focus + .btn-light, .btn-light:focus {
- color: #000;
- background-color: #f9fafb;
- border-color: #f9fafb;
- box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5);
-}
-.btn-check:checked + .btn-light, .btn-check:active + .btn-light, .btn-light:active, .btn-light.active, .show > .btn-light.dropdown-toggle {
- color: #000;
- background-color: #f9fafb;
- border-color: #f9fafb;
-}
-.btn-check:checked + .btn-light:focus, .btn-check:active + .btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show > .btn-light.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5);
-}
-.btn-light:disabled, .btn-light.disabled {
- color: #000;
- background-color: #f8f9fa;
- border-color: #f8f9fa;
+ --bs-btn-color: #000;
+ --bs-btn-bg: #f8f9fa;
+ --bs-btn-border-color: #f8f9fa;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #f9fafb;
+ --bs-btn-hover-border-color: #f9fafb;
+ --bs-btn-focus-shadow-rgb: 211, 212, 213;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #f9fafb;
+ --bs-btn-active-border-color: #f9fafb;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #000;
+ --bs-btn-disabled-bg: #f8f9fa;
+ --bs-btn-disabled-border-color: #f8f9fa;
}
.btn-dark {
- color: #fff;
- background-color: #212529;
- border-color: #212529;
-}
-.btn-dark:hover {
- color: #fff;
- background-color: #1c1f23;
- border-color: #1a1e21;
-}
-.btn-check:focus + .btn-dark, .btn-dark:focus {
- color: #fff;
- background-color: #1c1f23;
- border-color: #1a1e21;
- box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5);
-}
-.btn-check:checked + .btn-dark, .btn-check:active + .btn-dark, .btn-dark:active, .btn-dark.active, .show > .btn-dark.dropdown-toggle {
- color: #fff;
- background-color: #1a1e21;
- border-color: #191c1f;
-}
-.btn-check:checked + .btn-dark:focus, .btn-check:active + .btn-dark:focus, .btn-dark:active:focus, .btn-dark.active:focus, .show > .btn-dark.dropdown-toggle:focus {
- box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5);
-}
-.btn-dark:disabled, .btn-dark.disabled {
- color: #fff;
- background-color: #212529;
- border-color: #212529;
+ --bs-btn-color: #fff;
+ --bs-btn-bg: #212529;
+ --bs-btn-border-color: #212529;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #1c1f23;
+ --bs-btn-hover-border-color: #1a1e21;
+ --bs-btn-focus-shadow-rgb: 66, 70, 73;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #1a1e21;
+ --bs-btn-active-border-color: #191c1f;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #fff;
+ --bs-btn-disabled-bg: #212529;
+ --bs-btn-disabled-border-color: #212529;
}
.btn-outline-primary {
- color: #0d6efd;
- border-color: #0d6efd;
-}
-.btn-outline-primary:hover {
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
-}
-.btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus {
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5);
-}
-.btn-check:checked + .btn-outline-primary, .btn-check:active + .btn-outline-primary, .btn-outline-primary:active, .btn-outline-primary.active, .btn-outline-primary.dropdown-toggle.show {
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
-}
-.btn-check:checked + .btn-outline-primary:focus, .btn-check:active + .btn-outline-primary:focus, .btn-outline-primary:active:focus, .btn-outline-primary.active:focus, .btn-outline-primary.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5);
-}
-.btn-outline-primary:disabled, .btn-outline-primary.disabled {
- color: #0d6efd;
- background-color: transparent;
+ --bs-btn-color: #0d6efd;
+ --bs-btn-border-color: #0d6efd;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #0d6efd;
+ --bs-btn-hover-border-color: #0d6efd;
+ --bs-btn-focus-shadow-rgb: 13, 110, 253;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #0d6efd;
+ --bs-btn-active-border-color: #0d6efd;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #0d6efd;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-secondary {
- color: #6c757d;
- border-color: #6c757d;
-}
-.btn-outline-secondary:hover {
- color: #fff;
- background-color: #6c757d;
- border-color: #6c757d;
-}
-.btn-check:focus + .btn-outline-secondary, .btn-outline-secondary:focus {
- box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5);
-}
-.btn-check:checked + .btn-outline-secondary, .btn-check:active + .btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
- color: #fff;
- background-color: #6c757d;
- border-color: #6c757d;
-}
-.btn-check:checked + .btn-outline-secondary:focus, .btn-check:active + .btn-outline-secondary:focus, .btn-outline-secondary:active:focus, .btn-outline-secondary.active:focus, .btn-outline-secondary.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5);
-}
-.btn-outline-secondary:disabled, .btn-outline-secondary.disabled {
- color: #6c757d;
- background-color: transparent;
+ --bs-btn-color: #6c757d;
+ --bs-btn-border-color: #6c757d;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #6c757d;
+ --bs-btn-hover-border-color: #6c757d;
+ --bs-btn-focus-shadow-rgb: 108, 117, 125;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #6c757d;
+ --bs-btn-active-border-color: #6c757d;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #6c757d;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-success {
- color: #198754;
- border-color: #198754;
-}
-.btn-outline-success:hover {
- color: #fff;
- background-color: #198754;
- border-color: #198754;
-}
-.btn-check:focus + .btn-outline-success, .btn-outline-success:focus {
- box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5);
-}
-.btn-check:checked + .btn-outline-success, .btn-check:active + .btn-outline-success, .btn-outline-success:active, .btn-outline-success.active, .btn-outline-success.dropdown-toggle.show {
- color: #fff;
- background-color: #198754;
- border-color: #198754;
-}
-.btn-check:checked + .btn-outline-success:focus, .btn-check:active + .btn-outline-success:focus, .btn-outline-success:active:focus, .btn-outline-success.active:focus, .btn-outline-success.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5);
-}
-.btn-outline-success:disabled, .btn-outline-success.disabled {
- color: #198754;
- background-color: transparent;
+ --bs-btn-color: #198754;
+ --bs-btn-border-color: #198754;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #198754;
+ --bs-btn-hover-border-color: #198754;
+ --bs-btn-focus-shadow-rgb: 25, 135, 84;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #198754;
+ --bs-btn-active-border-color: #198754;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #198754;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-info {
- color: #0dcaf0;
- border-color: #0dcaf0;
-}
-.btn-outline-info:hover {
- color: #000;
- background-color: #0dcaf0;
- border-color: #0dcaf0;
-}
-.btn-check:focus + .btn-outline-info, .btn-outline-info:focus {
- box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5);
-}
-.btn-check:checked + .btn-outline-info, .btn-check:active + .btn-outline-info, .btn-outline-info:active, .btn-outline-info.active, .btn-outline-info.dropdown-toggle.show {
- color: #000;
- background-color: #0dcaf0;
- border-color: #0dcaf0;
-}
-.btn-check:checked + .btn-outline-info:focus, .btn-check:active + .btn-outline-info:focus, .btn-outline-info:active:focus, .btn-outline-info.active:focus, .btn-outline-info.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5);
-}
-.btn-outline-info:disabled, .btn-outline-info.disabled {
- color: #0dcaf0;
- background-color: transparent;
+ --bs-btn-color: #0dcaf0;
+ --bs-btn-border-color: #0dcaf0;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #0dcaf0;
+ --bs-btn-hover-border-color: #0dcaf0;
+ --bs-btn-focus-shadow-rgb: 13, 202, 240;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #0dcaf0;
+ --bs-btn-active-border-color: #0dcaf0;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #0dcaf0;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-warning {
- color: #ffc107;
- border-color: #ffc107;
-}
-.btn-outline-warning:hover {
- color: #000;
- background-color: #ffc107;
- border-color: #ffc107;
-}
-.btn-check:focus + .btn-outline-warning, .btn-outline-warning:focus {
- box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5);
-}
-.btn-check:checked + .btn-outline-warning, .btn-check:active + .btn-outline-warning, .btn-outline-warning:active, .btn-outline-warning.active, .btn-outline-warning.dropdown-toggle.show {
- color: #000;
- background-color: #ffc107;
- border-color: #ffc107;
-}
-.btn-check:checked + .btn-outline-warning:focus, .btn-check:active + .btn-outline-warning:focus, .btn-outline-warning:active:focus, .btn-outline-warning.active:focus, .btn-outline-warning.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5);
-}
-.btn-outline-warning:disabled, .btn-outline-warning.disabled {
- color: #ffc107;
- background-color: transparent;
+ --bs-btn-color: #ffc107;
+ --bs-btn-border-color: #ffc107;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #ffc107;
+ --bs-btn-hover-border-color: #ffc107;
+ --bs-btn-focus-shadow-rgb: 255, 193, 7;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #ffc107;
+ --bs-btn-active-border-color: #ffc107;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #ffc107;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-danger {
- color: #dc3545;
- border-color: #dc3545;
-}
-.btn-outline-danger:hover {
- color: #fff;
- background-color: #dc3545;
- border-color: #dc3545;
-}
-.btn-check:focus + .btn-outline-danger, .btn-outline-danger:focus {
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5);
-}
-.btn-check:checked + .btn-outline-danger, .btn-check:active + .btn-outline-danger, .btn-outline-danger:active, .btn-outline-danger.active, .btn-outline-danger.dropdown-toggle.show {
- color: #fff;
- background-color: #dc3545;
- border-color: #dc3545;
-}
-.btn-check:checked + .btn-outline-danger:focus, .btn-check:active + .btn-outline-danger:focus, .btn-outline-danger:active:focus, .btn-outline-danger.active:focus, .btn-outline-danger.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5);
-}
-.btn-outline-danger:disabled, .btn-outline-danger.disabled {
- color: #dc3545;
- background-color: transparent;
+ --bs-btn-color: #dc3545;
+ --bs-btn-border-color: #dc3545;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #dc3545;
+ --bs-btn-hover-border-color: #dc3545;
+ --bs-btn-focus-shadow-rgb: 220, 53, 69;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #dc3545;
+ --bs-btn-active-border-color: #dc3545;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #dc3545;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-light {
- color: #f8f9fa;
- border-color: #f8f9fa;
-}
-.btn-outline-light:hover {
- color: #000;
- background-color: #f8f9fa;
- border-color: #f8f9fa;
-}
-.btn-check:focus + .btn-outline-light, .btn-outline-light:focus {
- box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5);
-}
-.btn-check:checked + .btn-outline-light, .btn-check:active + .btn-outline-light, .btn-outline-light:active, .btn-outline-light.active, .btn-outline-light.dropdown-toggle.show {
- color: #000;
- background-color: #f8f9fa;
- border-color: #f8f9fa;
-}
-.btn-check:checked + .btn-outline-light:focus, .btn-check:active + .btn-outline-light:focus, .btn-outline-light:active:focus, .btn-outline-light.active:focus, .btn-outline-light.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5);
-}
-.btn-outline-light:disabled, .btn-outline-light.disabled {
- color: #f8f9fa;
- background-color: transparent;
+ --bs-btn-color: #f8f9fa;
+ --bs-btn-border-color: #f8f9fa;
+ --bs-btn-hover-color: #000;
+ --bs-btn-hover-bg: #f8f9fa;
+ --bs-btn-hover-border-color: #f8f9fa;
+ --bs-btn-focus-shadow-rgb: 248, 249, 250;
+ --bs-btn-active-color: #000;
+ --bs-btn-active-bg: #f8f9fa;
+ --bs-btn-active-border-color: #f8f9fa;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #f8f9fa;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-outline-dark {
- color: #212529;
- border-color: #212529;
-}
-.btn-outline-dark:hover {
- color: #fff;
- background-color: #212529;
- border-color: #212529;
-}
-.btn-check:focus + .btn-outline-dark, .btn-outline-dark:focus {
- box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5);
-}
-.btn-check:checked + .btn-outline-dark, .btn-check:active + .btn-outline-dark, .btn-outline-dark:active, .btn-outline-dark.active, .btn-outline-dark.dropdown-toggle.show {
- color: #fff;
- background-color: #212529;
- border-color: #212529;
-}
-.btn-check:checked + .btn-outline-dark:focus, .btn-check:active + .btn-outline-dark:focus, .btn-outline-dark:active:focus, .btn-outline-dark.active:focus, .btn-outline-dark.dropdown-toggle.show:focus {
- box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5);
-}
-.btn-outline-dark:disabled, .btn-outline-dark.disabled {
- color: #212529;
- background-color: transparent;
+ --bs-btn-color: #212529;
+ --bs-btn-border-color: #212529;
+ --bs-btn-hover-color: #fff;
+ --bs-btn-hover-bg: #212529;
+ --bs-btn-hover-border-color: #212529;
+ --bs-btn-focus-shadow-rgb: 33, 37, 41;
+ --bs-btn-active-color: #fff;
+ --bs-btn-active-bg: #212529;
+ --bs-btn-active-border-color: #212529;
+ --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ --bs-btn-disabled-color: #212529;
+ --bs-btn-disabled-bg: transparent;
+ --bs-gradient: none;
}
.btn-link {
- font-weight: 400;
- color: #0d6efd;
+ --bs-btn-font-weight: 400;
+ --bs-btn-color: var(--bs-link-color);
+ --bs-btn-bg: transparent;
+ --bs-btn-border-color: transparent;
+ --bs-btn-hover-color: var(--bs-link-hover-color);
+ --bs-btn-hover-border-color: transparent;
+ --bs-btn-active-border-color: transparent;
+ --bs-btn-disabled-color: #6c757d;
+ --bs-btn-disabled-border-color: transparent;
+ --bs-btn-box-shadow: none;
text-decoration: underline;
}
-.btn-link:hover {
- color: #0a58ca;
-}
-.btn-link:disabled, .btn-link.disabled {
- color: #6c757d;
-}
-
.btn-lg, .btn-group-lg > .btn {
- padding: 0.5rem 1rem;
- font-size: 1.25rem;
- border-radius: 0.3rem;
+ --bs-btn-padding-y: 0.5rem;
+ --bs-btn-padding-x: 1rem;
+ --bs-btn-font-size: 1.25rem;
+ --bs-btn-border-radius: 0.5rem;
}
.btn-sm, .btn-group-sm > .btn {
- padding: 0.25rem 0.5rem;
- font-size: 0.875rem;
- border-radius: 0.2rem;
+ --bs-btn-padding-y: 0.25rem;
+ --bs-btn-padding-x: 0.5rem;
+ --bs-btn-font-size: 0.875rem;
+ --bs-btn-border-radius: 0.25rem;
}
.fade {
@@ -3511,7 +3403,9 @@ textarea.form-control-lg {
.dropup,
.dropend,
.dropdown,
-.dropstart {
+.dropstart,
+.dropup-center,
+.dropdown-center {
position: relative;
}
@@ -3533,25 +3427,50 @@ textarea.form-control-lg {
}
.dropdown-menu {
+ --bs-dropdown-min-width: 10rem;
+ --bs-dropdown-padding-x: 0;
+ --bs-dropdown-padding-y: 0.5rem;
+ --bs-dropdown-spacer: 0.125rem;
+ --bs-dropdown-font-size: 1rem;
+ --bs-dropdown-color: #212529;
+ --bs-dropdown-bg: #fff;
+ --bs-dropdown-border-color: var(--bs-border-color-translucent);
+ --bs-dropdown-border-radius: 0.375rem;
+ --bs-dropdown-border-width: 1px;
+ --bs-dropdown-inner-border-radius: calc(0.375rem - 1px);
+ --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
+ --bs-dropdown-divider-margin-y: 0.5rem;
+ --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-dropdown-link-color: #212529;
+ --bs-dropdown-link-hover-color: #1e2125;
+ --bs-dropdown-link-hover-bg: #e9ecef;
+ --bs-dropdown-link-active-color: #fff;
+ --bs-dropdown-link-active-bg: #0d6efd;
+ --bs-dropdown-link-disabled-color: #adb5bd;
+ --bs-dropdown-item-padding-x: 1rem;
+ --bs-dropdown-item-padding-y: 0.25rem;
+ --bs-dropdown-header-color: #6c757d;
+ --bs-dropdown-header-padding-x: 1rem;
+ --bs-dropdown-header-padding-y: 0.5rem;
position: absolute;
z-index: 1000;
display: none;
- min-width: 10rem;
- padding: 0.5rem 0;
+ min-width: var(--bs-dropdown-min-width);
+ padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);
margin: 0;
- font-size: 1rem;
- color: #212529;
+ font-size: var(--bs-dropdown-font-size);
+ color: var(--bs-dropdown-color);
text-align: left;
list-style: none;
- background-color: #fff;
+ background-color: var(--bs-dropdown-bg);
background-clip: padding-box;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 0.25rem;
+ border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);
+ border-radius: var(--bs-dropdown-border-radius);
}
.dropdown-menu[data-bs-popper] {
top: 100%;
left: 0;
- margin-top: 0.125rem;
+ margin-top: var(--bs-dropdown-spacer);
}
.dropdown-menu-start {
@@ -3659,7 +3578,7 @@ textarea.form-control-lg {
top: auto;
bottom: 100%;
margin-top: 0;
- margin-bottom: 0.125rem;
+ margin-bottom: var(--bs-dropdown-spacer);
}
.dropup .dropdown-toggle::after {
display: inline-block;
@@ -3680,7 +3599,7 @@ textarea.form-control-lg {
right: auto;
left: 100%;
margin-top: 0;
- margin-left: 0.125rem;
+ margin-left: var(--bs-dropdown-spacer);
}
.dropend .dropdown-toggle::after {
display: inline-block;
@@ -3704,7 +3623,7 @@ textarea.form-control-lg {
right: 100%;
left: auto;
margin-top: 0;
- margin-right: 0.125rem;
+ margin-right: var(--bs-dropdown-spacer);
}
.dropstart .dropdown-toggle::after {
display: inline-block;
@@ -3733,18 +3652,19 @@ textarea.form-control-lg {
.dropdown-divider {
height: 0;
- margin: 0.5rem 0;
+ margin: var(--bs-dropdown-divider-margin-y) 0;
overflow: hidden;
- border-top: 1px solid rgba(0, 0, 0, 0.15);
+ border-top: 1px solid var(--bs-dropdown-divider-bg);
+ opacity: 1;
}
.dropdown-item {
display: block;
width: 100%;
- padding: 0.25rem 1rem;
+ padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
clear: both;
font-weight: 400;
- color: #212529;
+ color: var(--bs-dropdown-link-color);
text-align: inherit;
text-decoration: none;
white-space: nowrap;
@@ -3752,16 +3672,16 @@ textarea.form-control-lg {
border: 0;
}
.dropdown-item:hover, .dropdown-item:focus {
- color: #1e2125;
- background-color: #e9ecef;
+ color: var(--bs-dropdown-link-hover-color);
+ background-color: var(--bs-dropdown-link-hover-bg);
}
.dropdown-item.active, .dropdown-item:active {
- color: #fff;
+ color: var(--bs-dropdown-link-active-color);
text-decoration: none;
- background-color: #0d6efd;
+ background-color: var(--bs-dropdown-link-active-bg);
}
.dropdown-item.disabled, .dropdown-item:disabled {
- color: #adb5bd;
+ color: var(--bs-dropdown-link-disabled-color);
pointer-events: none;
background-color: transparent;
}
@@ -3772,46 +3692,32 @@ textarea.form-control-lg {
.dropdown-header {
display: block;
- padding: 0.5rem 1rem;
+ padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);
margin-bottom: 0;
font-size: 0.875rem;
- color: #6c757d;
+ color: var(--bs-dropdown-header-color);
white-space: nowrap;
}
.dropdown-item-text {
display: block;
- padding: 0.25rem 1rem;
- color: #212529;
+ padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
+ color: var(--bs-dropdown-link-color);
}
.dropdown-menu-dark {
- color: #dee2e6;
- background-color: #343a40;
- border-color: rgba(0, 0, 0, 0.15);
-}
-.dropdown-menu-dark .dropdown-item {
- color: #dee2e6;
-}
-.dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus {
- color: #fff;
- background-color: rgba(255, 255, 255, 0.15);
-}
-.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active {
- color: #fff;
- background-color: #0d6efd;
-}
-.dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled {
- color: #adb5bd;
-}
-.dropdown-menu-dark .dropdown-divider {
- border-color: rgba(0, 0, 0, 0.15);
-}
-.dropdown-menu-dark .dropdown-item-text {
- color: #dee2e6;
-}
-.dropdown-menu-dark .dropdown-header {
- color: #adb5bd;
+ --bs-dropdown-color: #dee2e6;
+ --bs-dropdown-bg: #343a40;
+ --bs-dropdown-border-color: var(--bs-border-color-translucent);
+ --bs-dropdown-box-shadow: ;
+ --bs-dropdown-link-color: #dee2e6;
+ --bs-dropdown-link-hover-color: #fff;
+ --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
+ --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);
+ --bs-dropdown-link-active-color: #fff;
+ --bs-dropdown-link-active-bg: #0d6efd;
+ --bs-dropdown-link-disabled-color: #adb5bd;
+ --bs-dropdown-header-color: #adb5bd;
}
.btn-group,
@@ -3849,11 +3755,15 @@ textarea.form-control-lg {
width: auto;
}
+.btn-group {
+ border-radius: 0.375rem;
+}
.btn-group > .btn:not(:first-child),
.btn-group > .btn-group:not(:first-child) {
margin-left: -1px;
}
.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group > .btn.dropdown-toggle-split:first-child,
.btn-group > .btn-group:not(:last-child) > .btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
@@ -3911,6 +3821,12 @@ textarea.form-control-lg {
}
.nav {
+ --bs-nav-link-padding-x: 1rem;
+ --bs-nav-link-padding-y: 0.5rem;
+ --bs-nav-link-font-weight: ;
+ --bs-nav-link-color: var(--bs-link-color);
+ --bs-nav-link-hover-color: var(--bs-link-hover-color);
+ --bs-nav-link-disabled-color: #6c757d;
display: flex;
flex-wrap: wrap;
padding-left: 0;
@@ -3920,8 +3836,10 @@ textarea.form-control-lg {
.nav-link {
display: block;
- padding: 0.5rem 1rem;
- color: #0d6efd;
+ padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
+ font-size: var(--bs-nav-link-font-size);
+ font-weight: var(--bs-nav-link-font-weight);
+ color: var(--bs-nav-link-color);
text-decoration: none;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
}
@@ -3931,54 +3849,71 @@ textarea.form-control-lg {
}
}
.nav-link:hover, .nav-link:focus {
- color: #0a58ca;
+ color: var(--bs-nav-link-hover-color);
}
.nav-link.disabled {
- color: #6c757d;
+ color: var(--bs-nav-link-disabled-color);
pointer-events: none;
cursor: default;
}
.nav-tabs {
- border-bottom: 1px solid #dee2e6;
+ --bs-nav-tabs-border-width: 1px;
+ --bs-nav-tabs-border-color: #dee2e6;
+ --bs-nav-tabs-border-radius: 0.375rem;
+ --bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6;
+ --bs-nav-tabs-link-active-color: #495057;
+ --bs-nav-tabs-link-active-bg: #fff;
+ --bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff;
+ border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);
}
.nav-tabs .nav-link {
- margin-bottom: -1px;
+ margin-bottom: calc(var(--bs-nav-tabs-border-width) * -1);
background: none;
- border: 1px solid transparent;
- border-top-left-radius: 0.25rem;
- border-top-right-radius: 0.25rem;
+ border: var(--bs-nav-tabs-border-width) solid transparent;
+ border-top-left-radius: var(--bs-nav-tabs-border-radius);
+ border-top-right-radius: var(--bs-nav-tabs-border-radius);
}
.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {
- border-color: #e9ecef #e9ecef #dee2e6;
isolation: isolate;
+ border-color: var(--bs-nav-tabs-link-hover-border-color);
}
-.nav-tabs .nav-link.disabled {
- color: #6c757d;
+.nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled {
+ color: var(--bs-nav-link-disabled-color);
background-color: transparent;
border-color: transparent;
}
.nav-tabs .nav-link.active,
.nav-tabs .nav-item.show .nav-link {
- color: #495057;
- background-color: #fff;
- border-color: #dee2e6 #dee2e6 #fff;
+ color: var(--bs-nav-tabs-link-active-color);
+ background-color: var(--bs-nav-tabs-link-active-bg);
+ border-color: var(--bs-nav-tabs-link-active-border-color);
}
.nav-tabs .dropdown-menu {
- margin-top: -1px;
+ margin-top: calc(var(--bs-nav-tabs-border-width) * -1);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
+.nav-pills {
+ --bs-nav-pills-border-radius: 0.375rem;
+ --bs-nav-pills-link-active-color: #fff;
+ --bs-nav-pills-link-active-bg: #0d6efd;
+}
.nav-pills .nav-link {
background: none;
border: 0;
- border-radius: 0.25rem;
+ border-radius: var(--bs-nav-pills-border-radius);
+}
+.nav-pills .nav-link:disabled {
+ color: var(--bs-nav-link-disabled-color);
+ background-color: transparent;
+ border-color: transparent;
}
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
- color: #fff;
- background-color: #0d6efd;
+ color: var(--bs-nav-pills-link-active-color);
+ background-color: var(--bs-nav-pills-link-active-bg);
}
.nav-fill > .nav-link,
@@ -4007,13 +3942,32 @@ textarea.form-control-lg {
}
.navbar {
+ --bs-navbar-padding-x: 0;
+ --bs-navbar-padding-y: 0.5rem;
+ --bs-navbar-color: rgba(0, 0, 0, 0.55);
+ --bs-navbar-hover-color: rgba(0, 0, 0, 0.7);
+ --bs-navbar-disabled-color: rgba(0, 0, 0, 0.3);
+ --bs-navbar-active-color: rgba(0, 0, 0, 0.9);
+ --bs-navbar-brand-padding-y: 0.3125rem;
+ --bs-navbar-brand-margin-end: 1rem;
+ --bs-navbar-brand-font-size: 1.25rem;
+ --bs-navbar-brand-color: rgba(0, 0, 0, 0.9);
+ --bs-navbar-brand-hover-color: rgba(0, 0, 0, 0.9);
+ --bs-navbar-nav-link-padding-x: 0.5rem;
+ --bs-navbar-toggler-padding-y: 0.25rem;
+ --bs-navbar-toggler-padding-x: 0.75rem;
+ --bs-navbar-toggler-font-size: 1.25rem;
+ --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+ --bs-navbar-toggler-border-color: rgba(0, 0, 0, 0.1);
+ --bs-navbar-toggler-border-radius: 0.375rem;
+ --bs-navbar-toggler-focus-width: 0.25rem;
+ --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;
position: relative;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
- padding-top: 0.5rem;
- padding-bottom: 0.5rem;
+ padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);
}
.navbar > .container,
.navbar > .container-fluid,
@@ -4028,23 +3982,33 @@ textarea.form-control-lg {
justify-content: space-between;
}
.navbar-brand {
- padding-top: 0.3125rem;
- padding-bottom: 0.3125rem;
- margin-right: 1rem;
- font-size: 1.25rem;
+ padding-top: var(--bs-navbar-brand-padding-y);
+ padding-bottom: var(--bs-navbar-brand-padding-y);
+ margin-right: var(--bs-navbar-brand-margin-end);
+ font-size: var(--bs-navbar-brand-font-size);
+ color: var(--bs-navbar-brand-color);
text-decoration: none;
white-space: nowrap;
}
+.navbar-brand:hover, .navbar-brand:focus {
+ color: var(--bs-navbar-brand-hover-color);
+}
+
.navbar-nav {
+ --bs-nav-link-padding-x: 0;
+ --bs-nav-link-padding-y: 0.5rem;
+ --bs-nav-link-color: var(--bs-navbar-color);
+ --bs-nav-link-hover-color: var(--bs-navbar-hover-color);
+ --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0;
list-style: none;
}
-.navbar-nav .nav-link {
- padding-right: 0;
- padding-left: 0;
+.navbar-nav .show > .nav-link,
+.navbar-nav .nav-link.active {
+ color: var(--bs-navbar-active-color);
}
.navbar-nav .dropdown-menu {
position: static;
@@ -4053,6 +4017,12 @@ textarea.form-control-lg {
.navbar-text {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
+ color: var(--bs-navbar-color);
+}
+.navbar-text a,
+.navbar-text a:hover,
+.navbar-text a:focus {
+ color: var(--bs-navbar-active-color);
}
.navbar-collapse {
@@ -4062,13 +4032,14 @@ textarea.form-control-lg {
}
.navbar-toggler {
- padding: 0.25rem 0.75rem;
- font-size: 1.25rem;
+ padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);
+ font-size: var(--bs-navbar-toggler-font-size);
line-height: 1;
+ color: var(--bs-navbar-color);
background-color: transparent;
- border: 1px solid transparent;
- border-radius: 0.25rem;
- transition: box-shadow 0.15s ease-in-out;
+ border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);
+ border-radius: var(--bs-navbar-toggler-border-radius);
+ transition: var(--bs-navbar-toggler-transition);
}
@media (prefers-reduced-motion: reduce) {
.navbar-toggler {
@@ -4081,7 +4052,7 @@ textarea.form-control-lg {
.navbar-toggler:focus {
text-decoration: none;
outline: 0;
- box-shadow: 0 0 0 0.25rem;
+ box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width);
}
.navbar-toggler-icon {
@@ -4089,6 +4060,7 @@ textarea.form-control-lg {
width: 1.5em;
height: 1.5em;
vertical-align: middle;
+ background-image: var(--bs-navbar-toggler-icon-bg);
background-repeat: no-repeat;
background-position: center;
background-size: 100%;
@@ -4111,8 +4083,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand-sm .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand-sm .navbar-nav-scroll {
overflow: visible;
@@ -4124,28 +4096,22 @@ textarea.form-control-lg {
.navbar-expand-sm .navbar-toggler {
display: none;
}
- .navbar-expand-sm .offcanvas-header {
- display: none;
- }
.navbar-expand-sm .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
- .navbar-expand-sm .offcanvas-top,
-.navbar-expand-sm .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+ .navbar-expand-sm .offcanvas .offcanvas-header {
+ display: none;
}
- .navbar-expand-sm .offcanvas-body {
+ .navbar-expand-sm .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
@@ -4164,8 +4130,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand-md .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand-md .navbar-nav-scroll {
overflow: visible;
@@ -4177,28 +4143,22 @@ textarea.form-control-lg {
.navbar-expand-md .navbar-toggler {
display: none;
}
- .navbar-expand-md .offcanvas-header {
- display: none;
- }
.navbar-expand-md .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
- .navbar-expand-md .offcanvas-top,
-.navbar-expand-md .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+ .navbar-expand-md .offcanvas .offcanvas-header {
+ display: none;
}
- .navbar-expand-md .offcanvas-body {
+ .navbar-expand-md .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
@@ -4217,8 +4177,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand-lg .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand-lg .navbar-nav-scroll {
overflow: visible;
@@ -4230,28 +4190,22 @@ textarea.form-control-lg {
.navbar-expand-lg .navbar-toggler {
display: none;
}
- .navbar-expand-lg .offcanvas-header {
- display: none;
- }
.navbar-expand-lg .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
- .navbar-expand-lg .offcanvas-top,
-.navbar-expand-lg .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+ .navbar-expand-lg .offcanvas .offcanvas-header {
+ display: none;
}
- .navbar-expand-lg .offcanvas-body {
+ .navbar-expand-lg .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
@@ -4270,8 +4224,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand-xl .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand-xl .navbar-nav-scroll {
overflow: visible;
@@ -4283,28 +4237,22 @@ textarea.form-control-lg {
.navbar-expand-xl .navbar-toggler {
display: none;
}
- .navbar-expand-xl .offcanvas-header {
- display: none;
- }
.navbar-expand-xl .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
- .navbar-expand-xl .offcanvas-top,
-.navbar-expand-xl .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+ .navbar-expand-xl .offcanvas .offcanvas-header {
+ display: none;
}
- .navbar-expand-xl .offcanvas-body {
+ .navbar-expand-xl .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
@@ -4323,8 +4271,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand-xxl .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand-xxl .navbar-nav-scroll {
overflow: visible;
@@ -4336,28 +4284,22 @@ textarea.form-control-lg {
.navbar-expand-xxl .navbar-toggler {
display: none;
}
- .navbar-expand-xxl .offcanvas-header {
- display: none;
- }
.navbar-expand-xxl .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
- .navbar-expand-xxl .offcanvas-top,
-.navbar-expand-xxl .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+ .navbar-expand-xxl .offcanvas .offcanvas-header {
+ display: none;
}
- .navbar-expand-xxl .offcanvas-body {
+ .navbar-expand-xxl .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
@@ -4375,8 +4317,8 @@ textarea.form-control-lg {
position: absolute;
}
.navbar-expand .navbar-nav .nav-link {
- padding-right: 0.5rem;
- padding-left: 0.5rem;
+ padding-right: var(--bs-navbar-nav-link-padding-x);
+ padding-left: var(--bs-navbar-nav-link-padding-x);
}
.navbar-expand .navbar-nav-scroll {
overflow: visible;
@@ -4388,114 +4330,67 @@ textarea.form-control-lg {
.navbar-expand .navbar-toggler {
display: none;
}
-.navbar-expand .offcanvas-header {
- display: none;
-}
.navbar-expand .offcanvas {
- position: inherit;
- bottom: 0;
- z-index: 1000;
+ position: static;
+ z-index: auto;
flex-grow: 1;
+ width: auto !important;
+ height: auto !important;
visibility: visible !important;
- background-color: transparent;
- border-right: 0;
- border-left: 0;
+ background-color: transparent !important;
+ border: 0 !important;
+ transform: none !important;
transition: none;
- transform: none;
}
-.navbar-expand .offcanvas-top,
-.navbar-expand .offcanvas-bottom {
- height: auto;
- border-top: 0;
- border-bottom: 0;
+.navbar-expand .offcanvas .offcanvas-header {
+ display: none;
}
-.navbar-expand .offcanvas-body {
+.navbar-expand .offcanvas .offcanvas-body {
display: flex;
flex-grow: 0;
padding: 0;
overflow-y: visible;
}
-.navbar-light .navbar-brand {
- color: rgba(0, 0, 0, 0.9);
-}
-.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {
- color: rgba(0, 0, 0, 0.9);
-}
-.navbar-light .navbar-nav .nav-link {
- color: rgba(0, 0, 0, 0.55);
-}
-.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {
- color: rgba(0, 0, 0, 0.7);
-}
-.navbar-light .navbar-nav .nav-link.disabled {
- color: rgba(0, 0, 0, 0.3);
-}
-.navbar-light .navbar-nav .show > .nav-link,
-.navbar-light .navbar-nav .nav-link.active {
- color: rgba(0, 0, 0, 0.9);
-}
-.navbar-light .navbar-toggler {
- color: rgba(0, 0, 0, 0.55);
- border-color: rgba(0, 0, 0, 0.1);
-}
-.navbar-light .navbar-toggler-icon {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
-}
-.navbar-light .navbar-text {
- color: rgba(0, 0, 0, 0.55);
-}
-.navbar-light .navbar-text a,
-.navbar-light .navbar-text a:hover,
-.navbar-light .navbar-text a:focus {
- color: rgba(0, 0, 0, 0.9);
-}
-
-.navbar-dark .navbar-brand {
- color: #fff;
-}
-.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {
- color: #fff;
-}
-.navbar-dark .navbar-nav .nav-link {
- color: rgba(255, 255, 255, 0.55);
-}
-.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {
- color: rgba(255, 255, 255, 0.75);
-}
-.navbar-dark .navbar-nav .nav-link.disabled {
- color: rgba(255, 255, 255, 0.25);
-}
-.navbar-dark .navbar-nav .show > .nav-link,
-.navbar-dark .navbar-nav .nav-link.active {
- color: #fff;
-}
-.navbar-dark .navbar-toggler {
- color: rgba(255, 255, 255, 0.55);
- border-color: rgba(255, 255, 255, 0.1);
-}
-.navbar-dark .navbar-toggler-icon {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
-}
-.navbar-dark .navbar-text {
- color: rgba(255, 255, 255, 0.55);
-}
-.navbar-dark .navbar-text a,
-.navbar-dark .navbar-text a:hover,
-.navbar-dark .navbar-text a:focus {
- color: #fff;
+.navbar-dark {
+ --bs-navbar-color: rgba(255, 255, 255, 0.55);
+ --bs-navbar-hover-color: rgba(255, 255, 255, 0.75);
+ --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);
+ --bs-navbar-active-color: #fff;
+ --bs-navbar-brand-color: #fff;
+ --bs-navbar-brand-hover-color: #fff;
+ --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);
+ --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
.card {
+ --bs-card-spacer-y: 1rem;
+ --bs-card-spacer-x: 1rem;
+ --bs-card-title-spacer-y: 0.5rem;
+ --bs-card-border-width: 1px;
+ --bs-card-border-color: var(--bs-border-color-translucent);
+ --bs-card-border-radius: 0.375rem;
+ --bs-card-box-shadow: ;
+ --bs-card-inner-border-radius: calc(0.375rem - 1px);
+ --bs-card-cap-padding-y: 0.5rem;
+ --bs-card-cap-padding-x: 1rem;
+ --bs-card-cap-bg: rgba(0, 0, 0, 0.03);
+ --bs-card-cap-color: ;
+ --bs-card-height: ;
+ --bs-card-color: ;
+ --bs-card-bg: #fff;
+ --bs-card-img-overlay-padding: 1rem;
+ --bs-card-group-margin: 0.75rem;
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
+ height: var(--bs-card-height);
word-wrap: break-word;
- background-color: #fff;
+ background-color: var(--bs-card-bg);
background-clip: border-box;
- border: 1px solid rgba(0, 0, 0, 0.125);
- border-radius: 0.25rem;
+ border: var(--bs-card-border-width) solid var(--bs-card-border-color);
+ border-radius: var(--bs-card-border-radius);
}
.card > hr {
margin-right: 0;
@@ -4507,13 +4402,13 @@ textarea.form-control-lg {
}
.card > .list-group:first-child {
border-top-width: 0;
- border-top-left-radius: calc(0.25rem - 1px);
- border-top-right-radius: calc(0.25rem - 1px);
+ border-top-left-radius: var(--bs-card-inner-border-radius);
+ border-top-right-radius: var(--bs-card-inner-border-radius);
}
.card > .list-group:last-child {
border-bottom-width: 0;
- border-bottom-right-radius: calc(0.25rem - 1px);
- border-bottom-left-radius: calc(0.25rem - 1px);
+ border-bottom-right-radius: var(--bs-card-inner-border-radius);
+ border-bottom-left-radius: var(--bs-card-inner-border-radius);
}
.card > .card-header + .list-group,
.card > .list-group + .card-footer {
@@ -4522,15 +4417,16 @@ textarea.form-control-lg {
.card-body {
flex: 1 1 auto;
- padding: 1rem 1rem;
+ padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x);
+ color: var(--bs-card-color);
}
.card-title {
- margin-bottom: 0.5rem;
+ margin-bottom: var(--bs-card-title-spacer-y);
}
.card-subtitle {
- margin-top: -0.25rem;
+ margin-top: calc(-0.5 * var(--bs-card-title-spacer-y));
margin-bottom: 0;
}
@@ -4539,38 +4435,44 @@ textarea.form-control-lg {
}
.card-link + .card-link {
- margin-left: 1rem;
+ margin-left: var(--bs-card-spacer-x);
}
.card-header {
- padding: 0.5rem 1rem;
+ padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);
margin-bottom: 0;
- background-color: rgba(0, 0, 0, 0.03);
- border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+ color: var(--bs-card-cap-color);
+ background-color: var(--bs-card-cap-bg);
+ border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);
}
.card-header:first-child {
- border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;
+ border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0;
}
.card-footer {
- padding: 0.5rem 1rem;
- background-color: rgba(0, 0, 0, 0.03);
- border-top: 1px solid rgba(0, 0, 0, 0.125);
+ padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);
+ color: var(--bs-card-cap-color);
+ background-color: var(--bs-card-cap-bg);
+ border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);
}
.card-footer:last-child {
- border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);
+ border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius);
}
.card-header-tabs {
- margin-right: -0.5rem;
- margin-bottom: -0.5rem;
- margin-left: -0.5rem;
+ margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));
+ margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));
+ margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));
border-bottom: 0;
}
+.card-header-tabs .nav-link.active {
+ background-color: var(--bs-card-bg);
+ border-bottom-color: var(--bs-card-bg);
+}
.card-header-pills {
- margin-right: -0.5rem;
- margin-left: -0.5rem;
+ margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));
+ margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));
}
.card-img-overlay {
@@ -4579,8 +4481,8 @@ textarea.form-control-lg {
right: 0;
bottom: 0;
left: 0;
- padding: 1rem;
- border-radius: calc(0.25rem - 1px);
+ padding: var(--bs-card-img-overlay-padding);
+ border-radius: var(--bs-card-inner-border-radius);
}
.card-img,
@@ -4591,18 +4493,18 @@ textarea.form-control-lg {
.card-img,
.card-img-top {
- border-top-left-radius: calc(0.25rem - 1px);
- border-top-right-radius: calc(0.25rem - 1px);
+ border-top-left-radius: var(--bs-card-inner-border-radius);
+ border-top-right-radius: var(--bs-card-inner-border-radius);
}
.card-img,
.card-img-bottom {
- border-bottom-right-radius: calc(0.25rem - 1px);
- border-bottom-left-radius: calc(0.25rem - 1px);
+ border-bottom-right-radius: var(--bs-card-inner-border-radius);
+ border-bottom-left-radius: var(--bs-card-inner-border-radius);
}
.card-group > .card {
- margin-bottom: 0.75rem;
+ margin-bottom: var(--bs-card-group-margin);
}
@media (min-width: 576px) {
.card-group {
@@ -4643,20 +4545,45 @@ textarea.form-control-lg {
}
}
+.accordion {
+ --bs-accordion-color: #000;
+ --bs-accordion-bg: #fff;
+ --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;
+ --bs-accordion-border-color: var(--bs-border-color);
+ --bs-accordion-border-width: 1px;
+ --bs-accordion-border-radius: 0.375rem;
+ --bs-accordion-inner-border-radius: calc(0.375rem - 1px);
+ --bs-accordion-btn-padding-x: 1.25rem;
+ --bs-accordion-btn-padding-y: 1rem;
+ --bs-accordion-btn-color: var(--bs-body-color);
+ --bs-accordion-btn-bg: var(--bs-accordion-bg);
+ --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='var%28--bs-body-color%29'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ --bs-accordion-btn-icon-width: 1.25rem;
+ --bs-accordion-btn-icon-transform: rotate(-180deg);
+ --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;
+ --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ --bs-accordion-btn-focus-border-color: #86b7fe;
+ --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+ --bs-accordion-body-padding-x: 1.25rem;
+ --bs-accordion-body-padding-y: 1rem;
+ --bs-accordion-active-color: #0c63e4;
+ --bs-accordion-active-bg: #e7f1ff;
+}
+
.accordion-button {
position: relative;
display: flex;
align-items: center;
width: 100%;
- padding: 1rem 1.25rem;
+ padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);
font-size: 1rem;
- color: #212529;
+ color: var(--bs-accordion-btn-color);
text-align: left;
- background-color: #fff;
+ background-color: var(--bs-accordion-btn-bg);
border: 0;
border-radius: 0;
overflow-anchor: none;
- transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;
+ transition: var(--bs-accordion-transition);
}
@media (prefers-reduced-motion: reduce) {
.accordion-button {
@@ -4664,24 +4591,24 @@ textarea.form-control-lg {
}
}
.accordion-button:not(.collapsed) {
- color: #0c63e4;
- background-color: #e7f1ff;
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125);
+ color: var(--bs-accordion-active-color);
+ background-color: var(--bs-accordion-active-bg);
+ box-shadow: inset 0 calc(var(--bs-accordion-border-width) * -1) 0 var(--bs-accordion-border-color);
}
.accordion-button:not(.collapsed)::after {
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
- transform: rotate(-180deg);
+ background-image: var(--bs-accordion-btn-active-icon);
+ transform: var(--bs-accordion-btn-icon-transform);
}
.accordion-button::after {
flex-shrink: 0;
- width: 1.25rem;
- height: 1.25rem;
+ width: var(--bs-accordion-btn-icon-width);
+ height: var(--bs-accordion-btn-icon-width);
margin-left: auto;
content: "";
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
+ background-image: var(--bs-accordion-btn-icon);
background-repeat: no-repeat;
- background-size: 1.25rem;
- transition: transform 0.2s ease-in-out;
+ background-size: var(--bs-accordion-btn-icon-width);
+ transition: var(--bs-accordion-btn-icon-transition);
}
@media (prefers-reduced-motion: reduce) {
.accordion-button::after {
@@ -4693,9 +4620,9 @@ textarea.form-control-lg {
}
.accordion-button:focus {
z-index: 3;
- border-color: #86b7fe;
+ border-color: var(--bs-accordion-btn-focus-border-color);
outline: 0;
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+ box-shadow: var(--bs-accordion-btn-focus-box-shadow);
}
.accordion-header {
@@ -4703,35 +4630,36 @@ textarea.form-control-lg {
}
.accordion-item {
- background-color: #fff;
- border: 1px solid rgba(0, 0, 0, 0.125);
+ color: var(--bs-accordion-color);
+ background-color: var(--bs-accordion-bg);
+ border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color);
}
.accordion-item:first-of-type {
- border-top-left-radius: 0.25rem;
- border-top-right-radius: 0.25rem;
+ border-top-left-radius: var(--bs-accordion-border-radius);
+ border-top-right-radius: var(--bs-accordion-border-radius);
}
.accordion-item:first-of-type .accordion-button {
- border-top-left-radius: calc(0.25rem - 1px);
- border-top-right-radius: calc(0.25rem - 1px);
+ border-top-left-radius: var(--bs-accordion-inner-border-radius);
+ border-top-right-radius: var(--bs-accordion-inner-border-radius);
}
.accordion-item:not(:first-of-type) {
border-top: 0;
}
.accordion-item:last-of-type {
- border-bottom-right-radius: 0.25rem;
- border-bottom-left-radius: 0.25rem;
+ border-bottom-right-radius: var(--bs-accordion-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-border-radius);
}
.accordion-item:last-of-type .accordion-button.collapsed {
- border-bottom-right-radius: calc(0.25rem - 1px);
- border-bottom-left-radius: calc(0.25rem - 1px);
+ border-bottom-right-radius: var(--bs-accordion-inner-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-inner-border-radius);
}
.accordion-item:last-of-type .accordion-collapse {
- border-bottom-right-radius: 0.25rem;
- border-bottom-left-radius: 0.25rem;
+ border-bottom-right-radius: var(--bs-accordion-border-radius);
+ border-bottom-left-radius: var(--bs-accordion-border-radius);
}
.accordion-body {
- padding: 1rem 1.25rem;
+ padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);
}
.accordion-flush .accordion-collapse {
@@ -4753,27 +4681,58 @@ textarea.form-control-lg {
}
.breadcrumb {
+ --bs-breadcrumb-padding-x: 0;
+ --bs-breadcrumb-padding-y: 0;
+ --bs-breadcrumb-margin-bottom: 1rem;
+ --bs-breadcrumb-bg: ;
+ --bs-breadcrumb-border-radius: ;
+ --bs-breadcrumb-divider-color: #6c757d;
+ --bs-breadcrumb-item-padding-x: 0.5rem;
+ --bs-breadcrumb-item-active-color: #6c757d;
display: flex;
flex-wrap: wrap;
- padding: 0 0;
- margin-bottom: 1rem;
+ padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);
+ margin-bottom: var(--bs-breadcrumb-margin-bottom);
+ font-size: var(--bs-breadcrumb-font-size);
list-style: none;
+ background-color: var(--bs-breadcrumb-bg);
+ border-radius: var(--bs-breadcrumb-border-radius);
}
.breadcrumb-item + .breadcrumb-item {
- padding-left: 0.5rem;
+ padding-left: var(--bs-breadcrumb-item-padding-x);
}
.breadcrumb-item + .breadcrumb-item::before {
float: left;
- padding-right: 0.5rem;
- color: #6c757d;
+ padding-right: var(--bs-breadcrumb-item-padding-x);
+ color: var(--bs-breadcrumb-divider-color);
content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */;
}
.breadcrumb-item.active {
- color: #6c757d;
+ color: var(--bs-breadcrumb-item-active-color);
}
.pagination {
+ --bs-pagination-padding-x: 0.75rem;
+ --bs-pagination-padding-y: 0.375rem;
+ --bs-pagination-font-size: 1rem;
+ --bs-pagination-color: var(--bs-link-color);
+ --bs-pagination-bg: #fff;
+ --bs-pagination-border-width: 1px;
+ --bs-pagination-border-color: #dee2e6;
+ --bs-pagination-border-radius: 0.375rem;
+ --bs-pagination-hover-color: var(--bs-link-hover-color);
+ --bs-pagination-hover-bg: #e9ecef;
+ --bs-pagination-hover-border-color: #dee2e6;
+ --bs-pagination-focus-color: var(--bs-link-hover-color);
+ --bs-pagination-focus-bg: #e9ecef;
+ --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
+ --bs-pagination-active-color: #fff;
+ --bs-pagination-active-bg: #0d6efd;
+ --bs-pagination-active-border-color: #0d6efd;
+ --bs-pagination-disabled-color: #6c757d;
+ --bs-pagination-disabled-bg: #fff;
+ --bs-pagination-disabled-border-color: #dee2e6;
display: flex;
padding-left: 0;
list-style: none;
@@ -4782,10 +4741,12 @@ textarea.form-control-lg {
.page-link {
position: relative;
display: block;
- color: #0d6efd;
+ padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);
+ font-size: var(--bs-pagination-font-size);
+ color: var(--bs-pagination-color);
text-decoration: none;
- background-color: #fff;
- border: 1px solid #dee2e6;
+ background-color: var(--bs-pagination-bg);
+ border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
@@ -4795,84 +4756,73 @@ textarea.form-control-lg {
}
.page-link:hover {
z-index: 2;
- color: #0a58ca;
- background-color: #e9ecef;
- border-color: #dee2e6;
+ color: var(--bs-pagination-hover-color);
+ background-color: var(--bs-pagination-hover-bg);
+ border-color: var(--bs-pagination-hover-border-color);
}
.page-link:focus {
z-index: 3;
- color: #0a58ca;
- background-color: #e9ecef;
+ color: var(--bs-pagination-focus-color);
+ background-color: var(--bs-pagination-focus-bg);
outline: 0;
- box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
-}
-
-.page-item:not(:first-child) .page-link {
- margin-left: -1px;
+ box-shadow: var(--bs-pagination-focus-box-shadow);
}
-.page-item.active .page-link {
+.page-link.active, .active > .page-link {
z-index: 3;
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
+ color: var(--bs-pagination-active-color);
+ background-color: var(--bs-pagination-active-bg);
+ border-color: var(--bs-pagination-active-border-color);
}
-.page-item.disabled .page-link {
- color: #6c757d;
+.page-link.disabled, .disabled > .page-link {
+ color: var(--bs-pagination-disabled-color);
pointer-events: none;
- background-color: #fff;
- border-color: #dee2e6;
+ background-color: var(--bs-pagination-disabled-bg);
+ border-color: var(--bs-pagination-disabled-border-color);
}
-.page-link {
- padding: 0.375rem 0.75rem;
+.page-item:not(:first-child) .page-link {
+ margin-left: -1px;
}
-
.page-item:first-child .page-link {
- border-top-left-radius: 0.25rem;
- border-bottom-left-radius: 0.25rem;
+ border-top-left-radius: var(--bs-pagination-border-radius);
+ border-bottom-left-radius: var(--bs-pagination-border-radius);
}
.page-item:last-child .page-link {
- border-top-right-radius: 0.25rem;
- border-bottom-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-pagination-border-radius);
+ border-bottom-right-radius: var(--bs-pagination-border-radius);
}
-.pagination-lg .page-link {
- padding: 0.75rem 1.5rem;
- font-size: 1.25rem;
-}
-.pagination-lg .page-item:first-child .page-link {
- border-top-left-radius: 0.3rem;
- border-bottom-left-radius: 0.3rem;
-}
-.pagination-lg .page-item:last-child .page-link {
- border-top-right-radius: 0.3rem;
- border-bottom-right-radius: 0.3rem;
+.pagination-lg {
+ --bs-pagination-padding-x: 1.5rem;
+ --bs-pagination-padding-y: 0.75rem;
+ --bs-pagination-font-size: 1.25rem;
+ --bs-pagination-border-radius: 0.5rem;
}
-.pagination-sm .page-link {
- padding: 0.25rem 0.5rem;
- font-size: 0.875rem;
-}
-.pagination-sm .page-item:first-child .page-link {
- border-top-left-radius: 0.2rem;
- border-bottom-left-radius: 0.2rem;
-}
-.pagination-sm .page-item:last-child .page-link {
- border-top-right-radius: 0.2rem;
- border-bottom-right-radius: 0.2rem;
+.pagination-sm {
+ --bs-pagination-padding-x: 0.5rem;
+ --bs-pagination-padding-y: 0.25rem;
+ --bs-pagination-font-size: 0.875rem;
+ --bs-pagination-border-radius: 0.25rem;
}
.badge {
+ --bs-badge-padding-x: 0.65em;
+ --bs-badge-padding-y: 0.35em;
+ --bs-badge-font-size: 0.75em;
+ --bs-badge-font-weight: 700;
+ --bs-badge-color: #fff;
+ --bs-badge-border-radius: 0.375rem;
display: inline-block;
- padding: 0.35em 0.65em;
- font-size: 0.75em;
- font-weight: 700;
+ padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);
+ font-size: var(--bs-badge-font-size);
+ font-weight: var(--bs-badge-font-weight);
line-height: 1;
- color: #fff;
+ color: var(--bs-badge-color);
text-align: center;
white-space: nowrap;
vertical-align: baseline;
- border-radius: 0.25rem;
+ border-radius: var(--bs-badge-border-radius, 0);
}
.badge:empty {
display: none;
@@ -4884,11 +4834,21 @@ textarea.form-control-lg {
}
.alert {
+ --bs-alert-bg: transparent;
+ --bs-alert-padding-x: 1rem;
+ --bs-alert-padding-y: 1rem;
+ --bs-alert-margin-bottom: 1rem;
+ --bs-alert-color: inherit;
+ --bs-alert-border-color: transparent;
+ --bs-alert-border: 1px solid var(--bs-alert-border-color);
+ --bs-alert-border-radius: 0.375rem;
position: relative;
- padding: 1rem 1rem;
- margin-bottom: 1rem;
- border: 1px solid transparent;
- border-radius: 0.25rem;
+ padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);
+ margin-bottom: var(--bs-alert-margin-bottom);
+ color: var(--bs-alert-color);
+ background-color: var(--bs-alert-bg);
+ border: var(--bs-alert-border);
+ border-radius: var(--bs-alert-border-radius, 0);
}
.alert-heading {
@@ -4911,72 +4871,72 @@ textarea.form-control-lg {
}
.alert-primary {
- color: #084298;
- background-color: #cfe2ff;
- border-color: #b6d4fe;
+ --bs-alert-color: #084298;
+ --bs-alert-bg: #cfe2ff;
+ --bs-alert-border-color: #b6d4fe;
}
.alert-primary .alert-link {
color: #06357a;
}
.alert-secondary {
- color: #41464b;
- background-color: #e2e3e5;
- border-color: #d3d6d8;
+ --bs-alert-color: #41464b;
+ --bs-alert-bg: #e2e3e5;
+ --bs-alert-border-color: #d3d6d8;
}
.alert-secondary .alert-link {
color: #34383c;
}
.alert-success {
- color: #0f5132;
- background-color: #d1e7dd;
- border-color: #badbcc;
+ --bs-alert-color: #0f5132;
+ --bs-alert-bg: #d1e7dd;
+ --bs-alert-border-color: #badbcc;
}
.alert-success .alert-link {
color: #0c4128;
}
.alert-info {
- color: #055160;
- background-color: #cff4fc;
- border-color: #b6effb;
+ --bs-alert-color: #055160;
+ --bs-alert-bg: #cff4fc;
+ --bs-alert-border-color: #b6effb;
}
.alert-info .alert-link {
color: #04414d;
}
.alert-warning {
- color: #664d03;
- background-color: #fff3cd;
- border-color: #ffecb5;
+ --bs-alert-color: #664d03;
+ --bs-alert-bg: #fff3cd;
+ --bs-alert-border-color: #ffecb5;
}
.alert-warning .alert-link {
color: #523e02;
}
.alert-danger {
- color: #842029;
- background-color: #f8d7da;
- border-color: #f5c2c7;
+ --bs-alert-color: #842029;
+ --bs-alert-bg: #f8d7da;
+ --bs-alert-border-color: #f5c2c7;
}
.alert-danger .alert-link {
color: #6a1a21;
}
.alert-light {
- color: #636464;
- background-color: #fefefe;
- border-color: #fdfdfe;
+ --bs-alert-color: #636464;
+ --bs-alert-bg: #fefefe;
+ --bs-alert-border-color: #fdfdfe;
}
.alert-light .alert-link {
color: #4f5050;
}
.alert-dark {
- color: #141619;
- background-color: #d3d3d4;
- border-color: #bcbebf;
+ --bs-alert-color: #141619;
+ --bs-alert-bg: #d3d3d4;
+ --bs-alert-border-color: #bcbebf;
}
.alert-dark .alert-link {
color: #101214;
@@ -4994,12 +4954,20 @@ textarea.form-control-lg {
}
}
.progress {
+ --bs-progress-height: 1rem;
+ --bs-progress-font-size: 0.75rem;
+ --bs-progress-bg: #e9ecef;
+ --bs-progress-border-radius: 0.375rem;
+ --bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075);
+ --bs-progress-bar-color: #fff;
+ --bs-progress-bar-bg: #0d6efd;
+ --bs-progress-bar-transition: width 0.6s ease;
display: flex;
- height: 1rem;
+ height: var(--bs-progress-height);
overflow: hidden;
- font-size: 0.75rem;
- background-color: #e9ecef;
- border-radius: 0.25rem;
+ font-size: var(--bs-progress-font-size);
+ background-color: var(--bs-progress-bg);
+ border-radius: var(--bs-progress-border-radius);
}
.progress-bar {
@@ -5007,11 +4975,11 @@ textarea.form-control-lg {
flex-direction: column;
justify-content: center;
overflow: hidden;
- color: #fff;
+ color: var(--bs-progress-bar-color);
text-align: center;
white-space: nowrap;
- background-color: #0d6efd;
- transition: width 0.6s ease;
+ background-color: var(--bs-progress-bar-bg);
+ transition: var(--bs-progress-bar-transition);
}
@media (prefers-reduced-motion: reduce) {
.progress-bar {
@@ -5021,7 +4989,7 @@ textarea.form-control-lg {
.progress-bar-striped {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-size: 1rem 1rem;
+ background-size: var(--bs-progress-height) var(--bs-progress-height);
}
.progress-bar-animated {
@@ -5036,46 +5004,63 @@ textarea.form-control-lg {
}
.list-group {
+ --bs-list-group-color: #212529;
+ --bs-list-group-bg: #fff;
+ --bs-list-group-border-color: rgba(0, 0, 0, 0.125);
+ --bs-list-group-border-width: 1px;
+ --bs-list-group-border-radius: 0.375rem;
+ --bs-list-group-item-padding-x: 1rem;
+ --bs-list-group-item-padding-y: 0.5rem;
+ --bs-list-group-action-color: #495057;
+ --bs-list-group-action-hover-color: #495057;
+ --bs-list-group-action-hover-bg: #f8f9fa;
+ --bs-list-group-action-active-color: #212529;
+ --bs-list-group-action-active-bg: #e9ecef;
+ --bs-list-group-disabled-color: #6c757d;
+ --bs-list-group-disabled-bg: #fff;
+ --bs-list-group-active-color: #fff;
+ --bs-list-group-active-bg: #0d6efd;
+ --bs-list-group-active-border-color: #0d6efd;
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0;
- border-radius: 0.25rem;
+ border-radius: var(--bs-list-group-border-radius);
}
.list-group-numbered {
list-style-type: none;
counter-reset: section;
}
-.list-group-numbered > li::before {
+.list-group-numbered > .list-group-item::before {
content: counters(section, ".") ". ";
counter-increment: section;
}
.list-group-item-action {
width: 100%;
- color: #495057;
+ color: var(--bs-list-group-action-color);
text-align: inherit;
}
.list-group-item-action:hover, .list-group-item-action:focus {
z-index: 1;
- color: #495057;
+ color: var(--bs-list-group-action-hover-color);
text-decoration: none;
- background-color: #f8f9fa;
+ background-color: var(--bs-list-group-action-hover-bg);
}
.list-group-item-action:active {
- color: #212529;
- background-color: #e9ecef;
+ color: var(--bs-list-group-action-active-color);
+ background-color: var(--bs-list-group-action-active-bg);
}
.list-group-item {
position: relative;
display: block;
- padding: 0.5rem 1rem;
- color: #212529;
+ padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);
+ color: var(--bs-list-group-color);
text-decoration: none;
- background-color: #fff;
- border: 1px solid rgba(0, 0, 0, 0.125);
+ background-color: var(--bs-list-group-bg);
+ border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);
}
.list-group-item:first-child {
border-top-left-radius: inherit;
@@ -5086,45 +5071,45 @@ textarea.form-control-lg {
border-bottom-left-radius: inherit;
}
.list-group-item.disabled, .list-group-item:disabled {
- color: #6c757d;
+ color: var(--bs-list-group-disabled-color);
pointer-events: none;
- background-color: #fff;
+ background-color: var(--bs-list-group-disabled-bg);
}
.list-group-item.active {
z-index: 2;
- color: #fff;
- background-color: #0d6efd;
- border-color: #0d6efd;
+ color: var(--bs-list-group-active-color);
+ background-color: var(--bs-list-group-active-bg);
+ border-color: var(--bs-list-group-active-border-color);
}
.list-group-item + .list-group-item {
border-top-width: 0;
}
.list-group-item + .list-group-item.active {
- margin-top: -1px;
- border-top-width: 1px;
+ margin-top: calc(var(--bs-list-group-border-width) * -1);
+ border-top-width: var(--bs-list-group-border-width);
}
.list-group-horizontal {
flex-direction: row;
}
.list-group-horizontal > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
@media (min-width: 576px) {
@@ -5132,23 +5117,23 @@ textarea.form-control-lg {
flex-direction: row;
}
.list-group-horizontal-sm > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal-sm > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal-sm > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-sm > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal-sm > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
}
@media (min-width: 768px) {
@@ -5156,23 +5141,23 @@ textarea.form-control-lg {
flex-direction: row;
}
.list-group-horizontal-md > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal-md > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal-md > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-md > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal-md > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
}
@media (min-width: 992px) {
@@ -5180,23 +5165,23 @@ textarea.form-control-lg {
flex-direction: row;
}
.list-group-horizontal-lg > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal-lg > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal-lg > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-lg > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal-lg > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
}
@media (min-width: 1200px) {
@@ -5204,23 +5189,23 @@ textarea.form-control-lg {
flex-direction: row;
}
.list-group-horizontal-xl > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal-xl > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal-xl > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-xl > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal-xl > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
}
@media (min-width: 1400px) {
@@ -5228,30 +5213,30 @@ textarea.form-control-lg {
flex-direction: row;
}
.list-group-horizontal-xxl > .list-group-item:first-child {
- border-bottom-left-radius: 0.25rem;
+ border-bottom-left-radius: var(--bs-list-group-border-radius);
border-top-right-radius: 0;
}
.list-group-horizontal-xxl > .list-group-item:last-child {
- border-top-right-radius: 0.25rem;
+ border-top-right-radius: var(--bs-list-group-border-radius);
border-bottom-left-radius: 0;
}
.list-group-horizontal-xxl > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-xxl > .list-group-item + .list-group-item {
- border-top-width: 1px;
+ border-top-width: var(--bs-list-group-border-width);
border-left-width: 0;
}
.list-group-horizontal-xxl > .list-group-item + .list-group-item.active {
- margin-left: -1px;
- border-left-width: 1px;
+ margin-left: calc(var(--bs-list-group-border-width) * -1);
+ border-left-width: var(--bs-list-group-border-width);
}
}
.list-group-flush {
border-radius: 0;
}
.list-group-flush > .list-group-item {
- border-width: 0 0 1px;
+ border-width: 0 0 var(--bs-list-group-border-width);
}
.list-group-flush > .list-group-item:last-child {
border-bottom-width: 0;
@@ -5375,9 +5360,9 @@ textarea.form-control-lg {
height: 1em;
padding: 0.25em 0.25em;
color: #000;
- background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;
+ background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;
border: 0;
- border-radius: 0.25rem;
+ border-radius: 0.375rem;
opacity: 0.5;
}
.btn-close:hover {
@@ -5403,15 +5388,30 @@ textarea.form-control-lg {
}
.toast {
- width: 350px;
+ --bs-toast-padding-x: 0.75rem;
+ --bs-toast-padding-y: 0.5rem;
+ --bs-toast-spacing: 1.5rem;
+ --bs-toast-max-width: 350px;
+ --bs-toast-font-size: 0.875rem;
+ --bs-toast-color: ;
+ --bs-toast-bg: rgba(255, 255, 255, 0.85);
+ --bs-toast-border-width: 1px;
+ --bs-toast-border-color: var(--bs-border-color-translucent);
+ --bs-toast-border-radius: 0.375rem;
+ --bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-toast-header-color: #6c757d;
+ --bs-toast-header-bg: rgba(255, 255, 255, 0.85);
+ --bs-toast-header-border-color: rgba(0, 0, 0, 0.05);
+ width: var(--bs-toast-max-width);
max-width: 100%;
- font-size: 0.875rem;
+ font-size: var(--bs-toast-font-size);
+ color: var(--bs-toast-color);
pointer-events: auto;
- background-color: rgba(255, 255, 255, 0.85);
+ background-color: var(--bs-toast-bg);
background-clip: padding-box;
- border: 1px solid rgba(0, 0, 0, 0.1);
- box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
- border-radius: 0.25rem;
+ border: var(--bs-toast-border-width) solid var(--bs-toast-border-color);
+ box-shadow: var(--bs-toast-box-shadow);
+ border-radius: var(--bs-toast-border-radius);
}
.toast.showing {
opacity: 0;
@@ -5421,6 +5421,8 @@ textarea.form-control-lg {
}
.toast-container {
+ position: absolute;
+ z-index: 1090;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
@@ -5428,35 +5430,56 @@ textarea.form-control-lg {
pointer-events: none;
}
.toast-container > :not(:last-child) {
- margin-bottom: 0.75rem;
+ margin-bottom: var(--bs-toast-spacing);
}
.toast-header {
display: flex;
align-items: center;
- padding: 0.5rem 0.75rem;
- color: #6c757d;
- background-color: rgba(255, 255, 255, 0.85);
+ padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);
+ color: var(--bs-toast-header-color);
+ background-color: var(--bs-toast-header-bg);
background-clip: padding-box;
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- border-top-left-radius: calc(0.25rem - 1px);
- border-top-right-radius: calc(0.25rem - 1px);
+ border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);
+ border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));
+ border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));
}
.toast-header .btn-close {
- margin-right: -0.375rem;
- margin-left: 0.75rem;
+ margin-right: calc(var(--bs-toast-padding-x) * -0.5);
+ margin-left: var(--bs-toast-padding-x);
}
.toast-body {
- padding: 0.75rem;
+ padding: var(--bs-toast-padding-x);
word-wrap: break-word;
}
.modal {
+ --bs-modal-zindex: 1055;
+ --bs-modal-width: 500px;
+ --bs-modal-padding: 1rem;
+ --bs-modal-margin: 0.5rem;
+ --bs-modal-color: ;
+ --bs-modal-bg: #fff;
+ --bs-modal-border-color: var(--bs-border-color-translucent);
+ --bs-modal-border-width: 1px;
+ --bs-modal-border-radius: 0.5rem;
+ --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+ --bs-modal-inner-border-radius: calc(0.5rem - 1px);
+ --bs-modal-header-padding-x: 1rem;
+ --bs-modal-header-padding-y: 1rem;
+ --bs-modal-header-padding: 1rem 1rem;
+ --bs-modal-header-border-color: var(--bs-border-color);
+ --bs-modal-header-border-width: 1px;
+ --bs-modal-title-line-height: 1.5;
+ --bs-modal-footer-gap: 0.5rem;
+ --bs-modal-footer-bg: ;
+ --bs-modal-footer-border-color: var(--bs-border-color);
+ --bs-modal-footer-border-width: 1px;
position: fixed;
top: 0;
left: 0;
- z-index: 1055;
+ z-index: var(--bs-modal-zindex);
display: none;
width: 100%;
height: 100%;
@@ -5468,7 +5491,7 @@ textarea.form-control-lg {
.modal-dialog {
position: relative;
width: auto;
- margin: 0.5rem;
+ margin: var(--bs-modal-margin);
pointer-events: none;
}
.modal.fade .modal-dialog {
@@ -5488,7 +5511,7 @@ textarea.form-control-lg {
}
.modal-dialog-scrollable {
- height: calc(100% - 1rem);
+ height: calc(100% - var(--bs-modal-margin) * 2);
}
.modal-dialog-scrollable .modal-content {
max-height: 100%;
@@ -5501,7 +5524,7 @@ textarea.form-control-lg {
.modal-dialog-centered {
display: flex;
align-items: center;
- min-height: calc(100% - 1rem);
+ min-height: calc(100% - var(--bs-modal-margin) * 2);
}
.modal-content {
@@ -5509,28 +5532,32 @@ textarea.form-control-lg {
display: flex;
flex-direction: column;
width: 100%;
+ color: var(--bs-modal-color);
pointer-events: auto;
- background-color: #fff;
+ background-color: var(--bs-modal-bg);
background-clip: padding-box;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 0.3rem;
+ border: var(--bs-modal-border-width) solid var(--bs-modal-border-color);
+ border-radius: var(--bs-modal-border-radius);
outline: 0;
}
.modal-backdrop {
+ --bs-backdrop-zindex: 1050;
+ --bs-backdrop-bg: #000;
+ --bs-backdrop-opacity: 0.5;
position: fixed;
top: 0;
left: 0;
- z-index: 1050;
+ z-index: var(--bs-backdrop-zindex);
width: 100vw;
height: 100vh;
- background-color: #000;
+ background-color: var(--bs-backdrop-bg);
}
.modal-backdrop.fade {
opacity: 0;
}
.modal-backdrop.show {
- opacity: 0.5;
+ opacity: var(--bs-backdrop-opacity);
}
.modal-header {
@@ -5538,69 +5565,68 @@ textarea.form-control-lg {
flex-shrink: 0;
align-items: center;
justify-content: space-between;
- padding: 1rem 1rem;
- border-bottom: 1px solid #dee2e6;
- border-top-left-radius: calc(0.3rem - 1px);
- border-top-right-radius: calc(0.3rem - 1px);
+ padding: var(--bs-modal-header-padding);
+ border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);
+ border-top-left-radius: var(--bs-modal-inner-border-radius);
+ border-top-right-radius: var(--bs-modal-inner-border-radius);
}
.modal-header .btn-close {
- padding: 0.5rem 0.5rem;
- margin: -0.5rem -0.5rem -0.5rem auto;
+ padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5);
+ margin: calc(var(--bs-modal-header-padding-y) * -0.5) calc(var(--bs-modal-header-padding-x) * -0.5) calc(var(--bs-modal-header-padding-y) * -0.5) auto;
}
.modal-title {
margin-bottom: 0;
- line-height: 1.5;
+ line-height: var(--bs-modal-title-line-height);
}
.modal-body {
position: relative;
flex: 1 1 auto;
- padding: 1rem;
+ padding: var(--bs-modal-padding);
}
.modal-footer {
display: flex;
- flex-wrap: wrap;
flex-shrink: 0;
+ flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
- padding: 0.75rem;
- border-top: 1px solid #dee2e6;
- border-bottom-right-radius: calc(0.3rem - 1px);
- border-bottom-left-radius: calc(0.3rem - 1px);
+ padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5);
+ background-color: var(--bs-modal-footer-bg);
+ border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);
+ border-bottom-right-radius: var(--bs-modal-inner-border-radius);
+ border-bottom-left-radius: var(--bs-modal-inner-border-radius);
}
.modal-footer > * {
- margin: 0.25rem;
+ margin: calc(var(--bs-modal-footer-gap) * 0.5);
}
@media (min-width: 576px) {
- .modal-dialog {
- max-width: 500px;
- margin: 1.75rem auto;
+ .modal {
+ --bs-modal-margin: 1.75rem;
+ --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
- .modal-dialog-scrollable {
- height: calc(100% - 3.5rem);
- }
-
- .modal-dialog-centered {
- min-height: calc(100% - 3.5rem);
+ .modal-dialog {
+ max-width: var(--bs-modal-width);
+ margin-right: auto;
+ margin-left: auto;
}
.modal-sm {
- max-width: 300px;
+ --bs-modal-width: 300px;
}
}
@media (min-width: 992px) {
.modal-lg,
.modal-xl {
- max-width: 800px;
+ --bs-modal-width: 800px;
}
}
@media (min-width: 1200px) {
.modal-xl {
- max-width: 1140px;
+ --bs-modal-width: 1140px;
}
}
.modal-fullscreen {
@@ -5614,15 +5640,13 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
-.modal-fullscreen .modal-header {
+.modal-fullscreen .modal-header,
+.modal-fullscreen .modal-footer {
border-radius: 0;
}
.modal-fullscreen .modal-body {
overflow-y: auto;
}
-.modal-fullscreen .modal-footer {
- border-radius: 0;
-}
@media (max-width: 575.98px) {
.modal-fullscreen-sm-down {
@@ -5636,15 +5660,13 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
- .modal-fullscreen-sm-down .modal-header {
+ .modal-fullscreen-sm-down .modal-header,
+.modal-fullscreen-sm-down .modal-footer {
border-radius: 0;
}
.modal-fullscreen-sm-down .modal-body {
overflow-y: auto;
}
- .modal-fullscreen-sm-down .modal-footer {
- border-radius: 0;
- }
}
@media (max-width: 767.98px) {
.modal-fullscreen-md-down {
@@ -5658,15 +5680,13 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
- .modal-fullscreen-md-down .modal-header {
+ .modal-fullscreen-md-down .modal-header,
+.modal-fullscreen-md-down .modal-footer {
border-radius: 0;
}
.modal-fullscreen-md-down .modal-body {
overflow-y: auto;
}
- .modal-fullscreen-md-down .modal-footer {
- border-radius: 0;
- }
}
@media (max-width: 991.98px) {
.modal-fullscreen-lg-down {
@@ -5680,15 +5700,13 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
- .modal-fullscreen-lg-down .modal-header {
+ .modal-fullscreen-lg-down .modal-header,
+.modal-fullscreen-lg-down .modal-footer {
border-radius: 0;
}
.modal-fullscreen-lg-down .modal-body {
overflow-y: auto;
}
- .modal-fullscreen-lg-down .modal-footer {
- border-radius: 0;
- }
}
@media (max-width: 1199.98px) {
.modal-fullscreen-xl-down {
@@ -5702,15 +5720,13 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
- .modal-fullscreen-xl-down .modal-header {
+ .modal-fullscreen-xl-down .modal-header,
+.modal-fullscreen-xl-down .modal-footer {
border-radius: 0;
}
.modal-fullscreen-xl-down .modal-body {
overflow-y: auto;
}
- .modal-fullscreen-xl-down .modal-footer {
- border-radius: 0;
- }
}
@media (max-width: 1399.98px) {
.modal-fullscreen-xxl-down {
@@ -5724,21 +5740,31 @@ textarea.form-control-lg {
border: 0;
border-radius: 0;
}
- .modal-fullscreen-xxl-down .modal-header {
+ .modal-fullscreen-xxl-down .modal-header,
+.modal-fullscreen-xxl-down .modal-footer {
border-radius: 0;
}
.modal-fullscreen-xxl-down .modal-body {
overflow-y: auto;
}
- .modal-fullscreen-xxl-down .modal-footer {
- border-radius: 0;
- }
}
.tooltip {
- position: absolute;
- z-index: 1080;
+ --bs-tooltip-zindex: 1080;
+ --bs-tooltip-max-width: 200px;
+ --bs-tooltip-padding-x: 0.5rem;
+ --bs-tooltip-padding-y: 0.25rem;
+ --bs-tooltip-margin: ;
+ --bs-tooltip-font-size: 0.875rem;
+ --bs-tooltip-color: #fff;
+ --bs-tooltip-bg: #000;
+ --bs-tooltip-border-radius: 0.375rem;
+ --bs-tooltip-opacity: 0.9;
+ --bs-tooltip-arrow-width: 0.8rem;
+ --bs-tooltip-arrow-height: 0.4rem;
+ z-index: var(--bs-tooltip-zindex);
display: block;
- margin: 0;
+ padding: var(--bs-tooltip-arrow-height);
+ margin: var(--bs-tooltip-margin);
font-family: var(--bs-font-sans-serif);
font-style: normal;
font-weight: 400;
@@ -5750,21 +5776,20 @@ textarea.form-control-lg {
text-transform: none;
letter-spacing: normal;
word-break: normal;
- word-spacing: normal;
white-space: normal;
+ word-spacing: normal;
line-break: auto;
- font-size: 0.875rem;
+ font-size: var(--bs-tooltip-font-size);
word-wrap: break-word;
opacity: 0;
}
.tooltip.show {
- opacity: 0.9;
+ opacity: var(--bs-tooltip-opacity);
}
.tooltip .tooltip-arrow {
- position: absolute;
display: block;
- width: 0.8rem;
- height: 0.4rem;
+ width: var(--bs-tooltip-arrow-width);
+ height: var(--bs-tooltip-arrow-height);
}
.tooltip .tooltip-arrow::before {
position: absolute;
@@ -5773,74 +5798,83 @@ textarea.form-control-lg {
border-style: solid;
}
-.bs-tooltip-top, .bs-tooltip-auto[data-popper-placement^=top] {
- padding: 0.4rem 0;
-}
.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {
bottom: 0;
}
.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {
top: -1px;
- border-width: 0.4rem 0.4rem 0;
- border-top-color: #000;
+ border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+ border-top-color: var(--bs-tooltip-bg);
}
-.bs-tooltip-end, .bs-tooltip-auto[data-popper-placement^=right] {
- padding: 0 0.4rem;
-}
+/* rtl:begin:ignore */
.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {
left: 0;
- width: 0.4rem;
- height: 0.8rem;
+ width: var(--bs-tooltip-arrow-height);
+ height: var(--bs-tooltip-arrow-width);
}
.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {
right: -1px;
- border-width: 0.4rem 0.4rem 0.4rem 0;
- border-right-color: #000;
+ border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;
+ border-right-color: var(--bs-tooltip-bg);
}
-.bs-tooltip-bottom, .bs-tooltip-auto[data-popper-placement^=bottom] {
- padding: 0.4rem 0;
-}
+/* rtl:end:ignore */
.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {
top: 0;
}
.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {
bottom: -1px;
- border-width: 0 0.4rem 0.4rem;
- border-bottom-color: #000;
+ border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);
+ border-bottom-color: var(--bs-tooltip-bg);
}
-.bs-tooltip-start, .bs-tooltip-auto[data-popper-placement^=left] {
- padding: 0 0.4rem;
-}
+/* rtl:begin:ignore */
.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {
right: 0;
- width: 0.4rem;
- height: 0.8rem;
+ width: var(--bs-tooltip-arrow-height);
+ height: var(--bs-tooltip-arrow-width);
}
.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {
left: -1px;
- border-width: 0.4rem 0 0.4rem 0.4rem;
- border-left-color: #000;
+ border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);
+ border-left-color: var(--bs-tooltip-bg);
}
+/* rtl:end:ignore */
.tooltip-inner {
- max-width: 200px;
- padding: 0.25rem 0.5rem;
- color: #fff;
+ max-width: var(--bs-tooltip-max-width);
+ padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);
+ color: var(--bs-tooltip-color);
text-align: center;
- background-color: #000;
- border-radius: 0.25rem;
+ background-color: var(--bs-tooltip-bg);
+ border-radius: var(--bs-tooltip-border-radius, 0);
}
.popover {
- position: absolute;
- top: 0;
- left: 0 /* rtl:ignore */;
- z-index: 1070;
+ --bs-popover-zindex: 1070;
+ --bs-popover-max-width: 276px;
+ --bs-popover-font-size: 0.875rem;
+ --bs-popover-bg: #fff;
+ --bs-popover-border-width: 1px;
+ --bs-popover-border-color: var(--bs-border-color-translucent);
+ --bs-popover-border-radius: 0.5rem;
+ --bs-popover-inner-border-radius: calc(0.5rem - 1px);
+ --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
+ --bs-popover-header-padding-x: 1rem;
+ --bs-popover-header-padding-y: 0.5rem;
+ --bs-popover-header-font-size: 1rem;
+ --bs-popover-header-color: var(--bs-heading-color);
+ --bs-popover-header-bg: #f0f0f0;
+ --bs-popover-body-padding-x: 1rem;
+ --bs-popover-body-padding-y: 1rem;
+ --bs-popover-body-color: #212529;
+ --bs-popover-arrow-width: 1rem;
+ --bs-popover-arrow-height: 0.5rem;
+ --bs-popover-arrow-border: var(--bs-popover-border-color);
+ z-index: var(--bs-popover-zindex);
display: block;
- max-width: 276px;
+ max-width: var(--bs-popover-max-width);
font-family: var(--bs-font-sans-serif);
font-style: normal;
font-weight: 400;
@@ -5852,21 +5886,20 @@ textarea.form-control-lg {
text-transform: none;
letter-spacing: normal;
word-break: normal;
- word-spacing: normal;
white-space: normal;
+ word-spacing: normal;
line-break: auto;
- font-size: 0.875rem;
+ font-size: var(--bs-popover-font-size);
word-wrap: break-word;
- background-color: #fff;
+ background-color: var(--bs-popover-bg);
background-clip: padding-box;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 0.3rem;
+ border: var(--bs-popover-border-width) solid var(--bs-popover-border-color);
+ border-radius: var(--bs-popover-border-radius);
}
.popover .popover-arrow {
- position: absolute;
display: block;
- width: 1rem;
- height: 0.5rem;
+ width: var(--bs-popover-arrow-width);
+ height: var(--bs-popover-arrow-height);
}
.popover .popover-arrow::before, .popover .popover-arrow::after {
position: absolute;
@@ -5874,94 +5907,104 @@ textarea.form-control-lg {
content: "";
border-color: transparent;
border-style: solid;
+ border-width: 0;
}
.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {
- bottom: calc(-0.5rem - 1px);
+ bottom: calc(var(--bs-popover-arrow-height) * -1 - var(--bs-popover-border-width));
+}
+.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {
+ border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;
}
.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {
bottom: 0;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: rgba(0, 0, 0, 0.25);
+ border-top-color: var(--bs-popover-arrow-border);
}
.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {
- bottom: 1px;
- border-width: 0.5rem 0.5rem 0;
- border-top-color: #fff;
+ bottom: var(--bs-popover-border-width);
+ border-top-color: var(--bs-popover-bg);
}
+/* rtl:begin:ignore */
.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow {
- left: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
+ left: calc(var(--bs-popover-arrow-height) * -1 - var(--bs-popover-border-width));
+ width: var(--bs-popover-arrow-height);
+ height: var(--bs-popover-arrow-width);
+}
+.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {
+ border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;
}
.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {
left: 0;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: rgba(0, 0, 0, 0.25);
+ border-right-color: var(--bs-popover-arrow-border);
}
.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {
- left: 1px;
- border-width: 0.5rem 0.5rem 0.5rem 0;
- border-right-color: #fff;
+ left: var(--bs-popover-border-width);
+ border-right-color: var(--bs-popover-bg);
}
+/* rtl:end:ignore */
.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {
- top: calc(-0.5rem - 1px);
+ top: calc(var(--bs-popover-arrow-height) * -1 - var(--bs-popover-border-width));
+}
+.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {
+ border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);
}
.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {
top: 0;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: rgba(0, 0, 0, 0.25);
+ border-bottom-color: var(--bs-popover-arrow-border);
}
.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {
- top: 1px;
- border-width: 0 0.5rem 0.5rem 0.5rem;
- border-bottom-color: #fff;
+ top: var(--bs-popover-border-width);
+ border-bottom-color: var(--bs-popover-bg);
}
.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {
position: absolute;
top: 0;
left: 50%;
display: block;
- width: 1rem;
- margin-left: -0.5rem;
+ width: var(--bs-popover-arrow-width);
+ margin-left: calc(var(--bs-popover-arrow-width) * -0.5);
content: "";
- border-bottom: 1px solid #f0f0f0;
+ border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg);
}
+/* rtl:begin:ignore */
.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow {
- right: calc(-0.5rem - 1px);
- width: 0.5rem;
- height: 1rem;
+ right: calc(var(--bs-popover-arrow-height) * -1 - var(--bs-popover-border-width));
+ width: var(--bs-popover-arrow-height);
+ height: var(--bs-popover-arrow-width);
+}
+.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {
+ border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);
}
.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {
right: 0;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: rgba(0, 0, 0, 0.25);
+ border-left-color: var(--bs-popover-arrow-border);
}
.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {
- right: 1px;
- border-width: 0.5rem 0 0.5rem 0.5rem;
- border-left-color: #fff;
+ right: var(--bs-popover-border-width);
+ border-left-color: var(--bs-popover-bg);
}
+/* rtl:end:ignore */
.popover-header {
- padding: 0.5rem 1rem;
+ padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);
margin-bottom: 0;
- font-size: 1rem;
- background-color: #f0f0f0;
- border-bottom: 1px solid rgba(0, 0, 0, 0.2);
- border-top-left-radius: calc(0.3rem - 1px);
- border-top-right-radius: calc(0.3rem - 1px);
+ font-size: var(--bs-popover-header-font-size);
+ color: var(--bs-popover-header-color);
+ background-color: var(--bs-popover-header-bg);
+ border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color);
+ border-top-left-radius: var(--bs-popover-inner-border-radius);
+ border-top-right-radius: var(--bs-popover-inner-border-radius);
}
.popover-header:empty {
display: none;
}
.popover-body {
- padding: 1rem 1rem;
- color: #212529;
+ padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);
+ color: var(--bs-popover-body-color);
}
.carousel {
@@ -6171,6 +6214,17 @@ textarea.form-control-lg {
color: #000;
}
+.spinner-grow,
+.spinner-border {
+ display: inline-block;
+ width: var(--bs-spinner-width);
+ height: var(--bs-spinner-height);
+ vertical-align: var(--bs-spinner-vertical-align);
+ border-radius: 50%;
+ -webkit-animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);
+ animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);
+}
+
@-webkit-keyframes spinner-border {
to {
transform: rotate(360deg) /* rtl:ignore */;
@@ -6183,21 +6237,20 @@ textarea.form-control-lg {
}
}
.spinner-border {
- display: inline-block;
- width: 2rem;
- height: 2rem;
- vertical-align: -0.125em;
- border: 0.25em solid currentColor;
+ --bs-spinner-width: 2rem;
+ --bs-spinner-height: 2rem;
+ --bs-spinner-vertical-align: -0.125em;
+ --bs-spinner-border-width: 0.25em;
+ --bs-spinner-animation-speed: 0.75s;
+ --bs-spinner-animation-name: spinner-border;
+ border: var(--bs-spinner-border-width) solid currentcolor;
border-right-color: transparent;
- border-radius: 50%;
- -webkit-animation: 0.75s linear infinite spinner-border;
- animation: 0.75s linear infinite spinner-border;
}
.spinner-border-sm {
- width: 1rem;
- height: 1rem;
- border-width: 0.2em;
+ --bs-spinner-width: 1rem;
+ --bs-spinner-height: 1rem;
+ --bs-spinner-border-width: 0.2em;
}
@-webkit-keyframes spinner-grow {
@@ -6220,29 +6273,478 @@ textarea.form-control-lg {
}
}
.spinner-grow {
- display: inline-block;
- width: 2rem;
- height: 2rem;
- vertical-align: -0.125em;
- background-color: currentColor;
- border-radius: 50%;
+ --bs-spinner-width: 2rem;
+ --bs-spinner-height: 2rem;
+ --bs-spinner-vertical-align: -0.125em;
+ --bs-spinner-animation-speed: 0.75s;
+ --bs-spinner-animation-name: spinner-grow;
+ background-color: currentcolor;
opacity: 0;
- -webkit-animation: 0.75s linear infinite spinner-grow;
- animation: 0.75s linear infinite spinner-grow;
}
.spinner-grow-sm {
- width: 1rem;
- height: 1rem;
+ --bs-spinner-width: 1rem;
+ --bs-spinner-height: 1rem;
}
@media (prefers-reduced-motion: reduce) {
.spinner-border,
.spinner-grow {
- -webkit-animation-duration: 1.5s;
- animation-duration: 1.5s;
+ --bs-spinner-animation-speed: 1.5s;
+ }
+}
+.offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm {
+ --bs-offcanvas-width: 400px;
+ --bs-offcanvas-height: 30vh;
+ --bs-offcanvas-padding-x: 1rem;
+ --bs-offcanvas-padding-y: 1rem;
+ --bs-offcanvas-color: ;
+ --bs-offcanvas-bg: #fff;
+ --bs-offcanvas-border-width: 1px;
+ --bs-offcanvas-border-color: var(--bs-border-color-translucent);
+ --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+}
+
+@media (max-width: 575.98px) {
+ .offcanvas-sm {
+ position: fixed;
+ bottom: 0;
+ z-index: 1045;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: transform 0.3s ease-in-out;
+ }
+}
+@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-sm {
+ transition: none;
+ }
+}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) {
+ transform: none;
}
}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show {
+ visibility: visible;
+ }
+}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+}
+@media (max-width: 575.98px) {
+ .offcanvas-sm.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+}
+@media (min-width: 576px) {
+ .offcanvas-sm {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+ .offcanvas-sm .offcanvas-header {
+ display: none;
+ }
+ .offcanvas-sm .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .offcanvas-md {
+ position: fixed;
+ bottom: 0;
+ z-index: 1045;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: transform 0.3s ease-in-out;
+ }
+}
+@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-md {
+ transition: none;
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) {
+ transform: none;
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show {
+ visibility: visible;
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+}
+@media (max-width: 767.98px) {
+ .offcanvas-md.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+}
+@media (min-width: 768px) {
+ .offcanvas-md {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+ .offcanvas-md .offcanvas-header {
+ display: none;
+ }
+ .offcanvas-md .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .offcanvas-lg {
+ position: fixed;
+ bottom: 0;
+ z-index: 1045;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: transform 0.3s ease-in-out;
+ }
+}
+@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-lg {
+ transition: none;
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) {
+ transform: none;
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show {
+ visibility: visible;
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+}
+@media (max-width: 991.98px) {
+ .offcanvas-lg.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+}
+@media (min-width: 992px) {
+ .offcanvas-lg {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+ .offcanvas-lg .offcanvas-header {
+ display: none;
+ }
+ .offcanvas-lg .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .offcanvas-xl {
+ position: fixed;
+ bottom: 0;
+ z-index: 1045;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: transform 0.3s ease-in-out;
+ }
+}
+@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-xl {
+ transition: none;
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) {
+ transform: none;
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show {
+ visibility: visible;
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+}
+@media (max-width: 1199.98px) {
+ .offcanvas-xl.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+}
+@media (min-width: 1200px) {
+ .offcanvas-xl {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+ .offcanvas-xl .offcanvas-header {
+ display: none;
+ }
+ .offcanvas-xl .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl {
+ position: fixed;
+ bottom: 0;
+ z-index: 1045;
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ color: var(--bs-offcanvas-color);
+ visibility: hidden;
+ background-color: var(--bs-offcanvas-bg);
+ background-clip: padding-box;
+ outline: 0;
+ transition: transform 0.3s ease-in-out;
+ }
+}
+@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) {
+ .offcanvas-xxl {
+ transition: none;
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) {
+ transform: none;
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {
+ visibility: visible;
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+ }
+}
+@media (max-width: 1399.98px) {
+ .offcanvas-xxl.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+ }
+}
+@media (min-width: 1400px) {
+ .offcanvas-xxl {
+ --bs-offcanvas-height: auto;
+ --bs-offcanvas-border-width: 0;
+ background-color: transparent !important;
+ }
+ .offcanvas-xxl .offcanvas-header {
+ display: none;
+ }
+ .offcanvas-xxl .offcanvas-body {
+ display: flex;
+ flex-grow: 0;
+ padding: 0;
+ overflow-y: visible;
+ background-color: transparent !important;
+ }
+}
+
.offcanvas {
position: fixed;
bottom: 0;
@@ -6250,8 +6752,9 @@ textarea.form-control-lg {
display: flex;
flex-direction: column;
max-width: 100%;
+ color: var(--bs-offcanvas-color);
visibility: hidden;
- background-color: #fff;
+ background-color: var(--bs-offcanvas-bg);
background-clip: padding-box;
outline: 0;
transition: transform 0.3s ease-in-out;
@@ -6261,6 +6764,43 @@ textarea.form-control-lg {
transition: none;
}
}
+.offcanvas.showing, .offcanvas.show:not(.hiding) {
+ transform: none;
+}
+.offcanvas.showing, .offcanvas.hiding, .offcanvas.show {
+ visibility: visible;
+}
+.offcanvas.offcanvas-start {
+ top: 0;
+ left: 0;
+ width: var(--bs-offcanvas-width);
+ border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(-100%);
+}
+.offcanvas.offcanvas-end {
+ top: 0;
+ right: 0;
+ width: var(--bs-offcanvas-width);
+ border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateX(100%);
+}
+.offcanvas.offcanvas-top {
+ top: 0;
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(-100%);
+}
+.offcanvas.offcanvas-bottom {
+ right: 0;
+ left: 0;
+ height: var(--bs-offcanvas-height);
+ max-height: 100%;
+ border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);
+ transform: translateY(100%);
+}
.offcanvas-backdrop {
position: fixed;
@@ -6282,13 +6822,13 @@ textarea.form-control-lg {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 1rem 1rem;
+ padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);
}
.offcanvas-header .btn-close {
- padding: 0.5rem 0.5rem;
- margin-top: -0.5rem;
- margin-right: -0.5rem;
- margin-bottom: -0.5rem;
+ padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5);
+ margin-top: calc(var(--bs-offcanvas-padding-y) * -0.5);
+ margin-right: calc(var(--bs-offcanvas-padding-x) * -0.5);
+ margin-bottom: calc(var(--bs-offcanvas-padding-y) * -0.5);
}
.offcanvas-title {
@@ -6298,55 +6838,16 @@ textarea.form-control-lg {
.offcanvas-body {
flex-grow: 1;
- padding: 1rem 1rem;
+ padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);
overflow-y: auto;
}
-.offcanvas-start {
- top: 0;
- left: 0;
- width: 400px;
- border-right: 1px solid rgba(0, 0, 0, 0.2);
- transform: translateX(-100%);
-}
-
-.offcanvas-end {
- top: 0;
- right: 0;
- width: 400px;
- border-left: 1px solid rgba(0, 0, 0, 0.2);
- transform: translateX(100%);
-}
-
-.offcanvas-top {
- top: 0;
- right: 0;
- left: 0;
- height: 30vh;
- max-height: 100%;
- border-bottom: 1px solid rgba(0, 0, 0, 0.2);
- transform: translateY(-100%);
-}
-
-.offcanvas-bottom {
- right: 0;
- left: 0;
- height: 30vh;
- max-height: 100%;
- border-top: 1px solid rgba(0, 0, 0, 0.2);
- transform: translateY(100%);
-}
-
-.offcanvas.show {
- transform: none;
-}
-
.placeholder {
display: inline-block;
min-height: 1em;
vertical-align: middle;
cursor: wait;
- background-color: currentColor;
+ background-color: currentcolor;
opacity: 0.5;
}
.placeholder.btn::before {
@@ -6410,60 +6911,100 @@ textarea.form-control-lg {
content: "";
}
+.text-bg-primary {
+ color: #fff !important;
+ background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-secondary {
+ color: #fff !important;
+ background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-success {
+ color: #fff !important;
+ background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-info {
+ color: #000 !important;
+ background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-warning {
+ color: #000 !important;
+ background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-danger {
+ color: #fff !important;
+ background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-light {
+ color: #000 !important;
+ background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important;
+}
+
+.text-bg-dark {
+ color: #fff !important;
+ background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important;
+}
+
.link-primary {
- color: #0d6efd;
+ color: #0d6efd !important;
}
.link-primary:hover, .link-primary:focus {
- color: #0a58ca;
+ color: #0a58ca !important;
}
.link-secondary {
- color: #6c757d;
+ color: #6c757d !important;
}
.link-secondary:hover, .link-secondary:focus {
- color: #565e64;
+ color: #565e64 !important;
}
.link-success {
- color: #198754;
+ color: #198754 !important;
}
.link-success:hover, .link-success:focus {
- color: #146c43;
+ color: #146c43 !important;
}
.link-info {
- color: #0dcaf0;
+ color: #0dcaf0 !important;
}
.link-info:hover, .link-info:focus {
- color: #3dd5f3;
+ color: #3dd5f3 !important;
}
.link-warning {
- color: #ffc107;
+ color: #ffc107 !important;
}
.link-warning:hover, .link-warning:focus {
- color: #ffcd39;
+ color: #ffcd39 !important;
}
.link-danger {
- color: #dc3545;
+ color: #dc3545 !important;
}
.link-danger:hover, .link-danger:focus {
- color: #b02a37;
+ color: #b02a37 !important;
}
.link-light {
- color: #f8f9fa;
+ color: #f8f9fa !important;
}
.link-light:hover, .link-light:focus {
- color: #f9fafb;
+ color: #f9fafb !important;
}
.link-dark {
- color: #212529;
+ color: #212529 !important;
}
.link-dark:hover, .link-dark:focus {
- color: #1a1e21;
+ color: #1a1e21 !important;
}
.ratio {
@@ -6522,6 +7063,13 @@ textarea.form-control-lg {
z-index: 1020;
}
+.sticky-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+}
+
@media (min-width: 576px) {
.sticky-sm-top {
position: -webkit-sticky;
@@ -6529,6 +7077,13 @@ textarea.form-control-lg {
top: 0;
z-index: 1020;
}
+
+ .sticky-sm-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
}
@media (min-width: 768px) {
.sticky-md-top {
@@ -6537,6 +7092,13 @@ textarea.form-control-lg {
top: 0;
z-index: 1020;
}
+
+ .sticky-md-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
}
@media (min-width: 992px) {
.sticky-lg-top {
@@ -6545,6 +7107,13 @@ textarea.form-control-lg {
top: 0;
z-index: 1020;
}
+
+ .sticky-lg-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
}
@media (min-width: 1200px) {
.sticky-xl-top {
@@ -6553,6 +7122,13 @@ textarea.form-control-lg {
top: 0;
z-index: 1020;
}
+
+ .sticky-xl-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
}
@media (min-width: 1400px) {
.sticky-xxl-top {
@@ -6561,6 +7137,13 @@ textarea.form-control-lg {
top: 0;
z-index: 1020;
}
+
+ .sticky-xxl-bottom {
+ position: -webkit-sticky;
+ position: sticky;
+ bottom: 0;
+ z-index: 1020;
+ }
}
.hstack {
display: flex;
@@ -6610,7 +7193,7 @@ textarea.form-control-lg {
align-self: stretch;
width: 1px;
min-height: 1em;
- background-color: currentColor;
+ background-color: currentcolor;
opacity: 0.25;
}
@@ -6824,7 +7407,7 @@ textarea.form-control-lg {
}
.border {
- border: 1px solid #dee2e6 !important;
+ border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.border-0 {
@@ -6832,7 +7415,7 @@ textarea.form-control-lg {
}
.border-top {
- border-top: 1px solid #dee2e6 !important;
+ border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.border-top-0 {
@@ -6840,7 +7423,7 @@ textarea.form-control-lg {
}
.border-end {
- border-right: 1px solid #dee2e6 !important;
+ border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.border-end-0 {
@@ -6848,7 +7431,7 @@ textarea.form-control-lg {
}
.border-bottom {
- border-bottom: 1px solid #dee2e6 !important;
+ border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.border-bottom-0 {
@@ -6856,7 +7439,7 @@ textarea.form-control-lg {
}
.border-start {
- border-left: 1px solid #dee2e6 !important;
+ border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.border-start-0 {
@@ -6864,59 +7447,88 @@ textarea.form-control-lg {
}
.border-primary {
- border-color: #0d6efd !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;
}
.border-secondary {
- border-color: #6c757d !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;
}
.border-success {
- border-color: #198754 !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;
}
.border-info {
- border-color: #0dcaf0 !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;
}
.border-warning {
- border-color: #ffc107 !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;
}
.border-danger {
- border-color: #dc3545 !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;
}
.border-light {
- border-color: #f8f9fa !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;
}
.border-dark {
- border-color: #212529 !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;
}
.border-white {
- border-color: #fff !important;
+ --bs-border-opacity: 1;
+ border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;
}
.border-1 {
- border-width: 1px !important;
+ --bs-border-width: 1px;
}
.border-2 {
- border-width: 2px !important;
+ --bs-border-width: 2px;
}
.border-3 {
- border-width: 3px !important;
+ --bs-border-width: 3px;
}
.border-4 {
- border-width: 4px !important;
+ --bs-border-width: 4px;
}
.border-5 {
- border-width: 5px !important;
+ --bs-border-width: 5px;
+}
+
+.border-opacity-10 {
+ --bs-border-opacity: 0.1;
+}
+
+.border-opacity-25 {
+ --bs-border-opacity: 0.25;
+}
+
+.border-opacity-50 {
+ --bs-border-opacity: 0.5;
+}
+
+.border-opacity-75 {
+ --bs-border-opacity: 0.75;
+}
+
+.border-opacity-100 {
+ --bs-border-opacity: 1;
}
.w-25 {
@@ -7031,30 +7643,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
-.gap-0 {
- gap: 0 !important;
-}
-
-.gap-1 {
- gap: 0.25rem !important;
-}
-
-.gap-2 {
- gap: 0.5rem !important;
-}
-
-.gap-3 {
- gap: 1rem !important;
-}
-
-.gap-4 {
- gap: 1.5rem !important;
-}
-
-.gap-5 {
- gap: 3rem !important;
-}
-
.justify-content-start {
justify-content: flex-start !important;
}
@@ -7569,6 +8157,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+.gap-0 {
+ gap: 0 !important;
+}
+
+.gap-1 {
+ gap: 0.25rem !important;
+}
+
+.gap-2 {
+ gap: 0.5rem !important;
+}
+
+.gap-3 {
+ gap: 1rem !important;
+}
+
+.gap-4 {
+ gap: 1.5rem !important;
+}
+
+.gap-5 {
+ gap: 3rem !important;
+}
+
.font-monospace {
font-family: var(--bs-font-monospace) !important;
}
@@ -7621,6 +8233,10 @@ textarea.form-control-lg {
font-weight: 700 !important;
}
+.fw-semibold {
+ font-weight: 600 !important;
+}
+
.fw-bolder {
font-weight: bolder !important;
}
@@ -7749,7 +8365,7 @@ textarea.form-control-lg {
.text-muted {
--bs-text-opacity: 1;
- color: #6c757d !important;
+ color: rgba(var(--bs-body-color-rgb), 0.75) !important;
}
.text-black-50 {
@@ -7894,7 +8510,7 @@ textarea.form-control-lg {
}
.rounded {
- border-radius: 0.25rem !important;
+ border-radius: var(--bs-border-radius) !important;
}
.rounded-0 {
@@ -7902,15 +8518,23 @@ textarea.form-control-lg {
}
.rounded-1 {
- border-radius: 0.2rem !important;
+ border-radius: var(--bs-border-radius-sm) !important;
}
.rounded-2 {
- border-radius: 0.25rem !important;
+ border-radius: var(--bs-border-radius) !important;
}
.rounded-3 {
- border-radius: 0.3rem !important;
+ border-radius: var(--bs-border-radius-lg) !important;
+}
+
+.rounded-4 {
+ border-radius: var(--bs-border-radius-xl) !important;
+}
+
+.rounded-5 {
+ border-radius: var(--bs-border-radius-2xl) !important;
}
.rounded-circle {
@@ -7918,27 +8542,27 @@ textarea.form-control-lg {
}
.rounded-pill {
- border-radius: 50rem !important;
+ border-radius: var(--bs-border-radius-pill) !important;
}
.rounded-top {
- border-top-left-radius: 0.25rem !important;
- border-top-right-radius: 0.25rem !important;
+ border-top-left-radius: var(--bs-border-radius) !important;
+ border-top-right-radius: var(--bs-border-radius) !important;
}
.rounded-end {
- border-top-right-radius: 0.25rem !important;
- border-bottom-right-radius: 0.25rem !important;
+ border-top-right-radius: var(--bs-border-radius) !important;
+ border-bottom-right-radius: var(--bs-border-radius) !important;
}
.rounded-bottom {
- border-bottom-right-radius: 0.25rem !important;
- border-bottom-left-radius: 0.25rem !important;
+ border-bottom-right-radius: var(--bs-border-radius) !important;
+ border-bottom-left-radius: var(--bs-border-radius) !important;
}
.rounded-start {
- border-bottom-left-radius: 0.25rem !important;
- border-top-left-radius: 0.25rem !important;
+ border-bottom-left-radius: var(--bs-border-radius) !important;
+ border-top-left-radius: var(--bs-border-radius) !important;
}
.visible {
@@ -8050,30 +8674,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
- .gap-sm-0 {
- gap: 0 !important;
- }
-
- .gap-sm-1 {
- gap: 0.25rem !important;
- }
-
- .gap-sm-2 {
- gap: 0.5rem !important;
- }
-
- .gap-sm-3 {
- gap: 1rem !important;
- }
-
- .gap-sm-4 {
- gap: 1.5rem !important;
- }
-
- .gap-sm-5 {
- gap: 3rem !important;
- }
-
.justify-content-sm-start {
justify-content: flex-start !important;
}
@@ -8588,6 +9188,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+ .gap-sm-0 {
+ gap: 0 !important;
+ }
+
+ .gap-sm-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-sm-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-sm-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-sm-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-sm-5 {
+ gap: 3rem !important;
+ }
+
.text-sm-start {
text-align: left !important;
}
@@ -8701,30 +9325,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
- .gap-md-0 {
- gap: 0 !important;
- }
-
- .gap-md-1 {
- gap: 0.25rem !important;
- }
-
- .gap-md-2 {
- gap: 0.5rem !important;
- }
-
- .gap-md-3 {
- gap: 1rem !important;
- }
-
- .gap-md-4 {
- gap: 1.5rem !important;
- }
-
- .gap-md-5 {
- gap: 3rem !important;
- }
-
.justify-content-md-start {
justify-content: flex-start !important;
}
@@ -9239,6 +9839,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+ .gap-md-0 {
+ gap: 0 !important;
+ }
+
+ .gap-md-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-md-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-md-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-md-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-md-5 {
+ gap: 3rem !important;
+ }
+
.text-md-start {
text-align: left !important;
}
@@ -9352,30 +9976,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
- .gap-lg-0 {
- gap: 0 !important;
- }
-
- .gap-lg-1 {
- gap: 0.25rem !important;
- }
-
- .gap-lg-2 {
- gap: 0.5rem !important;
- }
-
- .gap-lg-3 {
- gap: 1rem !important;
- }
-
- .gap-lg-4 {
- gap: 1.5rem !important;
- }
-
- .gap-lg-5 {
- gap: 3rem !important;
- }
-
.justify-content-lg-start {
justify-content: flex-start !important;
}
@@ -9890,6 +10490,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+ .gap-lg-0 {
+ gap: 0 !important;
+ }
+
+ .gap-lg-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-lg-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-lg-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-lg-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-lg-5 {
+ gap: 3rem !important;
+ }
+
.text-lg-start {
text-align: left !important;
}
@@ -10003,30 +10627,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
- .gap-xl-0 {
- gap: 0 !important;
- }
-
- .gap-xl-1 {
- gap: 0.25rem !important;
- }
-
- .gap-xl-2 {
- gap: 0.5rem !important;
- }
-
- .gap-xl-3 {
- gap: 1rem !important;
- }
-
- .gap-xl-4 {
- gap: 1.5rem !important;
- }
-
- .gap-xl-5 {
- gap: 3rem !important;
- }
-
.justify-content-xl-start {
justify-content: flex-start !important;
}
@@ -10541,6 +11141,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+ .gap-xl-0 {
+ gap: 0 !important;
+ }
+
+ .gap-xl-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-xl-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-xl-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-xl-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-xl-5 {
+ gap: 3rem !important;
+ }
+
.text-xl-start {
text-align: left !important;
}
@@ -10654,30 +11278,6 @@ textarea.form-control-lg {
flex-wrap: wrap-reverse !important;
}
- .gap-xxl-0 {
- gap: 0 !important;
- }
-
- .gap-xxl-1 {
- gap: 0.25rem !important;
- }
-
- .gap-xxl-2 {
- gap: 0.5rem !important;
- }
-
- .gap-xxl-3 {
- gap: 1rem !important;
- }
-
- .gap-xxl-4 {
- gap: 1.5rem !important;
- }
-
- .gap-xxl-5 {
- gap: 3rem !important;
- }
-
.justify-content-xxl-start {
justify-content: flex-start !important;
}
@@ -11192,6 +11792,30 @@ textarea.form-control-lg {
padding-left: 3rem !important;
}
+ .gap-xxl-0 {
+ gap: 0 !important;
+ }
+
+ .gap-xxl-1 {
+ gap: 0.25rem !important;
+ }
+
+ .gap-xxl-2 {
+ gap: 0.5rem !important;
+ }
+
+ .gap-xxl-3 {
+ gap: 1rem !important;
+ }
+
+ .gap-xxl-4 {
+ gap: 1.5rem !important;
+ }
+
+ .gap-xxl-5 {
+ gap: 3rem !important;
+ }
+
.text-xxl-start {
text-align: left !important;
}
diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css
index c67a3b0e..bc9eb81c 100644
--- a/src/static/scripts/datatables.css
+++ b/src/static/scripts/datatables.css
@@ -4,13 +4,175 @@
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
- * https://datatables.net/download/#bs5/dt-1.11.5
+ * https://datatables.net/download/#bs5/dt-1.12.1
*
* Included libraries:
- * DataTables 1.11.5
+ * DataTables 1.12.1
*/
@charset "UTF-8";
+table.dataTable td.dt-control {
+ text-align: center;
+ cursor: pointer;
+}
+table.dataTable td.dt-control:before {
+ height: 1em;
+ width: 1em;
+ margin-top: -9px;
+ display: inline-block;
+ color: white;
+ border: 0.15em solid white;
+ border-radius: 1em;
+ box-shadow: 0 0 0.2em #444;
+ box-sizing: content-box;
+ text-align: center;
+ text-indent: 0 !important;
+ font-family: "Courier New", Courier, monospace;
+ line-height: 1em;
+ content: "+";
+ background-color: #31b131;
+}
+table.dataTable tr.dt-hasChild td.dt-control:before {
+ content: "-";
+ background-color: #d33333;
+}
+
+table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,
+table.dataTable thead > tr > td.sorting,
+table.dataTable thead > tr > td.sorting_asc,
+table.dataTable thead > tr > td.sorting_desc,
+table.dataTable thead > tr > td.sorting_asc_disabled,
+table.dataTable thead > tr > td.sorting_desc_disabled {
+ cursor: pointer;
+ position: relative;
+ padding-right: 26px;
+}
+table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,
+table.dataTable thead > tr > td.sorting:before,
+table.dataTable thead > tr > td.sorting:after,
+table.dataTable thead > tr > td.sorting_asc:before,
+table.dataTable thead > tr > td.sorting_asc:after,
+table.dataTable thead > tr > td.sorting_desc:before,
+table.dataTable thead > tr > td.sorting_desc:after,
+table.dataTable thead > tr > td.sorting_asc_disabled:before,
+table.dataTable thead > tr > td.sorting_asc_disabled:after,
+table.dataTable thead > tr > td.sorting_desc_disabled:before,
+table.dataTable thead > tr > td.sorting_desc_disabled:after {
+ position: absolute;
+ display: block;
+ opacity: 0.125;
+ right: 10px;
+ line-height: 9px;
+ font-size: 0.9em;
+}
+table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,
+table.dataTable thead > tr > td.sorting:before,
+table.dataTable thead > tr > td.sorting_asc:before,
+table.dataTable thead > tr > td.sorting_desc:before,
+table.dataTable thead > tr > td.sorting_asc_disabled:before,
+table.dataTable thead > tr > td.sorting_desc_disabled:before {
+ bottom: 50%;
+ content: "â–´";
+}
+table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,
+table.dataTable thead > tr > td.sorting:after,
+table.dataTable thead > tr > td.sorting_asc:after,
+table.dataTable thead > tr > td.sorting_desc:after,
+table.dataTable thead > tr > td.sorting_asc_disabled:after,
+table.dataTable thead > tr > td.sorting_desc_disabled:after {
+ top: 50%;
+ content: "â–¾";
+}
+table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,
+table.dataTable thead > tr > td.sorting_asc:before,
+table.dataTable thead > tr > td.sorting_desc:after {
+ opacity: 0.6;
+}
+table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,
+table.dataTable thead > tr > td.sorting_desc_disabled:after,
+table.dataTable thead > tr > td.sorting_asc_disabled:before {
+ display: none;
+}
+table.dataTable thead > tr > th:active,
+table.dataTable thead > tr > td:active {
+ outline: none;
+}
+
+div.dataTables_scrollBody table.dataTable thead > tr > th:before, div.dataTables_scrollBody table.dataTable thead > tr > th:after,
+div.dataTables_scrollBody table.dataTable thead > tr > td:before,
+div.dataTables_scrollBody table.dataTable thead > tr > td:after {
+ display: none;
+}
+
+div.dataTables_processing {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 200px;
+ margin-left: -100px;
+ margin-top: -26px;
+ text-align: center;
+ padding: 2px;
+}
+div.dataTables_processing > div:last-child {
+ position: relative;
+ width: 80px;
+ height: 15px;
+ margin: 1em auto;
+}
+div.dataTables_processing > div:last-child > div {
+ position: absolute;
+ top: 0;
+ width: 13px;
+ height: 13px;
+ border-radius: 50%;
+ background: rgba(13, 110, 253, 0.9);
+ animation-timing-function: cubic-bezier(0, 1, 1, 0);
+}
+div.dataTables_processing > div:last-child > div:nth-child(1) {
+ left: 8px;
+ animation: datatables-loader-1 0.6s infinite;
+}
+div.dataTables_processing > div:last-child > div:nth-child(2) {
+ left: 8px;
+ animation: datatables-loader-2 0.6s infinite;
+}
+div.dataTables_processing > div:last-child > div:nth-child(3) {
+ left: 32px;
+ animation: datatables-loader-2 0.6s infinite;
+}
+div.dataTables_processing > div:last-child > div:nth-child(4) {
+ left: 56px;
+ animation: datatables-loader-3 0.6s infinite;
+}
+
+@keyframes datatables-loader-1 {
+ 0% {
+ transform: scale(0);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+@keyframes datatables-loader-3 {
+ 0% {
+ transform: scale(1);
+ }
+ 100% {
+ transform: scale(0);
+ }
+}
+@keyframes datatables-loader-2 {
+ 0% {
+ transform: translate(0, 0);
+ }
+ 100% {
+ transform: translate(24px, 0);
+ }
+}
+table.dataTable.nowrap th, table.dataTable.nowrap td {
+ white-space: nowrap;
+}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
@@ -32,6 +194,12 @@ table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
+table.dataTable thead th,
+table.dataTable thead td,
+table.dataTable tfoot th,
+table.dataTable tfoot td {
+ text-align: left;
+}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
@@ -82,31 +250,6 @@ table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap;
}
-table.dataTable td.dt-control {
- text-align: center;
- cursor: pointer;
-}
-table.dataTable td.dt-control:before {
- height: 1em;
- width: 1em;
- margin-top: -9px;
- display: inline-block;
- color: white;
- border: 0.15em solid white;
- border-radius: 1em;
- box-shadow: 0 0 0.2em #444;
- box-sizing: content-box;
- text-align: center;
- text-indent: 0 !important;
- font-family: "Courier New", Courier, monospace;
- line-height: 1em;
- content: "+";
- background-color: #31b131;
-}
-table.dataTable tr.dt-hasChild td.dt-control:before {
- content: "-";
- background-color: #d33333;
-}
/*! Bootstrap 5 integration for DataTables
*
@@ -134,6 +277,28 @@ table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
+table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
+ box-shadow: none;
+}
+table.dataTable > tbody > tr {
+ background-color: transparent;
+}
+table.dataTable > tbody > tr.selected > * {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);
+ color: white;
+}
+table.dataTable.table-striped > tbody > tr.odd > * {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05);
+}
+table.dataTable.table-striped > tbody > tr.odd.selected > * {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
+}
+table.dataTable.table-hover > tbody > tr:hover > * {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075);
+}
+table.dataTable.table-hover > tbody > tr.selected:hover > * {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
+}
div.dataTables_wrapper div.dataTables_length label {
font-weight: normal;
@@ -170,71 +335,6 @@ div.dataTables_wrapper div.dataTables_paginate ul.pagination {
white-space: nowrap;
justify-content: flex-end;
}
-div.dataTables_wrapper div.dataTables_processing {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 200px;
- margin-left: -100px;
- margin-top: -26px;
- text-align: center;
- padding: 1em 0;
-}
-
-table.dataTable > thead > tr > th:active,
-table.dataTable > thead > tr > td:active {
- outline: none;
-}
-table.dataTable > thead > tr > th:not(.sorting_disabled),
-table.dataTable > thead > tr > td:not(.sorting_disabled) {
- padding-right: 30px;
-}
-table.dataTable > thead .sorting,
-table.dataTable > thead .sorting_asc,
-table.dataTable > thead .sorting_desc,
-table.dataTable > thead .sorting_asc_disabled,
-table.dataTable > thead .sorting_desc_disabled {
- cursor: pointer;
- position: relative;
-}
-table.dataTable > thead .sorting:before, table.dataTable > thead .sorting:after,
-table.dataTable > thead .sorting_asc:before,
-table.dataTable > thead .sorting_asc:after,
-table.dataTable > thead .sorting_desc:before,
-table.dataTable > thead .sorting_desc:after,
-table.dataTable > thead .sorting_asc_disabled:before,
-table.dataTable > thead .sorting_asc_disabled:after,
-table.dataTable > thead .sorting_desc_disabled:before,
-table.dataTable > thead .sorting_desc_disabled:after {
- position: absolute;
- bottom: 0.5em;
- display: block;
- opacity: 0.3;
-}
-table.dataTable > thead .sorting:before,
-table.dataTable > thead .sorting_asc:before,
-table.dataTable > thead .sorting_desc:before,
-table.dataTable > thead .sorting_asc_disabled:before,
-table.dataTable > thead .sorting_desc_disabled:before {
- right: 1em;
- content: "↑";
-}
-table.dataTable > thead .sorting:after,
-table.dataTable > thead .sorting_asc:after,
-table.dataTable > thead .sorting_desc:after,
-table.dataTable > thead .sorting_asc_disabled:after,
-table.dataTable > thead .sorting_desc_disabled:after {
- right: 0.5em;
- content: "↓";
-}
-table.dataTable > thead .sorting_asc:before,
-table.dataTable > thead .sorting_desc:after {
- opacity: 1;
-}
-table.dataTable > thead .sorting_asc_disabled:before,
-table.dataTable > thead .sorting_desc_disabled:after {
- opacity: 0;
-}
div.dataTables_scrollHead table.dataTable {
margin-bottom: 0 !important;
@@ -280,17 +380,6 @@ div.dataTables_wrapper div.dataTables_paginate {
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
padding-right: 20px;
}
-table.dataTable.table-sm .sorting:before,
-table.dataTable.table-sm .sorting_asc:before,
-table.dataTable.table-sm .sorting_desc:before {
- top: 5px;
- right: 0.85em;
-}
-table.dataTable.table-sm .sorting:after,
-table.dataTable.table-sm .sorting_asc:after,
-table.dataTable.table-sm .sorting_desc:after {
- top: 5px;
-}
table.table-bordered.dataTable {
border-right-width: 0;
@@ -332,11 +421,4 @@ div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-
padding-right: 0;
}
-table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) {
- --bs-table-accent-bg: transparent;
-}
-table.dataTable.table-striped > tbody > tr.odd {
- --bs-table-accent-bg: var(--bs-table-striped-bg);
-}
-
diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js
index f4b6fd95..9b1448ad 100644
--- a/src/static/scripts/datatables.js
+++ b/src/static/scripts/datatables.js
@@ -4,24 +4,23 @@
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
- * https://datatables.net/download/#bs5/dt-1.11.5
+ * https://datatables.net/download/#bs5/dt-1.12.1
*
* Included libraries:
- * DataTables 1.11.5
+ * DataTables 1.12.1
*/
-/*! DataTables 1.11.5
- * ©2008-2021 SpryMedia Ltd - datatables.net/license
+/*! DataTables 1.12.1
+ * ©2008-2022 SpryMedia Ltd - datatables.net/license
*/
/**
* @summary DataTables
* @description Paginate, search and order HTML tables
- * @version 1.11.5
- * @file jquery.dataTables.js
+ * @version 1.12.1
* @author SpryMedia Ltd
* @contact www.datatables.net
- * @copyright Copyright 2008-2021 SpryMedia Ltd.
+ * @copyright SpryMedia Ltd.
*
* This source file is free software, available under the following license:
* MIT license - http://datatables.net/license
@@ -1077,7 +1076,7 @@
success: function ( json ) {
_fnCamelToHungarian( defaults.oLanguage, json );
_fnLanguageCompat( json );
- $.extend( true, oLanguage, json );
+ $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
_fnInitialise( oSettings );
@@ -2310,9 +2309,17 @@
th.addClass( oOptions.sClass );
}
+ var origClass = oCol.sClass;
+
$.extend( oCol, oOptions );
_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
+ // Merge class from previously defined classes with this one, rather than just
+ // overwriting it in the extend above
+ if (origClass !== oCol.sClass) {
+ oCol.sClass = origClass + ' ' + oCol.sClass;
+ }
+
/* iDataSort to be applied (backwards compatibility), but aDataSort will take
* priority if defined
*/
@@ -2585,9 +2592,11 @@
def = aoColDefs[i];
/* Each definition can target multiple columns, as it is an array */
- var aTargets = def.targets !== undefined ?
- def.targets :
- def.aTargets;
+ var aTargets = def.target !== undefined
+ ? def.target
+ : def.targets !== undefined
+ ? def.targets
+ : def.aTargets;
if ( ! Array.isArray( aTargets ) )
{
@@ -5089,6 +5098,7 @@
'class': settings.oClasses.sProcessing
} )
.html( settings.oLanguage.sProcessing )
+ .append('<div><div></div><div></div><div></div><div></div></div>')
.insertBefore( settings.nTable )[0];
}
@@ -5338,6 +5348,7 @@
footerCopy = footer.clone().prependTo( table );
footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
footerSrcEls = footerCopy.find('tr');
+ footerCopy.find('[id]').removeAttr('id');
}
// Clone the current header and footer elements and then place it into the inner table
@@ -5345,6 +5356,7 @@
headerTrgEls = header.find('tr'); // original header is in its own table
headerSrcEls = headerCopy.find('tr');
headerCopy.find('th, td').removeAttr('tabindex');
+ headerCopy.find('[id]').removeAttr('id');
/*
@@ -6471,6 +6483,17 @@
// Store the saved state so it might be accessed at any time
settings.oLoadedState = $.extend( true, {}, s );
+ // Page Length
+ if ( s.length !== undefined ) {
+ // If already initialised just set the value directly so that the select element is also updated
+ if (api) {
+ api.page.len(s.length)
+ }
+ else {
+ settings._iDisplayLength = s.length;
+ }
+ }
+
// Restore key features - todo - for 1.11 this needs to be done by
// subscribed events
if ( s.start !== undefined ) {
@@ -6479,13 +6502,9 @@
settings.iInitDisplayStart = s.start;
}
else {
- _fnPageChange(settings, s.start/s.length);
-
+ _fnPageChange(settings, s.start/settings._iDisplayLength);
}
}
- if ( s.length !== undefined ) {
- settings._iDisplayLength = s.length;
- }
// Order
if ( s.order !== undefined ) {
@@ -7218,8 +7237,10 @@
pluck: function ( prop )
{
+ let fn = DataTable.util.get(prop);
+
return this.map( function ( el ) {
- return el[ prop ];
+ return fn(el);
} );
},
@@ -8448,7 +8469,7 @@
var api = new _Api( settings );
var namespace = '.dt.DT_details';
var drawEvent = 'draw'+namespace;
- var colvisEvent = 'column-visibility'+namespace;
+ var colvisEvent = 'column-sizing'+namespace;
var destroyEvent = 'destroy'+namespace;
var data = settings.aoData;
@@ -9500,7 +9521,6 @@
remove = remove || false;
return this.iterator( 'table', function ( settings ) {
- var orig = settings.nTableWrapper.parentNode;
var classes = settings.oClasses;
var table = settings.nTable;
var tbody = settings.nTBody;
@@ -9555,6 +9575,8 @@
jqTbody.children().detach();
jqTbody.append( rows );
+ var orig = settings.nTableWrapper.parentNode;
+
// Remove the DataTables generated nodes, events and classes
var removedMethod = remove ? 'remove' : 'detach';
jqTable[ removedMethod ]();
@@ -9648,7 +9670,7 @@
* @type string
* @default Version number
*/
- DataTable.version = "1.11.5";
+ DataTable.version = "1.12.1";
/**
* Private data store, containing all of the settings objects that are
@@ -9719,5632 +9741,5854 @@
"return": false
};
-
-
-
+
+
+
+ /**
+ * Template object for the way in which DataTables holds information about
+ * each individual row. This is the object format used for the settings
+ * aoData array.
+ * @namespace
+ */
+ DataTable.models.oRow = {
/**
- * Template object for the way in which DataTables holds information about
- * each individual row. This is the object format used for the settings
- * aoData array.
- * @namespace
+ * TR element for the row
+ * @type node
+ * @default null
*/
- DataTable.models.oRow = {
- /**
- * TR element for the row
- * @type node
- * @default null
- */
- "nTr": null,
-
- /**
- * Array of TD elements for each row. This is null until the row has been
- * created.
- * @type array nodes
- * @default []
- */
- "anCells": null,
-
- /**
- * Data object from the original data source for the row. This is either
- * an array if using the traditional form of DataTables, or an object if
- * using mData options. The exact type will depend on the passed in
- * data from the data source, or will be an array if using DOM a data
- * source.
- * @type array|object
- * @default []
- */
- "_aData": [],
-
- /**
- * Sorting data cache - this array is ostensibly the same length as the
- * number of columns (although each index is generated only as it is
- * needed), and holds the data that is used for sorting each column in the
- * row. We do this cache generation at the start of the sort in order that
- * the formatting of the sort data need be done only once for each cell
- * per sort. This array should not be read from or written to by anything
- * other than the master sorting methods.
- * @type array
- * @default null
- * @private
- */
- "_aSortData": null,
-
- /**
- * Per cell filtering data cache. As per the sort data cache, used to
- * increase the performance of the filtering in DataTables
- * @type array
- * @default null
- * @private
- */
- "_aFilterData": null,
-
- /**
- * Filtering data cache. This is the same as the cell filtering cache, but
- * in this case a string rather than an array. This is easily computed with
- * a join on `_aFilterData`, but is provided as a cache so the join isn't
- * needed on every search (memory traded for performance)
- * @type array
- * @default null
- * @private
- */
- "_sFilterRow": null,
-
- /**
- * Cache of the class name that DataTables has applied to the row, so we
- * can quickly look at this variable rather than needing to do a DOM check
- * on className for the nTr property.
- * @type string
- * @default <i>Empty string</i>
- * @private
- */
- "_sRowStripe": "",
-
- /**
- * Denote if the original data source was from the DOM, or the data source
- * object. This is used for invalidating data, so DataTables can
- * automatically read data from the original source, unless uninstructed
- * otherwise.
- * @type string
- * @default null
- * @private
- */
- "src": null,
-
- /**
- * Index in the aoData array. This saves an indexOf lookup when we have the
- * object, but want to know the index
- * @type integer
- * @default -1
- * @private
- */
- "idx": -1
- };
-
-
+ "nTr": null,
+
+ /**
+ * Array of TD elements for each row. This is null until the row has been
+ * created.
+ * @type array nodes
+ * @default []
+ */
+ "anCells": null,
+
+ /**
+ * Data object from the original data source for the row. This is either
+ * an array if using the traditional form of DataTables, or an object if
+ * using mData options. The exact type will depend on the passed in
+ * data from the data source, or will be an array if using DOM a data
+ * source.
+ * @type array|object
+ * @default []
+ */
+ "_aData": [],
+
+ /**
+ * Sorting data cache - this array is ostensibly the same length as the
+ * number of columns (although each index is generated only as it is
+ * needed), and holds the data that is used for sorting each column in the
+ * row. We do this cache generation at the start of the sort in order that
+ * the formatting of the sort data need be done only once for each cell
+ * per sort. This array should not be read from or written to by anything
+ * other than the master sorting methods.
+ * @type array
+ * @default null
+ * @private
+ */
+ "_aSortData": null,
+
+ /**
+ * Per cell filtering data cache. As per the sort data cache, used to
+ * increase the performance of the filtering in DataTables
+ * @type array
+ * @default null
+ * @private
+ */
+ "_aFilterData": null,
+
+ /**
+ * Filtering data cache. This is the same as the cell filtering cache, but
+ * in this case a string rather than an array. This is easily computed with
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
+ * needed on every search (memory traded for performance)
+ * @type array
+ * @default null
+ * @private
+ */
+ "_sFilterRow": null,
+
+ /**
+ * Cache of the class name that DataTables has applied to the row, so we
+ * can quickly look at this variable rather than needing to do a DOM check
+ * on className for the nTr property.
+ * @type string
+ * @default <i>Empty string</i>
+ * @private
+ */
+ "_sRowStripe": "",
+
+ /**
+ * Denote if the original data source was from the DOM, or the data source
+ * object. This is used for invalidating data, so DataTables can
+ * automatically read data from the original source, unless uninstructed
+ * otherwise.
+ * @type string
+ * @default null
+ * @private
+ */
+ "src": null,
+
+ /**
+ * Index in the aoData array. This saves an indexOf lookup when we have the
+ * object, but want to know the index
+ * @type integer
+ * @default -1
+ * @private
+ */
+ "idx": -1
+ };
+
+
+ /**
+ * Template object for the column information object in DataTables. This object
+ * is held in the settings aoColumns array and contains all the information that
+ * DataTables needs about each individual column.
+ *
+ * Note that this object is related to {@link DataTable.defaults.column}
+ * but this one is the internal data store for DataTables's cache of columns.
+ * It should NOT be manipulated outside of DataTables. Any configuration should
+ * be done through the initialisation options.
+ * @namespace
+ */
+ DataTable.models.oColumn = {
+ /**
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
+ * is faster to just hold it as a variable
+ * @type integer
+ * @default null
+ */
+ "idx": null,
+
+ /**
+ * A list of the columns that sorting should occur on when this column
+ * is sorted. That this property is an array allows multi-column sorting
+ * to be defined for a column (for example first name / last name columns
+ * would benefit from this). The values are integers pointing to the
+ * columns to be sorted on (typically it will be a single integer pointing
+ * at itself, but that doesn't need to be the case).
+ * @type array
+ */
+ "aDataSort": null,
+
+ /**
+ * Define the sorting directions that are applied to the column, in sequence
+ * as the column is repeatedly sorted upon - i.e. the first value is used
+ * as the sorting direction when the column if first sorted (clicked on).
+ * Sort it again (click again) and it will move on to the next index.
+ * Repeat until loop.
+ * @type array
+ */
+ "asSorting": null,
+
+ /**
+ * Flag to indicate if the column is searchable, and thus should be included
+ * in the filtering or not.
+ * @type boolean
+ */
+ "bSearchable": null,
+
/**
- * Template object for the column information object in DataTables. This object
- * is held in the settings aoColumns array and contains all the information that
- * DataTables needs about each individual column.
+ * Flag to indicate if the column is sortable or not.
+ * @type boolean
+ */
+ "bSortable": null,
+
+ /**
+ * Flag to indicate if the column is currently visible in the table or not
+ * @type boolean
+ */
+ "bVisible": null,
+
+ /**
+ * Store for manual type assignment using the `column.type` option. This
+ * is held in store so we can manipulate the column's `sType` property.
+ * @type string
+ * @default null
+ * @private
+ */
+ "_sManualType": null,
+
+ /**
+ * Flag to indicate if HTML5 data attributes should be used as the data
+ * source for filtering or sorting. True is either are.
+ * @type boolean
+ * @default false
+ * @private
+ */
+ "_bAttrSrc": false,
+
+ /**
+ * Developer definable function that is called whenever a cell is created (Ajax source,
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
+ * allowing you to modify the DOM element (add background colour for example) when the
+ * element is available.
+ * @type function
+ * @param {element} nTd The TD node that has been created
+ * @param {*} sData The Data for the cell
+ * @param {array|object} oData The data for the whole row
+ * @param {int} iRow The row index for the aoData data store
+ * @default null
+ */
+ "fnCreatedCell": null,
+
+ /**
+ * Function to get data from a cell in a column. You should <b>never</b>
+ * access data directly through _aData internally in DataTables - always use
+ * the method attached to this property. It allows mData to function as
+ * required. This function is automatically assigned by the column
+ * initialisation method
+ * @type function
+ * @param {array|object} oData The data array/object for the array
+ * (i.e. aoData[]._aData)
+ * @param {string} sSpecific The specific data type you want to get -
+ * 'display', 'type' 'filter' 'sort'
+ * @returns {*} The data for the cell from the given row's data
+ * @default null
+ */
+ "fnGetData": null,
+
+ /**
+ * Function to set data for a cell in the column. You should <b>never</b>
+ * set the data directly to _aData internally in DataTables - always use
+ * this method. It allows mData to function as required. This function
+ * is automatically assigned by the column initialisation method
+ * @type function
+ * @param {array|object} oData The data array/object for the array
+ * (i.e. aoData[]._aData)
+ * @param {*} sValue Value to set
+ * @default null
+ */
+ "fnSetData": null,
+
+ /**
+ * Property to read the value for the cells in the column from the data
+ * source array / object. If null, then the default content is used, if a
+ * function is given then the return from the function is used.
+ * @type function|int|string|null
+ * @default null
+ */
+ "mData": null,
+
+ /**
+ * Partner property to mData which is used (only when defined) to get
+ * the data - i.e. it is basically the same as mData, but without the
+ * 'set' option, and also the data fed to it is the result from mData.
+ * This is the rendering method to match the data method of mData.
+ * @type function|int|string|null
+ * @default null
+ */
+ "mRender": null,
+
+ /**
+ * Unique header TH/TD element for this column - this is what the sorting
+ * listener is attached to (if sorting is enabled.)
+ * @type node
+ * @default null
+ */
+ "nTh": null,
+
+ /**
+ * Unique footer TH/TD element for this column (if there is one). Not used
+ * in DataTables as such, but can be used for plug-ins to reference the
+ * footer for each column.
+ * @type node
+ * @default null
+ */
+ "nTf": null,
+
+ /**
+ * The class to apply to all TD elements in the table's TBODY for the column
+ * @type string
+ * @default null
+ */
+ "sClass": null,
+
+ /**
+ * When DataTables calculates the column widths to assign to each column,
+ * it finds the longest string in each column and then constructs a
+ * temporary table and reads the widths from that. The problem with this
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * string - thus the calculation can go wrong (doing it properly and putting
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
+ * a "work around" we provide this option. It will append its value to the
+ * text that is found to be the longest string for the column - i.e. padding.
+ * @type string
+ */
+ "sContentPadding": null,
+
+ /**
+ * Allows a default value to be given for a column's data, and will be used
+ * whenever a null data source is encountered (this can be because mData
+ * is set to null, or because the data source itself is null).
+ * @type string
+ * @default null
+ */
+ "sDefaultContent": null,
+
+ /**
+ * Name for the column, allowing reference to the column by name as well as
+ * by index (needs a lookup to work by name).
+ * @type string
+ */
+ "sName": null,
+
+ /**
+ * Custom sorting data type - defines which of the available plug-ins in
+ * afnSortData the custom sorting will use - if any is defined.
+ * @type string
+ * @default std
+ */
+ "sSortDataType": 'std',
+
+ /**
+ * Class to be applied to the header element when sorting on this column
+ * @type string
+ * @default null
+ */
+ "sSortingClass": null,
+
+ /**
+ * Class to be applied to the header element when sorting on this column -
+ * when jQuery UI theming is used.
+ * @type string
+ * @default null
+ */
+ "sSortingClassJUI": null,
+
+ /**
+ * Title of the column - what is seen in the TH element (nTh).
+ * @type string
+ */
+ "sTitle": null,
+
+ /**
+ * Column sorting and filtering type
+ * @type string
+ * @default null
+ */
+ "sType": null,
+
+ /**
+ * Width of the column
+ * @type string
+ * @default null
+ */
+ "sWidth": null,
+
+ /**
+ * Width of the column when it was first "encountered"
+ * @type string
+ * @default null
+ */
+ "sWidthOrig": null
+ };
+
+
+ /*
+ * Developer note: The properties of the object below are given in Hungarian
+ * notation, that was used as the interface for DataTables prior to v1.10, however
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
+ * breaking backwards compatibility utterly with this change, the Hungarian
+ * version is still, internally the primary interface, but is is not documented
+ * - hence the @name tags in each doc comment. This allows a Javascript function
+ * to create a map from Hungarian notation to camel case (going the other direction
+ * would require each property to be listed, which would add around 3K to the size
+ * of DataTables, while this method is about a 0.5K hit).
+ *
+ * Ultimately this does pave the way for Hungarian notation to be dropped
+ * completely, but that is a massive amount of work and will break current
+ * installs (therefore is on-hold until v2).
+ */
+
+ /**
+ * Initialisation options that can be given to DataTables at initialisation
+ * time.
+ * @namespace
+ */
+ DataTable.defaults = {
+ /**
+ * An array of data to use for the table, passed in at initialisation which
+ * will be used in preference to any data which is already in the DOM. This is
+ * particularly useful for constructing tables purely in Javascript, for
+ * example with a custom Ajax call.
+ * @type array
+ * @default null
*
- * Note that this object is related to {@link DataTable.defaults.column}
- * but this one is the internal data store for DataTables's cache of columns.
- * It should NOT be manipulated outside of DataTables. Any configuration should
- * be done through the initialisation options.
- * @namespace
+ * @dtopt Option
+ * @name DataTable.defaults.data
+ *
+ * @example
+ * // Using a 2D array data source
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "data": [
+ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
+ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
+ * ],
+ * "columns": [
+ * { "title": "Engine" },
+ * { "title": "Browser" },
+ * { "title": "Platform" },
+ * { "title": "Version" },
+ * { "title": "Grade" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using an array of objects as a data source (`data`)
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "data": [
+ * {
+ * "engine": "Trident",
+ * "browser": "Internet Explorer 4.0",
+ * "platform": "Win 95+",
+ * "version": 4,
+ * "grade": "X"
+ * },
+ * {
+ * "engine": "Trident",
+ * "browser": "Internet Explorer 5.0",
+ * "platform": "Win 95+",
+ * "version": 5,
+ * "grade": "C"
+ * }
+ * ],
+ * "columns": [
+ * { "title": "Engine", "data": "engine" },
+ * { "title": "Browser", "data": "browser" },
+ * { "title": "Platform", "data": "platform" },
+ * { "title": "Version", "data": "version" },
+ * { "title": "Grade", "data": "grade" }
+ * ]
+ * } );
+ * } );
*/
- DataTable.models.oColumn = {
- /**
- * Column index. This could be worked out on-the-fly with $.inArray, but it
- * is faster to just hold it as a variable
- * @type integer
- * @default null
- */
- "idx": null,
-
- /**
- * A list of the columns that sorting should occur on when this column
- * is sorted. That this property is an array allows multi-column sorting
- * to be defined for a column (for example first name / last name columns
- * would benefit from this). The values are integers pointing to the
- * columns to be sorted on (typically it will be a single integer pointing
- * at itself, but that doesn't need to be the case).
- * @type array
- */
- "aDataSort": null,
-
- /**
- * Define the sorting directions that are applied to the column, in sequence
- * as the column is repeatedly sorted upon - i.e. the first value is used
- * as the sorting direction when the column if first sorted (clicked on).
- * Sort it again (click again) and it will move on to the next index.
- * Repeat until loop.
- * @type array
- */
- "asSorting": null,
-
- /**
- * Flag to indicate if the column is searchable, and thus should be included
- * in the filtering or not.
- * @type boolean
- */
- "bSearchable": null,
-
- /**
- * Flag to indicate if the column is sortable or not.
- * @type boolean
- */
- "bSortable": null,
-
- /**
- * Flag to indicate if the column is currently visible in the table or not
- * @type boolean
- */
- "bVisible": null,
-
- /**
- * Store for manual type assignment using the `column.type` option. This
- * is held in store so we can manipulate the column's `sType` property.
- * @type string
- * @default null
- * @private
- */
- "_sManualType": null,
-
- /**
- * Flag to indicate if HTML5 data attributes should be used as the data
- * source for filtering or sorting. True is either are.
- * @type boolean
- * @default false
- * @private
- */
- "_bAttrSrc": false,
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} nTd The TD node that has been created
- * @param {*} sData The Data for the cell
- * @param {array|object} oData The data for the whole row
- * @param {int} iRow The row index for the aoData data store
- * @default null
- */
- "fnCreatedCell": null,
-
- /**
- * Function to get data from a cell in a column. You should <b>never</b>
- * access data directly through _aData internally in DataTables - always use
- * the method attached to this property. It allows mData to function as
- * required. This function is automatically assigned by the column
- * initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {string} sSpecific The specific data type you want to get -
- * 'display', 'type' 'filter' 'sort'
- * @returns {*} The data for the cell from the given row's data
- * @default null
- */
- "fnGetData": null,
-
- /**
- * Function to set data for a cell in the column. You should <b>never</b>
- * set the data directly to _aData internally in DataTables - always use
- * this method. It allows mData to function as required. This function
- * is automatically assigned by the column initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {*} sValue Value to set
- * @default null
- */
- "fnSetData": null,
-
- /**
- * Property to read the value for the cells in the column from the data
- * source array / object. If null, then the default content is used, if a
- * function is given then the return from the function is used.
- * @type function|int|string|null
- * @default null
- */
- "mData": null,
-
- /**
- * Partner property to mData which is used (only when defined) to get
- * the data - i.e. it is basically the same as mData, but without the
- * 'set' option, and also the data fed to it is the result from mData.
- * This is the rendering method to match the data method of mData.
- * @type function|int|string|null
- * @default null
- */
- "mRender": null,
-
- /**
- * Unique header TH/TD element for this column - this is what the sorting
- * listener is attached to (if sorting is enabled.)
- * @type node
- * @default null
- */
- "nTh": null,
-
- /**
- * Unique footer TH/TD element for this column (if there is one). Not used
- * in DataTables as such, but can be used for plug-ins to reference the
- * footer for each column.
- * @type node
- * @default null
- */
- "nTf": null,
-
- /**
- * The class to apply to all TD elements in the table's TBODY for the column
- * @type string
- * @default null
- */
- "sClass": null,
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * @type string
- */
- "sContentPadding": null,
-
- /**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because mData
- * is set to null, or because the data source itself is null).
- * @type string
- * @default null
- */
- "sDefaultContent": null,
-
- /**
- * Name for the column, allowing reference to the column by name as well as
- * by index (needs a lookup to work by name).
- * @type string
- */
- "sName": null,
-
- /**
- * Custom sorting data type - defines which of the available plug-ins in
- * afnSortData the custom sorting will use - if any is defined.
- * @type string
- * @default std
- */
- "sSortDataType": 'std',
-
- /**
- * Class to be applied to the header element when sorting on this column
- * @type string
- * @default null
- */
- "sSortingClass": null,
-
- /**
- * Class to be applied to the header element when sorting on this column -
- * when jQuery UI theming is used.
- * @type string
- * @default null
- */
- "sSortingClassJUI": null,
-
- /**
- * Title of the column - what is seen in the TH element (nTh).
- * @type string
- */
- "sTitle": null,
-
- /**
- * Column sorting and filtering type
- * @type string
- * @default null
- */
- "sType": null,
-
- /**
- * Width of the column
- * @type string
- * @default null
- */
- "sWidth": null,
-
- /**
- * Width of the column when it was first "encountered"
- * @type string
- * @default null
- */
- "sWidthOrig": null
- };
-
-
- /*
- * Developer note: The properties of the object below are given in Hungarian
- * notation, that was used as the interface for DataTables prior to v1.10, however
- * from v1.10 onwards the primary interface is camel case. In order to avoid
- * breaking backwards compatibility utterly with this change, the Hungarian
- * version is still, internally the primary interface, but is is not documented
- * - hence the @name tags in each doc comment. This allows a Javascript function
- * to create a map from Hungarian notation to camel case (going the other direction
- * would require each property to be listed, which would add around 3K to the size
- * of DataTables, while this method is about a 0.5K hit).
- *
- * Ultimately this does pave the way for Hungarian notation to be dropped
- * completely, but that is a massive amount of work and will break current
- * installs (therefore is on-hold until v2).
+ "aaData": null,
+
+
+ /**
+ * If ordering is enabled, then DataTables will perform a first pass sort on
+ * initialisation. You can define which column(s) the sort is performed
+ * upon, and the sorting direction, with this variable. The `sorting` array
+ * should contain an array for each column to be sorted initially containing
+ * the column's index and a direction string ('asc' or 'desc').
+ * @type array
+ * @default [[0,'asc']]
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.order
+ *
+ * @example
+ * // Sort by 3rd column first, and then 4th column
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "order": [[2,'asc'], [3,'desc']]
+ * } );
+ * } );
+ *
+ * // No initial sorting
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "order": []
+ * } );
+ * } );
*/
-
+ "aaSorting": [[0,'asc']],
+
+
/**
- * Initialisation options that can be given to DataTables at initialisation
+ * This parameter is basically identical to the `sorting` parameter, but
+ * cannot be overridden by user interaction with the table. What this means
+ * is that you could have a column (visible or hidden) which the sorting
+ * will always be forced on first - any sorting after that (from the user)
+ * will then be performed as required. This can be useful for grouping rows
+ * together.
+ * @type array
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.orderFixed
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "orderFixed": [[0,'asc']]
+ * } );
+ * } )
+ */
+ "aaSortingFixed": [],
+
+
+ /**
+ * DataTables can be instructed to load data to display in the table from a
+ * Ajax source. This option defines how that Ajax call is made and where to.
+ *
+ * The `ajax` property has three different modes of operation, depending on
+ * how it is defined. These are:
+ *
+ * * `string` - Set the URL from where the data should be loaded from.
+ * * `object` - Define properties for `jQuery.ajax`.
+ * * `function` - Custom data get function
+ *
+ * `string`
+ * --------
+ *
+ * As a string, the `ajax` property simply defines the URL from which
+ * DataTables will load data.
+ *
+ * `object`
+ * --------
+ *
+ * As an object, the parameters in the object are passed to
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
+ * of the Ajax request. DataTables has a number of default parameters which
+ * you can override using this option. Please refer to the jQuery
+ * documentation for a full description of the options available, although
+ * the following parameters provide additional options in DataTables or
+ * require special consideration:
+ *
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
+ * can also be used as a function to manipulate the data DataTables sends
+ * to the server. The function takes a single parameter, an object of
+ * parameters with the values that DataTables has readied for sending. An
+ * object may be returned which will be merged into the DataTables
+ * defaults, or you can add the items to the object that was passed in and
+ * not return anything from the function. This supersedes `fnServerParams`
+ * from DataTables 1.9-.
+ *
+ * * `dataSrc` - By default DataTables will look for the property `data` (or
+ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
+ * from an Ajax source or for server-side processing - this parameter
+ * allows that property to be changed. You can use Javascript dotted
+ * object notation to get a data source for multiple levels of nesting, or
+ * it my be used as a function. As a function it takes a single parameter,
+ * the JSON returned from the server, which can be manipulated as
+ * required, with the returned value being that used by DataTables as the
+ * data source for the table. This supersedes `sAjaxDataProp` from
+ * DataTables 1.9-.
+ *
+ * * `success` - Should not be overridden it is used internally in
+ * DataTables. To manipulate / transform the data returned by the server
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
+ *
+ * `function`
+ * ----------
+ *
+ * As a function, making the Ajax call is left up to yourself allowing
+ * complete control of the Ajax request. Indeed, if desired, a method other
+ * than Ajax could be used to obtain the required data, such as Web storage
+ * or an AIR database.
+ *
+ * The function is given four parameters and no return is required. The
+ * parameters are:
+ *
+ * 1. _object_ - Data to send to the server
+ * 2. _function_ - Callback function that must be executed when the required
+ * data has been obtained. That data should be passed into the callback
+ * as the only parameter
+ * 3. _object_ - DataTables settings object for the table
+ *
+ * Note that this supersedes `fnServerData` from DataTables 1.9-.
+ *
+ * @type string|object|function
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.ajax
+ * @since 1.10.0
+ *
+ * @example
+ * // Get JSON data from a file via Ajax.
+ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
+ * $('#example').dataTable( {
+ * "ajax": "data.json"
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": "tableData"
+ * }
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
+ * // from a plain array rather than an array in an object
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": ""
+ * }
+ * } );
+ *
+ * @example
+ * // Manipulate the data returned from the server - add a link to data
+ * // (note this can, should, be done using `render` for the column - this
+ * // is just a simple example of how the data can be manipulated).
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": function ( json ) {
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
+ * }
+ * return json;
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Add data to the request
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "data": function ( d ) {
+ * return {
+ * "extra_search": $('#extra').val()
+ * };
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Send request as POST
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "type": "POST"
+ * }
+ * } );
+ *
+ * @example
+ * // Get the data from localStorage (could interface with a form for
+ * // adding, editing and removing rows).
+ * $('#example').dataTable( {
+ * "ajax": function (data, callback, settings) {
+ * callback(
+ * JSON.parse( localStorage.getItem('dataTablesData') )
+ * );
+ * }
+ * } );
+ */
+ "ajax": null,
+
+
+ /**
+ * This parameter allows you to readily specify the entries in the length drop
+ * down menu that DataTables shows when pagination is enabled. It can be
+ * either a 1D array of options which will be used for both the displayed
+ * option and the value, or a 2D array which will use the array in the first
+ * position as the value, and the array in the second position as the
+ * displayed options (useful for language strings such as 'All').
+ *
+ * Note that the `pageLength` property will be automatically set to the
+ * first value given in this array, unless `pageLength` is also provided.
+ * @type array
+ * @default [ 10, 25, 50, 100 ]
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.lengthMenu
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
+ * } );
+ * } );
+ */
+ "aLengthMenu": [ 10, 25, 50, 100 ],
+
+
+ /**
+ * The `columns` option in the initialisation parameter allows you to define
+ * details about the way individual columns behave. For a full list of
+ * column options that can be set, please see
+ * {@link DataTable.defaults.column}. Note that if you use `columns` to
+ * define your columns, you must have an entry in the array for every single
+ * column that you have in your table (these can be null if you don't which
+ * to specify any options).
+ * @member
+ *
+ * @name DataTable.defaults.column
+ */
+ "aoColumns": null,
+
+ /**
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
+ * column, multiple columns, or all columns, using the `targets` property of
+ * each object in the array. This allows great flexibility when creating
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
+ * columns you specifically want. `columnDefs` may use any of the column
+ * options available: {@link DataTable.defaults.column}, but it _must_
+ * have `targets` defined in each object in the array. Values in the `targets`
+ * array may be:
+ * <ul>
+ * <li>a string - class name will be matched on the TH for the column</li>
+ * <li>0 or a positive integer - column index counting from the left</li>
+ * <li>a negative integer - column index counting from the right</li>
+ * <li>the string "_all" - all columns (i.e. assign a default)</li>
+ * </ul>
+ * @member
+ *
+ * @name DataTable.defaults.columnDefs
+ */
+ "aoColumnDefs": null,
+
+
+ /**
+ * Basically the same as `search`, this parameter defines the individual column
+ * filtering state at initialisation time. The array must be of the same size
+ * as the number of columns, and each element be an object with the parameters
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
+ * accepted and the default will be used.
+ * @type array
+ * @default []
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.searchCols
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "searchCols": [
+ * null,
+ * { "search": "My filter" },
+ * null,
+ * { "search": "^[0-9]", "escapeRegex": false }
+ * ]
+ * } );
+ * } )
+ */
+ "aoSearchCols": [],
+
+
+ /**
+ * An array of CSS classes that should be applied to displayed rows. This
+ * array may be of any length, and DataTables will apply each class
+ * sequentially, looping when required.
+ * @type array
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
+ * options</i>
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.stripeClasses
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
+ * } );
+ * } )
+ */
+ "asStripeClasses": null,
+
+
+ /**
+ * Enable or disable automatic column width calculation. This can be disabled
+ * as an optimisation (it takes some time to calculate the widths) if the
+ * tables widths are passed in using `columns`.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.autoWidth
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "autoWidth": false
+ * } );
+ * } );
+ */
+ "bAutoWidth": true,
+
+
+ /**
+ * Deferred rendering can provide DataTables with a huge speed boost when you
+ * are using an Ajax or JS data source for the table. This option, when set to
+ * true, will cause DataTables to defer the creation of the table elements for
+ * each row until they are needed for a draw - saving a significant amount of
* time.
- * @namespace
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.deferRender
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajax": "sources/arrays.txt",
+ * "deferRender": true
+ * } );
+ * } );
*/
- DataTable.defaults = {
- /**
- * An array of data to use for the table, passed in at initialisation which
- * will be used in preference to any data which is already in the DOM. This is
- * particularly useful for constructing tables purely in Javascript, for
- * example with a custom Ajax call.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.data
- *
- * @example
- * // Using a 2D array data source
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
- * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
- * ],
- * "columns": [
- * { "title": "Engine" },
- * { "title": "Browser" },
- * { "title": "Platform" },
- * { "title": "Version" },
- * { "title": "Grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using an array of objects as a data source (`data`)
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 4.0",
- * "platform": "Win 95+",
- * "version": 4,
- * "grade": "X"
- * },
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 5.0",
- * "platform": "Win 95+",
- * "version": 5,
- * "grade": "C"
- * }
- * ],
- * "columns": [
- * { "title": "Engine", "data": "engine" },
- * { "title": "Browser", "data": "browser" },
- * { "title": "Platform", "data": "platform" },
- * { "title": "Version", "data": "version" },
- * { "title": "Grade", "data": "grade" }
- * ]
- * } );
- * } );
- */
- "aaData": null,
-
-
- /**
- * If ordering is enabled, then DataTables will perform a first pass sort on
- * initialisation. You can define which column(s) the sort is performed
- * upon, and the sorting direction, with this variable. The `sorting` array
- * should contain an array for each column to be sorted initially containing
- * the column's index and a direction string ('asc' or 'desc').
- * @type array
- * @default [[0,'asc']]
- *
- * @dtopt Option
- * @name DataTable.defaults.order
- *
- * @example
- * // Sort by 3rd column first, and then 4th column
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": [[2,'asc'], [3,'desc']]
- * } );
- * } );
- *
- * // No initial sorting
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": []
- * } );
- * } );
- */
- "aaSorting": [[0,'asc']],
-
-
- /**
- * This parameter is basically identical to the `sorting` parameter, but
- * cannot be overridden by user interaction with the table. What this means
- * is that you could have a column (visible or hidden) which the sorting
- * will always be forced on first - any sorting after that (from the user)
- * will then be performed as required. This can be useful for grouping rows
- * together.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.orderFixed
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderFixed": [[0,'asc']]
- * } );
- * } )
- */
- "aaSortingFixed": [],
-
-
- /**
- * DataTables can be instructed to load data to display in the table from a
- * Ajax source. This option defines how that Ajax call is made and where to.
- *
- * The `ajax` property has three different modes of operation, depending on
- * how it is defined. These are:
- *
- * * `string` - Set the URL from where the data should be loaded from.
- * * `object` - Define properties for `jQuery.ajax`.
- * * `function` - Custom data get function
- *
- * `string`
- * --------
- *
- * As a string, the `ajax` property simply defines the URL from which
- * DataTables will load data.
- *
- * `object`
- * --------
- *
- * As an object, the parameters in the object are passed to
- * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
- * of the Ajax request. DataTables has a number of default parameters which
- * you can override using this option. Please refer to the jQuery
- * documentation for a full description of the options available, although
- * the following parameters provide additional options in DataTables or
- * require special consideration:
- *
- * * `data` - As with jQuery, `data` can be provided as an object, but it
- * can also be used as a function to manipulate the data DataTables sends
- * to the server. The function takes a single parameter, an object of
- * parameters with the values that DataTables has readied for sending. An
- * object may be returned which will be merged into the DataTables
- * defaults, or you can add the items to the object that was passed in and
- * not return anything from the function. This supersedes `fnServerParams`
- * from DataTables 1.9-.
- *
- * * `dataSrc` - By default DataTables will look for the property `data` (or
- * `aaData` for compatibility with DataTables 1.9-) when obtaining data
- * from an Ajax source or for server-side processing - this parameter
- * allows that property to be changed. You can use Javascript dotted
- * object notation to get a data source for multiple levels of nesting, or
- * it my be used as a function. As a function it takes a single parameter,
- * the JSON returned from the server, which can be manipulated as
- * required, with the returned value being that used by DataTables as the
- * data source for the table. This supersedes `sAjaxDataProp` from
- * DataTables 1.9-.
- *
- * * `success` - Should not be overridden it is used internally in
- * DataTables. To manipulate / transform the data returned by the server
- * use `ajax.dataSrc`, or use `ajax` as a function (see below).
- *
- * `function`
- * ----------
- *
- * As a function, making the Ajax call is left up to yourself allowing
- * complete control of the Ajax request. Indeed, if desired, a method other
- * than Ajax could be used to obtain the required data, such as Web storage
- * or an AIR database.
- *
- * The function is given four parameters and no return is required. The
- * parameters are:
- *
- * 1. _object_ - Data to send to the server
- * 2. _function_ - Callback function that must be executed when the required
- * data has been obtained. That data should be passed into the callback
- * as the only parameter
- * 3. _object_ - DataTables settings object for the table
- *
- * Note that this supersedes `fnServerData` from DataTables 1.9-.
- *
- * @type string|object|function
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.ajax
- * @since 1.10.0
- *
- * @example
- * // Get JSON data from a file via Ajax.
- * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
- * $('#example').dataTable( {
- * "ajax": "data.json"
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to change
- * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": "tableData"
- * }
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to read data
- * // from a plain array rather than an array in an object
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": ""
- * }
- * } );
- *
- * @example
- * // Manipulate the data returned from the server - add a link to data
- * // (note this can, should, be done using `render` for the column - this
- * // is just a simple example of how the data can be manipulated).
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": function ( json ) {
- * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
- * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
- * }
- * return json;
- * }
- * }
- * } );
- *
- * @example
- * // Add data to the request
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "data": function ( d ) {
- * return {
- * "extra_search": $('#extra').val()
- * };
- * }
- * }
- * } );
- *
- * @example
- * // Send request as POST
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "type": "POST"
- * }
- * } );
- *
- * @example
- * // Get the data from localStorage (could interface with a form for
- * // adding, editing and removing rows).
- * $('#example').dataTable( {
- * "ajax": function (data, callback, settings) {
- * callback(
- * JSON.parse( localStorage.getItem('dataTablesData') )
- * );
- * }
- * } );
- */
- "ajax": null,
-
-
- /**
- * This parameter allows you to readily specify the entries in the length drop
- * down menu that DataTables shows when pagination is enabled. It can be
- * either a 1D array of options which will be used for both the displayed
- * option and the value, or a 2D array which will use the array in the first
- * position as the value, and the array in the second position as the
- * displayed options (useful for language strings such as 'All').
- *
- * Note that the `pageLength` property will be automatically set to the
- * first value given in this array, unless `pageLength` is also provided.
- * @type array
- * @default [ 10, 25, 50, 100 ]
- *
- * @dtopt Option
- * @name DataTable.defaults.lengthMenu
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
- * } );
- * } );
- */
- "aLengthMenu": [ 10, 25, 50, 100 ],
-
-
- /**
- * The `columns` option in the initialisation parameter allows you to define
- * details about the way individual columns behave. For a full list of
- * column options that can be set, please see
- * {@link DataTable.defaults.column}. Note that if you use `columns` to
- * define your columns, you must have an entry in the array for every single
- * column that you have in your table (these can be null if you don't which
- * to specify any options).
- * @member
- *
- * @name DataTable.defaults.column
- */
- "aoColumns": null,
-
- /**
- * Very similar to `columns`, `columnDefs` allows you to target a specific
- * column, multiple columns, or all columns, using the `targets` property of
- * each object in the array. This allows great flexibility when creating
- * tables, as the `columnDefs` arrays can be of any length, targeting the
- * columns you specifically want. `columnDefs` may use any of the column
- * options available: {@link DataTable.defaults.column}, but it _must_
- * have `targets` defined in each object in the array. Values in the `targets`
- * array may be:
- * <ul>
- * <li>a string - class name will be matched on the TH for the column</li>
- * <li>0 or a positive integer - column index counting from the left</li>
- * <li>a negative integer - column index counting from the right</li>
- * <li>the string "_all" - all columns (i.e. assign a default)</li>
- * </ul>
- * @member
- *
- * @name DataTable.defaults.columnDefs
- */
- "aoColumnDefs": null,
-
-
- /**
- * Basically the same as `search`, this parameter defines the individual column
- * filtering state at initialisation time. The array must be of the same size
- * as the number of columns, and each element be an object with the parameters
- * `search` and `escapeRegex` (the latter is optional). 'null' is also
- * accepted and the default will be used.
- * @type array
- * @default []
- *
- * @dtopt Option
- * @name DataTable.defaults.searchCols
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchCols": [
- * null,
- * { "search": "My filter" },
- * null,
- * { "search": "^[0-9]", "escapeRegex": false }
- * ]
- * } );
- * } )
- */
- "aoSearchCols": [],
-
-
- /**
- * An array of CSS classes that should be applied to displayed rows. This
- * array may be of any length, and DataTables will apply each class
- * sequentially, looping when required.
- * @type array
- * @default null <i>Will take the values determined by the `oClasses.stripe*`
- * options</i>
- *
- * @dtopt Option
- * @name DataTable.defaults.stripeClasses
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
- * } );
- * } )
- */
- "asStripeClasses": null,
-
-
- /**
- * Enable or disable automatic column width calculation. This can be disabled
- * as an optimisation (it takes some time to calculate the widths) if the
- * tables widths are passed in using `columns`.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.autoWidth
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "autoWidth": false
- * } );
- * } );
- */
- "bAutoWidth": true,
-
-
- /**
- * Deferred rendering can provide DataTables with a huge speed boost when you
- * are using an Ajax or JS data source for the table. This option, when set to
- * true, will cause DataTables to defer the creation of the table elements for
- * each row until they are needed for a draw - saving a significant amount of
- * time.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.deferRender
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajax": "sources/arrays.txt",
- * "deferRender": true
- * } );
- * } );
- */
- "bDeferRender": false,
-
-
- /**
- * Replace a DataTable which matches the given selector and replace it with
- * one which has the properties of the new initialisation object passed. If no
- * table matches the selector, then the new DataTable will be constructed as
- * per normal.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.destroy
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "srollY": "200px",
- * "paginate": false
- * } );
- *
- * // Some time later....
- * $('#example').dataTable( {
- * "filter": false,
- * "destroy": true
- * } );
- * } );
- */
- "bDestroy": false,
-
-
- /**
- * Enable or disable filtering of data. Filtering in DataTables is "smart" in
- * that it allows the end user to input multiple words (space separated) and
- * will match a row containing those words, even if not in the order that was
- * specified (this allow matching across multiple columns). Note that if you
- * wish to use filtering in DataTables this must remain 'true' - to remove the
- * default filtering input box and retain filtering abilities, please use
- * {@link DataTable.defaults.dom}.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.searching
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "searching": false
- * } );
- * } );
- */
- "bFilter": true,
-
-
- /**
- * Enable or disable the table information display. This shows information
- * about the data that is currently visible on the page, including information
- * about filtered data if that action is being performed.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.info
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "info": false
- * } );
- * } );
- */
- "bInfo": true,
-
-
- /**
- * Allows the end user to select the size of a formatted page from a select
- * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.lengthChange
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "lengthChange": false
- * } );
- * } );
- */
- "bLengthChange": true,
-
-
- /**
- * Enable or disable pagination.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.paging
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "paging": false
- * } );
- * } );
- */
- "bPaginate": true,
-
-
- /**
- * Enable or disable the display of a 'processing' indicator when the table is
- * being processed (e.g. a sort). This is particularly useful for tables with
- * large amounts of data where it can take a noticeable amount of time to sort
- * the entries.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.processing
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "processing": true
- * } );
- * } );
- */
- "bProcessing": false,
-
-
- /**
- * Retrieve the DataTables object for the given selector. Note that if the
- * table has already been initialised, this parameter will cause DataTables
- * to simply return the object that has already been set up - it will not take
- * account of any changes you might have made to the initialisation object
- * passed to DataTables (setting this parameter to true is an acknowledgement
- * that you understand this). `destroy` can be used to reinitialise a table if
- * you need.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.retrieve
- *
- * @example
- * $(document).ready( function() {
- * initTable();
- * tableActions();
- * } );
- *
- * function initTable ()
- * {
- * return $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false,
- * "retrieve": true
- * } );
- * }
- *
- * function tableActions ()
- * {
- * var table = initTable();
- * // perform API operations with oTable
- * }
- */
- "bRetrieve": false,
-
-
- /**
- * When vertical (y) scrolling is enabled, DataTables will force the height of
- * the table's viewport to the given height at all times (useful for layout).
- * However, this can look odd when filtering data down to a small data set,
- * and the footer is left "floating" further down. This parameter (when
- * enabled) will cause DataTables to collapse the table's viewport down when
- * the result set will fit within the given Y height.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollCollapse
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200",
- * "scrollCollapse": true
- * } );
- * } );
- */
- "bScrollCollapse": false,
-
-
- /**
- * Configure DataTables to use server-side processing. Note that the
- * `ajax` parameter must also be given in order to give DataTables a
- * source to obtain the required data for each draw.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @dtopt Server-side
- * @name DataTable.defaults.serverSide
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "xhr.php"
- * } );
- * } );
- */
- "bServerSide": false,
-
-
- /**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the `sortable` option for each column.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.ordering
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "ordering": false
- * } );
- * } );
- */
- "bSort": true,
-
-
- /**
- * Enable or display DataTables' ability to sort multiple columns at the
- * same time (activated by shift-click by the user).
- * @type boolean
- * @default true
- *
- * @dtopt Options
- * @name DataTable.defaults.orderMulti
- *
- * @example
- * // Disable multiple column sorting ability
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderMulti": false
- * } );
- * } );
- */
- "bSortMulti": true,
-
-
- /**
- * Allows control over whether DataTables should use the top (true) unique
- * cell that is found for a single column, or the bottom (false - default).
- * This is useful when using complex headers.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.orderCellsTop
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderCellsTop": true
- * } );
- * } );
- */
- "bSortCellsTop": false,
-
-
- /**
- * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
- * `sorting\_3` to the columns which are currently being sorted on. This is
- * presented as a feature switch as it can increase processing time (while
- * classes are removed and added) so for large data sets you might want to
- * turn this off.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.orderClasses
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderClasses": false
- * } );
- * } );
- */
- "bSortClasses": true,
-
-
- /**
- * Enable or disable state saving. When enabled HTML5 `localStorage` will be
- * used to save table display information such as pagination information,
- * display length, filtering and sorting. As such when the end user reloads
- * the page the display display will match what thy had previously set up.
- *
- * Due to the use of `localStorage` the default state saving is not supported
- * in IE6 or 7. If state saving is required in those browsers, use
- * `stateSaveCallback` to provide a storage solution such as cookies.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.stateSave
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "stateSave": true
- * } );
- * } );
- */
- "bStateSave": false,
-
-
- /**
- * This function is called when a TR element is created (and all TD child
- * elements have been inserted), or registered if using a DOM source, allowing
- * manipulation of the TR element (adding classes etc).
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} dataIndex The index of this row in the internal aoData array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.createdRow
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "createdRow": function( row, data, dataIndex ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" )
- * {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnCreatedRow": null,
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify any aspect you want about the created DOM.
- * @type function
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.drawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "drawCallback": function( settings ) {
- * alert( 'DataTables has redrawn the table' );
- * }
- * } );
- * } );
- */
- "fnDrawCallback": null,
-
-
- /**
- * Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' event.
- * @type function
- * @param {node} foot "TR" element for the footer
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.footerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "footerCallback": function( tfoot, data, start, end, display ) {
- * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
- * }
- * } );
- * } )
- */
- "fnFooterCallback": null,
-
-
- /**
- * When rendering large numbers in the information element for the table
- * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
- * to have a comma separator for the 'thousands' units (e.g. 1 million is
- * rendered as "1,000,000") to help readability for the end user. This
- * function will override the default method DataTables uses.
- * @type function
- * @member
- * @param {int} toFormat number to be formatted
- * @returns {string} formatted string for DataTables to show the number
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.formatNumber
- *
- * @example
- * // Format a number using a single quote for the separator (note that
- * // this can also be done with the language.thousands option)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "formatNumber": function ( toFormat ) {
- * return toFormat.toString().replace(
- * /\B(?=(\d{3})+(?!\d))/g, "'"
- * );
- * };
- * } );
- * } );
- */
- "fnFormatNumber": function ( toFormat ) {
- return toFormat.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g,
- this.oLanguage.sThousands
+ "bDeferRender": false,
+
+
+ /**
+ * Replace a DataTable which matches the given selector and replace it with
+ * one which has the properties of the new initialisation object passed. If no
+ * table matches the selector, then the new DataTable will be constructed as
+ * per normal.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.destroy
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "srollY": "200px",
+ * "paginate": false
+ * } );
+ *
+ * // Some time later....
+ * $('#example').dataTable( {
+ * "filter": false,
+ * "destroy": true
+ * } );
+ * } );
+ */
+ "bDestroy": false,
+
+
+ /**
+ * Enable or disable filtering of data. Filtering in DataTables is "smart" in
+ * that it allows the end user to input multiple words (space separated) and
+ * will match a row containing those words, even if not in the order that was
+ * specified (this allow matching across multiple columns). Note that if you
+ * wish to use filtering in DataTables this must remain 'true' - to remove the
+ * default filtering input box and retain filtering abilities, please use
+ * {@link DataTable.defaults.dom}.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.searching
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "searching": false
+ * } );
+ * } );
+ */
+ "bFilter": true,
+
+
+ /**
+ * Enable or disable the table information display. This shows information
+ * about the data that is currently visible on the page, including information
+ * about filtered data if that action is being performed.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.info
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "info": false
+ * } );
+ * } );
+ */
+ "bInfo": true,
+
+
+ /**
+ * Allows the end user to select the size of a formatted page from a select
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.lengthChange
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "lengthChange": false
+ * } );
+ * } );
+ */
+ "bLengthChange": true,
+
+
+ /**
+ * Enable or disable pagination.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.paging
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "paging": false
+ * } );
+ * } );
+ */
+ "bPaginate": true,
+
+
+ /**
+ * Enable or disable the display of a 'processing' indicator when the table is
+ * being processed (e.g. a sort). This is particularly useful for tables with
+ * large amounts of data where it can take a noticeable amount of time to sort
+ * the entries.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.processing
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "processing": true
+ * } );
+ * } );
+ */
+ "bProcessing": false,
+
+
+ /**
+ * Retrieve the DataTables object for the given selector. Note that if the
+ * table has already been initialised, this parameter will cause DataTables
+ * to simply return the object that has already been set up - it will not take
+ * account of any changes you might have made to the initialisation object
+ * passed to DataTables (setting this parameter to true is an acknowledgement
+ * that you understand this). `destroy` can be used to reinitialise a table if
+ * you need.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.retrieve
+ *
+ * @example
+ * $(document).ready( function() {
+ * initTable();
+ * tableActions();
+ * } );
+ *
+ * function initTable ()
+ * {
+ * return $('#example').dataTable( {
+ * "scrollY": "200px",
+ * "paginate": false,
+ * "retrieve": true
+ * } );
+ * }
+ *
+ * function tableActions ()
+ * {
+ * var table = initTable();
+ * // perform API operations with oTable
+ * }
+ */
+ "bRetrieve": false,
+
+
+ /**
+ * When vertical (y) scrolling is enabled, DataTables will force the height of
+ * the table's viewport to the given height at all times (useful for layout).
+ * However, this can look odd when filtering data down to a small data set,
+ * and the footer is left "floating" further down. This parameter (when
+ * enabled) will cause DataTables to collapse the table's viewport down when
+ * the result set will fit within the given Y height.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.scrollCollapse
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollY": "200",
+ * "scrollCollapse": true
+ * } );
+ * } );
+ */
+ "bScrollCollapse": false,
+
+
+ /**
+ * Configure DataTables to use server-side processing. Note that the
+ * `ajax` parameter must also be given in order to give DataTables a
+ * source to obtain the required data for each draw.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverSide
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "xhr.php"
+ * } );
+ * } );
+ */
+ "bServerSide": false,
+
+
+ /**
+ * Enable or disable sorting of columns. Sorting of individual columns can be
+ * disabled by the `sortable` option for each column.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.ordering
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "ordering": false
+ * } );
+ * } );
+ */
+ "bSort": true,
+
+
+ /**
+ * Enable or display DataTables' ability to sort multiple columns at the
+ * same time (activated by shift-click by the user).
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.orderMulti
+ *
+ * @example
+ * // Disable multiple column sorting ability
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "orderMulti": false
+ * } );
+ * } );
+ */
+ "bSortMulti": true,
+
+
+ /**
+ * Allows control over whether DataTables should use the top (true) unique
+ * cell that is found for a single column, or the bottom (false - default).
+ * This is useful when using complex headers.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.orderCellsTop
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "orderCellsTop": true
+ * } );
+ * } );
+ */
+ "bSortCellsTop": false,
+
+
+ /**
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
+ * `sorting\_3` to the columns which are currently being sorted on. This is
+ * presented as a feature switch as it can increase processing time (while
+ * classes are removed and added) so for large data sets you might want to
+ * turn this off.
+ * @type boolean
+ * @default true
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.orderClasses
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "orderClasses": false
+ * } );
+ * } );
+ */
+ "bSortClasses": true,
+
+
+ /**
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
+ * used to save table display information such as pagination information,
+ * display length, filtering and sorting. As such when the end user reloads
+ * the page the display display will match what thy had previously set up.
+ *
+ * Due to the use of `localStorage` the default state saving is not supported
+ * in IE6 or 7. If state saving is required in those browsers, use
+ * `stateSaveCallback` to provide a storage solution such as cookies.
+ * @type boolean
+ * @default false
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.stateSave
+ *
+ * @example
+ * $(document).ready( function () {
+ * $('#example').dataTable( {
+ * "stateSave": true
+ * } );
+ * } );
+ */
+ "bStateSave": false,
+
+
+ /**
+ * This function is called when a TR element is created (and all TD child
+ * elements have been inserted), or registered if using a DOM source, allowing
+ * manipulation of the TR element (adding classes etc).
+ * @type function
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} dataIndex The index of this row in the internal aoData array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.createdRow
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "createdRow": function( row, data, dataIndex ) {
+ * // Bold the grade for all 'A' grade browsers
+ * if ( data[4] == "A" )
+ * {
+ * $('td:eq(4)', row).html( '<b>A</b>' );
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnCreatedRow": null,
+
+
+ /**
+ * This function is called on every 'draw' event, and allows you to
+ * dynamically modify any aspect you want about the created DOM.
+ * @type function
+ * @param {object} settings DataTables settings object
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.drawCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "drawCallback": function( settings ) {
+ * alert( 'DataTables has redrawn the table' );
+ * }
+ * } );
+ * } );
+ */
+ "fnDrawCallback": null,
+
+
+ /**
+ * Identical to fnHeaderCallback() but for the table footer this function
+ * allows you to modify the table footer on every 'draw' event.
+ * @type function
+ * @param {node} foot "TR" element for the footer
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
+ * display array
+ * @param {int} end Index for the current display ending point in the
+ * display array
+ * @param {array int} display Index array to translate the visual position
+ * to the full data array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.footerCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "footerCallback": function( tfoot, data, start, end, display ) {
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
+ * }
+ * } );
+ * } )
+ */
+ "fnFooterCallback": null,
+
+
+ /**
+ * When rendering large numbers in the information element for the table
+ * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
+ * to have a comma separator for the 'thousands' units (e.g. 1 million is
+ * rendered as "1,000,000") to help readability for the end user. This
+ * function will override the default method DataTables uses.
+ * @type function
+ * @member
+ * @param {int} toFormat number to be formatted
+ * @returns {string} formatted string for DataTables to show the number
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.formatNumber
+ *
+ * @example
+ * // Format a number using a single quote for the separator (note that
+ * // this can also be done with the language.thousands option)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "formatNumber": function ( toFormat ) {
+ * return toFormat.toString().replace(
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
+ * );
+ * };
+ * } );
+ * } );
+ */
+ "fnFormatNumber": function ( toFormat ) {
+ return toFormat.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g,
+ this.oLanguage.sThousands
+ );
+ },
+
+
+ /**
+ * This function is called on every 'draw' event, and allows you to
+ * dynamically modify the header row. This can be used to calculate and
+ * display useful information about the table.
+ * @type function
+ * @param {node} head "TR" element for the header
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
+ * display array
+ * @param {int} end Index for the current display ending point in the
+ * display array
+ * @param {array int} display Index array to translate the visual position
+ * to the full data array
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.headerCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "fheaderCallback": function( head, data, start, end, display ) {
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
+ * }
+ * } );
+ * } )
+ */
+ "fnHeaderCallback": null,
+
+
+ /**
+ * The information element can be used to convey information about the current
+ * state of the table. Although the internationalisation options presented by
+ * DataTables are quite capable of dealing with most customisations, there may
+ * be times where you wish to customise the string further. This callback
+ * allows you to do exactly that.
+ * @type function
+ * @param {object} oSettings DataTables settings object
+ * @param {int} start Starting position in data for the draw
+ * @param {int} end End position in data for the draw
+ * @param {int} max Total number of rows in the table (regardless of
+ * filtering)
+ * @param {int} total Total number of rows in the data set, after filtering
+ * @param {string} pre The string that DataTables has formatted using it's
+ * own rules
+ * @returns {string} The string to be displayed in the information element.
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.infoCallback
+ *
+ * @example
+ * $('#example').dataTable( {
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
+ * return start +" to "+ end;
+ * }
+ * } );
+ */
+ "fnInfoCallback": null,
+
+
+ /**
+ * Called when the table has been initialised. Normally DataTables will
+ * initialise sequentially and there will be no need for this function,
+ * however, this does not hold true when using external language information
+ * since that is obtained using an async XHR call.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} json The JSON object request from the server - only
+ * present if client-side Ajax sourced data is used
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.initComplete
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "initComplete": function(settings, json) {
+ * alert( 'DataTables has finished its initialisation.' );
+ * }
+ * } );
+ * } )
+ */
+ "fnInitComplete": null,
+
+
+ /**
+ * Called at the very start of each table draw and can be used to cancel the
+ * draw by returning false, any other return (including undefined) results in
+ * the full draw occurring).
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @returns {boolean} False will cancel the draw, anything else (including no
+ * return) will allow it to complete.
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.preDrawCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "preDrawCallback": function( settings ) {
+ * if ( $('#test').val() == 1 ) {
+ * return false;
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnPreDrawCallback": null,
+
+
+ /**
+ * This function allows you to 'post process' each row after it have been
+ * generated for each table draw, but before it is rendered on screen. This
+ * function might be used for setting the row class name etc.
+ * @type function
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} displayIndex The display index for the current table draw
+ * @param {int} displayIndexFull The index of the data in the full list of
+ * rows (after filtering)
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.rowCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
+ * // Bold the grade for all 'A' grade browsers
+ * if ( data[4] == "A" ) {
+ * $('td:eq(4)', row).html( '<b>A</b>' );
+ * }
+ * }
+ * } );
+ * } );
+ */
+ "fnRowCallback": null,
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * This parameter allows you to override the default function which obtains
+ * the data from the server so something more suitable for your application.
+ * For example you could use POST data, or pull information from a Gears or
+ * AIR database.
+ * @type function
+ * @member
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
+ * @param {array} data A key/value pair object containing the data to send
+ * to the server
+ * @param {function} callback to be called on completion of the data get
+ * process that will draw the data on the page.
+ * @param {object} settings DataTables settings object
+ *
+ * @dtopt Callbacks
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverData
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "fnServerData": null,
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * It is often useful to send extra data to the server when making an Ajax
+ * request - for example custom filtering information, and this callback
+ * function makes it trivial to send extra information to the server. The
+ * passed in parameter is the data set that has been constructed by
+ * DataTables, and you can add to this or modify it as you require.
+ * @type function
+ * @param {array} data Data array (array of objects which are name/value
+ * pairs) that has been constructed by DataTables and will be sent to the
+ * server. In the case of Ajax sourced data with server-side processing
+ * this will be an empty array, for server-side processing there will be a
+ * significant number of parameters!
+ * @returns {undefined} Ensure that you modify the data array passed in,
+ * as this is passed by reference.
+ *
+ * @dtopt Callbacks
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverParams
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "fnServerParams": null,
+
+
+ /**
+ * Load the table state. With this function you can define from where, and how, the
+ * state of a table is loaded. By default DataTables will load from `localStorage`
+ * but you might wish to use a server-side database or cookies.
+ * @type function
+ * @member
+ * @param {object} settings DataTables settings object
+ * @param {object} callback Callback that can be executed when done. It
+ * should be passed the loaded state object.
+ * @return {object} The DataTables state object to be loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoadCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadCallback": function (settings, callback) {
+ * $.ajax( {
+ * "url": "/state_load",
+ * "dataType": "json",
+ * "success": function (json) {
+ * callback( json );
+ * }
+ * } );
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoadCallback": function ( settings ) {
+ try {
+ return JSON.parse(
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname
+ )
);
- },
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify the header row. This can be used to calculate and
- * display useful information about the table.
- * @type function
- * @param {node} head "TR" element for the header
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.headerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "fheaderCallback": function( head, data, start, end, display ) {
- * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
- * }
- * } );
- * } )
- */
- "fnHeaderCallback": null,
-
-
- /**
- * The information element can be used to convey information about the current
- * state of the table. Although the internationalisation options presented by
- * DataTables are quite capable of dealing with most customisations, there may
- * be times where you wish to customise the string further. This callback
- * allows you to do exactly that.
- * @type function
- * @param {object} oSettings DataTables settings object
- * @param {int} start Starting position in data for the draw
- * @param {int} end End position in data for the draw
- * @param {int} max Total number of rows in the table (regardless of
- * filtering)
- * @param {int} total Total number of rows in the data set, after filtering
- * @param {string} pre The string that DataTables has formatted using it's
- * own rules
- * @returns {string} The string to be displayed in the information element.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.infoCallback
- *
- * @example
- * $('#example').dataTable( {
- * "infoCallback": function( settings, start, end, max, total, pre ) {
- * return start +" to "+ end;
- * }
- * } );
- */
- "fnInfoCallback": null,
-
-
- /**
- * Called when the table has been initialised. Normally DataTables will
- * initialise sequentially and there will be no need for this function,
- * however, this does not hold true when using external language information
- * since that is obtained using an async XHR call.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.initComplete
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "initComplete": function(settings, json) {
- * alert( 'DataTables has finished its initialisation.' );
- * }
- * } );
- * } )
- */
- "fnInitComplete": null,
-
-
- /**
- * Called at the very start of each table draw and can be used to cancel the
- * draw by returning false, any other return (including undefined) results in
- * the full draw occurring).
- * @type function
- * @param {object} settings DataTables settings object
- * @returns {boolean} False will cancel the draw, anything else (including no
- * return) will allow it to complete.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.preDrawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "preDrawCallback": function( settings ) {
- * if ( $('#test').val() == 1 ) {
- * return false;
- * }
- * }
- * } );
- * } );
- */
- "fnPreDrawCallback": null,
-
-
- /**
- * This function allows you to 'post process' each row after it have been
- * generated for each table draw, but before it is rendered on screen. This
- * function might be used for setting the row class name etc.
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} displayIndex The display index for the current table draw
- * @param {int} displayIndexFull The index of the data in the full list of
- * rows (after filtering)
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.rowCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" ) {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnRowCallback": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * This parameter allows you to override the default function which obtains
- * the data from the server so something more suitable for your application.
- * For example you could use POST data, or pull information from a Gears or
- * AIR database.
- * @type function
- * @member
- * @param {string} source HTTP source to obtain the data from (`ajax`)
- * @param {array} data A key/value pair object containing the data to send
- * to the server
- * @param {function} callback to be called on completion of the data get
- * process that will draw the data on the page.
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverData
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerData": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * It is often useful to send extra data to the server when making an Ajax
- * request - for example custom filtering information, and this callback
- * function makes it trivial to send extra information to the server. The
- * passed in parameter is the data set that has been constructed by
- * DataTables, and you can add to this or modify it as you require.
- * @type function
- * @param {array} data Data array (array of objects which are name/value
- * pairs) that has been constructed by DataTables and will be sent to the
- * server. In the case of Ajax sourced data with server-side processing
- * this will be an empty array, for server-side processing there will be a
- * significant number of parameters!
- * @returns {undefined} Ensure that you modify the data array passed in,
- * as this is passed by reference.
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverParams
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerParams": null,
-
-
- /**
- * Load the table state. With this function you can define from where, and how, the
- * state of a table is loaded. By default DataTables will load from `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} callback Callback that can be executed when done. It
- * should be passed the loaded state object.
- * @return {object} The DataTables state object to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadCallback": function (settings, callback) {
- * $.ajax( {
- * "url": "/state_load",
- * "dataType": "json",
- * "success": function (json) {
- * callback( json );
- * }
- * } );
- * }
- * } );
- * } );
- */
- "fnStateLoadCallback": function ( settings ) {
- try {
- return JSON.parse(
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname
- )
- );
- } catch (e) {
- return {};
- }
- },
-
-
- /**
- * Callback which allows modification of the saved state prior to loading that state.
- * This callback is called when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state. Note that for
- * plug-in authors, you should use the `stateLoadParams` event to load parameters for
- * a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that is to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadParams
- *
- * @example
- * // Remove a saved filter, so filtering is never loaded
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- *
- * @example
- * // Disallow state loading by returning false
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * return false;
- * }
- * } );
- * } );
- */
- "fnStateLoadParams": null,
-
-
- /**
- * Callback that is called when the state has been loaded from the state saving method
- * and the DataTables settings object has been modified as a result of the loaded state.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that was loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoaded
- *
- * @example
- * // Show an alert with the filtering value that was saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoaded": function (settings, data) {
- * alert( 'Saved filter was: '+data.oSearch.sSearch );
- * }
- * } );
- * } );
- */
- "fnStateLoaded": null,
-
-
- /**
- * Save the table state. This function allows you to define where and how the state
- * information for the table is stored By default DataTables will use `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveCallback": function (settings, data) {
- * // Send an Ajax request to the server with the state object
- * $.ajax( {
- * "url": "/state_save",
- * "data": data,
- * "dataType": "json",
- * "method": "POST"
- * "success": function () {}
- * } );
- * }
- * } );
- * } );
- */
- "fnStateSaveCallback": function ( settings, data ) {
- try {
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname,
- JSON.stringify( data )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the state to be saved. Called when the table
- * has changed state a new state save is required. This method allows modification of
- * the state saving object prior to actually doing the save, including addition or
- * other state properties or modification. Note that for plug-in authors, you should
- * use the `stateSaveParams` event to save parameters for a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveParams
- *
- * @example
- * // Remove a saved filter, so filtering is never saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- */
- "fnStateSaveParams": null,
-
-
- /**
- * Duration for which the saved state information is considered valid. After this period
- * has elapsed the state will be returned to the default.
- * Value is given in seconds.
- * @type int
- * @default 7200 <i>(2 hours)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.stateDuration
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateDuration": 60*60*24; // 1 day
- * } );
- * } )
- */
- "iStateDuration": 7200,
-
-
- /**
- * When enabled DataTables will not make a request to the server for the first
- * page draw - rather it will use the data already on the page (no sorting etc
- * will be applied to it), thus saving on an XHR at load time. `deferLoading`
- * is used to indicate that deferred loading is required, but it is also used
- * to tell DataTables how many records there are in the full table (allowing
- * the information element and pagination to be displayed correctly). In the case
- * where a filtering is applied to the table on initial load, this can be
- * indicated by giving the parameter as an array, where the first element is
- * the number of records available after filtering and the second element is the
- * number of records without filtering (allowing the table information element
- * to be shown correctly).
- * @type int | array
- * @default null
- *
- * @dtopt Options
- * @name DataTable.defaults.deferLoading
- *
- * @example
- * // 57 records available in the table, no filtering applied
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": 57
- * } );
- * } );
- *
- * @example
- * // 57 records after filtering, 100 without filtering (an initial filter applied)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": [ 57, 100 ],
- * "search": {
- * "search": "my_filter"
- * }
- * } );
- * } );
- */
- "iDeferLoading": null,
-
-
- /**
- * Number of rows to display on a single page when using pagination. If
- * feature enabled (`lengthChange`) then the end user will be able to override
- * this to a custom setting using a pop-up menu.
- * @type int
- * @default 10
- *
- * @dtopt Options
- * @name DataTable.defaults.pageLength
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pageLength": 50
- * } );
- * } )
- */
- "iDisplayLength": 10,
-
-
- /**
- * Define the starting point for data display when using DataTables with
- * pagination. Note that this parameter is the number of records, rather than
- * the page number, so if you have 10 records per page and want to start on
- * the third page, it should be "20".
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.displayStart
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "displayStart": 20
- * } );
- * } )
- */
- "iDisplayStart": 0,
-
-
- /**
- * By default DataTables allows keyboard navigation of the table (sorting, paging,
- * and filtering) by adding a `tabindex` attribute to the required elements. This
- * allows you to tab through the controls and press the enter key to activate them.
- * The tabindex is default 0, meaning that the tab follows the flow of the document.
- * You can overrule this using this parameter if you wish. Use a value of -1 to
- * disable built-in keyboard navigation.
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.tabIndex
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "tabIndex": 1
- * } );
- * } );
- */
- "iTabIndex": 0,
-
-
- /**
- * Classes that DataTables assigns to the various components and features
- * that it adds to the HTML table. This allows classes to be configured
- * during initialisation in addition to through the static
- * {@link DataTable.ext.oStdClasses} object).
- * @namespace
- * @name DataTable.defaults.classes
- */
- "oClasses": {},
-
-
+ } catch (e) {
+ return {};
+ }
+ },
+
+
+ /**
+ * Callback which allows modification of the saved state prior to loading that state.
+ * This callback is called when the table is loading state from the stored data, but
+ * prior to the settings object being modified by the saved state. Note that for
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
+ * a plug-in.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that is to be loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoadParams
+ *
+ * @example
+ * // Remove a saved filter, so filtering is never loaded
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
+ * }
+ * } );
+ * } );
+ *
+ * @example
+ * // Disallow state loading by returning false
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
+ * return false;
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoadParams": null,
+
+
+ /**
+ * Callback that is called when the state has been loaded from the state saving method
+ * and the DataTables settings object has been modified as a result of the loaded state.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that was loaded
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateLoaded
+ *
+ * @example
+ * // Show an alert with the filtering value that was saved
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateLoaded": function (settings, data) {
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
+ * }
+ * } );
+ * } );
+ */
+ "fnStateLoaded": null,
+
+
+ /**
+ * Save the table state. This function allows you to define where and how the state
+ * information for the table is stored By default DataTables will use `localStorage`
+ * but you might wish to use a server-side database or cookies.
+ * @type function
+ * @member
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateSaveCallback
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateSaveCallback": function (settings, data) {
+ * // Send an Ajax request to the server with the state object
+ * $.ajax( {
+ * "url": "/state_save",
+ * "data": data,
+ * "dataType": "json",
+ * "method": "POST"
+ * "success": function () {}
+ * } );
+ * }
+ * } );
+ * } );
+ */
+ "fnStateSaveCallback": function ( settings, data ) {
+ try {
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname,
+ JSON.stringify( data )
+ );
+ } catch (e) {}
+ },
+
+
+ /**
+ * Callback which allows modification of the state to be saved. Called when the table
+ * has changed state a new state save is required. This method allows modification of
+ * the state saving object prior to actually doing the save, including addition or
+ * other state properties or modification. Note that for plug-in authors, you should
+ * use the `stateSaveParams` event to save parameters for a plug-in.
+ * @type function
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
+ * @dtopt Callbacks
+ * @name DataTable.defaults.stateSaveParams
+ *
+ * @example
+ * // Remove a saved filter, so filtering is never saved
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateSave": true,
+ * "stateSaveParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
+ * }
+ * } );
+ * } );
+ */
+ "fnStateSaveParams": null,
+
+
+ /**
+ * Duration for which the saved state information is considered valid. After this period
+ * has elapsed the state will be returned to the default.
+ * Value is given in seconds.
+ * @type int
+ * @default 7200 <i>(2 hours)</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.stateDuration
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "stateDuration": 60*60*24; // 1 day
+ * } );
+ * } )
+ */
+ "iStateDuration": 7200,
+
+
+ /**
+ * When enabled DataTables will not make a request to the server for the first
+ * page draw - rather it will use the data already on the page (no sorting etc
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
+ * is used to indicate that deferred loading is required, but it is also used
+ * to tell DataTables how many records there are in the full table (allowing
+ * the information element and pagination to be displayed correctly). In the case
+ * where a filtering is applied to the table on initial load, this can be
+ * indicated by giving the parameter as an array, where the first element is
+ * the number of records available after filtering and the second element is the
+ * number of records without filtering (allowing the table information element
+ * to be shown correctly).
+ * @type int | array
+ * @default null
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.deferLoading
+ *
+ * @example
+ * // 57 records available in the table, no filtering applied
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": 57
+ * } );
+ * } );
+ *
+ * @example
+ * // 57 records after filtering, 100 without filtering (an initial filter applied)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": [ 57, 100 ],
+ * "search": {
+ * "search": "my_filter"
+ * }
+ * } );
+ * } );
+ */
+ "iDeferLoading": null,
+
+
+ /**
+ * Number of rows to display on a single page when using pagination. If
+ * feature enabled (`lengthChange`) then the end user will be able to override
+ * this to a custom setting using a pop-up menu.
+ * @type int
+ * @default 10
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.pageLength
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "pageLength": 50
+ * } );
+ * } )
+ */
+ "iDisplayLength": 10,
+
+
+ /**
+ * Define the starting point for data display when using DataTables with
+ * pagination. Note that this parameter is the number of records, rather than
+ * the page number, so if you have 10 records per page and want to start on
+ * the third page, it should be "20".
+ * @type int
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.displayStart
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "displayStart": 20
+ * } );
+ * } )
+ */
+ "iDisplayStart": 0,
+
+
+ /**
+ * By default DataTables allows keyboard navigation of the table (sorting, paging,
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
+ * allows you to tab through the controls and press the enter key to activate them.
+ * The tabindex is default 0, meaning that the tab follows the flow of the document.
+ * You can overrule this using this parameter if you wish. Use a value of -1 to
+ * disable built-in keyboard navigation.
+ * @type int
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.tabIndex
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "tabIndex": 1
+ * } );
+ * } );
+ */
+ "iTabIndex": 0,
+
+
+ /**
+ * Classes that DataTables assigns to the various components and features
+ * that it adds to the HTML table. This allows classes to be configured
+ * during initialisation in addition to through the static
+ * {@link DataTable.ext.oStdClasses} object).
+ * @namespace
+ * @name DataTable.defaults.classes
+ */
+ "oClasses": {},
+
+
+ /**
+ * All strings that DataTables uses in the user interface that it creates
+ * are defined in this object, allowing you to modified them individually or
+ * completely replace them all as required.
+ * @namespace
+ * @name DataTable.defaults.language
+ */
+ "oLanguage": {
/**
- * All strings that DataTables uses in the user interface that it creates
- * are defined in this object, allowing you to modified them individually or
- * completely replace them all as required.
+ * Strings that are used for WAI-ARIA labels and controls only (these are not
+ * actually visible on the page, but will be read by screenreaders, and thus
+ * must be internationalised as well).
* @namespace
- * @name DataTable.defaults.language
+ * @name DataTable.defaults.language.aria
*/
- "oLanguage": {
- /**
- * Strings that are used for WAI-ARIA labels and controls only (these are not
- * actually visible on the page, but will be read by screenreaders, and thus
- * must be internationalised as well).
- * @namespace
- * @name DataTable.defaults.language.aria
- */
- "oAria": {
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted ascending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortAscending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortAscending": " - click/return to sort ascending"
- * }
- * }
- * } );
- * } );
- */
- "sSortAscending": ": activate to sort column ascending",
-
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted descending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortDescending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortDescending": " - click/return to sort descending"
- * }
- * }
- * } );
- * } );
- */
- "sSortDescending": ": activate to sort column descending"
- },
-
- /**
- * Pagination string used by DataTables for the built-in pagination
- * control types.
- * @namespace
- * @name DataTable.defaults.language.paginate
- */
- "oPaginate": {
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the first page.
- * @type string
- * @default First
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.first
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "first": "First page"
- * }
- * }
- * } );
- * } );
- */
- "sFirst": "First",
-
-
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the last page.
- * @type string
- * @default Last
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.last
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "last": "Last page"
- * }
- * }
- * } );
- * } );
- */
- "sLast": "Last",
-
-
- /**
- * Text to use for the 'next' pagination button (to take the user to the
- * next page).
- * @type string
- * @default Next
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.next
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "next": "Next page"
- * }
- * }
- * } );
- * } );
- */
- "sNext": "Next",
-
-
- /**
- * Text to use for the 'previous' pagination button (to take the user to
- * the previous page).
- * @type string
- * @default Previous
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.previous
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "previous": "Previous page"
- * }
- * }
- * } );
- * } );
- */
- "sPrevious": "Previous"
- },
-
- /**
- * This string is shown in preference to `zeroRecords` when the table is
- * empty of data (regardless of filtering). Note that this is an optional
- * parameter - if it is not given, the value of `zeroRecords` will be used
- * instead (either the default or given value).
- * @type string
- * @default No data available in table
- *
- * @dtopt Language
- * @name DataTable.defaults.language.emptyTable
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "emptyTable": "No data available in table"
- * }
- * } );
- * } );
- */
- "sEmptyTable": "No data available in table",
-
-
+ "oAria": {
/**
- * This string gives information to the end user about the information
- * that is current on display on the page. The following tokens can be
- * used in the string and will be dynamically replaced as the table
- * display updates. This tokens can be placed anywhere in the string, or
- * removed as needed by the language requires:
- *
- * * `\_START\_` - Display index of the first record on the current page
- * * `\_END\_` - Display index of the last record on the current page
- * * `\_TOTAL\_` - Number of records in the table after filtering
- * * `\_MAX\_` - Number of records in the table without filtering
- * * `\_PAGE\_` - Current page number
- * * `\_PAGES\_` - Total number of pages of data in the table
- *
+ * ARIA label that is added to the table headers when the column may be
+ * sorted ascending by activing the column (click or return when focused).
+ * Note that the column header is prefixed to this string.
* @type string
- * @default Showing _START_ to _END_ of _TOTAL_ entries
+ * @default : activate to sort column ascending
*
* @dtopt Language
- * @name DataTable.defaults.language.info
+ * @name DataTable.defaults.language.aria.sortAscending
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "info": "Showing page _PAGE_ of _PAGES_"
+ * "aria": {
+ * "sortAscending": " - click/return to sort ascending"
+ * }
* }
* } );
* } );
*/
- "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
-
-
- /**
- * Display information string for when the table is empty. Typically the
- * format of this string should match `info`.
- * @type string
- * @default Showing 0 to 0 of 0 entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoEmpty
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoEmpty": "No entries to show"
- * }
- * } );
- * } );
- */
- "sInfoEmpty": "Showing 0 to 0 of 0 entries",
-
-
- /**
- * When a user filters the information in a table, this string is appended
- * to the information (`info`) to give an idea of how strong the filtering
- * is. The variable _MAX_ is dynamically updated.
- * @type string
- * @default (filtered from _MAX_ total entries)
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoFiltered
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoFiltered": " - filtering from _MAX_ records"
- * }
- * } );
- * } );
- */
- "sInfoFiltered": "(filtered from _MAX_ total entries)",
-
-
- /**
- * If can be useful to append extra information to the info string at times,
- * and this variable does exactly that. This information will be appended to
- * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
- * being used) at all times.
- * @type string
- * @default <i>Empty string</i>
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoPostFix
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoPostFix": "All records shown are derived from real information."
- * }
- * } );
- * } );
- */
- "sInfoPostFix": "",
-
-
- /**
- * This decimal place operator is a little different from the other
- * language options since DataTables doesn't output floating point
- * numbers, so it won't ever use this for display of a number. Rather,
- * what this parameter does is modify the sort methods of the table so
- * that numbers which are in a format which has a character other than
- * a period (`.`) as a decimal place will be sorted numerically.
- *
- * Note that numbers with different decimal places cannot be shown in
- * the same table and still be sortable, the table must be consistent.
- * However, multiple different tables on the page can use different
- * decimal place characters.
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.decimal
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "decimal": ","
- * "thousands": "."
- * }
- * } );
- * } );
- */
- "sDecimal": "",
-
-
- /**
- * DataTables has a build in number formatter (`formatNumber`) which is
- * used to format large numbers that are used in the table information.
- * By default a comma is used, but this can be trivially changed to any
- * character you wish with this parameter.
- * @type string
- * @default ,
- *
- * @dtopt Language
- * @name DataTable.defaults.language.thousands
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "thousands": "'"
- * }
- * } );
- * } );
- */
- "sThousands": ",",
-
-
+ "sSortAscending": ": activate to sort column ascending",
+
/**
- * Detail the action that will be taken when the drop down menu for the
- * pagination length option is changed. The '_MENU_' variable is replaced
- * with a default select list of 10, 25, 50 and 100, and can be replaced
- * with a custom select box if required.
+ * ARIA label that is added to the table headers when the column may be
+ * sorted descending by activing the column (click or return when focused).
+ * Note that the column header is prefixed to this string.
* @type string
- * @default Show _MENU_ entries
+ * @default : activate to sort column ascending
*
* @dtopt Language
- * @name DataTable.defaults.language.lengthMenu
- *
- * @example
- * // Language change only
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "lengthMenu": "Display _MENU_ records"
- * }
- * } );
- * } );
+ * @name DataTable.defaults.language.aria.sortDescending
*
* @example
- * // Language and options change
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "lengthMenu": 'Display <select>'+
- * '<option value="10">10</option>'+
- * '<option value="20">20</option>'+
- * '<option value="30">30</option>'+
- * '<option value="40">40</option>'+
- * '<option value="50">50</option>'+
- * '<option value="-1">All</option>'+
- * '</select> records'
+ * "aria": {
+ * "sortDescending": " - click/return to sort descending"
+ * }
* }
* } );
* } );
*/
- "sLengthMenu": "Show _MENU_ entries",
-
-
+ "sSortDescending": ": activate to sort column descending"
+ },
+
+ /**
+ * Pagination string used by DataTables for the built-in pagination
+ * control types.
+ * @namespace
+ * @name DataTable.defaults.language.paginate
+ */
+ "oPaginate": {
/**
- * When using Ajax sourced data and during the first draw when DataTables is
- * gathering the data, this message is shown in an empty row in the table to
- * indicate to the end user the the data is being loaded. Note that this
- * parameter is not used when loading data by server-side processing, just
- * Ajax sourced data with client-side processing.
+ * Text to use when using the 'full_numbers' type of pagination for the
+ * button to take the user to the first page.
* @type string
- * @default Loading...
+ * @default First
*
* @dtopt Language
- * @name DataTable.defaults.language.loadingRecords
+ * @name DataTable.defaults.language.paginate.first
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "loadingRecords": "Please wait - loading..."
+ * "paginate": {
+ * "first": "First page"
+ * }
* }
* } );
* } );
*/
- "sLoadingRecords": "Loading...",
-
-
+ "sFirst": "First",
+
+
/**
- * Text which is displayed when the table is processing a user action
- * (usually a sort command or similar).
+ * Text to use when using the 'full_numbers' type of pagination for the
+ * button to take the user to the last page.
* @type string
- * @default Processing...
+ * @default Last
*
* @dtopt Language
- * @name DataTable.defaults.language.processing
+ * @name DataTable.defaults.language.paginate.last
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "processing": "DataTables is currently busy"
+ * "paginate": {
+ * "last": "Last page"
+ * }
* }
* } );
* } );
*/
- "sProcessing": "Processing...",
-
-
- /**
- * Details the actions that will be taken when the user types into the
- * filtering input text box. The variable "_INPUT_", if used in the string,
- * is replaced with the HTML text box for the filtering input allowing
- * control over where it appears in the string. If "_INPUT_" is not given
- * then the input box is appended to the string automatically.
- * @type string
- * @default Search:
- *
- * @dtopt Language
- * @name DataTable.defaults.language.search
- *
- * @example
- * // Input text box will be appended at the end automatically
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Filter records:"
- * }
- * } );
- * } );
- *
- * @example
- * // Specify where the filter should appear
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Apply filter _INPUT_ to table"
- * }
- * } );
- * } );
- */
- "sSearch": "Search:",
-
-
- /**
- * Assign a `placeholder` attribute to the search `input` element
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.searchPlaceholder
- */
- "sSearchPlaceholder": "",
-
-
+ "sLast": "Last",
+
+
/**
- * All of the language information can be stored in a file on the
- * server-side, which DataTables will look up if this parameter is passed.
- * It must store the URL of the language file, which is in a JSON format,
- * and the object has the same properties as the oLanguage object in the
- * initialiser object (i.e. the above parameters). Please refer to one of
- * the example language files to see how this works in action.
+ * Text to use for the 'next' pagination button (to take the user to the
+ * next page).
* @type string
- * @default <i>Empty string - i.e. disabled</i>
+ * @default Next
*
* @dtopt Language
- * @name DataTable.defaults.language.url
+ * @name DataTable.defaults.language.paginate.next
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
+ * "paginate": {
+ * "next": "Next page"
+ * }
* }
* } );
* } );
*/
- "sUrl": "",
-
-
+ "sNext": "Next",
+
+
/**
- * Text shown inside the table records when the is no information to be
- * displayed after filtering. `emptyTable` is shown when there is simply no
- * information in the table at all (regardless of filtering).
+ * Text to use for the 'previous' pagination button (to take the user to
+ * the previous page).
* @type string
- * @default No matching records found
+ * @default Previous
*
* @dtopt Language
- * @name DataTable.defaults.language.zeroRecords
+ * @name DataTable.defaults.language.paginate.previous
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
* "language": {
- * "zeroRecords": "No records to display"
+ * "paginate": {
+ * "previous": "Previous page"
+ * }
* }
* } );
* } );
*/
- "sZeroRecords": "No matching records found"
+ "sPrevious": "Previous"
},
-
-
- /**
- * This parameter allows you to have define the global filtering state at
- * initialisation time. As an object the `search` parameter must be
- * defined, but all other parameters are optional. When `regex` is true,
- * the search string will be treated as a regular expression, when false
- * (default) it will be treated as a straight string. When `smart`
- * DataTables will use it's smart filtering methods (to word match at
- * any point in the data), when false this will not be done.
- * @namespace
- * @extends DataTable.models.oSearch
- *
- * @dtopt Options
- * @name DataTable.defaults.search
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "search": {"search": "Initial search"}
- * } );
- * } )
- */
- "oSearch": $.extend( {}, DataTable.models.oSearch ),
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * By default DataTables will look for the property `data` (or `aaData` for
- * compatibility with DataTables 1.9-) when obtaining data from an Ajax
- * source or for server-side processing - this parameter allows that
- * property to be changed. You can use Javascript dotted object notation to
- * get a data source for multiple levels of nesting.
- * @type string
- * @default data
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxDataProp
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxDataProp": "data",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * You can instruct DataTables to load data from an external
- * source using this parameter (use aData if you want to pass data in you
- * already have). Simply provide a url a JSON object can be obtained from.
- * @type string
- * @default null
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxSource
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxSource": null,
-
-
+
/**
- * This initialisation variable allows you to specify exactly where in the
- * DOM you want DataTables to inject the various controls it adds to the page
- * (for example you might want the pagination controls at the top of the
- * table). DIV elements (with or without a custom class) can also be added to
- * aid styling. The follow syntax is used:
- * <ul>
- * <li>The following options are allowed:
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * </li>
- * <li>The following constants are allowed:
- * <ul>
- * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
- * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
- * </ul>
- * </li>
- * <li>The following syntax is expected:
- * <ul>
- * <li>'&lt;' and '&gt;' - div elements</li>
- * <li>'&lt;"class" and '&gt;' - div with a class</li>
- * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
- * </ul>
- * </li>
- * <li>Examples:
- * <ul>
- * <li>'&lt;"wrapper"flipt&gt;'</li>
- * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
- * </ul>
- * </li>
- * </ul>
+ * This string is shown in preference to `zeroRecords` when the table is
+ * empty of data (regardless of filtering). Note that this is an optional
+ * parameter - if it is not given, the value of `zeroRecords` will be used
+ * instead (either the default or given value).
* @type string
- * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
- * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
+ * @default No data available in table
*
- * @dtopt Options
- * @name DataTable.defaults.dom
+ * @dtopt Language
+ * @name DataTable.defaults.language.emptyTable
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
+ * "language": {
+ * "emptyTable": "No data available in table"
+ * }
* } );
* } );
*/
- "sDom": "lfrtip",
-
-
+ "sEmptyTable": "No data available in table",
+
+
/**
- * Search delay option. This will throttle full table searches that use the
- * DataTables provided search input element (it does not effect calls to
- * `dt-api search()`, providing a delay before the search is made.
- * @type integer
- * @default 0
+ * This string gives information to the end user about the information
+ * that is current on display on the page. The following tokens can be
+ * used in the string and will be dynamically replaced as the table
+ * display updates. This tokens can be placed anywhere in the string, or
+ * removed as needed by the language requires:
*
- * @dtopt Options
- * @name DataTable.defaults.searchDelay
+ * * `\_START\_` - Display index of the first record on the current page
+ * * `\_END\_` - Display index of the last record on the current page
+ * * `\_TOTAL\_` - Number of records in the table after filtering
+ * * `\_MAX\_` - Number of records in the table without filtering
+ * * `\_PAGE\_` - Current page number
+ * * `\_PAGES\_` - Total number of pages of data in the table
*
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchDelay": 200
- * } );
- * } )
- */
- "searchDelay": null,
-
-
- /**
- * DataTables features six different built-in options for the buttons to
- * display for pagination control:
- *
- * * `numbers` - Page number buttons only
- * * `simple` - 'Previous' and 'Next' buttons only
- * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
- * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
- * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
- * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
- *
- * Further methods can be added using {@link DataTable.ext.oPagination}.
* @type string
- * @default simple_numbers
- *
- * @dtopt Options
- * @name DataTable.defaults.pagingType
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pagingType": "full_numbers"
- * } );
- * } )
- */
- "sPaginationType": "simple_numbers",
-
-
- /**
- * Enable horizontal scrolling. When a table is too wide to fit into a
- * certain layout, or you have a large number of columns in the table, you
- * can enable x-scrolling to show the table in a viewport, which can be
- * scrolled. This property can be `true` which will allow the table to
- * scroll horizontally when needed, or any CSS unit, or a number (in which
- * case it will be treated as a pixel measurement). Setting as simply `true`
- * is recommended.
- * @type boolean|string
- * @default <i>blank string - i.e. disabled</i>
+ * @default Showing _START_ to _END_ of _TOTAL_ entries
*
- * @dtopt Features
- * @name DataTable.defaults.scrollX
+ * @dtopt Language
+ * @name DataTable.defaults.language.info
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "scrollX": true,
- * "scrollCollapse": true
+ * "language": {
+ * "info": "Showing page _PAGE_ of _PAGES_"
+ * }
* } );
* } );
*/
- "sScrollX": "",
-
-
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
+
+
/**
- * This property can be used to force a DataTable to use more width than it
- * might otherwise do when x-scrolling is enabled. For example if you have a
- * table which requires to be well spaced, this parameter is useful for
- * "over-sizing" the table, and thus forcing scrolling. This property can by
- * any CSS unit, or a number (in which case it will be treated as a pixel
- * measurement).
+ * Display information string for when the table is empty. Typically the
+ * format of this string should match `info`.
* @type string
- * @default <i>blank string - i.e. disabled</i>
+ * @default Showing 0 to 0 of 0 entries
*
- * @dtopt Options
- * @name DataTable.defaults.scrollXInner
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoEmpty
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "scrollX": "100%",
- * "scrollXInner": "110%"
+ * "language": {
+ * "infoEmpty": "No entries to show"
+ * }
* } );
* } );
*/
- "sScrollXInner": "",
-
-
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
+
+
/**
- * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
- * to the given height, and enable scrolling for any data which overflows the
- * current viewport. This can be used as an alternative to paging to display
- * a lot of data in a small area (although paging and scrolling can both be
- * enabled at the same time). This property can be any CSS unit, or a number
- * (in which case it will be treated as a pixel measurement).
+ * When a user filters the information in a table, this string is appended
+ * to the information (`info`) to give an idea of how strong the filtering
+ * is. The variable _MAX_ is dynamically updated.
* @type string
- * @default <i>blank string - i.e. disabled</i>
+ * @default (filtered from _MAX_ total entries)
*
- * @dtopt Features
- * @name DataTable.defaults.scrollY
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoFiltered
*
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false
+ * "language": {
+ * "infoFiltered": " - filtering from _MAX_ records"
+ * }
* } );
* } );
*/
- "sScrollY": "",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * Set the HTTP method that is used to make the Ajax call for server-side
- * processing or Ajax sourced data.
- * @type string
- * @default GET
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.serverMethod
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sServerMethod": "GET",
-
-
- /**
- * DataTables makes use of renderers when displaying HTML elements for
- * a table. These renderers can be added or modified by plug-ins to
- * generate suitable mark-up for a site. For example the Bootstrap
- * integration plug-in for DataTables uses a paging button renderer to
- * display pagination buttons in the mark-up required by Bootstrap.
- *
- * For further information about the renderers available see
- * DataTable.ext.renderer
- * @type string|object
- * @default null
- *
- * @name DataTable.defaults.renderer
- *
- */
- "renderer": null,
-
-
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
+
+
/**
- * Set the data property name that DataTables should use to get a row's id
- * to set as the `id` property in the node.
+ * If can be useful to append extra information to the info string at times,
+ * and this variable does exactly that. This information will be appended to
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
+ * being used) at all times.
* @type string
- * @default DT_RowId
- *
- * @name DataTable.defaults.rowId
- */
- "rowId": "DT_RowId"
- };
-
- _fnHungarianMap( DataTable.defaults );
-
-
-
- /*
- * Developer note - See note in model.defaults.js about the use of Hungarian
- * notation and camel case.
- */
-
- /**
- * Column options that can be given to DataTables at initialisation time.
- * @namespace
- */
- DataTable.defaults.column = {
- /**
- * Define which column(s) an order will occur on for this column. This
- * allows a column's ordering to take multiple columns into account when
- * doing a sort or use the data from a different column. For example first
- * name / last name columns make sense to do a multi-column sort over the
- * two columns.
- * @type array|int
- * @default null <i>Takes the value of the column index automatically</i>
- *
- * @name DataTable.defaults.column.orderData
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
- * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
- * { "orderData": 2, "targets": [ 2 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderData": [ 0, 1 ] },
- * { "orderData": [ 1, 0 ] },
- * { "orderData": 2 },
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "aDataSort": null,
- "iDataSort": -1,
-
-
- /**
- * You can control the default ordering direction, and even alter the
- * behaviour of the sort handler (i.e. only allow ascending ordering etc)
- * using this parameter.
- * @type array
- * @default [ 'asc', 'desc' ]
- *
- * @name DataTable.defaults.column.orderSequence
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
- * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
- * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * { "orderSequence": [ "asc" ] },
- * { "orderSequence": [ "desc", "asc", "asc" ] },
- * { "orderSequence": [ "desc" ] },
- * null
- * ]
- * } );
- * } );
- */
- "asSorting": [ 'asc', 'desc' ],
-
-
- /**
- * Enable or disable filtering on the data in this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.searchable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "searchable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "searchable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSearchable": true,
-
-
- /**
- * Enable or disable ordering on this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.orderable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSortable": true,
-
-
- /**
- * Enable or disable the display of this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.visible
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "visible": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "visible": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bVisible": true,
-
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} td The TD node that has been created
- * @param {*} cellData The Data for the cell
- * @param {array|object} rowData The data for the whole row
- * @param {int} row The row index for the aoData data store
- * @param {int} col The column index for aoColumns
- *
- * @name DataTable.defaults.column.createdCell
- * @dtopt Columns
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [3],
- * "createdCell": function (td, cellData, rowData, row, col) {
- * if ( cellData == "1.7" ) {
- * $(td).css('color', 'blue')
- * }
- * }
- * } ]
- * });
- * } );
- */
- "fnCreatedCell": null,
-
-
- /**
- * This parameter has been replaced by `data` in DataTables to ensure naming
- * consistency. `dataProp` can still be used, as there is backwards
- * compatibility in DataTables for this option, but it is strongly
- * recommended that you use `data` in preference to `dataProp`.
- * @name DataTable.defaults.column.dataProp
- */
-
-
- /**
- * This property can be used to read data from any data source property,
- * including deeply nested objects / properties. `data` can be given in a
- * number of different ways which effect its behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object. Note that
- * function notation is recommended for use in `render` rather than
- * `data` as it is much simpler to use as a renderer.
- * * `null` - use the original data source for the row rather than plucking
- * data directly from it. This action has effects on two other
- * initialisation options:
- * * `defaultContent` - When null is given as the `data` option and
- * `defaultContent` is specified for the column, the value defined by
- * `defaultContent` will be used for the cell.
- * * `render` - When null is used for the `data` option and the `render`
- * option is specified for the column, the whole data source for the
- * row is used for the renderer.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * `{array|object}` The data source for the row
- * * `{string}` The type call data requested - this will be 'set' when
- * setting data or 'filter', 'display', 'type', 'sort' or undefined
- * when gathering data. Note that when `undefined` is given for the
- * type DataTables expects to get the raw data for the object back<
- * * `{*}` Data to set when the second parameter is 'set'.
- * * Return:
- * * The return value from the function is not required when 'set' is
- * the type of call, but otherwise the return is what will be used
- * for the data requested.
- *
- * Note that `data` is a getter and setter option. If you just require
- * formatting of data for output, you will likely want to use `render` which
- * is simply a getter and thus simpler to use.
- *
- * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
- * name change reflects the flexibility of this property and is consistent
- * with the naming of mRender. If 'mDataProp' is given, then it will still
- * be used by DataTables, as it automatically maps the old name to the new
- * if required.
- *
- * @type string|int|function|null
- * @default null <i>Use automatically calculated column index</i>
- *
- * @name DataTable.defaults.column.data
- * @dtopt Columns
- *
- * @example
- * // Read table data from objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {value},
- * // "version": {value},
- * // "grade": {value}
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/objects.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform" },
- * { "data": "version" },
- * { "data": "grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Read information from deeply nested objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {
- * // "inner": {value}
- * // },
- * // "details": [
- * // {value}, {value}
- * // ]
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform.inner" },
- * { "data": "details.0" },
- * { "data": "details.1" }
- * ]
- * } );
- * } );
+ * @default <i>Empty string</i>
*
- * @example
- * // Using `data` as a function to provide different information for
- * // sorting, filtering and display. In this case, currency (price)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": function ( source, type, val ) {
- * if (type === 'set') {
- * source.price = val;
- * // Store the computed display and filter values for efficiency
- * source.price_display = val=="" ? "" : "$"+numberFormat(val);
- * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
- * return;
- * }
- * else if (type === 'display') {
- * return source.price_display;
- * }
- * else if (type === 'filter') {
- * return source.price_filter;
- * }
- * // 'sort', 'type' and undefined all just use the integer
- * return source.price;
- * }
- * } ]
- * } );
- * } );
+ * @dtopt Language
+ * @name DataTable.defaults.language.infoPostFix
*
* @example
- * // Using default content
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null,
- * "defaultContent": "Click to edit"
- * } ]
- * } );
- * } );
- *
- * @example
- * // Using array notation - outputting a list from an array
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "name[, ]"
- * } ]
+ * "language": {
+ * "infoPostFix": "All records shown are derived from real information."
+ * }
* } );
* } );
- *
*/
- "mData": null,
-
-
+ "sInfoPostFix": "",
+
+
/**
- * This property is the rendering partner to `data` and it is suggested that
- * when you want to manipulate data for display (including filtering,
- * sorting etc) without altering the underlying data for the table, use this
- * property. `render` can be considered to be the the read only companion to
- * `data` which is read / write (then as such more complex). Like `data`
- * this option can be given in a number of different ways to effect its
- * behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object.
- * * `object` - use different data for the different data types requested by
- * DataTables ('filter', 'display', 'type' or 'sort'). The property names
- * of the object is the data type the property refers to and the value can
- * defined using an integer, string or function using the same rules as
- * `render` normally does. Note that an `_` option _must_ be specified.
- * This is the default value to use if you haven't specified a value for
- * the data type requested by DataTables.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * {array|object} The data source for the row (based on `data`)
- * * {string} The type call data requested - this will be 'filter',
- * 'display', 'type' or 'sort'.
- * * {array|object} The full data source for the row (not based on
- * `data`)
- * * Return:
- * * The return value from the function is what will be used for the
- * data requested.
+ * This decimal place operator is a little different from the other
+ * language options since DataTables doesn't output floating point
+ * numbers, so it won't ever use this for display of a number. Rather,
+ * what this parameter does is modify the sort methods of the table so
+ * that numbers which are in a format which has a character other than
+ * a period (`.`) as a decimal place will be sorted numerically.
*
- * @type string|int|function|object|null
- * @default null Use the data source value.
- *
- * @name DataTable.defaults.column.render
- * @dtopt Columns
- *
- * @example
- * // Create a comma separated list from an array of objects
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * {
- * "data": "platform",
- * "render": "[, ].name"
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Execute a function to obtain data
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": "browserName()"
- * } ]
- * } );
- * } );
+ * Note that numbers with different decimal places cannot be shown in
+ * the same table and still be sortable, the table must be consistent.
+ * However, multiple different tables on the page can use different
+ * decimal place characters.
+ * @type string
+ * @default
*
- * @example
- * // As an object, extracting different data for the different types
- * // This would be used with a data source such as:
- * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
- * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
- * // (which has both forms) is used for filtering for if a user inputs either format, while
- * // the formatted phone number is the one that is shown in the table.
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": {
- * "_": "phone",
- * "filter": "phone_filter",
- * "display": "phone_display"
- * }
- * } ]
- * } );
- * } );
+ * @dtopt Language
+ * @name DataTable.defaults.language.decimal
*
* @example
- * // Use as a function to create a link from the data source
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "download_link",
- * "render": function ( data, type, full ) {
- * return '<a href="'+data+'">Download</a>';
- * }
- * } ]
+ * "language": {
+ * "decimal": ","
+ * "thousands": "."
+ * }
* } );
* } );
*/
- "mRender": null,
-
-
+ "sDecimal": "",
+
+
/**
- * Change the cell type created for the column - either TD cells or TH cells. This
- * can be useful as TH cells have semantic meaning in the table body, allowing them
- * to act as a header for a row (you may wish to add scope='row' to the TH elements).
+ * DataTables has a build in number formatter (`formatNumber`) which is
+ * used to format large numbers that are used in the table information.
+ * By default a comma is used, but this can be trivially changed to any
+ * character you wish with this parameter.
* @type string
- * @default td
+ * @default ,
*
- * @name DataTable.defaults.column.cellType
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.thousands
*
* @example
- * // Make the first column use TH cells
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "cellType": "th"
- * } ]
+ * "language": {
+ * "thousands": "'"
+ * }
* } );
* } );
*/
- "sCellType": "td",
-
-
+ "sThousands": ",",
+
+
/**
- * Class to give to each cell in this column.
+ * Detail the action that will be taken when the drop down menu for the
+ * pagination length option is changed. The '_MENU_' variable is replaced
+ * with a default select list of 10, 25, 50 and 100, and can be replaced
+ * with a custom select box if required.
* @type string
- * @default <i>Empty string</i>
+ * @default Show _MENU_ entries
*
- * @name DataTable.defaults.column.class
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.lengthMenu
*
* @example
- * // Using `columnDefs`
+ * // Language change only
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [
- * { "class": "my_class", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "class": "my_class" },
- * null,
- * null,
- * null,
- * null
- * ]
+ * "language": {
+ * "lengthMenu": "Display _MENU_ records"
+ * }
* } );
* } );
- */
- "sClass": "",
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * Generally you shouldn't need this!
- * @type string
- * @default <i>Empty string<i>
- *
- * @name DataTable.defaults.column.contentPadding
- * @dtopt Columns
*
* @example
- * // Using `columns`
+ * // Language and options change
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "contentPadding": "mmm"
- * }
- * ]
+ * "language": {
+ * "lengthMenu": 'Display <select>'+
+ * '<option value="10">10</option>'+
+ * '<option value="20">20</option>'+
+ * '<option value="30">30</option>'+
+ * '<option value="40">40</option>'+
+ * '<option value="50">50</option>'+
+ * '<option value="-1">All</option>'+
+ * '</select> records'
+ * }
* } );
* } );
*/
- "sContentPadding": "",
-
-
+ "sLengthMenu": "Show _MENU_ entries",
+
+
/**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because `data`
- * is set to null, or because the data source itself is null).
+ * When using Ajax sourced data and during the first draw when DataTables is
+ * gathering the data, this message is shown in an empty row in the table to
+ * indicate to the end user the the data is being loaded. Note that this
+ * parameter is not used when loading data by server-side processing, just
+ * Ajax sourced data with client-side processing.
* @type string
- * @default null
+ * @default Loading...
*
- * @name DataTable.defaults.column.defaultContent
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.loadingRecords
*
* @example
- * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [
- * {
- * "data": null,
- * "defaultContent": "Edit",
- * "targets": [ -1 ]
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "data": null,
- * "defaultContent": "Edit"
- * }
- * ]
+ * "language": {
+ * "loadingRecords": "Please wait - loading..."
+ * }
* } );
* } );
*/
- "sDefaultContent": null,
-
-
+ "sLoadingRecords": "Loading...",
+
+
/**
- * This parameter is only used in DataTables' server-side processing. It can
- * be exceptionally useful to know what columns are being displayed on the
- * client side, and to map these to database fields. When defined, the names
- * also allow DataTables to reorder information from the server if it comes
- * back in an unexpected order (i.e. if you switch your columns around on the
- * client-side, your server-side code does not also need updating).
+ * Text which is displayed when the table is processing a user action
+ * (usually a sort command or similar).
* @type string
- * @default <i>Empty string</i>
*
- * @name DataTable.defaults.column.name
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.processing
*
* @example
- * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [
- * { "name": "engine", "targets": [ 0 ] },
- * { "name": "browser", "targets": [ 1 ] },
- * { "name": "platform", "targets": [ 2 ] },
- * { "name": "version", "targets": [ 3 ] },
- * { "name": "grade", "targets": [ 4 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "name": "engine" },
- * { "name": "browser" },
- * { "name": "platform" },
- * { "name": "version" },
- * { "name": "grade" }
- * ]
+ * "language": {
+ * "processing": "DataTables is currently busy"
+ * }
* } );
* } );
*/
- "sName": "",
-
-
+ "sProcessing": "",
+
+
/**
- * Defines a data source type for the ordering which can be used to read
- * real-time information from the table (updating the internally cached
- * version) prior to ordering. This allows ordering to occur on user
- * editable elements such as form inputs.
+ * Details the actions that will be taken when the user types into the
+ * filtering input text box. The variable "_INPUT_", if used in the string,
+ * is replaced with the HTML text box for the filtering input allowing
+ * control over where it appears in the string. If "_INPUT_" is not given
+ * then the input box is appended to the string automatically.
* @type string
- * @default std
+ * @default Search:
*
- * @name DataTable.defaults.column.orderDataType
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.search
*
* @example
- * // Using `columnDefs`
+ * // Input text box will be appended at the end automatically
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [
- * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
- * { "type": "numeric", "targets": [ 3 ] },
- * { "orderDataType": "dom-select", "targets": [ 4 ] },
- * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
- * ]
+ * "language": {
+ * "search": "Filter records:"
+ * }
* } );
* } );
*
* @example
- * // Using `columns`
+ * // Specify where the filter should appear
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * { "orderDataType": "dom-text" },
- * { "orderDataType": "dom-text", "type": "numeric" },
- * { "orderDataType": "dom-select" },
- * { "orderDataType": "dom-checkbox" }
- * ]
+ * "language": {
+ * "search": "Apply filter _INPUT_ to table"
+ * }
* } );
* } );
*/
- "sSortDataType": "std",
-
-
+ "sSearch": "Search:",
+
+
/**
- * The title of this column.
+ * Assign a `placeholder` attribute to the search `input` element
* @type string
- * @default null <i>Derived from the 'TH' value for this column in the
- * original HTML table.</i>
+ * @default
*
- * @name DataTable.defaults.column.title
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "title": "My column title", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "title": "My column title" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
+ * @dtopt Language
+ * @name DataTable.defaults.language.searchPlaceholder
*/
- "sTitle": null,
-
-
+ "sSearchPlaceholder": "",
+
+
/**
- * The type allows you to specify how the data for this column will be
- * ordered. Four types (string, numeric, date and html (which will strip
- * HTML tags before ordering)) are currently available. Note that only date
- * formats understood by Javascript's Date() object will be accepted as type
- * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
- * 'numeric', 'date' or 'html' (by default). Further types can be adding
- * through plug-ins.
+ * All of the language information can be stored in a file on the
+ * server-side, which DataTables will look up if this parameter is passed.
+ * It must store the URL of the language file, which is in a JSON format,
+ * and the object has the same properties as the oLanguage object in the
+ * initialiser object (i.e. the above parameters). Please refer to one of
+ * the example language files to see how this works in action.
* @type string
- * @default null <i>Auto-detected from raw data</i>
+ * @default <i>Empty string - i.e. disabled</i>
*
- * @name DataTable.defaults.column.type
- * @dtopt Columns
+ * @dtopt Language
+ * @name DataTable.defaults.language.url
*
* @example
- * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columnDefs": [
- * { "type": "html", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "type": "html" },
- * null,
- * null,
- * null,
- * null
- * ]
+ * "language": {
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
+ * }
* } );
* } );
*/
- "sType": null,
-
-
+ "sUrl": "",
+
+
/**
- * Defining the width of the column, this parameter may take any CSS value
- * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
- * been given a specific width through this interface ensuring that the table
- * remains readable.
+ * Text shown inside the table records when the is no information to be
+ * displayed after filtering. `emptyTable` is shown when there is simply no
+ * information in the table at all (regardless of filtering).
* @type string
- * @default null <i>Automatic</i>
- *
- * @name DataTable.defaults.column.width
- * @dtopt Columns
+ * @default No matching records found
*
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "width": "20%", "targets": [ 0 ] }
- * ]
- * } );
- * } );
+ * @dtopt Language
+ * @name DataTable.defaults.language.zeroRecords
*
* @example
- * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "columns": [
- * { "width": "20%" },
- * null,
- * null,
- * null,
- * null
- * ]
+ * "language": {
+ * "zeroRecords": "No records to display"
+ * }
* } );
* } );
*/
- "sWidth": null
- };
-
- _fnHungarianMap( DataTable.defaults.column );
-
-
-
+ "sZeroRecords": "No matching records found"
+ },
+
+
/**
- * DataTables settings object - this holds all the information needed for a
- * given table, including configuration, data and current application of the
- * table options. DataTables does not have a single instance for each DataTable
- * with the settings attached to that instance, but rather instances of the
- * DataTable "class" are created on-the-fly as needed (typically by a
- * $().dataTable() call) and the settings object is then applied to that
- * instance.
- *
- * Note that this object is related to {@link DataTable.defaults} but this
- * one is the internal data store for DataTables's cache of columns. It should
- * NOT be manipulated outside of DataTables. Any configuration should be done
- * through the initialisation options.
+ * This parameter allows you to have define the global filtering state at
+ * initialisation time. As an object the `search` parameter must be
+ * defined, but all other parameters are optional. When `regex` is true,
+ * the search string will be treated as a regular expression, when false
+ * (default) it will be treated as a straight string. When `smart`
+ * DataTables will use it's smart filtering methods (to word match at
+ * any point in the data), when false this will not be done.
* @namespace
- * @todo Really should attach the settings object to individual instances so we
- * don't need to create new instances on each $().dataTable() call (if the
- * table already exists). It would also save passing oSettings around and
- * into every single function. However, this is a very significant
- * architecture change for DataTables and will almost certainly break
- * backwards compatibility with older installations. This is something that
- * will be done in 2.0.
- */
- DataTable.models.oSettings = {
- /**
- * Primary features of DataTables and their enablement state.
- * @namespace
- */
- "oFeatures": {
-
- /**
- * Flag to say if DataTables should automatically try to calculate the
- * optimum table and columns widths (true) or not (false).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bAutoWidth": null,
-
- /**
- * Delay the creation of TR and TD elements until they are actually
- * needed by a driven page draw. This can give a significant speed
- * increase for Ajax source and Javascript source data, but makes no
- * difference at all for DOM and server-side processing tables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bDeferRender": null,
-
- /**
- * Enable filtering on the table or not. Note that if this is disabled
- * then there is no filtering at all on the table, including fnFilter.
- * To just remove the filtering input use sDom and remove the 'f' option.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bFilter": null,
-
- /**
- * Table information element (the 'Showing x of y records' div) enable
- * flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bInfo": null,
-
- /**
- * Present a user control allowing the end user to change the page size
- * when pagination is enabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bLengthChange": null,
-
- /**
- * Pagination enabled or not. Note that if this is disabled then length
- * changing must also be disabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bPaginate": null,
-
- /**
- * Processing indicator enable flag whenever DataTables is enacting a
- * user request - typically an Ajax request for server-side processing.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bProcessing": null,
-
- /**
- * Server-side processing enabled flag - when enabled DataTables will
- * get all data from the server for every draw - there is no filtering,
- * sorting or paging done on the client-side.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bServerSide": null,
-
- /**
- * Sorting enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSort": null,
-
- /**
- * Multi-column sorting
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortMulti": null,
-
- /**
- * Apply a class to the columns which are being sorted to provide a
- * visual highlight or not. This can slow things down when enabled since
- * there is a lot of DOM interaction.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortClasses": null,
-
- /**
- * State saving enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bStateSave": null
- },
-
-
- /**
- * Scrolling settings for a table.
- * @namespace
- */
- "oScroll": {
- /**
- * When the table is shorter in height than sScrollY, collapse the
- * table container down to the height of the table (when true).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bCollapse": null,
-
- /**
- * Width of the scrollbar for the web-browser's platform. Calculated
- * during table initialisation.
- * @type int
- * @default 0
- */
- "iBarWidth": 0,
-
- /**
- * Viewport width for horizontal scrolling. Horizontal scrolling is
- * disabled if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sX": null,
-
- /**
- * Width to expand the table to when using x-scrolling. Typically you
- * should not need to use this.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @deprecated
- */
- "sXInner": null,
-
- /**
- * Viewport height for vertical scrolling. Vertical scrolling is disabled
- * if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sY": null
- },
-
- /**
- * Language information for the table.
- * @namespace
- * @extends DataTable.defaults.oLanguage
- */
- "oLanguage": {
- /**
- * Information callback function. See
- * {@link DataTable.defaults.fnInfoCallback}
- * @type function
- * @default null
- */
- "fnInfoCallback": null
- },
-
- /**
- * Browser support parameters
- * @namespace
- */
- "oBrowser": {
- /**
- * Indicate if the browser incorrectly calculates width:100% inside a
- * scrolling element (IE6/7)
- * @type boolean
- * @default false
- */
- "bScrollOversize": false,
-
- /**
- * Determine if the vertical scrollbar is on the right or left of the
- * scrolling container - needed for rtl language layout, although not
- * all browsers move the scrollbar (Safari).
- * @type boolean
- * @default false
- */
- "bScrollbarLeft": false,
-
- /**
- * Flag for if `getBoundingClientRect` is fully supported or not
- * @type boolean
- * @default false
- */
- "bBounding": false,
-
- /**
- * Browser scrollbar width
- * @type integer
- * @default 0
- */
- "barWidth": 0
- },
-
-
- "ajax": null,
-
-
- /**
- * Array referencing the nodes which are used for the features. The
- * parameters of this object match what is allowed by sDom - i.e.
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * @type array
- * @default []
- */
- "aanFeatures": [],
-
- /**
- * Store data information - see {@link DataTable.models.oRow} for detailed
- * information.
- * @type array
- * @default []
- */
- "aoData": [],
-
- /**
- * Array of indexes which are in the current display (after filtering etc)
- * @type array
- * @default []
- */
- "aiDisplay": [],
-
- /**
- * Array of indexes for display - no filtering
- * @type array
- * @default []
- */
- "aiDisplayMaster": [],
-
- /**
- * Map of row ids to data indexes
- * @type object
- * @default {}
- */
- "aIds": {},
-
- /**
- * Store information about each column that is in use
- * @type array
- * @default []
- */
- "aoColumns": [],
-
- /**
- * Store information about the table's header
- * @type array
- * @default []
- */
- "aoHeader": [],
-
- /**
- * Store information about the table's footer
- * @type array
- * @default []
- */
- "aoFooter": [],
-
+ * @extends DataTable.models.oSearch
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.search
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "search": {"search": "Initial search"}
+ * } );
+ * } )
+ */
+ "oSearch": $.extend( {}, DataTable.models.oSearch ),
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * By default DataTables will look for the property `data` (or `aaData` for
+ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
+ * source or for server-side processing - this parameter allows that
+ * property to be changed. You can use Javascript dotted object notation to
+ * get a data source for multiple levels of nesting.
+ * @type string
+ * @default data
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.ajaxDataProp
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sAjaxDataProp": "data",
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * You can instruct DataTables to load data from an external
+ * source using this parameter (use aData if you want to pass data in you
+ * already have). Simply provide a url a JSON object can be obtained from.
+ * @type string
+ * @default null
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.ajaxSource
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sAjaxSource": null,
+
+
+ /**
+ * This initialisation variable allows you to specify exactly where in the
+ * DOM you want DataTables to inject the various controls it adds to the page
+ * (for example you might want the pagination controls at the top of the
+ * table). DIV elements (with or without a custom class) can also be added to
+ * aid styling. The follow syntax is used:
+ * <ul>
+ * <li>The following options are allowed:
+ * <ul>
+ * <li>'l' - Length changing</li>
+ * <li>'f' - Filtering input</li>
+ * <li>'t' - The table!</li>
+ * <li>'i' - Information</li>
+ * <li>'p' - Pagination</li>
+ * <li>'r' - pRocessing</li>
+ * </ul>
+ * </li>
+ * <li>The following constants are allowed:
+ * <ul>
+ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
+ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
+ * </ul>
+ * </li>
+ * <li>The following syntax is expected:
+ * <ul>
+ * <li>'&lt;' and '&gt;' - div elements</li>
+ * <li>'&lt;"class" and '&gt;' - div with a class</li>
+ * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
+ * </ul>
+ * </li>
+ * <li>Examples:
+ * <ul>
+ * <li>'&lt;"wrapper"flipt&gt;'</li>
+ * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @type string
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.dom
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
+ * } );
+ * } );
+ */
+ "sDom": "lfrtip",
+
+
+ /**
+ * Search delay option. This will throttle full table searches that use the
+ * DataTables provided search input element (it does not effect calls to
+ * `dt-api search()`, providing a delay before the search is made.
+ * @type integer
+ * @default 0
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.searchDelay
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "searchDelay": 200
+ * } );
+ * } )
+ */
+ "searchDelay": null,
+
+
+ /**
+ * DataTables features six different built-in options for the buttons to
+ * display for pagination control:
+ *
+ * * `numbers` - Page number buttons only
+ * * `simple` - 'Previous' and 'Next' buttons only
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
+ * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
+ *
+ * Further methods can be added using {@link DataTable.ext.oPagination}.
+ * @type string
+ * @default simple_numbers
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.pagingType
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "pagingType": "full_numbers"
+ * } );
+ * } )
+ */
+ "sPaginationType": "simple_numbers",
+
+
+ /**
+ * Enable horizontal scrolling. When a table is too wide to fit into a
+ * certain layout, or you have a large number of columns in the table, you
+ * can enable x-scrolling to show the table in a viewport, which can be
+ * scrolled. This property can be `true` which will allow the table to
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
+ * case it will be treated as a pixel measurement). Setting as simply `true`
+ * is recommended.
+ * @type boolean|string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.scrollX
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollX": true,
+ * "scrollCollapse": true
+ * } );
+ * } );
+ */
+ "sScrollX": "",
+
+
+ /**
+ * This property can be used to force a DataTable to use more width than it
+ * might otherwise do when x-scrolling is enabled. For example if you have a
+ * table which requires to be well spaced, this parameter is useful for
+ * "over-sizing" the table, and thus forcing scrolling. This property can by
+ * any CSS unit, or a number (in which case it will be treated as a pixel
+ * measurement).
+ * @type string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.scrollXInner
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollX": "100%",
+ * "scrollXInner": "110%"
+ * } );
+ * } );
+ */
+ "sScrollXInner": "",
+
+
+ /**
+ * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
+ * to the given height, and enable scrolling for any data which overflows the
+ * current viewport. This can be used as an alternative to paging to display
+ * a lot of data in a small area (although paging and scrolling can both be
+ * enabled at the same time). This property can be any CSS unit, or a number
+ * (in which case it will be treated as a pixel measurement).
+ * @type string
+ * @default <i>blank string - i.e. disabled</i>
+ *
+ * @dtopt Features
+ * @name DataTable.defaults.scrollY
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "scrollY": "200px",
+ * "paginate": false
+ * } );
+ * } );
+ */
+ "sScrollY": "",
+
+
+ /**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * Set the HTTP method that is used to make the Ajax call for server-side
+ * processing or Ajax sourced data.
+ * @type string
+ * @default GET
+ *
+ * @dtopt Options
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverMethod
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
+ */
+ "sServerMethod": "GET",
+
+
+ /**
+ * DataTables makes use of renderers when displaying HTML elements for
+ * a table. These renderers can be added or modified by plug-ins to
+ * generate suitable mark-up for a site. For example the Bootstrap
+ * integration plug-in for DataTables uses a paging button renderer to
+ * display pagination buttons in the mark-up required by Bootstrap.
+ *
+ * For further information about the renderers available see
+ * DataTable.ext.renderer
+ * @type string|object
+ * @default null
+ *
+ * @name DataTable.defaults.renderer
+ *
+ */
+ "renderer": null,
+
+
+ /**
+ * Set the data property name that DataTables should use to get a row's id
+ * to set as the `id` property in the node.
+ * @type string
+ * @default DT_RowId
+ *
+ * @name DataTable.defaults.rowId
+ */
+ "rowId": "DT_RowId"
+ };
+
+ _fnHungarianMap( DataTable.defaults );
+
+
+
+ /*
+ * Developer note - See note in model.defaults.js about the use of Hungarian
+ * notation and camel case.
+ */
+
+ /**
+ * Column options that can be given to DataTables at initialisation time.
+ * @namespace
+ */
+ DataTable.defaults.column = {
+ /**
+ * Define which column(s) an order will occur on for this column. This
+ * allows a column's ordering to take multiple columns into account when
+ * doing a sort or use the data from a different column. For example first
+ * name / last name columns make sense to do a multi-column sort over the
+ * two columns.
+ * @type array|int
+ * @default null <i>Takes the value of the column index automatically</i>
+ *
+ * @name DataTable.defaults.column.orderData
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
+ * { "orderData": 2, "targets": [ 2 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "orderData": [ 0, 1 ] },
+ * { "orderData": [ 1, 0 ] },
+ * { "orderData": 2 },
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "aDataSort": null,
+ "iDataSort": -1,
+
+
+ /**
+ * You can control the default ordering direction, and even alter the
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
+ * using this parameter.
+ * @type array
+ * @default [ 'asc', 'desc' ]
+ *
+ * @name DataTable.defaults.column.orderSequence
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * { "orderSequence": [ "asc" ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
+ * { "orderSequence": [ "desc" ] },
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "asSorting": [ 'asc', 'desc' ],
+
+
+ /**
+ * Enable or disable filtering on the data in this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.searchable
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "searchable": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "searchable": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bSearchable": true,
+
+
+ /**
+ * Enable or disable ordering on this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.orderable
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderable": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "orderable": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bSortable": true,
+
+
+ /**
+ * Enable or disable the display of this column.
+ * @type boolean
+ * @default true
+ *
+ * @name DataTable.defaults.column.visible
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "visible": false, "targets": [ 0 ] }
+ * ] } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "visible": false },
+ * null,
+ * null,
+ * null,
+ * null
+ * ] } );
+ * } );
+ */
+ "bVisible": true,
+
+
+ /**
+ * Developer definable function that is called whenever a cell is created (Ajax source,
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
+ * allowing you to modify the DOM element (add background colour for example) when the
+ * element is available.
+ * @type function
+ * @param {element} td The TD node that has been created
+ * @param {*} cellData The Data for the cell
+ * @param {array|object} rowData The data for the whole row
+ * @param {int} row The row index for the aoData data store
+ * @param {int} col The column index for aoColumns
+ *
+ * @name DataTable.defaults.column.createdCell
+ * @dtopt Columns
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [3],
+ * "createdCell": function (td, cellData, rowData, row, col) {
+ * if ( cellData == "1.7" ) {
+ * $(td).css('color', 'blue')
+ * }
+ * }
+ * } ]
+ * });
+ * } );
+ */
+ "fnCreatedCell": null,
+
+
+ /**
+ * This parameter has been replaced by `data` in DataTables to ensure naming
+ * consistency. `dataProp` can still be used, as there is backwards
+ * compatibility in DataTables for this option, but it is strongly
+ * recommended that you use `data` in preference to `dataProp`.
+ * @name DataTable.defaults.column.dataProp
+ */
+
+
+ /**
+ * This property can be used to read data from any data source property,
+ * including deeply nested objects / properties. `data` can be given in a
+ * number of different ways which effect its behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object. Note that
+ * function notation is recommended for use in `render` rather than
+ * `data` as it is much simpler to use as a renderer.
+ * * `null` - use the original data source for the row rather than plucking
+ * data directly from it. This action has effects on two other
+ * initialisation options:
+ * * `defaultContent` - When null is given as the `data` option and
+ * `defaultContent` is specified for the column, the value defined by
+ * `defaultContent` will be used for the cell.
+ * * `render` - When null is used for the `data` option and the `render`
+ * option is specified for the column, the whole data source for the
+ * row is used for the renderer.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * `{array|object}` The data source for the row
+ * * `{string}` The type call data requested - this will be 'set' when
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
+ * when gathering data. Note that when `undefined` is given for the
+ * type DataTables expects to get the raw data for the object back<
+ * * `{*}` Data to set when the second parameter is 'set'.
+ * * Return:
+ * * The return value from the function is not required when 'set' is
+ * the type of call, but otherwise the return is what will be used
+ * for the data requested.
+ *
+ * Note that `data` is a getter and setter option. If you just require
+ * formatting of data for output, you will likely want to use `render` which
+ * is simply a getter and thus simpler to use.
+ *
+ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
+ * name change reflects the flexibility of this property and is consistent
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
+ * be used by DataTables, as it automatically maps the old name to the new
+ * if required.
+ *
+ * @type string|int|function|null
+ * @default null <i>Use automatically calculated column index</i>
+ *
+ * @name DataTable.defaults.column.data
+ * @dtopt Columns
+ *
+ * @example
+ * // Read table data from objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {value},
+ * // "version": {value},
+ * // "grade": {value}
+ * // }
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/objects.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform" },
+ * { "data": "version" },
+ * { "data": "grade" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Read information from deeply nested objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {
+ * // "inner": {value}
+ * // },
+ * // "details": [
+ * // {value}, {value}
+ * // ]
+ * // }
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform.inner" },
+ * { "data": "details.0" },
+ * { "data": "details.1" }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `data` as a function to provide different information for
+ * // sorting, filtering and display. In this case, currency (price)
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": function ( source, type, val ) {
+ * if (type === 'set') {
+ * source.price = val;
+ * // Store the computed display and filter values for efficiency
+ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
+ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
+ * return;
+ * }
+ * else if (type === 'display') {
+ * return source.price_display;
+ * }
+ * else if (type === 'filter') {
+ * return source.price_filter;
+ * }
+ * // 'sort', 'type' and undefined all just use the integer
+ * return source.price;
+ * }
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using default content
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null,
+ * "defaultContent": "Click to edit"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using array notation - outputting a list from an array
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "name[, ]"
+ * } ]
+ * } );
+ * } );
+ *
+ */
+ "mData": null,
+
+
+ /**
+ * This property is the rendering partner to `data` and it is suggested that
+ * when you want to manipulate data for display (including filtering,
+ * sorting etc) without altering the underlying data for the table, use this
+ * property. `render` can be considered to be the the read only companion to
+ * `data` which is read / write (then as such more complex). Like `data`
+ * this option can be given in a number of different ways to effect its
+ * behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object.
+ * * `object` - use different data for the different data types requested by
+ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
+ * of the object is the data type the property refers to and the value can
+ * defined using an integer, string or function using the same rules as
+ * `render` normally does. Note that an `_` option _must_ be specified.
+ * This is the default value to use if you haven't specified a value for
+ * the data type requested by DataTables.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * {array|object} The data source for the row (based on `data`)
+ * * {string} The type call data requested - this will be 'filter',
+ * 'display', 'type' or 'sort'.
+ * * {array|object} The full data source for the row (not based on
+ * `data`)
+ * * Return:
+ * * The return value from the function is what will be used for the
+ * data requested.
+ *
+ * @type string|int|function|object|null
+ * @default null Use the data source value.
+ *
+ * @name DataTable.defaults.column.render
+ * @dtopt Columns
+ *
+ * @example
+ * // Create a comma separated list from an array of objects
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * {
+ * "data": "platform",
+ * "render": "[, ].name"
+ * }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Execute a function to obtain data
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": "browserName()"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // As an object, extracting different data for the different types
+ * // This would be used with a data source such as:
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
+ * // the formatted phone number is the one that is shown in the table.
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": {
+ * "_": "phone",
+ * "filter": "phone_filter",
+ * "display": "phone_display"
+ * }
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Use as a function to create a link from the data source
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "download_link",
+ * "render": function ( data, type, full ) {
+ * return '<a href="'+data+'">Download</a>';
+ * }
+ * } ]
+ * } );
+ * } );
+ */
+ "mRender": null,
+
+
+ /**
+ * Change the cell type created for the column - either TD cells or TH cells. This
+ * can be useful as TH cells have semantic meaning in the table body, allowing them
+ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
+ * @type string
+ * @default td
+ *
+ * @name DataTable.defaults.column.cellType
+ * @dtopt Columns
+ *
+ * @example
+ * // Make the first column use TH cells
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "cellType": "th"
+ * } ]
+ * } );
+ * } );
+ */
+ "sCellType": "td",
+
+
+ /**
+ * Class to give to each cell in this column.
+ * @type string
+ * @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.class
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "class": "my_class", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "class": "my_class" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sClass": "",
+
+ /**
+ * When DataTables calculates the column widths to assign to each column,
+ * it finds the longest string in each column and then constructs a
+ * temporary table and reads the widths from that. The problem with this
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * string - thus the calculation can go wrong (doing it properly and putting
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
+ * a "work around" we provide this option. It will append its value to the
+ * text that is found to be the longest string for the column - i.e. padding.
+ * Generally you shouldn't need this!
+ * @type string
+ * @default <i>Empty string<i>
+ *
+ * @name DataTable.defaults.column.contentPadding
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * null,
+ * {
+ * "contentPadding": "mmm"
+ * }
+ * ]
+ * } );
+ * } );
+ */
+ "sContentPadding": "",
+
+
+ /**
+ * Allows a default value to be given for a column's data, and will be used
+ * whenever a null data source is encountered (this can be because `data`
+ * is set to null, or because the data source itself is null).
+ * @type string
+ * @default null
+ *
+ * @name DataTable.defaults.column.defaultContent
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * {
+ * "data": null,
+ * "defaultContent": "Edit",
+ * "targets": [ -1 ]
+ * }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * null,
+ * {
+ * "data": null,
+ * "defaultContent": "Edit"
+ * }
+ * ]
+ * } );
+ * } );
+ */
+ "sDefaultContent": null,
+
+
+ /**
+ * This parameter is only used in DataTables' server-side processing. It can
+ * be exceptionally useful to know what columns are being displayed on the
+ * client side, and to map these to database fields. When defined, the names
+ * also allow DataTables to reorder information from the server if it comes
+ * back in an unexpected order (i.e. if you switch your columns around on the
+ * client-side, your server-side code does not also need updating).
+ * @type string
+ * @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.name
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "name": "engine", "targets": [ 0 ] },
+ * { "name": "browser", "targets": [ 1 ] },
+ * { "name": "platform", "targets": [ 2 ] },
+ * { "name": "version", "targets": [ 3 ] },
+ * { "name": "grade", "targets": [ 4 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "name": "engine" },
+ * { "name": "browser" },
+ * { "name": "platform" },
+ * { "name": "version" },
+ * { "name": "grade" }
+ * ]
+ * } );
+ * } );
+ */
+ "sName": "",
+
+
+ /**
+ * Defines a data source type for the ordering which can be used to read
+ * real-time information from the table (updating the internally cached
+ * version) prior to ordering. This allows ordering to occur on user
+ * editable elements such as form inputs.
+ * @type string
+ * @default std
+ *
+ * @name DataTable.defaults.column.orderDataType
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
+ * { "type": "numeric", "targets": [ 3 ] },
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * null,
+ * null,
+ * { "orderDataType": "dom-text" },
+ * { "orderDataType": "dom-text", "type": "numeric" },
+ * { "orderDataType": "dom-select" },
+ * { "orderDataType": "dom-checkbox" }
+ * ]
+ * } );
+ * } );
+ */
+ "sSortDataType": "std",
+
+
+ /**
+ * The title of this column.
+ * @type string
+ * @default null <i>Derived from the 'TH' value for this column in the
+ * original HTML table.</i>
+ *
+ * @name DataTable.defaults.column.title
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "title": "My column title", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "title": "My column title" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sTitle": null,
+
+
+ /**
+ * The type allows you to specify how the data for this column will be
+ * ordered. Four types (string, numeric, date and html (which will strip
+ * HTML tags before ordering)) are currently available. Note that only date
+ * formats understood by Javascript's Date() object will be accepted as type
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
+ * through plug-ins.
+ * @type string
+ * @default null <i>Auto-detected from raw data</i>
+ *
+ * @name DataTable.defaults.column.type
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "type": "html", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "type": "html" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sType": null,
+
+
+ /**
+ * Defining the width of the column, this parameter may take any CSS value
+ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
+ * been given a specific width through this interface ensuring that the table
+ * remains readable.
+ * @type string
+ * @default null <i>Automatic</i>
+ *
+ * @name DataTable.defaults.column.width
+ * @dtopt Columns
+ *
+ * @example
+ * // Using `columnDefs`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [
+ * { "width": "20%", "targets": [ 0 ] }
+ * ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using `columns`
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columns": [
+ * { "width": "20%" },
+ * null,
+ * null,
+ * null,
+ * null
+ * ]
+ * } );
+ * } );
+ */
+ "sWidth": null
+ };
+
+ _fnHungarianMap( DataTable.defaults.column );
+
+
+
+ /**
+ * DataTables settings object - this holds all the information needed for a
+ * given table, including configuration, data and current application of the
+ * table options. DataTables does not have a single instance for each DataTable
+ * with the settings attached to that instance, but rather instances of the
+ * DataTable "class" are created on-the-fly as needed (typically by a
+ * $().dataTable() call) and the settings object is then applied to that
+ * instance.
+ *
+ * Note that this object is related to {@link DataTable.defaults} but this
+ * one is the internal data store for DataTables's cache of columns. It should
+ * NOT be manipulated outside of DataTables. Any configuration should be done
+ * through the initialisation options.
+ * @namespace
+ * @todo Really should attach the settings object to individual instances so we
+ * don't need to create new instances on each $().dataTable() call (if the
+ * table already exists). It would also save passing oSettings around and
+ * into every single function. However, this is a very significant
+ * architecture change for DataTables and will almost certainly break
+ * backwards compatibility with older installations. This is something that
+ * will be done in 2.0.
+ */
+ DataTable.models.oSettings = {
+ /**
+ * Primary features of DataTables and their enablement state.
+ * @namespace
+ */
+ "oFeatures": {
+
/**
- * Store the applied global search information in case we want to force a
- * research or compare the old search to a new one.
+ * Flag to say if DataTables should automatically try to calculate the
+ * optimum table and columns widths (true) or not (false).
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @namespace
- * @extends DataTable.models.oSearch
- */
- "oPreviousSearch": {},
-
- /**
- * Store the applied search for each column - see
- * {@link DataTable.models.oSearch} for the format that is used for the
- * filtering information for each column.
- * @type array
- * @default []
+ * @type boolean
*/
- "aoPreSearchCols": [],
-
+ "bAutoWidth": null,
+
/**
- * Sorting that is applied to the table. Note that the inner arrays are
- * used in the following manner:
- * <ul>
- * <li>Index 0 - column number</li>
- * <li>Index 1 - current sorting direction</li>
- * </ul>
+ * Delay the creation of TR and TD elements until they are actually
+ * needed by a driven page draw. This can give a significant speed
+ * increase for Ajax source and Javascript source data, but makes no
+ * difference at all for DOM and server-side processing tables.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @todo These inner arrays should really be objects
+ * @type boolean
*/
- "aaSorting": null,
-
+ "bDeferRender": null,
+
/**
- * Sorting that is always applied to the table (i.e. prefixed in front of
- * aaSorting).
+ * Enable filtering on the table or not. Note that if this is disabled
+ * then there is no filtering at all on the table, including fnFilter.
+ * To just remove the filtering input use sDom and remove the 'f' option.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
+ * @type boolean
*/
- "aaSortingFixed": [],
-
+ "bFilter": null,
+
/**
- * Classes to use for the striping of a table.
+ * Table information element (the 'Showing x of y records' div) enable
+ * flag.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "asStripeClasses": null,
-
- /**
- * If restoring a table - we should restore its striping classes as well
- * @type array
- * @default []
- */
- "asDestroyStripes": [],
-
- /**
- * If restoring a table - we should restore its width
- * @type int
- * @default 0
- */
- "sDestroyWidth": 0,
-
- /**
- * Callback functions array for every time a row is inserted (i.e. on a draw).
- * @type array
- * @default []
- */
- "aoRowCallback": [],
-
- /**
- * Callback functions for the header on each draw.
- * @type array
- * @default []
- */
- "aoHeaderCallback": [],
-
- /**
- * Callback function for the footer on each draw.
- * @type array
- * @default []
- */
- "aoFooterCallback": [],
-
- /**
- * Array of callback functions for draw callback functions
- * @type array
- * @default []
- */
- "aoDrawCallback": [],
-
- /**
- * Array of callback functions for row created function
- * @type array
- * @default []
- */
- "aoRowCreatedCallback": [],
-
- /**
- * Callback functions for just before the table is redrawn. A return of
- * false will be used to cancel the draw.
- * @type array
- * @default []
- */
- "aoPreDrawCallback": [],
-
- /**
- * Callback functions for when the table has been initialised.
- * @type array
- * @default []
- */
- "aoInitComplete": [],
-
-
- /**
- * Callbacks for modifying the settings to be stored for state saving, prior to
- * saving state.
- * @type array
- * @default []
- */
- "aoStateSaveParams": [],
-
- /**
- * Callbacks for modifying the settings that have been stored for state saving
- * prior to using the stored values to restore the state.
- * @type array
- * @default []
- */
- "aoStateLoadParams": [],
-
- /**
- * Callbacks for operating on the settings object once the saved state has been
- * loaded
- * @type array
- * @default []
- */
- "aoStateLoaded": [],
-
- /**
- * Cache the table ID for quick access
- * @type string
- * @default <i>Empty string</i>
- */
- "sTableId": "",
-
- /**
- * The TABLE node for the main table
- * @type node
- * @default null
- */
- "nTable": null,
-
- /**
- * Permanent ref to the thead element
- * @type node
- * @default null
- */
- "nTHead": null,
-
- /**
- * Permanent ref to the tfoot element - if it exists
- * @type node
- * @default null
- */
- "nTFoot": null,
-
- /**
- * Permanent ref to the tbody element
- * @type node
- * @default null
- */
- "nTBody": null,
-
- /**
- * Cache the wrapper node (contains all DataTables controlled elements)
- * @type node
- * @default null
+ * @type boolean
*/
- "nTableWrapper": null,
-
+ "bInfo": null,
+
/**
- * Indicate if when using server-side processing the loading of data
- * should be deferred until the second draw.
+ * Present a user control allowing the end user to change the page size
+ * when pagination is enabled.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type boolean
- * @default false
*/
- "bDeferLoading": false,
-
+ "bLengthChange": null,
+
/**
- * Indicate if all required information has been read in
+ * Pagination enabled or not. Note that if this is disabled then length
+ * changing must also be disabled.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
* @type boolean
- * @default false
*/
- "bInitialised": false,
-
- /**
- * Information about open rows. Each object in the array has the parameters
- * 'nTr' and 'nParent'
- * @type array
- * @default []
- */
- "aoOpenRows": [],
-
+ "bPaginate": null,
+
/**
- * Dictate the positioning of DataTables' control elements - see
- * {@link DataTable.model.oInit.sDom}.
+ * Processing indicator enable flag whenever DataTables is enacting a
+ * user request - typically an Ajax request for server-side processing.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
- */
- "sDom": null,
-
- /**
- * Search delay (in mS)
- * @type integer
- * @default null
+ * @type boolean
*/
- "searchDelay": null,
-
+ "bProcessing": null,
+
/**
- * Which type of pagination should be used.
+ * Server-side processing enabled flag - when enabled DataTables will
+ * get all data from the server for every draw - there is no filtering,
+ * sorting or paging done on the client-side.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default two_button
+ * @type boolean
*/
- "sPaginationType": "two_button",
-
+ "bServerSide": null,
+
/**
- * The state duration (for `stateSave`) in seconds.
+ * Sorting enablement flag.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type int
- * @default 0
- */
- "iStateDuration": 0,
-
- /**
- * Array of callback functions for state saving. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the JSON string to save that has been thus far created. Returns
- * a JSON string to be inserted into a json object
- * (i.e. '"param": [ 0, 1, 2]')</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateSave": [],
-
- /**
- * Array of callback functions for state loading. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the object stored. May return false to cancel state loading</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateLoad": [],
-
- /**
- * State that was saved. Useful for back reference
- * @type object
- * @default null
- */
- "oSavedState": null,
-
- /**
- * State that was loaded. Useful for back reference
- * @type object
- * @default null
+ * @type boolean
*/
- "oLoadedState": null,
-
+ "bSort": null,
+
/**
- * Source url for AJAX data for the table.
+ * Multi-column sorting
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
+ * @type boolean
*/
- "sAjaxSource": null,
-
+ "bSortMulti": null,
+
/**
- * Property from a given object from which to read the table data from. This
- * can be an empty string (when not server-side processing), in which case
- * it is assumed an an array is given directly.
+ * Apply a class to the columns which are being sorted to provide a
+ * visual highlight or not. This can slow things down when enabled since
+ * there is a lot of DOM interaction.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sAjaxDataProp": null,
-
- /**
- * The last jQuery XHR object that was used for server-side data gathering.
- * This can be used for working with the XHR information in one of the
- * callbacks
- * @type object
- * @default null
- */
- "jqXHR": null,
-
- /**
- * JSON returned from the server in the last Ajax request
- * @type object
- * @default undefined
+ * @type boolean
*/
- "json": undefined,
-
+ "bSortClasses": null,
+
/**
- * Data submitted as part of the last Ajax request
- * @type object
- * @default undefined
+ * State saving enablement flag.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
*/
- "oAjaxData": undefined,
-
+ "bStateSave": null
+ },
+
+
+ /**
+ * Scrolling settings for a table.
+ * @namespace
+ */
+ "oScroll": {
/**
- * Function to get the server-side data.
+ * When the table is shorter in height than sScrollY, collapse the
+ * table container down to the height of the table (when true).
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type function
+ * @type boolean
*/
- "fnServerData": null,
-
+ "bCollapse": null,
+
/**
- * Functions which are called prior to sending an Ajax request so extra
- * parameters can easily be sent to the server
- * @type array
- * @default []
+ * Width of the scrollbar for the web-browser's platform. Calculated
+ * during table initialisation.
+ * @type int
+ * @default 0
*/
- "aoServerParams": [],
-
+ "iBarWidth": 0,
+
/**
- * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
- * required).
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
+ * disabled if an empty string.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type string
*/
- "sServerMethod": null,
-
+ "sX": null,
+
/**
- * Format numbers for display.
+ * Width to expand the table to when using x-scrolling. Typically you
+ * should not need to use this.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type function
+ * @type string
+ * @deprecated
*/
- "fnFormatNumber": null,
-
+ "sXInner": null,
+
/**
- * List of options that can be used for the user selectable length menu.
+ * Viewport height for vertical scrolling. Vertical scrolling is disabled
+ * if an empty string.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
+ * @type string
*/
- "aLengthMenu": null,
-
+ "sY": null
+ },
+
+ /**
+ * Language information for the table.
+ * @namespace
+ * @extends DataTable.defaults.oLanguage
+ */
+ "oLanguage": {
/**
- * Counter for the draws that the table does. Also used as a tracker for
- * server-side processing
- * @type int
- * @default 0
+ * Information callback function. See
+ * {@link DataTable.defaults.fnInfoCallback}
+ * @type function
+ * @default null
*/
- "iDraw": 0,
-
+ "fnInfoCallback": null
+ },
+
+ /**
+ * Browser support parameters
+ * @namespace
+ */
+ "oBrowser": {
/**
- * Indicate if a redraw is being done - useful for Ajax
+ * Indicate if the browser incorrectly calculates width:100% inside a
+ * scrolling element (IE6/7)
* @type boolean
* @default false
*/
- "bDrawing": false,
-
- /**
- * Draw index (iDraw) of the last error when parsing the returned data
- * @type int
- * @default -1
- */
- "iDrawError": -1,
-
- /**
- * Paging display length
- * @type int
- * @default 10
- */
- "_iDisplayLength": 10,
-
- /**
- * Paging start point - aiDisplay index
- * @type int
- * @default 0
- */
- "_iDisplayStart": 0,
-
- /**
- * Server-side processing - number of records in the result set
- * (i.e. before filtering), Use fnRecordsTotal rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type int
- * @default 0
- * @private
- */
- "_iRecordsTotal": 0,
-
- /**
- * Server-side processing - number of records in the current display set
- * (i.e. after filtering). Use fnRecordsDisplay rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type boolean
- * @default 0
- * @private
- */
- "_iRecordsDisplay": 0,
-
- /**
- * The classes to use for the table
- * @type object
- * @default {}
- */
- "oClasses": {},
-
+ "bScrollOversize": false,
+
/**
- * Flag attached to the settings object so you can check in the draw
- * callback if filtering has been done in the draw. Deprecated in favour of
- * events.
+ * Determine if the vertical scrollbar is on the right or left of the
+ * scrolling container - needed for rtl language layout, although not
+ * all browsers move the scrollbar (Safari).
* @type boolean
* @default false
- * @deprecated
*/
- "bFiltered": false,
-
+ "bScrollbarLeft": false,
+
/**
- * Flag attached to the settings object so you can check in the draw
- * callback if sorting has been done in the draw. Deprecated in favour of
- * events.
+ * Flag for if `getBoundingClientRect` is fully supported or not
* @type boolean
* @default false
- * @deprecated
- */
- "bSorted": false,
-
- /**
- * Indicate that if multiple rows are in the header and there is more than
- * one unique cell per column, if the top one (true) or bottom one (false)
- * should be used for sorting / title by DataTables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortCellsTop": null,
-
- /**
- * Initialisation object that is used for the table
- * @type object
- * @default null
- */
- "oInit": null,
-
- /**
- * Destroy callback functions - for plug-ins to attach themselves to the
- * destroy so they can clean up markup and events.
- * @type array
- * @default []
- */
- "aoDestroyCallback": [],
-
-
- /**
- * Get the number of records in the current record set, before filtering
- * @type function
- */
- "fnRecordsTotal": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsTotal * 1 :
- this.aiDisplayMaster.length;
- },
-
- /**
- * Get the number of records in the current record set, after filtering
- * @type function
- */
- "fnRecordsDisplay": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsDisplay * 1 :
- this.aiDisplay.length;
- },
-
- /**
- * Get the display end point - aiDisplay index
- * @type function
- */
- "fnDisplayEnd": function ()
- {
- var
- len = this._iDisplayLength,
- start = this._iDisplayStart,
- calc = start + len,
- records = this.aiDisplay.length,
- features = this.oFeatures,
- paginate = features.bPaginate;
-
- if ( features.bServerSide ) {
- return paginate === false || len === -1 ?
- start + records :
- Math.min( start+len, this._iRecordsDisplay );
- }
- else {
- return ! paginate || calc>records || len===-1 ?
- records :
- calc;
- }
- },
-
- /**
- * The DataTables object for this table
- * @type object
- * @default null
- */
- "oInstance": null,
-
- /**
- * Unique identifier for each instance of the DataTables object. If there
- * is an ID on the table node, then it takes that value, otherwise an
- * incrementing internal counter is used.
- * @type string
- * @default null
- */
- "sInstance": null,
-
- /**
- * tabindex attribute value that is added to DataTables control elements, allowing
- * keyboard navigation of the table and its controls.
- */
- "iTabIndex": 0,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollHead": null,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollFoot": null,
-
- /**
- * Last applied sort
- * @type array
- * @default []
- */
- "aLastSort": [],
-
- /**
- * Stored plug-in instances
- * @type object
- * @default {}
*/
- "oPlugins": {},
-
- /**
- * Function used to get a row's id from the row's data
- * @type function
- * @default null
- */
- "rowIdFn": null,
-
+ "bBounding": false,
+
/**
- * Data location where to store a row's id
- * @type string
- * @default null
+ * Browser scrollbar width
+ * @type integer
+ * @default 0
*/
- "rowId": null
- };
-
+ "barWidth": 0
+ },
+
+
+ "ajax": null,
+
+
+ /**
+ * Array referencing the nodes which are used for the features. The
+ * parameters of this object match what is allowed by sDom - i.e.
+ * <ul>
+ * <li>'l' - Length changing</li>
+ * <li>'f' - Filtering input</li>
+ * <li>'t' - The table!</li>
+ * <li>'i' - Information</li>
+ * <li>'p' - Pagination</li>
+ * <li>'r' - pRocessing</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aanFeatures": [],
+
+ /**
+ * Store data information - see {@link DataTable.models.oRow} for detailed
+ * information.
+ * @type array
+ * @default []
+ */
+ "aoData": [],
+
/**
- * Extension object for DataTables that is used to provide all extension
+ * Array of indexes which are in the current display (after filtering etc)
+ * @type array
+ * @default []
+ */
+ "aiDisplay": [],
+
+ /**
+ * Array of indexes for display - no filtering
+ * @type array
+ * @default []
+ */
+ "aiDisplayMaster": [],
+
+ /**
+ * Map of row ids to data indexes
+ * @type object
+ * @default {}
+ */
+ "aIds": {},
+
+ /**
+ * Store information about each column that is in use
+ * @type array
+ * @default []
+ */
+ "aoColumns": [],
+
+ /**
+ * Store information about the table's header
+ * @type array
+ * @default []
+ */
+ "aoHeader": [],
+
+ /**
+ * Store information about the table's footer
+ * @type array
+ * @default []
+ */
+ "aoFooter": [],
+
+ /**
+ * Store the applied global search information in case we want to force a
+ * research or compare the old search to a new one.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @namespace
+ * @extends DataTable.models.oSearch
+ */
+ "oPreviousSearch": {},
+
+ /**
+ * Store the applied search for each column - see
+ * {@link DataTable.models.oSearch} for the format that is used for the
+ * filtering information for each column.
+ * @type array
+ * @default []
+ */
+ "aoPreSearchCols": [],
+
+ /**
+ * Sorting that is applied to the table. Note that the inner arrays are
+ * used in the following manner:
+ * <ul>
+ * <li>Index 0 - column number</li>
+ * <li>Index 1 - current sorting direction</li>
+ * </ul>
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @todo These inner arrays should really be objects
+ */
+ "aaSorting": null,
+
+ /**
+ * Sorting that is always applied to the table (i.e. prefixed in front of
+ * aaSorting).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "aaSortingFixed": [],
+
+ /**
+ * Classes to use for the striping of a table.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "asStripeClasses": null,
+
+ /**
+ * If restoring a table - we should restore its striping classes as well
+ * @type array
+ * @default []
+ */
+ "asDestroyStripes": [],
+
+ /**
+ * If restoring a table - we should restore its width
+ * @type int
+ * @default 0
+ */
+ "sDestroyWidth": 0,
+
+ /**
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
+ * @type array
+ * @default []
+ */
+ "aoRowCallback": [],
+
+ /**
+ * Callback functions for the header on each draw.
+ * @type array
+ * @default []
+ */
+ "aoHeaderCallback": [],
+
+ /**
+ * Callback function for the footer on each draw.
+ * @type array
+ * @default []
+ */
+ "aoFooterCallback": [],
+
+ /**
+ * Array of callback functions for draw callback functions
+ * @type array
+ * @default []
+ */
+ "aoDrawCallback": [],
+
+ /**
+ * Array of callback functions for row created function
+ * @type array
+ * @default []
+ */
+ "aoRowCreatedCallback": [],
+
+ /**
+ * Callback functions for just before the table is redrawn. A return of
+ * false will be used to cancel the draw.
+ * @type array
+ * @default []
+ */
+ "aoPreDrawCallback": [],
+
+ /**
+ * Callback functions for when the table has been initialised.
+ * @type array
+ * @default []
+ */
+ "aoInitComplete": [],
+
+
+ /**
+ * Callbacks for modifying the settings to be stored for state saving, prior to
+ * saving state.
+ * @type array
+ * @default []
+ */
+ "aoStateSaveParams": [],
+
+ /**
+ * Callbacks for modifying the settings that have been stored for state saving
+ * prior to using the stored values to restore the state.
+ * @type array
+ * @default []
+ */
+ "aoStateLoadParams": [],
+
+ /**
+ * Callbacks for operating on the settings object once the saved state has been
+ * loaded
+ * @type array
+ * @default []
+ */
+ "aoStateLoaded": [],
+
+ /**
+ * Cache the table ID for quick access
+ * @type string
+ * @default <i>Empty string</i>
+ */
+ "sTableId": "",
+
+ /**
+ * The TABLE node for the main table
+ * @type node
+ * @default null
+ */
+ "nTable": null,
+
+ /**
+ * Permanent ref to the thead element
+ * @type node
+ * @default null
+ */
+ "nTHead": null,
+
+ /**
+ * Permanent ref to the tfoot element - if it exists
+ * @type node
+ * @default null
+ */
+ "nTFoot": null,
+
+ /**
+ * Permanent ref to the tbody element
+ * @type node
+ * @default null
+ */
+ "nTBody": null,
+
+ /**
+ * Cache the wrapper node (contains all DataTables controlled elements)
+ * @type node
+ * @default null
+ */
+ "nTableWrapper": null,
+
+ /**
+ * Indicate if when using server-side processing the loading of data
+ * should be deferred until the second draw.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ * @default false
+ */
+ "bDeferLoading": false,
+
+ /**
+ * Indicate if all required information has been read in
+ * @type boolean
+ * @default false
+ */
+ "bInitialised": false,
+
+ /**
+ * Information about open rows. Each object in the array has the parameters
+ * 'nTr' and 'nParent'
+ * @type array
+ * @default []
+ */
+ "aoOpenRows": [],
+
+ /**
+ * Dictate the positioning of DataTables' control elements - see
+ * {@link DataTable.model.oInit.sDom}.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default null
+ */
+ "sDom": null,
+
+ /**
+ * Search delay (in mS)
+ * @type integer
+ * @default null
+ */
+ "searchDelay": null,
+
+ /**
+ * Which type of pagination should be used.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default two_button
+ */
+ "sPaginationType": "two_button",
+
+ /**
+ * The state duration (for `stateSave`) in seconds.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type int
+ * @default 0
+ */
+ "iStateDuration": 0,
+
+ /**
+ * Array of callback functions for state saving. Each array element is an
+ * object with the following parameters:
+ * <ul>
+ * <li>function:fn - function to call. Takes two parameters, oSettings
+ * and the JSON string to save that has been thus far created. Returns
+ * a JSON string to be inserted into a json object
+ * (i.e. '"param": [ 0, 1, 2]')</li>
+ * <li>string:sName - name of callback</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aoStateSave": [],
+
+ /**
+ * Array of callback functions for state loading. Each array element is an
+ * object with the following parameters:
+ * <ul>
+ * <li>function:fn - function to call. Takes two parameters, oSettings
+ * and the object stored. May return false to cancel state loading</li>
+ * <li>string:sName - name of callback</li>
+ * </ul>
+ * @type array
+ * @default []
+ */
+ "aoStateLoad": [],
+
+ /**
+ * State that was saved. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oSavedState": null,
+
+ /**
+ * State that was loaded. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oLoadedState": null,
+
+ /**
+ * Source url for AJAX data for the table.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ * @default null
+ */
+ "sAjaxSource": null,
+
+ /**
+ * Property from a given object from which to read the table data from. This
+ * can be an empty string (when not server-side processing), in which case
+ * it is assumed an an array is given directly.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sAjaxDataProp": null,
+
+ /**
+ * The last jQuery XHR object that was used for server-side data gathering.
+ * This can be used for working with the XHR information in one of the
+ * callbacks
+ * @type object
+ * @default null
+ */
+ "jqXHR": null,
+
+ /**
+ * JSON returned from the server in the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "json": undefined,
+
+ /**
+ * Data submitted as part of the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "oAjaxData": undefined,
+
+ /**
+ * Function to get the server-side data.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type function
+ */
+ "fnServerData": null,
+
+ /**
+ * Functions which are called prior to sending an Ajax request so extra
+ * parameters can easily be sent to the server
+ * @type array
+ * @default []
+ */
+ "aoServerParams": [],
+
+ /**
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
+ * required).
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type string
+ */
+ "sServerMethod": null,
+
+ /**
+ * Format numbers for display.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type function
+ */
+ "fnFormatNumber": null,
+
+ /**
+ * List of options that can be used for the user selectable length menu.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type array
+ * @default []
+ */
+ "aLengthMenu": null,
+
+ /**
+ * Counter for the draws that the table does. Also used as a tracker for
+ * server-side processing
+ * @type int
+ * @default 0
+ */
+ "iDraw": 0,
+
+ /**
+ * Indicate if a redraw is being done - useful for Ajax
+ * @type boolean
+ * @default false
+ */
+ "bDrawing": false,
+
+ /**
+ * Draw index (iDraw) of the last error when parsing the returned data
+ * @type int
+ * @default -1
+ */
+ "iDrawError": -1,
+
+ /**
+ * Paging display length
+ * @type int
+ * @default 10
+ */
+ "_iDisplayLength": 10,
+
+ /**
+ * Paging start point - aiDisplay index
+ * @type int
+ * @default 0
+ */
+ "_iDisplayStart": 0,
+
+ /**
+ * Server-side processing - number of records in the result set
+ * (i.e. before filtering), Use fnRecordsTotal rather than
+ * this property to get the value of the number of records, regardless of
+ * the server-side processing setting.
+ * @type int
+ * @default 0
+ * @private
+ */
+ "_iRecordsTotal": 0,
+
+ /**
+ * Server-side processing - number of records in the current display set
+ * (i.e. after filtering). Use fnRecordsDisplay rather than
+ * this property to get the value of the number of records, regardless of
+ * the server-side processing setting.
+ * @type boolean
+ * @default 0
+ * @private
+ */
+ "_iRecordsDisplay": 0,
+
+ /**
+ * The classes to use for the table
+ * @type object
+ * @default {}
+ */
+ "oClasses": {},
+
+ /**
+ * Flag attached to the settings object so you can check in the draw
+ * callback if filtering has been done in the draw. Deprecated in favour of
+ * events.
+ * @type boolean
+ * @default false
+ * @deprecated
+ */
+ "bFiltered": false,
+
+ /**
+ * Flag attached to the settings object so you can check in the draw
+ * callback if sorting has been done in the draw. Deprecated in favour of
+ * events.
+ * @type boolean
+ * @default false
+ * @deprecated
+ */
+ "bSorted": false,
+
+ /**
+ * Indicate that if multiple rows are in the header and there is more than
+ * one unique cell per column, if the top one (true) or bottom one (false)
+ * should be used for sorting / title by DataTables.
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSortCellsTop": null,
+
+ /**
+ * Initialisation object that is used for the table
+ * @type object
+ * @default null
+ */
+ "oInit": null,
+
+ /**
+ * Destroy callback functions - for plug-ins to attach themselves to the
+ * destroy so they can clean up markup and events.
+ * @type array
+ * @default []
+ */
+ "aoDestroyCallback": [],
+
+
+ /**
+ * Get the number of records in the current record set, before filtering
+ * @type function
+ */
+ "fnRecordsTotal": function ()
+ {
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsTotal * 1 :
+ this.aiDisplayMaster.length;
+ },
+
+ /**
+ * Get the number of records in the current record set, after filtering
+ * @type function
+ */
+ "fnRecordsDisplay": function ()
+ {
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsDisplay * 1 :
+ this.aiDisplay.length;
+ },
+
+ /**
+ * Get the display end point - aiDisplay index
+ * @type function
+ */
+ "fnDisplayEnd": function ()
+ {
+ var
+ len = this._iDisplayLength,
+ start = this._iDisplayStart,
+ calc = start + len,
+ records = this.aiDisplay.length,
+ features = this.oFeatures,
+ paginate = features.bPaginate;
+
+ if ( features.bServerSide ) {
+ return paginate === false || len === -1 ?
+ start + records :
+ Math.min( start+len, this._iRecordsDisplay );
+ }
+ else {
+ return ! paginate || calc>records || len===-1 ?
+ records :
+ calc;
+ }
+ },
+
+ /**
+ * The DataTables object for this table
+ * @type object
+ * @default null
+ */
+ "oInstance": null,
+
+ /**
+ * Unique identifier for each instance of the DataTables object. If there
+ * is an ID on the table node, then it takes that value, otherwise an
+ * incrementing internal counter is used.
+ * @type string
+ * @default null
+ */
+ "sInstance": null,
+
+ /**
+ * tabindex attribute value that is added to DataTables control elements, allowing
+ * keyboard navigation of the table and its controls.
+ */
+ "iTabIndex": 0,
+
+ /**
+ * DIV container for the footer scrolling table if scrolling
+ */
+ "nScrollHead": null,
+
+ /**
+ * DIV container for the footer scrolling table if scrolling
+ */
+ "nScrollFoot": null,
+
+ /**
+ * Last applied sort
+ * @type array
+ * @default []
+ */
+ "aLastSort": [],
+
+ /**
+ * Stored plug-in instances
+ * @type object
+ * @default {}
+ */
+ "oPlugins": {},
+
+ /**
+ * Function used to get a row's id from the row's data
+ * @type function
+ * @default null
+ */
+ "rowIdFn": null,
+
+ /**
+ * Data location where to store a row's id
+ * @type string
+ * @default null
+ */
+ "rowId": null
+ };
+
+ /**
+ * Extension object for DataTables that is used to provide all extension
+ * options.
+ *
+ * Note that the `DataTable.ext` object is available through
+ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
+ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
+ * @namespace
+ * @extends DataTable.models.ext
+ */
+
+
+ /**
+ * DataTables extensions
+ *
+ * This namespace acts as a collection area for plug-ins that can be used to
+ * extend DataTables capabilities. Indeed many of the build in methods
+ * use this method to provide their own capabilities (sorting methods for
+ * example).
+ *
+ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
+ * reasons
+ *
+ * @namespace
+ */
+ DataTable.ext = _ext = {
+ /**
+ * Buttons. For use with the Buttons extension for DataTables. This is
+ * defined here so other extensions can define buttons regardless of load
+ * order. It is _not_ used by DataTables core.
+ *
+ * @type object
+ * @default {}
+ */
+ buttons: {},
+
+
+ /**
+ * Element class names
+ *
+ * @type object
+ * @default {}
+ */
+ classes: {},
+
+
+ /**
+ * DataTables build type (expanded by the download builder)
+ *
+ * @type string
+ */
+ build:"bs5/dt-1.12.1",
+
+
+ /**
+ * Error reporting.
+ *
+ * How should DataTables report an error. Can take the value 'alert',
+ * 'throw', 'none' or a function.
+ *
+ * @type string|function
+ * @default alert
+ */
+ errMode: "alert",
+
+
+ /**
+ * Feature plug-ins.
+ *
+ * This is an array of objects which describe the feature plug-ins that are
+ * available to DataTables. These feature plug-ins are then available for
+ * use through the `dom` initialisation option.
+ *
+ * Each feature plug-in is described by an object which must have the
+ * following properties:
+ *
+ * * `fnInit` - function that is used to initialise the plug-in,
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
+ * instillation option. This is case sensitive.
+ *
+ * The `fnInit` function has the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ *
+ * And the following return is expected:
+ *
+ * * {node|null} The element which contains your feature. Note that the
+ * return may also be void if your plug-in does not require to inject any
+ * DOM elements into DataTables control (`dom`) - for example this might
+ * be useful when developing a plug-in which allows table control via
+ * keyboard entry
+ *
+ * @type array
+ *
+ * @example
+ * $.fn.dataTable.ext.features.push( {
+ * "fnInit": function( oSettings ) {
+ * return new TableTools( { "oDTSettings": oSettings } );
+ * },
+ * "cFeature": "T"
+ * } );
+ */
+ feature: [],
+
+
+ /**
+ * Row searching.
+ *
+ * This method of searching is complimentary to the default type based
+ * searching, and a lot more comprehensive as it allows you complete control
+ * over the searching logic. Each element in this array is a function
+ * (parameters described below) that is called for every row in the table,
+ * and your logic decides if it should be included in the searching data set
+ * or not.
+ *
+ * Searching functions have the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{array|object}` Data for the row to be processed (same as the
+ * original format that was passed in as the data source, or an array
+ * from a DOM data source
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
+ *
+ * And the following return is expected:
+ *
+ * * {boolean} Include the row in the searched result set (true) or not
+ * (false)
+ *
+ * Note that as with the main search ability in DataTables, technically this
+ * is "filtering", since it is subtractive. However, for consistency in
+ * naming we call it searching here.
+ *
+ * @type array
+ * @default []
+ *
+ * @example
+ * // The following example shows custom search being applied to the
+ * // fourth column (i.e. the data[3] index) based on two input values
+ * // from the end-user, matching the data in a certain range.
+ * $.fn.dataTable.ext.search.push(
+ * function( settings, data, dataIndex ) {
+ * var min = document.getElementById('min').value * 1;
+ * var max = document.getElementById('max').value * 1;
+ * var version = data[3] == "-" ? 0 : data[3]*1;
+ *
+ * if ( min == "" && max == "" ) {
+ * return true;
+ * }
+ * else if ( min == "" && version < max ) {
+ * return true;
+ * }
+ * else if ( min < version && "" == max ) {
+ * return true;
+ * }
+ * else if ( min < version && version < max ) {
+ * return true;
+ * }
+ * return false;
+ * }
+ * );
+ */
+ search: [],
+
+
+ /**
+ * Selector extensions
+ *
+ * The `selector` option can be used to extend the options available for the
+ * selector modifier options (`selector-modifier` object data type) that
+ * each of the three built in selector types offer (row, column and cell +
+ * their plural counterparts). For example the Select extension uses this
+ * mechanism to provide an option to select only rows, columns and cells
+ * that have been marked as selected by the end user (`{selected: true}`),
+ * which can be used in conjunction with the existing built in selector
* options.
*
- * Note that the `DataTable.ext` object is available through
- * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
- * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
- * @namespace
- * @extends DataTable.models.ext
+ * Each property is an array to which functions can be pushed. The functions
+ * take three attributes:
+ *
+ * * Settings object for the host table
+ * * Options object (`selector-modifier` object type)
+ * * Array of selected item indexes
+ *
+ * The return is an array of the resulting item indexes after the custom
+ * selector has been applied.
+ *
+ * @type object
*/
-
-
+ selector: {
+ cell: [],
+ column: [],
+ row: []
+ },
+
+
/**
- * DataTables extensions
+ * Internal functions, exposed for used in plug-ins.
*
- * This namespace acts as a collection area for plug-ins that can be used to
- * extend DataTables capabilities. Indeed many of the build in methods
- * use this method to provide their own capabilities (sorting methods for
- * example).
+ * Please note that you should not need to use the internal methods for
+ * anything other than a plug-in (and even then, try to avoid if possible).
+ * The internal function may change between releases.
*
- * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
- * reasons
+ * @type object
+ * @default {}
+ */
+ internal: {},
+
+
+ /**
+ * Legacy configuration options. Enable and disable legacy options that
+ * are available in DataTables.
*
- * @namespace
+ * @type object
*/
- DataTable.ext = _ext = {
- /**
- * Buttons. For use with the Buttons extension for DataTables. This is
- * defined here so other extensions can define buttons regardless of load
- * order. It is _not_ used by DataTables core.
- *
- * @type object
- * @default {}
- */
- buttons: {},
-
-
+ legacy: {
/**
- * Element class names
+ * Enable / disable DataTables 1.9 compatible server-side processing
+ * requests
*
- * @type object
- * @default {}
- */
- classes: {},
-
-
- /**
- * DataTables build type (expanded by the download builder)
- *
- * @type string
- */
- build:"bs5/dt-1.11.5",
-
-
- /**
- * Error reporting.
- *
- * How should DataTables report an error. Can take the value 'alert',
- * 'throw', 'none' or a function.
- *
- * @type string|function
- * @default alert
- */
- errMode: "alert",
-
-
- /**
- * Feature plug-ins.
- *
- * This is an array of objects which describe the feature plug-ins that are
- * available to DataTables. These feature plug-ins are then available for
- * use through the `dom` initialisation option.
- *
- * Each feature plug-in is described by an object which must have the
- * following properties:
- *
- * * `fnInit` - function that is used to initialise the plug-in,
- * * `cFeature` - a character so the feature can be enabled by the `dom`
- * instillation option. This is case sensitive.
- *
- * The `fnInit` function has the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- *
- * And the following return is expected:
- *
- * * {node|null} The element which contains your feature. Note that the
- * return may also be void if your plug-in does not require to inject any
- * DOM elements into DataTables control (`dom`) - for example this might
- * be useful when developing a plug-in which allows table control via
- * keyboard entry
- *
- * @type array
- *
- * @example
- * $.fn.dataTable.ext.features.push( {
- * "fnInit": function( oSettings ) {
- * return new TableTools( { "oDTSettings": oSettings } );
- * },
- * "cFeature": "T"
- * } );
+ * @type boolean
+ * @default null
*/
- feature: [],
-
-
+ ajax: null
+ },
+
+
+ /**
+ * Pagination plug-in methods.
+ *
+ * Each entry in this object is a function and defines which buttons should
+ * be shown by the pagination rendering method that is used for the table:
+ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
+ * buttons are displayed in the document, while the functions here tell it
+ * what buttons to display. This is done by returning an array of button
+ * descriptions (what each button will do).
+ *
+ * Pagination types (the four built in options and any additional plug-in
+ * options defined here) can be used through the `paginationType`
+ * initialisation parameter.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{int} page` The current page index
+ * 2. `{int} pages` The number of pages in the table
+ *
+ * Each function is expected to return an array where each element of the
+ * array can be one of:
+ *
+ * * `first` - Jump to first page when activated
+ * * `last` - Jump to last page when activated
+ * * `previous` - Show previous page when activated
+ * * `next` - Show next page when activated
+ * * `{int}` - Show page of the index given
+ * * `{array}` - A nested array containing the above elements to add a
+ * containing 'DIV' element (might be useful for styling).
+ *
+ * Note that DataTables v1.9- used this object slightly differently whereby
+ * an object with two functions would be defined for each plug-in. That
+ * ability is still supported by DataTables 1.10+ to provide backwards
+ * compatibility, but this option of use is now decremented and no longer
+ * documented in DataTables 1.10+.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * // Show previous, next and current page buttons only
+ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
+ * return [ 'previous', page, 'next' ];
+ * };
+ */
+ pager: {},
+
+
+ renderer: {
+ pageButton: {},
+ header: {}
+ },
+
+
+ /**
+ * Ordering plug-ins - custom data source
+ *
+ * The extension options for ordering of data available here is complimentary
+ * to the default type based ordering that DataTables typically uses. It
+ * allows much greater control over the the data that is being used to
+ * order a column, but is necessarily therefore more complex.
+ *
+ * This type of ordering is useful if you want to do ordering based on data
+ * live from the DOM (for example the contents of an 'input' element) rather
+ * than just the static string that DataTables knows of.
+ *
+ * The way these plug-ins work is that you create an array of the values you
+ * wish to be ordering for the column in question and then return that
+ * array. The data in the array much be in the index order of the rows in
+ * the table (not the currently ordering order!). Which order data gathering
+ * function is run here depends on the `dt-init columns.orderDataType`
+ * parameter that is used for the column (if any).
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{int}` Target column index
+ *
+ * Each function is expected to return an array:
+ *
+ * * `{array}` Data for the column to be ordering upon
+ *
+ * @type array
+ *
+ * @example
+ * // Ordering using `input` node values
+ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
+ * {
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
+ * return $('input', td).val();
+ * } );
+ * }
+ */
+ order: {},
+
+
+ /**
+ * Type based plug-ins.
+ *
+ * Each column in DataTables has a type assigned to it, either by automatic
+ * detection or by direct assignment using the `type` option for the column.
+ * The type of a column will effect how it is ordering and search (plug-ins
+ * can also make use of the column type if required).
+ *
+ * @namespace
+ */
+ type: {
/**
- * Row searching.
- *
- * This method of searching is complimentary to the default type based
- * searching, and a lot more comprehensive as it allows you complete control
- * over the searching logic. Each element in this array is a function
- * (parameters described below) that is called for every row in the table,
- * and your logic decides if it should be included in the searching data set
- * or not.
+ * Type detection functions.
*
- * Searching functions have the following input parameters:
+ * The functions defined in this object are used to automatically detect
+ * a column's type, making initialisation of DataTables super easy, even
+ * when complex data is in the table.
*
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{array|object}` Data for the row to be processed (same as the
- * original format that was passed in as the data source, or an array
- * from a DOM data source
- * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
- * can be useful to retrieve the `TR` element if you need DOM interaction.
+ * The functions defined take two parameters:
*
- * And the following return is expected:
+ * 1. `{*}` Data from the column cell to be analysed
+ * 2. `{settings}` DataTables settings object. This can be used to
+ * perform context specific type detection - for example detection
+ * based on language settings such as using a comma for a decimal
+ * place. Generally speaking the options from the settings will not
+ * be required
*
- * * {boolean} Include the row in the searched result set (true) or not
- * (false)
+ * Each function is expected to return:
*
- * Note that as with the main search ability in DataTables, technically this
- * is "filtering", since it is subtractive. However, for consistency in
- * naming we call it searching here.
+ * * `{string|null}` Data type detected, or null if unknown (and thus
+ * pass it on to the other type detection functions.
*
* @type array
- * @default []
*
* @example
- * // The following example shows custom search being applied to the
- * // fourth column (i.e. the data[3] index) based on two input values
- * // from the end-user, matching the data in a certain range.
- * $.fn.dataTable.ext.search.push(
- * function( settings, data, dataIndex ) {
- * var min = document.getElementById('min').value * 1;
- * var max = document.getElementById('max').value * 1;
- * var version = data[3] == "-" ? 0 : data[3]*1;
- *
- * if ( min == "" && max == "" ) {
- * return true;
- * }
- * else if ( min == "" && version < max ) {
- * return true;
- * }
- * else if ( min < version && "" == max ) {
- * return true;
+ * // Currency type detection plug-in:
+ * $.fn.dataTable.ext.type.detect.push(
+ * function ( data, settings ) {
+ * // Check the numeric part
+ * if ( ! data.substring(1).match(/[0-9]/) ) {
+ * return null;
* }
- * else if ( min < version && version < max ) {
- * return true;
+ *
+ * // Check prefixed by currency
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
+ * return 'currency';
* }
- * return false;
+ * return null;
* }
* );
*/
- search: [],
-
-
+ detect: [],
+
+
/**
- * Selector extensions
+ * Type based search formatting.
*
- * The `selector` option can be used to extend the options available for the
- * selector modifier options (`selector-modifier` object data type) that
- * each of the three built in selector types offer (row, column and cell +
- * their plural counterparts). For example the Select extension uses this
- * mechanism to provide an option to select only rows, columns and cells
- * that have been marked as selected by the end user (`{selected: true}`),
- * which can be used in conjunction with the existing built in selector
- * options.
+ * The type based searching functions can be used to pre-format the
+ * data to be search on. For example, it can be used to strip HTML
+ * tags or to de-format telephone numbers for numeric only searching.
*
- * Each property is an array to which functions can be pushed. The functions
- * take three attributes:
+ * Note that is a search is not defined for a column of a given type,
+ * no search formatting will be performed.
+ *
+ * Pre-processing of searching data plug-ins - When you assign the sType
+ * for a column (or have it automatically detected for you by DataTables
+ * or a type detection plug-in), you will typically be using this for
+ * custom sorting, but it can also be used to provide custom searching
+ * by allowing you to pre-processing the data and returning the data in
+ * the format that should be searched upon. This is done by adding
+ * functions this object with a parameter name which matches the sType
+ * for that target column. This is the corollary of <i>afnSortData</i>
+ * for searching data.
*
- * * Settings object for the host table
- * * Options object (`selector-modifier` object type)
- * * Array of selected item indexes
+ * The functions defined take a single parameter:
*
- * The return is an array of the resulting item indexes after the custom
- * selector has been applied.
+ * 1. `{*}` Data from the column cell to be prepared for searching
*
- * @type object
- */
- selector: {
- cell: [],
- column: [],
- row: []
- },
-
-
- /**
- * Internal functions, exposed for used in plug-ins.
- *
- * Please note that you should not need to use the internal methods for
- * anything other than a plug-in (and even then, try to avoid if possible).
- * The internal function may change between releases.
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Formatted string that will be used for the searching.
*
* @type object
* @default {}
- */
- internal: {},
-
-
- /**
- * Legacy configuration options. Enable and disable legacy options that
- * are available in DataTables.
*
- * @type object
+ * @example
+ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
+ * }
*/
- legacy: {
- /**
- * Enable / disable DataTables 1.9 compatible server-side processing
- * requests
- *
- * @type boolean
- * @default null
- */
- ajax: null
- },
-
-
+ search: {},
+
+
/**
- * Pagination plug-in methods.
- *
- * Each entry in this object is a function and defines which buttons should
- * be shown by the pagination rendering method that is used for the table:
- * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
- * buttons are displayed in the document, while the functions here tell it
- * what buttons to display. This is done by returning an array of button
- * descriptions (what each button will do).
+ * Type based ordering.
*
- * Pagination types (the four built in options and any additional plug-in
- * options defined here) can be used through the `paginationType`
- * initialisation parameter.
+ * The column type tells DataTables what ordering to apply to the table
+ * when a column is sorted upon. The order for each type that is defined,
+ * is defined by the functions available in this object.
*
- * The functions defined take two parameters:
- *
- * 1. `{int} page` The current page index
- * 2. `{int} pages` The number of pages in the table
+ * Each ordering option can be described by three properties added to
+ * this object:
*
- * Each function is expected to return an array where each element of the
- * array can be one of:
+ * * `{type}-pre` - Pre-formatting function
+ * * `{type}-asc` - Ascending order function
+ * * `{type}-desc` - Descending order function
*
- * * `first` - Jump to first page when activated
- * * `last` - Jump to last page when activated
- * * `previous` - Show previous page when activated
- * * `next` - Show next page when activated
- * * `{int}` - Show page of the index given
- * * `{array}` - A nested array containing the above elements to add a
- * containing 'DIV' element (might be useful for styling).
+ * All three can be used together, only `{type}-pre` or only
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
+ * that only `{type}-pre` is used, as this provides the optimal
+ * implementation in terms of speed, although the others are provided
+ * for compatibility with existing Javascript sort functions.
*
- * Note that DataTables v1.9- used this object slightly differently whereby
- * an object with two functions would be defined for each plug-in. That
- * ability is still supported by DataTables 1.10+ to provide backwards
- * compatibility, but this option of use is now decremented and no longer
- * documented in DataTables 1.10+.
+ * `{type}-pre`: Functions defined take a single parameter:
*
- * @type object
- * @default {}
+ * 1. `{*}` Data from the column cell to be prepared for ordering
*
- * @example
- * // Show previous, next and current page buttons only
- * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
- * return [ 'previous', page, 'next' ];
- * };
- */
- pager: {},
-
-
- renderer: {
- pageButton: {},
- header: {}
- },
-
-
- /**
- * Ordering plug-ins - custom data source
- *
- * The extension options for ordering of data available here is complimentary
- * to the default type based ordering that DataTables typically uses. It
- * allows much greater control over the the data that is being used to
- * order a column, but is necessarily therefore more complex.
- *
- * This type of ordering is useful if you want to do ordering based on data
- * live from the DOM (for example the contents of an 'input' element) rather
- * than just the static string that DataTables knows of.
- *
- * The way these plug-ins work is that you create an array of the values you
- * wish to be ordering for the column in question and then return that
- * array. The data in the array much be in the index order of the rows in
- * the table (not the currently ordering order!). Which order data gathering
- * function is run here depends on the `dt-init columns.orderDataType`
- * parameter that is used for the column (if any).
+ * And return:
*
- * The functions defined take two parameters:
+ * * `{*}` Data to be sorted upon
*
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{int}` Target column index
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
+ * functions, taking two parameters:
*
- * Each function is expected to return an array:
+ * 1. `{*}` Data to compare to the second parameter
+ * 2. `{*}` Data to compare to the first parameter
*
- * * `{array}` Data for the column to be ordering upon
+ * And returning:
*
- * @type array
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
+ * than the second parameter, ===0 if the two parameters are equal and
+ * >0 if the first parameter should be sorted height than the second
+ * parameter.
+ *
+ * @type object
+ * @default {}
*
* @example
- * // Ordering using `input` node values
- * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
- * {
- * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
- * return $('input', td).val();
- * } );
- * }
- */
- order: {},
-
-
- /**
- * Type based plug-ins.
- *
- * Each column in DataTables has a type assigned to it, either by automatic
- * detection or by direct assignment using the `type` option for the column.
- * The type of a column will effect how it is ordering and search (plug-ins
- * can also make use of the column type if required).
- *
- * @namespace
- */
- type: {
- /**
- * Type detection functions.
- *
- * The functions defined in this object are used to automatically detect
- * a column's type, making initialisation of DataTables super easy, even
- * when complex data is in the table.
- *
- * The functions defined take two parameters:
- *
- * 1. `{*}` Data from the column cell to be analysed
- * 2. `{settings}` DataTables settings object. This can be used to
- * perform context specific type detection - for example detection
- * based on language settings such as using a comma for a decimal
- * place. Generally speaking the options from the settings will not
- * be required
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Data type detected, or null if unknown (and thus
- * pass it on to the other type detection functions.
- *
- * @type array
- *
- * @example
- * // Currency type detection plug-in:
- * $.fn.dataTable.ext.type.detect.push(
- * function ( data, settings ) {
- * // Check the numeric part
- * if ( ! data.substring(1).match(/[0-9]/) ) {
- * return null;
- * }
- *
- * // Check prefixed by currency
- * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- detect: [],
-
-
- /**
- * Type based search formatting.
- *
- * The type based searching functions can be used to pre-format the
- * data to be search on. For example, it can be used to strip HTML
- * tags or to de-format telephone numbers for numeric only searching.
- *
- * Note that is a search is not defined for a column of a given type,
- * no search formatting will be performed.
- *
- * Pre-processing of searching data plug-ins - When you assign the sType
- * for a column (or have it automatically detected for you by DataTables
- * or a type detection plug-in), you will typically be using this for
- * custom sorting, but it can also be used to provide custom searching
- * by allowing you to pre-processing the data and returning the data in
- * the format that should be searched upon. This is done by adding
- * functions this object with a parameter name which matches the sType
- * for that target column. This is the corollary of <i>afnSortData</i>
- * for searching data.
- *
- * The functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for searching
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Formatted string that will be used for the searching.
- *
- * @type object
- * @default {}
- *
- * @example
- * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
- * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
- * }
- */
- search: {},
-
-
- /**
- * Type based ordering.
- *
- * The column type tells DataTables what ordering to apply to the table
- * when a column is sorted upon. The order for each type that is defined,
- * is defined by the functions available in this object.
- *
- * Each ordering option can be described by three properties added to
- * this object:
- *
- * * `{type}-pre` - Pre-formatting function
- * * `{type}-asc` - Ascending order function
- * * `{type}-desc` - Descending order function
- *
- * All three can be used together, only `{type}-pre` or only
- * `{type}-asc` and `{type}-desc` together. It is generally recommended
- * that only `{type}-pre` is used, as this provides the optimal
- * implementation in terms of speed, although the others are provided
- * for compatibility with existing Javascript sort functions.
- *
- * `{type}-pre`: Functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for ordering
- *
- * And return:
- *
- * * `{*}` Data to be sorted upon
- *
- * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
- * functions, taking two parameters:
- *
- * 1. `{*}` Data to compare to the second parameter
- * 2. `{*}` Data to compare to the first parameter
- *
- * And returning:
- *
- * * `{*}` Ordering match: <0 if first parameter should be sorted lower
- * than the second parameter, ===0 if the two parameters are equal and
- * >0 if the first parameter should be sorted height than the second
- * parameter.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Numeric ordering of formatted numbers with a pre-formatter
- * $.extend( $.fn.dataTable.ext.type.order, {
- * "string-pre": function(x) {
- * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
- * return parseFloat( a );
- * }
- * } );
- *
- * @example
- * // Case-sensitive string ordering, with no pre-formatting method
- * $.extend( $.fn.dataTable.ext.order, {
- * "string-case-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-case-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- */
- order: {}
- },
-
- /**
- * Unique DataTables instance counter
+ * // Numeric ordering of formatted numbers with a pre-formatter
+ * $.extend( $.fn.dataTable.ext.type.order, {
+ * "string-pre": function(x) {
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
+ * return parseFloat( a );
+ * }
+ * } );
*
- * @type int
- * @private
- */
- _unique: 0,
-
-
- //
- // Depreciated
- // The following properties are retained for backwards compatibility only.
- // The should not be used in new projects and will be removed in a future
- // version
- //
-
- /**
- * Version check function.
- * @type function
- * @depreciated Since 1.10
- */
- fnVersionCheck: DataTable.fnVersionCheck,
-
-
- /**
- * Index for what 'this' index API functions should use
- * @type int
- * @deprecated Since v1.10
- */
- iApiIndex: 0,
-
-
- /**
- * jQuery UI class container
- * @type object
- * @deprecated Since v1.10
- */
- oJUIClasses: {},
-
-
- /**
- * Software version
- * @type string
- * @deprecated Since v1.10
+ * @example
+ * // Case-sensitive string ordering, with no pre-formatting method
+ * $.extend( $.fn.dataTable.ext.order, {
+ * "string-case-asc": function(x,y) {
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ * },
+ * "string-case-desc": function(x,y) {
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ * }
+ * } );
*/
- sVersion: DataTable.version
- };
-
-
+ order: {}
+ },
+
+ /**
+ * Unique DataTables instance counter
+ *
+ * @type int
+ * @private
+ */
+ _unique: 0,
+
+
//
- // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
+ // Depreciated
+ // The following properties are retained for backwards compatibility only.
+ // The should not be used in new projects and will be removed in a future
+ // version
//
- $.extend( _ext, {
- afnFiltering: _ext.search,
- aTypes: _ext.type.detect,
- ofnSearch: _ext.type.search,
- oSort: _ext.type.order,
- afnSortData: _ext.order,
- aoFeatures: _ext.feature,
- oApi: _ext.internal,
- oStdClasses: _ext.classes,
- oPagination: _ext.pager
- } );
-
-
- $.extend( DataTable.ext.classes, {
- "sTable": "dataTable",
- "sNoFooter": "no-footer",
-
- /* Paging buttons */
- "sPageButton": "paginate_button",
- "sPageButtonActive": "current",
- "sPageButtonDisabled": "disabled",
-
- /* Striping classes */
- "sStripeOdd": "odd",
- "sStripeEven": "even",
-
- /* Empty row */
- "sRowEmpty": "dataTables_empty",
-
- /* Features */
- "sWrapper": "dataTables_wrapper",
- "sFilter": "dataTables_filter",
- "sInfo": "dataTables_info",
- "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
- "sLength": "dataTables_length",
- "sProcessing": "dataTables_processing",
-
- /* Sorting */
- "sSortAsc": "sorting_asc",
- "sSortDesc": "sorting_desc",
- "sSortable": "sorting", /* Sortable in both directions */
- "sSortableAsc": "sorting_desc_disabled",
- "sSortableDesc": "sorting_asc_disabled",
- "sSortableNone": "sorting_disabled",
- "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
-
- /* Filtering */
- "sFilterInput": "",
-
- /* Page length */
- "sLengthSelect": "",
-
- /* Scrolling */
- "sScrollWrapper": "dataTables_scroll",
- "sScrollHead": "dataTables_scrollHead",
- "sScrollHeadInner": "dataTables_scrollHeadInner",
- "sScrollBody": "dataTables_scrollBody",
- "sScrollFoot": "dataTables_scrollFoot",
- "sScrollFootInner": "dataTables_scrollFootInner",
-
- /* Misc */
- "sHeaderTH": "",
- "sFooterTH": "",
-
- // Deprecated
- "sSortJUIAsc": "",
- "sSortJUIDesc": "",
- "sSortJUI": "",
- "sSortJUIAscAllowed": "",
- "sSortJUIDescAllowed": "",
- "sSortJUIWrapper": "",
- "sSortIcon": "",
- "sJUIHeader": "",
- "sJUIFooter": ""
- } );
-
-
- var extPagination = DataTable.ext.pager;
-
- function _numbers ( page, pages ) {
- var
- numbers = [],
- buttons = extPagination.numbers_length,
- half = Math.floor( buttons / 2 ),
- i = 1;
-
- if ( pages <= buttons ) {
- numbers = _range( 0, pages );
- }
- else if ( page <= half ) {
- numbers = _range( 0, buttons-2 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- }
- else if ( page >= pages - 1 - half ) {
- numbers = _range( pages-(buttons-2), pages );
- numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
- numbers.splice( 0, 0, 0 );
- }
- else {
- numbers = _range( page-half+2, page+half-1 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- numbers.splice( 0, 0, 'ellipsis' );
- numbers.splice( 0, 0, 0 );
- }
-
- numbers.DT_el = 'span';
- return numbers;
+
+ /**
+ * Version check function.
+ * @type function
+ * @depreciated Since 1.10
+ */
+ fnVersionCheck: DataTable.fnVersionCheck,
+
+
+ /**
+ * Index for what 'this' index API functions should use
+ * @type int
+ * @deprecated Since v1.10
+ */
+ iApiIndex: 0,
+
+
+ /**
+ * jQuery UI class container
+ * @type object
+ * @deprecated Since v1.10
+ */
+ oJUIClasses: {},
+
+
+ /**
+ * Software version
+ * @type string
+ * @deprecated Since v1.10
+ */
+ sVersion: DataTable.version
+ };
+
+
+ //
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
+ //
+ $.extend( _ext, {
+ afnFiltering: _ext.search,
+ aTypes: _ext.type.detect,
+ ofnSearch: _ext.type.search,
+ oSort: _ext.type.order,
+ afnSortData: _ext.order,
+ aoFeatures: _ext.feature,
+ oApi: _ext.internal,
+ oStdClasses: _ext.classes,
+ oPagination: _ext.pager
+ } );
+
+
+ $.extend( DataTable.ext.classes, {
+ "sTable": "dataTable",
+ "sNoFooter": "no-footer",
+
+ /* Paging buttons */
+ "sPageButton": "paginate_button",
+ "sPageButtonActive": "current",
+ "sPageButtonDisabled": "disabled",
+
+ /* Striping classes */
+ "sStripeOdd": "odd",
+ "sStripeEven": "even",
+
+ /* Empty row */
+ "sRowEmpty": "dataTables_empty",
+
+ /* Features */
+ "sWrapper": "dataTables_wrapper",
+ "sFilter": "dataTables_filter",
+ "sInfo": "dataTables_info",
+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
+ "sLength": "dataTables_length",
+ "sProcessing": "dataTables_processing",
+
+ /* Sorting */
+ "sSortAsc": "sorting_asc",
+ "sSortDesc": "sorting_desc",
+ "sSortable": "sorting", /* Sortable in both directions */
+ "sSortableAsc": "sorting_desc_disabled",
+ "sSortableDesc": "sorting_asc_disabled",
+ "sSortableNone": "sorting_disabled",
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
+
+ /* Filtering */
+ "sFilterInput": "",
+
+ /* Page length */
+ "sLengthSelect": "",
+
+ /* Scrolling */
+ "sScrollWrapper": "dataTables_scroll",
+ "sScrollHead": "dataTables_scrollHead",
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
+ "sScrollBody": "dataTables_scrollBody",
+ "sScrollFoot": "dataTables_scrollFoot",
+ "sScrollFootInner": "dataTables_scrollFootInner",
+
+ /* Misc */
+ "sHeaderTH": "",
+ "sFooterTH": "",
+
+ // Deprecated
+ "sSortJUIAsc": "",
+ "sSortJUIDesc": "",
+ "sSortJUI": "",
+ "sSortJUIAscAllowed": "",
+ "sSortJUIDescAllowed": "",
+ "sSortJUIWrapper": "",
+ "sSortIcon": "",
+ "sJUIHeader": "",
+ "sJUIFooter": ""
+ } );
+
+
+ var extPagination = DataTable.ext.pager;
+
+ function _numbers ( page, pages ) {
+ var
+ numbers = [],
+ buttons = extPagination.numbers_length,
+ half = Math.floor( buttons / 2 ),
+ i = 1;
+
+ if ( pages <= buttons ) {
+ numbers = _range( 0, pages );
}
+ else if ( page <= half ) {
+ numbers = _range( 0, buttons-2 );
+ numbers.push( 'ellipsis' );
+ numbers.push( pages-1 );
+ }
+ else if ( page >= pages - 1 - half ) {
+ numbers = _range( pages-(buttons-2), pages );
+ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
+ numbers.splice( 0, 0, 0 );
+ }
+ else {
+ numbers = _range( page-half+2, page+half-1 );
+ numbers.push( 'ellipsis' );
+ numbers.push( pages-1 );
+ numbers.splice( 0, 0, 'ellipsis' );
+ numbers.splice( 0, 0, 0 );
+ }
+
+ numbers.DT_el = 'span';
+ return numbers;
+ }
+
+
+ $.extend( extPagination, {
+ simple: function ( page, pages ) {
+ return [ 'previous', 'next' ];
+ },
+
+ full: function ( page, pages ) {
+ return [ 'first', 'previous', 'next', 'last' ];
+ },
+
+ numbers: function ( page, pages ) {
+ return [ _numbers(page, pages) ];
+ },
+
+ simple_numbers: function ( page, pages ) {
+ return [ 'previous', _numbers(page, pages), 'next' ];
+ },
+
+ full_numbers: function ( page, pages ) {
+ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
+ },
-
- $.extend( extPagination, {
- simple: function ( page, pages ) {
- return [ 'previous', 'next' ];
- },
-
- full: function ( page, pages ) {
- return [ 'first', 'previous', 'next', 'last' ];
- },
-
- numbers: function ( page, pages ) {
- return [ _numbers(page, pages) ];
- },
-
- simple_numbers: function ( page, pages ) {
- return [ 'previous', _numbers(page, pages), 'next' ];
- },
-
- full_numbers: function ( page, pages ) {
- return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
- },
-
- first_last_numbers: function (page, pages) {
- return ['first', _numbers(page, pages), 'last'];
- },
-
- // For testing and plug-ins to use
- _numbers: _numbers,
-
- // Number of number buttons (including ellipsis) to show. _Must be odd!_
- numbers_length: 7
- } );
-
-
- $.extend( true, DataTable.ext.renderer, {
- pageButton: {
- _: function ( settings, host, idx, buttons, page, pages ) {
- var classes = settings.oClasses;
- var lang = settings.oLanguage.oPaginate;
- var aria = settings.oLanguage.oAria.paginate || {};
- var btnDisplay, btnClass, counter=0;
-
- var attach = function( container, buttons ) {
- var i, ien, node, button, tabIndex;
- var disabledClass = classes.sPageButtonDisabled;
- var clickHandler = function ( e ) {
- _fnPageChange( settings, e.data.action, true );
- };
-
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
-
- if ( Array.isArray( button ) ) {
- var inner = $( '<'+(button.DT_el || 'div')+'/>' )
- .appendTo( container );
- attach( inner, button );
+ first_last_numbers: function (page, pages) {
+ return ['first', _numbers(page, pages), 'last'];
+ },
+
+ // For testing and plug-ins to use
+ _numbers: _numbers,
+
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
+ numbers_length: 7
+ } );
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ pageButton: {
+ _: function ( settings, host, idx, buttons, page, pages ) {
+ var classes = settings.oClasses;
+ var lang = settings.oLanguage.oPaginate;
+ var aria = settings.oLanguage.oAria.paginate || {};
+ var btnDisplay, btnClass, counter=0;
+
+ var attach = function( container, buttons ) {
+ var i, ien, node, button, tabIndex;
+ var disabledClass = classes.sPageButtonDisabled;
+ var clickHandler = function ( e ) {
+ _fnPageChange( settings, e.data.action, true );
+ };
+
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
+ button = buttons[i];
+
+ if ( Array.isArray( button ) ) {
+ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
+ .appendTo( container );
+ attach( inner, button );
+ }
+ else {
+ btnDisplay = null;
+ btnClass = button;
+ tabIndex = settings.iTabIndex;
+
+ switch ( button ) {
+ case 'ellipsis':
+ container.append('<span class="ellipsis">&#x2026;</span>');
+ break;
+
+ case 'first':
+ btnDisplay = lang.sFirst;
+
+ if ( page === 0 ) {
+ tabIndex = -1;
+ btnClass += ' ' + disabledClass;
+ }
+ break;
+
+ case 'previous':
+ btnDisplay = lang.sPrevious;
+
+ if ( page === 0 ) {
+ tabIndex = -1;
+ btnClass += ' ' + disabledClass;
+ }
+ break;
+
+ case 'next':
+ btnDisplay = lang.sNext;
+
+ if ( pages === 0 || page === pages-1 ) {
+ tabIndex = -1;
+ btnClass += ' ' + disabledClass;
+ }
+ break;
+
+ case 'last':
+ btnDisplay = lang.sLast;
+
+ if ( pages === 0 || page === pages-1 ) {
+ tabIndex = -1;
+ btnClass += ' ' + disabledClass;
+ }
+ break;
+
+ default:
+ btnDisplay = settings.fnFormatNumber( button + 1 );
+ btnClass = page === button ?
+ classes.sPageButtonActive : '';
+ break;
}
- else {
- btnDisplay = null;
- btnClass = button;
- tabIndex = settings.iTabIndex;
-
- switch ( button ) {
- case 'ellipsis':
- container.append('<span class="ellipsis">&#x2026;</span>');
- break;
-
- case 'first':
- btnDisplay = lang.sFirst;
-
- if ( page === 0 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'previous':
- btnDisplay = lang.sPrevious;
-
- if ( page === 0 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'next':
- btnDisplay = lang.sNext;
-
- if ( pages === 0 || page === pages-1 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'last':
- btnDisplay = lang.sLast;
-
- if ( pages === 0 || page === pages-1 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- default:
- btnDisplay = settings.fnFormatNumber( button + 1 );
- btnClass = page === button ?
- classes.sPageButtonActive : '';
- break;
- }
-
- if ( btnDisplay !== null ) {
- node = $('<a>', {
- 'class': classes.sPageButton+' '+btnClass,
- 'aria-controls': settings.sTableId,
- 'aria-label': aria[ button ],
- 'data-dt-idx': counter,
- 'tabindex': tabIndex,
- 'id': idx === 0 && typeof button === 'string' ?
- settings.sTableId +'_'+ button :
- null
- } )
- .html( btnDisplay )
- .appendTo( container );
-
- _fnBindAction(
- node, {action: button}, clickHandler
- );
-
- counter++;
- }
+
+ if ( btnDisplay !== null ) {
+ node = $('<a>', {
+ 'class': classes.sPageButton+' '+btnClass,
+ 'aria-controls': settings.sTableId,
+ 'aria-label': aria[ button ],
+ 'data-dt-idx': counter,
+ 'tabindex': tabIndex,
+ 'id': idx === 0 && typeof button === 'string' ?
+ settings.sTableId +'_'+ button :
+ null
+ } )
+ .html( btnDisplay )
+ .appendTo( container );
+
+ _fnBindAction(
+ node, {action: button}, clickHandler
+ );
+
+ counter++;
}
}
- };
-
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame. Try / catch the error. Not good for
- // accessibility, but neither are frames.
- var activeEl;
-
- try {
- // Because this approach is destroying and recreating the paging
- // elements, focus is lost on the select button which is bad for
- // accessibility. So we want to restore focus once the draw has
- // completed
- activeEl = $(host).find(document.activeElement).data('dt-idx');
- }
- catch (e) {}
-
- attach( $(host).empty(), buttons );
-
- if ( activeEl !== undefined ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
}
+ };
+
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame. Try / catch the error. Not good for
+ // accessibility, but neither are frames.
+ var activeEl;
+
+ try {
+ // Because this approach is destroying and recreating the paging
+ // elements, focus is lost on the select button which is bad for
+ // accessibility. So we want to restore focus once the draw has
+ // completed
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
+ }
+ catch (e) {}
+
+ attach( $(host).empty(), buttons );
+
+ if ( activeEl !== undefined ) {
+ $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
}
}
- } );
-
-
-
- // Built in type detection. See model.ext.aTypes for information about
- // what is required from this methods.
- $.extend( DataTable.ext.type.detect, [
- // Plain numbers - first since V8 detects some plain numbers as dates
- // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal ) ? 'num'+decimal : null;
- },
-
- // Dates (only those recognised by the browser's Date.parse)
- function ( d, settings )
- {
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
- // valid, so we need to use a regex to restrict date formats. Use a
- // plug-in for anything other than ISO8601 style strings
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
- return null;
- }
- var parsed = Date.parse(d);
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
- },
-
- // Formatted numbers
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
- },
-
- // HTML numeric
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
- },
-
- // HTML numeric, formatted
- function ( d, settings )
+ }
+ } );
+
+
+
+ // Built in type detection. See model.ext.aTypes for information about
+ // what is required from this methods.
+ $.extend( DataTable.ext.type.detect, [
+ // Plain numbers - first since V8 detects some plain numbers as dates
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
+ },
+
+ // Dates (only those recognised by the browser's Date.parse)
+ function ( d, settings )
+ {
+ // V8 tries _very_ hard to make a string passed into `Date.parse()`
+ // valid, so we need to use a regex to restrict date formats. Use a
+ // plug-in for anything other than ISO8601 style strings
+ if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
+ return null;
+ }
+ var parsed = Date.parse(d);
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
+ },
+
+ // Formatted numbers
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
+ },
+
+ // HTML numeric
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
+ },
+
+ // HTML numeric, formatted
+ function ( d, settings )
+ {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
+ },
+
+ // HTML (this is strict checking - there must be html)
+ function ( d, settings )
+ {
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
+ 'html' : null;
+ }
+ ] );
+
+
+
+ // Filter formatting functions. See model.ext.ofnSearch for information about
+ // what is required from these methods.
+ //
+ // Note that additional search methods are added for the html numbers and
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
+ // place is
+
+
+ $.extend( DataTable.ext.type.search, {
+ html: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data
+ .replace( _re_new_lines, " " )
+ .replace( _re_html, "" ) :
+ '';
+ },
+
+ string: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data.replace( _re_new_lines, " " ) :
+ data;
+ }
+ } );
+
+
+
+ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
+ if ( d !== 0 && (!d || d === '-') ) {
+ return -Infinity;
+ }
+
+ // If a decimal place other than `.` is used, it needs to be given to the
+ // function so we can detect it and replace with a `.` which is the only
+ // decimal place Javascript recognises - it is not locale aware.
+ if ( decimalPlace ) {
+ d = _numToDecimal( d, decimalPlace );
+ }
+
+ if ( d.replace ) {
+ if ( re1 ) {
+ d = d.replace( re1, '' );
+ }
+
+ if ( re2 ) {
+ d = d.replace( re2, '' );
+ }
+ }
+
+ return d * 1;
+ };
+
+
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
+ // in a function to provide an easy ability for the language options to add
+ // additional methods if a non-period decimal place is used.
+ function _addNumericSort ( decimalPlace ) {
+ $.each(
{
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
+ // Plain numbers
+ "num": function ( d ) {
+ return __numericReplace( d, decimalPlace );
+ },
+
+ // Formatted numbers
+ "num-fmt": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
+ },
+
+ // HTML numeric
+ "html-num": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_html );
+ },
+
+ // HTML numeric, formatted
+ "html-num-fmt": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
+ }
},
-
- // HTML (this is strict checking - there must be html)
- function ( d, settings )
- {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
+ function ( key, fn ) {
+ // Add the ordering method
+ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
+
+ // For HTML types add a search formatter that will strip the HTML
+ if ( key.match(/^html\-/) ) {
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
+ }
}
- ] );
-
-
-
- // Filter formatting functions. See model.ext.ofnSearch for information about
- // what is required from these methods.
- //
- // Note that additional search methods are added for the html numbers and
- // html formatted numbers by `_addNumericSort()` when we know what the decimal
- // place is
-
-
- $.extend( DataTable.ext.type.search, {
- html: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data
- .replace( _re_new_lines, " " )
- .replace( _re_html, "" ) :
- '';
+ );
+ }
+
+
+ // Default sort methods
+ $.extend( _ext.type.order, {
+ // Dates
+ "date-pre": function ( d ) {
+ var ts = Date.parse( d );
+ return isNaN(ts) ? -Infinity : ts;
+ },
+
+ // html
+ "html-pre": function ( a ) {
+ return _empty(a) ?
+ '' :
+ a.replace ?
+ a.replace( /<.*?>/g, "" ).toLowerCase() :
+ a+'';
+ },
+
+ // string
+ "string-pre": function ( a ) {
+ // This is a little complex, but faster than always calling toString,
+ // http://jsperf.com/tostring-v-check
+ return _empty(a) ?
+ '' :
+ typeof a === 'string' ?
+ a.toLowerCase() :
+ ! a.toString ?
+ '' :
+ a.toString();
+ },
+
+ // string-asc and -desc are retained only for compatibility with the old
+ // sort methods
+ "string-asc": function ( x, y ) {
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ },
+
+ "string-desc": function ( x, y ) {
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ }
+ } );
+
+
+ // Numeric sorting types - order doesn't matter here
+ _addNumericSort( '' );
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ header: {
+ _: function ( settings, cell, column, classes ) {
+ // No additional mark-up required
+ // Attach a sort listener to update on sort - note that using the
+ // `DT` namespace will allow the event to be removed automatically
+ // on destroy, while the `dt` namespaced event is the one we are
+ // listening for
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) { // need to check this this is the host
+ return; // table, not a nested one
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass(
+ classes.sSortAsc +' '+
+ classes.sSortDesc
+ )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+ } );
},
-
- string: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data.replace( _re_new_lines, " " ) :
- data;
+
+ jqueryui: function ( settings, cell, column, classes ) {
+ $('<div/>')
+ .addClass( classes.sSortJUIWrapper )
+ .append( cell.contents() )
+ .append( $('<span/>')
+ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
+ )
+ .appendTo( cell );
+
+ // Attach a sort listener to update on sort
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+
+ cell
+ .find( 'span.'+classes.sSortIcon )
+ .removeClass(
+ classes.sSortJUIAsc +" "+
+ classes.sSortJUIDesc +" "+
+ classes.sSortJUI +" "+
+ classes.sSortJUIAscAllowed +" "+
+ classes.sSortJUIDescAllowed
+ )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortJUIDesc :
+ column.sSortingClassJUI
+ );
+ } );
}
- } );
-
-
+ }
+ } );
+
+ /*
+ * Public helper functions. These aren't used internally by DataTables, or
+ * called by any of the options passed into DataTables, but they can be used
+ * externally by developers working with DataTables. They are helper functions
+ * to make working with DataTables a little bit easier.
+ */
+
+ var __htmlEscapeEntities = function ( d ) {
+ if (Array.isArray(d)) {
+ d = d.join(',');
+ }
+
+ return typeof d === 'string' ?
+ d
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;') :
+ d;
+ };
+
+ // Common logic for moment, luxon or a date action
+ function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
+ if (window.moment) {
+ return dt[momentFn]( arg1 );
+ }
+ else if (window.luxon) {
+ return dt[luxonFn]( arg1 );
+ }
- var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( d !== 0 && (!d || d === '-') ) {
- return -Infinity;
+ return dateFn ? dt[dateFn]( arg1 ) : dt;
+ }
+
+
+ var __mlWarning = false;
+ function __mldObj (d, format, locale) {
+ var dt;
+
+ if (window.moment) {
+ dt = window.moment.utc( d, format, locale, true );
+
+ if (! dt.isValid()) {
+ return null;
}
-
- // If a decimal place other than `.` is used, it needs to be given to the
- // function so we can detect it and replace with a `.` which is the only
- // decimal place Javascript recognises - it is not locale aware.
- if ( decimalPlace ) {
- d = _numToDecimal( d, decimalPlace );
+ }
+ else if (window.luxon) {
+ dt = format
+ ? window.luxon.DateTime.fromFormat( d, format )
+ : window.luxon.DateTime.fromISO( d );
+
+ if (! dt.isValid) {
+ return null;
}
-
- if ( d.replace ) {
- if ( re1 ) {
- d = d.replace( re1, '' );
- }
-
- if ( re2 ) {
- d = d.replace( re2, '' );
- }
+
+ dt.setLocale(locale);
+ }
+ else if (! format) {
+ // No format given, must be ISO
+ dt = new Date(d);
+ }
+ else {
+ if (! __mlWarning) {
+ alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
}
-
- return d * 1;
- };
-
-
- // Add the numeric 'deformatting' functions for sorting and search. This is done
- // in a function to provide an easy ability for the language options to add
- // additional methods if a non-period decimal place is used.
- function _addNumericSort ( decimalPlace ) {
- $.each(
- {
- // Plain numbers
- "num": function ( d ) {
- return __numericReplace( d, decimalPlace );
- },
-
- // Formatted numbers
- "num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_formatted_numeric );
- },
-
- // HTML numeric
- "html-num": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html );
- },
-
- // HTML numeric, formatted
- "html-num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
+
+ __mlWarning = true;
+ }
+
+ return dt;
+ }
+
+ // Wrapper for date, datetime and time which all operate the same way with the exception of
+ // the output string for auto locale support
+ function __mlHelper (localeString) {
+ return function ( from, to, locale, def ) {
+ // Luxon and Moment support
+ // Argument shifting
+ if ( arguments.length === 0 ) {
+ locale = 'en';
+ to = null; // means toLocaleString
+ from = null; // means iso8601
+ }
+ else if ( arguments.length === 1 ) {
+ locale = 'en';
+ to = from;
+ from = null;
+ }
+ else if ( arguments.length === 2 ) {
+ locale = to;
+ to = from;
+ from = null;
+ }
+
+ var typeName = 'datetime-' + to;
+
+ // Add type detection and sorting specific to this date format - we need to be able to identify
+ // date type columns as such, rather than as numbers in extensions. Hence the need for this.
+ if (! DataTable.ext.type.order[typeName]) {
+ // The renderer will give the value to type detect as the type!
+ DataTable.ext.type.detect.unshift(function (d) {
+ return d === typeName ? typeName : false;
+ });
+
+ // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
+ // `valueOf` which gives milliseconds epoch
+ DataTable.ext.type.order[typeName + '-asc'] = function (a, b) {
+ var x = a.valueOf();
+ var y = b.valueOf();
+
+ return x === y
+ ? 0
+ : x < y
+ ? -1
+ : 1;
+ }
+
+ DataTable.ext.type.order[typeName + '-desc'] = function (a, b) {
+ var x = a.valueOf();
+ var y = b.valueOf();
+
+ return x === y
+ ? 0
+ : x > y
+ ? -1
+ : 1;
+ }
+ }
+
+ return function ( d, type ) {
+ // Allow for a default value
+ if (d === null || d === undefined) {
+ if (def === '--now') {
+ // We treat everything as UTC further down, so no changes are
+ // made, as such need to get the local date / time as if it were
+ // UTC
+ var local = new Date();
+ d = new Date( Date.UTC(
+ local.getFullYear(), local.getMonth(), local.getDate(),
+ local.getHours(), local.getMinutes(), local.getSeconds()
+ ) );
}
- },
- function ( key, fn ) {
- // Add the ordering method
- _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
-
- // For HTML types add a search formatter that will strip the HTML
- if ( key.match(/^html\-/) ) {
- _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
+ else {
+ d = '';
}
}
- );
+
+ if (type === 'type') {
+ // Typing uses the type name for fast matching
+ return typeName;
+ }
+
+ if (d === '') {
+ return type !== 'sort'
+ ? ''
+ : __mldObj('0000-01-01 00:00:00', null, locale);
+ }
+
+ // Shortcut. If `from` and `to` are the same, we are using the renderer to
+ // format for ordering, not display - its already in the display format.
+ if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
+ return d;
+ }
+
+ var dt = __mldObj(d, from, locale);
+
+ if (dt === null) {
+ return d;
+ }
+
+ if (type === 'sort') {
+ return dt;
+ }
+
+ var formatted = to === null
+ ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
+ : __mld(dt, 'format', 'toFormat', 'toISOString', to);
+
+ // XSS protection
+ return type === 'display' ?
+ __htmlEscapeEntities( formatted ) :
+ formatted;
+ };
}
+ }
+
+ // Based on locale, determine standard number formatting
+ // Fallback for legacy browsers is US English
+ var __thousands = ',';
+ var __decimal = '.';
+
+ if (Intl) {
+ try {
+ var num = new Intl.NumberFormat().formatToParts(100000.1);
-
- // Default sort methods
- $.extend( _ext.type.order, {
- // Dates
- "date-pre": function ( d ) {
- var ts = Date.parse( d );
- return isNaN(ts) ? -Infinity : ts;
- },
-
- // html
- "html-pre": function ( a ) {
- return _empty(a) ?
- '' :
- a.replace ?
- a.replace( /<.*?>/g, "" ).toLowerCase() :
- a+'';
- },
-
- // string
- "string-pre": function ( a ) {
- // This is a little complex, but faster than always calling toString,
- // http://jsperf.com/tostring-v-check
- return _empty(a) ?
- '' :
- typeof a === 'string' ?
- a.toLowerCase() :
- ! a.toString ?
- '' :
- a.toString();
- },
-
- // string-asc and -desc are retained only for compatibility with the old
- // sort methods
- "string-asc": function ( x, y ) {
- return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- },
-
- "string-desc": function ( x, y ) {
- return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- }
- } );
-
-
- // Numeric sorting types - order doesn't matter here
- _addNumericSort( '' );
-
-
- $.extend( true, DataTable.ext.renderer, {
- header: {
- _: function ( settings, cell, column, classes ) {
- // No additional mark-up required
- // Attach a sort listener to update on sort - note that using the
- // `DT` namespace will allow the event to be removed automatically
- // on destroy, while the `dt` namespaced event is the one we are
- // listening for
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) { // need to check this this is the host
- return; // table, not a nested one
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass(
- classes.sSortAsc +' '+
- classes.sSortDesc
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
- } );
- },
-
- jqueryui: function ( settings, cell, column, classes ) {
- $('<div/>')
- .addClass( classes.sSortJUIWrapper )
- .append( cell.contents() )
- .append( $('<span/>')
- .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
- )
- .appendTo( cell );
-
- // Attach a sort listener to update on sort
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) {
- return;
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
-
- cell
- .find( 'span.'+classes.sSortIcon )
- .removeClass(
- classes.sSortJUIAsc +" "+
- classes.sSortJUIDesc +" "+
- classes.sSortJUI +" "+
- classes.sSortJUIAscAllowed +" "+
- classes.sSortJUIDescAllowed
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortJUIDesc :
- column.sSortingClassJUI
- );
- } );
+ for (var i=0 ; i<num.length ; i++) {
+ if (num[i].type === 'group') {
+ __thousands = num[i].value;
+ }
+ else if (num[i].type === 'decimal') {
+ __decimal = num[i].value;
}
}
- } );
-
- /*
- * Public helper functions. These aren't used internally by DataTables, or
- * called by any of the options passed into DataTables, but they can be used
- * externally by developers working with DataTables. They are helper functions
- * to make working with DataTables a little bit easier.
- */
-
- var __htmlEscapeEntities = function ( d ) {
- if (Array.isArray(d)) {
- d = d.join(',');
+ }
+ catch (e) {
+ // noop
+ }
+ }
+
+ // Formatted date time detection - use by declaring the formats you are going to use
+ DataTable.datetime = function ( format, locale ) {
+ var typeName = 'datetime-detect-' + format;
+
+ if (! locale) {
+ locale = 'en';
+ }
+
+ if (! DataTable.ext.type.order[typeName]) {
+ DataTable.ext.type.detect.unshift(function (d) {
+ var dt = __mldObj(d, format, locale);
+ return d === '' || dt ? typeName : false;
+ });
+
+ DataTable.ext.type.order[typeName + '-pre'] = function (d) {
+ return __mldObj(d, format, locale) || 0;
}
-
- return typeof d === 'string' ?
- d
- .replace(/&/g, '&amp;')
- .replace(/</g, '&lt;')
- .replace(/>/g, '&gt;')
- .replace(/"/g, '&quot;') :
- d;
- };
-
- /**
- * Helpers for `columns.render`.
- *
- * The options defined here can be used with the `columns.render` initialisation
- * option to provide a display renderer. The following functions are defined:
- *
- * * `number` - Will format numeric data (defined by `columns.data`) for
- * display, retaining the original unformatted data for sorting and filtering.
- * It takes 5 parameters:
- * * `string` - Thousands grouping separator
- * * `string` - Decimal point indicator
- * * `integer` - Number of decimal points to show
- * * `string` (optional) - Prefix.
- * * `string` (optional) - Postfix (/suffix).
- * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
- * parameters.
- *
- * @example
- * // Column definition using the number renderer
- * {
- * data: "salary",
- * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
- * }
- *
- * @namespace
- */
- DataTable.render = {
- number: function ( thousands, decimal, precision, prefix, postfix ) {
- return {
- display: function ( d ) {
- if ( typeof d !== 'number' && typeof d !== 'string' ) {
- return d;
- }
-
- var negative = d < 0 ? '-' : '';
- var flo = parseFloat( d );
-
- // If NaN then there isn't much formatting that we can do - just
- // return immediately, escaping any HTML (this was supposed to
- // be a number after all)
- if ( isNaN( flo ) ) {
- return __htmlEscapeEntities( d );
- }
-
- flo = flo.toFixed( precision );
- d = Math.abs( flo );
-
- var intPart = parseInt( d, 10 );
- var floatPart = precision ?
- decimal+(d - intPart).toFixed( precision ).substring( 2 ):
- '';
-
- // If zero, then can't have a negative prefix
- if (intPart === 0 && parseFloat(floatPart) === 0) {
- negative = '';
- }
-
- return negative + (prefix||'') +
- intPart.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g, thousands
- ) +
- floatPart +
- (postfix||'');
- }
- };
- },
-
- text: function () {
- return {
- display: __htmlEscapeEntities,
- filter: __htmlEscapeEntities
- };
+ }
+ }
+
+ /**
+ * Helpers for `columns.render`.
+ *
+ * The options defined here can be used with the `columns.render` initialisation
+ * option to provide a display renderer. The following functions are defined:
+ *
+ * * `number` - Will format numeric data (defined by `columns.data`) for
+ * display, retaining the original unformatted data for sorting and filtering.
+ * It takes 5 parameters:
+ * * `string` - Thousands grouping separator
+ * * `string` - Decimal point indicator
+ * * `integer` - Number of decimal points to show
+ * * `string` (optional) - Prefix.
+ * * `string` (optional) - Postfix (/suffix).
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
+ * parameters.
+ *
+ * @example
+ * // Column definition using the number renderer
+ * {
+ * data: "salary",
+ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
+ * }
+ *
+ * @namespace
+ */
+ DataTable.render = {
+ date: __mlHelper('toLocaleDateString'),
+ datetime: __mlHelper('toLocaleString'),
+ time: __mlHelper('toLocaleTimeString'),
+ number: function ( thousands, decimal, precision, prefix, postfix ) {
+ // Auto locale detection
+ if (thousands === null || thousands === undefined) {
+ thousands = __thousands;
}
- };
-
-
- /*
- * This is really a good bit rubbish this method of exposing the internal methods
- * publicly... - To be fixed in 2.0 using methods on the prototype
- */
-
-
- /**
- * Create a wrapper function for exporting an internal functions to an external API.
- * @param {string} fn API function name
- * @returns {function} wrapped function
- * @memberof DataTable#internal
- */
- function _fnExternApiFunc (fn)
- {
- return function() {
- var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
- Array.prototype.slice.call(arguments)
- );
- return DataTable.ext.internal[fn].apply( this, args );
+
+ if (decimal === null || decimal === undefined) {
+ decimal = __decimal;
+ }
+
+ return {
+ display: function ( d ) {
+ if ( typeof d !== 'number' && typeof d !== 'string' ) {
+ return d;
+ }
+
+ if (d === '' || d === null) {
+ return d;
+ }
+
+ var negative = d < 0 ? '-' : '';
+ var flo = parseFloat( d );
+
+ // If NaN then there isn't much formatting that we can do - just
+ // return immediately, escaping any HTML (this was supposed to
+ // be a number after all)
+ if ( isNaN( flo ) ) {
+ return __htmlEscapeEntities( d );
+ }
+
+ flo = flo.toFixed( precision );
+ d = Math.abs( flo );
+
+ var intPart = parseInt( d, 10 );
+ var floatPart = precision ?
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
+ '';
+
+ // If zero, then can't have a negative prefix
+ if (intPart === 0 && parseFloat(floatPart) === 0) {
+ negative = '';
+ }
+
+ return negative + (prefix||'') +
+ intPart.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g, thousands
+ ) +
+ floatPart +
+ (postfix||'');
+ }
+ };
+ },
+
+ text: function () {
+ return {
+ display: __htmlEscapeEntities,
+ filter: __htmlEscapeEntities
};
}
-
-
- /**
- * Reference to internal functions for use by plug-in developers. Note that
- * these methods are references to internal functions and are considered to be
- * private. If you use these methods, be aware that they are liable to change
- * between versions.
- * @namespace
- */
- $.extend( DataTable.ext.internal, {
- _fnExternApiFunc: _fnExternApiFunc,
- _fnBuildAjax: _fnBuildAjax,
- _fnAjaxUpdate: _fnAjaxUpdate,
- _fnAjaxParameters: _fnAjaxParameters,
- _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
- _fnAjaxDataSrc: _fnAjaxDataSrc,
- _fnAddColumn: _fnAddColumn,
- _fnColumnOptions: _fnColumnOptions,
- _fnAdjustColumnSizing: _fnAdjustColumnSizing,
- _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
- _fnColumnIndexToVisible: _fnColumnIndexToVisible,
- _fnVisbleColumns: _fnVisbleColumns,
- _fnGetColumns: _fnGetColumns,
- _fnColumnTypes: _fnColumnTypes,
- _fnApplyColumnDefs: _fnApplyColumnDefs,
- _fnHungarianMap: _fnHungarianMap,
- _fnCamelToHungarian: _fnCamelToHungarian,
- _fnLanguageCompat: _fnLanguageCompat,
- _fnBrowserDetect: _fnBrowserDetect,
- _fnAddData: _fnAddData,
- _fnAddTr: _fnAddTr,
- _fnNodeToDataIndex: _fnNodeToDataIndex,
- _fnNodeToColumnIndex: _fnNodeToColumnIndex,
- _fnGetCellData: _fnGetCellData,
- _fnSetCellData: _fnSetCellData,
- _fnSplitObjNotation: _fnSplitObjNotation,
- _fnGetObjectDataFn: _fnGetObjectDataFn,
- _fnSetObjectDataFn: _fnSetObjectDataFn,
- _fnGetDataMaster: _fnGetDataMaster,
- _fnClearTable: _fnClearTable,
- _fnDeleteIndex: _fnDeleteIndex,
- _fnInvalidate: _fnInvalidate,
- _fnGetRowElements: _fnGetRowElements,
- _fnCreateTr: _fnCreateTr,
- _fnBuildHead: _fnBuildHead,
- _fnDrawHead: _fnDrawHead,
- _fnDraw: _fnDraw,
- _fnReDraw: _fnReDraw,
- _fnAddOptionsHtml: _fnAddOptionsHtml,
- _fnDetectHeader: _fnDetectHeader,
- _fnGetUniqueThs: _fnGetUniqueThs,
- _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
- _fnFilterComplete: _fnFilterComplete,
- _fnFilterCustom: _fnFilterCustom,
- _fnFilterColumn: _fnFilterColumn,
- _fnFilter: _fnFilter,
- _fnFilterCreateSearch: _fnFilterCreateSearch,
- _fnEscapeRegex: _fnEscapeRegex,
- _fnFilterData: _fnFilterData,
- _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
- _fnUpdateInfo: _fnUpdateInfo,
- _fnInfoMacros: _fnInfoMacros,
- _fnInitialise: _fnInitialise,
- _fnInitComplete: _fnInitComplete,
- _fnLengthChange: _fnLengthChange,
- _fnFeatureHtmlLength: _fnFeatureHtmlLength,
- _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
- _fnPageChange: _fnPageChange,
- _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
- _fnProcessingDisplay: _fnProcessingDisplay,
- _fnFeatureHtmlTable: _fnFeatureHtmlTable,
- _fnScrollDraw: _fnScrollDraw,
- _fnApplyToChildren: _fnApplyToChildren,
- _fnCalculateColumnWidths: _fnCalculateColumnWidths,
- _fnThrottle: _fnThrottle,
- _fnConvertToWidth: _fnConvertToWidth,
- _fnGetWidestNode: _fnGetWidestNode,
- _fnGetMaxLenString: _fnGetMaxLenString,
- _fnStringToCss: _fnStringToCss,
- _fnSortFlatten: _fnSortFlatten,
- _fnSort: _fnSort,
- _fnSortAria: _fnSortAria,
- _fnSortListener: _fnSortListener,
- _fnSortAttachListener: _fnSortAttachListener,
- _fnSortingClasses: _fnSortingClasses,
- _fnSortData: _fnSortData,
- _fnSaveState: _fnSaveState,
- _fnLoadState: _fnLoadState,
- _fnImplementState: _fnImplementState,
- _fnSettingsFromNode: _fnSettingsFromNode,
- _fnLog: _fnLog,
- _fnMap: _fnMap,
- _fnBindAction: _fnBindAction,
- _fnCallbackReg: _fnCallbackReg,
- _fnCallbackFire: _fnCallbackFire,
- _fnLengthOverflow: _fnLengthOverflow,
- _fnRenderer: _fnRenderer,
- _fnDataSource: _fnDataSource,
- _fnRowAttributes: _fnRowAttributes,
- _fnExtend: _fnExtend,
- _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
- // in 1.10, so this dead-end function is
- // added to prevent errors
- } );
-
-
- // jQuery access
- $.fn.dataTable = DataTable;
-
- // Provide access to the host jQuery object (circular reference)
- DataTable.$ = $;
-
- // Legacy aliases
- $.fn.dataTableSettings = DataTable.settings;
- $.fn.dataTableExt = DataTable.ext;
-
- // With a capital `D` we return a DataTables API instance rather than a
- // jQuery object
- $.fn.DataTable = function ( opts ) {
- return $(this).dataTable( opts ).api();
+ };
+
+
+ /*
+ * This is really a good bit rubbish this method of exposing the internal methods
+ * publicly... - To be fixed in 2.0 using methods on the prototype
+ */
+
+
+ /**
+ * Create a wrapper function for exporting an internal functions to an external API.
+ * @param {string} fn API function name
+ * @returns {function} wrapped function
+ * @memberof DataTable#internal
+ */
+ function _fnExternApiFunc (fn)
+ {
+ return function() {
+ var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
+ Array.prototype.slice.call(arguments)
+ );
+ return DataTable.ext.internal[fn].apply( this, args );
};
-
- // All properties that are available to $.fn.dataTable should also be
- // available on $.fn.DataTable
- $.each( DataTable, function ( prop, val ) {
- $.fn.DataTable[ prop ] = val;
- } );
+ }
+
+
+ /**
+ * Reference to internal functions for use by plug-in developers. Note that
+ * these methods are references to internal functions and are considered to be
+ * private. If you use these methods, be aware that they are liable to change
+ * between versions.
+ * @namespace
+ */
+ $.extend( DataTable.ext.internal, {
+ _fnExternApiFunc: _fnExternApiFunc,
+ _fnBuildAjax: _fnBuildAjax,
+ _fnAjaxUpdate: _fnAjaxUpdate,
+ _fnAjaxParameters: _fnAjaxParameters,
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
+ _fnAjaxDataSrc: _fnAjaxDataSrc,
+ _fnAddColumn: _fnAddColumn,
+ _fnColumnOptions: _fnColumnOptions,
+ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
+ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
+ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
+ _fnVisbleColumns: _fnVisbleColumns,
+ _fnGetColumns: _fnGetColumns,
+ _fnColumnTypes: _fnColumnTypes,
+ _fnApplyColumnDefs: _fnApplyColumnDefs,
+ _fnHungarianMap: _fnHungarianMap,
+ _fnCamelToHungarian: _fnCamelToHungarian,
+ _fnLanguageCompat: _fnLanguageCompat,
+ _fnBrowserDetect: _fnBrowserDetect,
+ _fnAddData: _fnAddData,
+ _fnAddTr: _fnAddTr,
+ _fnNodeToDataIndex: _fnNodeToDataIndex,
+ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
+ _fnGetCellData: _fnGetCellData,
+ _fnSetCellData: _fnSetCellData,
+ _fnSplitObjNotation: _fnSplitObjNotation,
+ _fnGetObjectDataFn: _fnGetObjectDataFn,
+ _fnSetObjectDataFn: _fnSetObjectDataFn,
+ _fnGetDataMaster: _fnGetDataMaster,
+ _fnClearTable: _fnClearTable,
+ _fnDeleteIndex: _fnDeleteIndex,
+ _fnInvalidate: _fnInvalidate,
+ _fnGetRowElements: _fnGetRowElements,
+ _fnCreateTr: _fnCreateTr,
+ _fnBuildHead: _fnBuildHead,
+ _fnDrawHead: _fnDrawHead,
+ _fnDraw: _fnDraw,
+ _fnReDraw: _fnReDraw,
+ _fnAddOptionsHtml: _fnAddOptionsHtml,
+ _fnDetectHeader: _fnDetectHeader,
+ _fnGetUniqueThs: _fnGetUniqueThs,
+ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
+ _fnFilterComplete: _fnFilterComplete,
+ _fnFilterCustom: _fnFilterCustom,
+ _fnFilterColumn: _fnFilterColumn,
+ _fnFilter: _fnFilter,
+ _fnFilterCreateSearch: _fnFilterCreateSearch,
+ _fnEscapeRegex: _fnEscapeRegex,
+ _fnFilterData: _fnFilterData,
+ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
+ _fnUpdateInfo: _fnUpdateInfo,
+ _fnInfoMacros: _fnInfoMacros,
+ _fnInitialise: _fnInitialise,
+ _fnInitComplete: _fnInitComplete,
+ _fnLengthChange: _fnLengthChange,
+ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
+ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
+ _fnPageChange: _fnPageChange,
+ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
+ _fnProcessingDisplay: _fnProcessingDisplay,
+ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
+ _fnScrollDraw: _fnScrollDraw,
+ _fnApplyToChildren: _fnApplyToChildren,
+ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
+ _fnThrottle: _fnThrottle,
+ _fnConvertToWidth: _fnConvertToWidth,
+ _fnGetWidestNode: _fnGetWidestNode,
+ _fnGetMaxLenString: _fnGetMaxLenString,
+ _fnStringToCss: _fnStringToCss,
+ _fnSortFlatten: _fnSortFlatten,
+ _fnSort: _fnSort,
+ _fnSortAria: _fnSortAria,
+ _fnSortListener: _fnSortListener,
+ _fnSortAttachListener: _fnSortAttachListener,
+ _fnSortingClasses: _fnSortingClasses,
+ _fnSortData: _fnSortData,
+ _fnSaveState: _fnSaveState,
+ _fnLoadState: _fnLoadState,
+ _fnImplementState: _fnImplementState,
+ _fnSettingsFromNode: _fnSettingsFromNode,
+ _fnLog: _fnLog,
+ _fnMap: _fnMap,
+ _fnBindAction: _fnBindAction,
+ _fnCallbackReg: _fnCallbackReg,
+ _fnCallbackFire: _fnCallbackFire,
+ _fnLengthOverflow: _fnLengthOverflow,
+ _fnRenderer: _fnRenderer,
+ _fnDataSource: _fnDataSource,
+ _fnRowAttributes: _fnRowAttributes,
+ _fnExtend: _fnExtend,
+ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
+ // in 1.10, so this dead-end function is
+ // added to prevent errors
+ } );
+
+
+ // jQuery access
+ $.fn.dataTable = DataTable;
+
+ // Provide access to the host jQuery object (circular reference)
+ DataTable.$ = $;
+
+ // Legacy aliases
+ $.fn.dataTableSettings = DataTable.settings;
+ $.fn.dataTableExt = DataTable.ext;
+
+ // With a capital `D` we return a DataTables API instance rather than a
+ // jQuery object
+ $.fn.DataTable = function ( opts ) {
+ return $(this).dataTable( opts ).api();
+ };
+
+ // All properties that are available to $.fn.dataTable should also be
+ // available on $.fn.DataTable
+ $.each( DataTable, function ( prop, val ) {
+ $.fn.DataTable[ prop ] = val;
+ } );
- return DataTable;
+ return DataTable;
}));
diff --git a/src/static/templates/admin/base.hbs b/src/static/templates/admin/base.hbs
index d385cdcd..c63be3e1 100644
--- a/src/static/templates/admin/base.hbs
+++ b/src/static/templates/admin/base.hbs
@@ -20,8 +20,15 @@
width: auto;
margin: -5px 0 0 0;
}
+ /* Special alert-row class to use Bootstrap v5.2+ variable colors */
+ .alert-row {
+ --bs-alert-border: 1px solid var(--bs-alert-border-color);
+ color: var(--bs-alert-color);
+ background-color: var(--bs-alert-bg);
+ border: var(--bs-alert-border);
+ }
</style>
- <script src="{{urlpath}}/vw_static/identicon.js"></script>
+ <script defer="defer" src="{{urlpath}}/vw_static/identicon.js"></script>
<script>
'use strict';
@@ -135,6 +142,6 @@
}
})();
</script>
- <script src="{{urlpath}}/vw_static/bootstrap-native.js"></script>
+ <script defer="defer" src="{{urlpath}}/vw_static/bootstrap-native.js"></script>
</body>
</html>
diff --git a/src/static/templates/admin/settings.hbs b/src/static/templates/admin/settings.hbs
index 454891ce..04142785 100644
--- a/src/static/templates/admin/settings.hbs
+++ b/src/static/templates/admin/settings.hbs
@@ -5,7 +5,7 @@
<div class="small text-white mb-3">
<span class="font-weight-bolder">NOTE:</span> The settings here override the environment variables. Once saved, it's recommended to stop setting them to avoid confusion.<br>
This does not apply to the read-only section, which can only be set via environment variables.<br>
- Settings which are overridden are shown with <span class="is-overridden-true">double underscores</span>.
+ Settings which are overridden are shown with <span class="is-overridden-true alert-row px-1">a yellow colored background</span>.
</div>
<form class="form needs-validation" id="config-form" onsubmit="saveConfig(); return false;" novalidate>
@@ -16,7 +16,7 @@
<div id="g_{{group}}" class="card-body collapse">
{{#each elements}}
{{#if editable}}
- <div class="row my-2 align-items-center is-overridden-{{overridden}}" title="[{{name}}] {{doc.description}}">
+ <div class="row my-2 align-items-center is-overridden-{{overridden}} alert-row" title="[{{name}}] {{doc.description}}">
{{#case type "text" "number" "password"}}
<label for="input_{{name}}" class="col-sm-3 col-form-label">{{doc.name}}</label>
<div class="col-sm-8">
@@ -71,16 +71,25 @@
{{#each config}}
{{#each elements}}
{{#unless editable}}
- <div class="row my-2 align-items-center" title="[{{name}}] {{doc.description}}">
+ <div class="row my-2 align-items-center alert-row" title="[{{name}}] {{doc.description}}">
{{#case type "text" "number" "password"}}
<label for="input_{{name}}" class="col-sm-3 col-form-label">{{doc.name}}</label>
<div class="col-sm-8">
<div class="input-group">
- <input readonly class="form-control" id="input_{{name}}" type="{{type}}"
- value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}>
- {{#case type "password"}}
+ {{!--
+ Also set the database_url input as password here.
+ If we would set it to password in config.rs it will not be character masked for the support string.
+ And sometimes this is more useful for providing support than just 3 asterisk.
+ --}}
+ {{#if (eq name "database_url")}}
+ <input readonly class="form-control" id="input_{{name}}" type="password" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}>
<button class="btn btn-outline-secondary" type="button" onclick="toggleVis('input_{{name}}');">Show/hide</button>
- {{/case}}
+ {{else}}
+ <input readonly class="form-control" id="input_{{name}}" type="{{type}}" value="{{value}}" {{#if default}} placeholder="Default: {{default}}" {{/if}}>
+ {{#case type "password"}}
+ <button class="btn btn-outline-secondary" type="button" onclick="toggleVis('input_{{name}}');">Show/hide</button>
+ {{/case}}
+ {{/if}}
</div>
</div>
{{/case}}
@@ -134,7 +143,9 @@
}
.is-overridden-true {
- text-decoration: underline double;
+ --bs-alert-color: #664d03;
+ --bs-alert-bg: #fff3cd;
+ --bs-alert-border-color: #ffecb5;
}
</style>
@@ -238,19 +249,45 @@
return Array.from(form).some(el => 'origValue' in el.dataset && ( el.dataset.origValue !== el.value));
}
- // Trigger Form Change Detection
+ // This function will prevent submitting a from when someone presses enter.
+ function preventFormSubmitOnEnter(form) {
+ form.onkeypress = function(e) {
+ let key = e.charCode || e.keyCode || 0;
+ if (key == 13) {
+ e.preventDefault();
+ }
+ }
+ }
+
+ // Initialize Form Change Detection
const config_form = document.getElementById('config-form');
initChangeDetection(config_form);
+ // Prevent enter to submitting the form and save the config.
+ // Users need to really click on save, this also to prevent accidental submits.
+ preventFormSubmitOnEnter(config_form);
+
+ // This function will hook into the smtp-test-email input field and will call the smtpTest() function when enter is pressed.
+ function submitTestEmailOnEnter() {
+ const smtp_test_email_input = document.getElementById('smtp-test-email');
+ smtp_test_email_input.onkeypress = function(e) {
+ let key = e.charCode || e.keyCode || 0;
+ if (key == 13) {
+ e.preventDefault();
+ smtpTest();
+ }
+ }
+ }
+ submitTestEmailOnEnter();
// Colorize some settings which are high risk
- const risk_items = document.getElementsByClassName('col-form-label');
- function colorRiskSettings(risk_el) {
- Array.from(risk_el).forEach((el) => {
+ function colorRiskSettings() {
+ const risk_items = document.getElementsByClassName('col-form-label');
+ Array.from(risk_items).forEach((el) => {
if (el.innerText.toLowerCase().includes('risks') ) {
el.parentElement.className += ' alert-danger'
}
});
}
- colorRiskSettings(risk_items);
+ colorRiskSettings();
</script>
diff --git a/src/util.rs b/src/util.rs
index 677e5860..55208aeb 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -29,21 +29,48 @@ impl Fairing for AppHeaders {
}
}
- async fn on_response<'r>(&self, _req: &'r Request<'_>, res: &mut Response<'r>) {
- res.set_raw_header("Permissions-Policy", "accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(self \"https://haveibeenpwned.com\" \"https://2fa.directory\"), usb=(), vr=()");
+ async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
+ res.set_raw_header("Permissions-Policy", "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()");
res.set_raw_header("Referrer-Policy", "same-origin");
- res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
res.set_raw_header("X-Content-Type-Options", "nosniff");
// Obsolete in modern browsers, unsafe (XS-Leak), and largely replaced by CSP
res.set_raw_header("X-XSS-Protection", "0");
- let csp = format!(
- // Chrome Web Store: https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb
- // Edge Add-ons: https://microsoftedge.microsoft.com/addons/detail/bitwarden-free-password/jbkfoedolllekgbhcbcoahefnbanhhlh?hl=en-US
- // Firefox Browser Add-ons: https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/
- "frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://* {};",
- CONFIG.allowed_iframe_ancestors()
- );
- res.set_raw_header("Content-Security-Policy", csp);
+
+ let req_uri_path = req.uri().path();
+
+ // Check if we are requesting an admin page, if so, allow unsafe-inline for scripts.
+ // TODO: In the future maybe we need to see if we can generate a sha256 hash or have no scripts inline at all.
+ let admin_path = format!("{}/admin", CONFIG.domain_path());
+ let mut script_src = "";
+ if req_uri_path.starts_with(admin_path.as_str()) {
+ script_src = " 'unsafe-inline'";
+ }
+
+ // Do not send the Content-Security-Policy (CSP) Header and X-Frame-Options for the *-connector.html files.
+ // This can cause issues when some MFA requests needs to open a popup or page within the clients like WebAuthn, or Duo.
+ // This is the same behaviour as upstream Bitwarden.
+ if !req_uri_path.ends_with("connector.html") {
+ let csp = format!(
+ // Chrome Web Store: https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb
+ // Edge Add-ons: https://microsoftedge.microsoft.com/addons/detail/bitwarden-free-password/jbkfoedolllekgbhcbcoahefnbanhhlh?hl=en-US
+ // Firefox Browser Add-ons: https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/
+ "default-src 'self'; \
+ script-src 'self'{script_src}; \
+ style-src 'self' 'unsafe-inline'; \
+ img-src 'self' data: https://haveibeenpwned.com/ https://www.gravatar.com; \
+ child-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
+ frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
+ connect-src 'self' https://api.pwnedpasswords.com/range/ https://2fa.directory/api/ https://app.simplelogin.io/api/ https://app.anonaddy.com/api/; \
+ object-src 'self' blob:; \
+ frame-ancestors 'self' chrome-extension://nngceckbapebfimnlniiiahkandclblb chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh moz-extension://* {};",
+ CONFIG.allowed_iframe_ancestors()
+ );
+ res.set_raw_header("Content-Security-Policy", csp);
+ res.set_raw_header("X-Frame-Options", "SAMEORIGIN");
+ } else {
+ // It looks like this header get's set somewhere else also, make sure this is not sent for these files, it will cause MFA issues.
+ res.remove_header("X-Frame-Options");
+ }
// Disable cache unless otherwise specified
if !res.headers().contains("cache-control") {