commit 5d05ec58be9e6dcb028d69a4280a1d9e9d99f20e
parent f95bd3bb04839ea4fa8f2700cd3867ee12b260b0
Author: BlackDex <black.dex@gmail.com>
Date:   Wed,  8 Jun 2022 19:46:33 +0200
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
Diffstat:
15 files changed, 9177 insertions(+), 8083 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,523 +3045,368 @@ 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;
+  --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-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-light {
+  --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-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-dark {
+  --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-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-outline-primary {
+  --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-danger:disabled, .btn-danger.disabled {
-  color: #fff;
-  background-color: #dc3545;
-  border-color: #dc3545;
+
+.btn-outline-secondary {
+  --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-light {
-  color: #000;
-  background-color: #f8f9fa;
-  border-color: #f8f9fa;
+.btn-outline-success {
+  --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-light:hover {
-  color: #000;
-  background-color: #f9fafb;
-  border-color: #f9fafb;
+
+.btn-outline-info {
+  --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-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-outline-warning {
+  --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-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-outline-danger {
+  --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-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-outline-light {
+  --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-light:disabled, .btn-light.disabled {
-  color: #000;
-  background-color: #f8f9fa;
-  border-color: #f8f9fa;
+
+.btn-outline-dark {
+  --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-dark {
-  color: #fff;
-  background-color: #212529;
-  border-color: #212529;
+.btn-link {
+  --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-dark:hover {
-  color: #fff;
-  background-color: #1c1f23;
-  border-color: #1a1e21;
+.btn-lg, .btn-group-lg > .btn {
+  --bs-btn-padding-y: 0.5rem;
+  --bs-btn-padding-x: 1rem;
+  --bs-btn-font-size: 1.25rem;
+  --bs-btn-border-radius: 0.5rem;
 }
-.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-sm, .btn-group-sm > .btn {
+  --bs-btn-padding-y: 0.25rem;
+  --bs-btn-padding-x: 0.5rem;
+  --bs-btn-font-size: 0.875rem;
+  --bs-btn-border-radius: 0.25rem;
 }
-.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;
+
+.fade {
+  transition: opacity 0.15s linear;
 }
-.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);
+@media (prefers-reduced-motion: reduce) {
+  .fade {
+    transition: none;
+  }
 }
-.btn-dark:disabled, .btn-dark.disabled {
-  color: #fff;
-  background-color: #212529;
-  border-color: #212529;
+.fade:not(.show) {
+  opacity: 0;
 }
 
-.btn-outline-primary {
-  color: #0d6efd;
-  border-color: #0d6efd;
+.collapse:not(.show) {
+  display: none;
 }
-.btn-outline-primary:hover {
-  color: #fff;
-  background-color: #0d6efd;
-  border-color: #0d6efd;
+
+.collapsing {
+  height: 0;
+  overflow: hidden;
+  transition: height 0.35s ease;
 }
-.btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus {
-  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5);
+@media (prefers-reduced-motion: reduce) {
+  .collapsing {
+    transition: none;
+  }
 }
-.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;
+.collapsing.collapse-horizontal {
+  width: 0;
+  height: auto;
+  transition: width 0.35s ease;
 }
-.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);
+@media (prefers-reduced-motion: reduce) {
+  .collapsing.collapse-horizontal {
+    transition: none;
+  }
 }
-.btn-outline-primary:disabled, .btn-outline-primary.disabled {
-  color: #0d6efd;
-  background-color: transparent;
-}
-
-.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;
-}
-
-.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;
-}
-
-.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;
-}
-
-.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;
-}
-
-.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;
-}
-
-.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;
-}
-
-.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;
-}
-
-.btn-link {
-  font-weight: 400;
-  color: #0d6efd;
-  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;
-}
-
-.btn-sm, .btn-group-sm > .btn {
-  padding: 0.25rem 0.5rem;
-  font-size: 0.875rem;
-  border-radius: 0.2rem;
-}
-
-.fade {
-  transition: opacity 0.15s linear;
-}
-@media (prefers-reduced-motion: reduce) {
-  .fade {
-    transition: none;
-  }
-}
-.fade:not(.show) {
-  opacity: 0;
-}
-
-.collapse:not(.show) {
-  display: none;
-}
-
-.collapsing {
-  height: 0;
-  overflow: hidden;
-  transition: height 0.35s ease;
-}
-@media (prefers-reduced-motion: reduce) {
-  .collapsing {
-    transition: none;
-  }
-}
-.collapsing.collapse-horizontal {
-  width: 0;
-  height: auto;
-  transition: width 0.35s ease;
-}
-@media (prefers-reduced-motion: reduce) {
-  .collapsing.collapse-horizontal {
-    transition: none;
-  }
-}
-
-.dropup,
-.dropend,
-.dropdown,
-.dropstart {
-  position: relative;
+
+.dropup,
+.dropend,
+.dropdown,
+.dropstart,
+.dropup-center,
+.dropdown-center {
+  position: relative;
 }
 
 .dropdown-toggle {
@@ -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);
+  box-shadow: var(--bs-pagination-focus-box-shadow);
 }
-
-.page-item:not(:first-child) .page-link {
-  margin-left: -1px;
-}
-.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-dialog-scrollable {
-    height: calc(100% - 3.5rem);
+  .modal {
+    --bs-modal-margin: 1.75rem;
+    --bs-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
   }
 
-  .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,78 +6214,537 @@ 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 */;
   }
 }
-
-@keyframes spinner-border {
-  to {
-    transform: rotate(360deg) /* rtl:ignore */;
+
+@keyframes spinner-border {
+  to {
+    transform: rotate(360deg) /* rtl:ignore */;
+  }
+}
+.spinner-border {
+  --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;
+}
+
+.spinner-border-sm {
+  --bs-spinner-width: 1rem;
+  --bs-spinner-height: 1rem;
+  --bs-spinner-border-width: 0.2em;
+}
+
+@-webkit-keyframes spinner-grow {
+  0% {
+    transform: scale(0);
+  }
+  50% {
+    opacity: 1;
+    transform: none;
+  }
+}
+
+@keyframes spinner-grow {
+  0% {
+    transform: scale(0);
+  }
+  50% {
+    opacity: 1;
+    transform: none;
+  }
+}
+.spinner-grow {
+  --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;
+}
+
+.spinner-grow-sm {
+  --bs-spinner-width: 1rem;
+  --bs-spinner-height: 1rem;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  .spinner-border,
+.spinner-grow {
+    --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%);
   }
 }
-.spinner-border {
-  display: inline-block;
-  width: 2rem;
-  height: 2rem;
-  vertical-align: -0.125em;
-  border: 0.25em solid currentColor;
-  border-right-color: transparent;
-  border-radius: 50%;
-  -webkit-animation: 0.75s linear infinite spinner-border;
-  animation: 0.75s linear infinite spinner-border;
+@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%);
+  }
 }
-
-.spinner-border-sm {
-  width: 1rem;
-  height: 1rem;
-  border-width: 0.2em;
+@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;
+  }
 }
 
-@-webkit-keyframes spinner-grow {
-  0% {
-    transform: scale(0);
+@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;
   }
-  50% {
-    opacity: 1;
+}
+@media (max-width: 1399.98px) {
+  .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) {
     transform: none;
   }
 }
-
-@keyframes spinner-grow {
-  0% {
-    transform: scale(0);
+@media (max-width: 1399.98px) {
+  .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {
+    visibility: visible;
   }
-  50% {
-    opacity: 1;
-    transform: none;
+}
+@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%);
   }
 }
-.spinner-grow {
-  display: inline-block;
-  width: 2rem;
-  height: 2rem;
-  vertical-align: -0.125em;
-  background-color: currentColor;
-  border-radius: 50%;
-  opacity: 0;
-  -webkit-animation: 0.75s linear infinite spinner-grow;
-  animation: 0.75s linear infinite spinner-grow;
+@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%);
+  }
 }
-
-.spinner-grow-sm {
-  width: 1rem;
-  height: 1rem;
+@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 (prefers-reduced-motion: reduce) {
-  .spinner-border,
-.spinner-grow {
-    -webkit-animation-duration: 1.5s;
-    animation-duration: 1.5s;
+@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;
 }
@@ -7561,12 +8149,36 @@ textarea.form-control-lg {
   padding-left: 1rem !important;
 }
 
-.ps-4 {
-  padding-left: 1.5rem !important;
+.ps-4 {
+  padding-left: 1.5rem !important;
+}
+
+.ps-5 {
+  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;
 }
 
-.ps-5 {
-  padding-left: 3rem !important;
+.gap-5 {
+  gap: 3rem !important;
 }
 
 .font-monospace {
@@ -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
@@ -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
@@ -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,
+	
 		/**
-		 * 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
+		 * Array of TD elements for each row. This is null until the row has been
+		 * created.
+		 *  @type array nodes
+		 *  @default []
 		 */
-		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).
+		"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": [],
+	
 		/**
-		 * Initialisation options that can be given to DataTables at initialisation
-		 * time.
-		 *  @namespace
+		 * 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
 		 */
-		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
-				);
-			},
-		
-		
-			/**
-			 * 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": {},
-		
-		
-			/**
-			 * 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": {
-				/**
-				 * 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",
-		
-		
-				/**
-				 * 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
-				 *
-				 *  @type string
-				 *  @default Showing _START_ to _END_ of _TOTAL_ entries
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.info
-				 *
-				 *  @example
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "info": "Showing page _PAGE_ of _PAGES_"
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"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": ",",
-		
-		
-				/**
-				 * 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 Show _MENU_ entries
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.lengthMenu
-				 *
-				 *  @example
-				 *    // Language change only
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "lengthMenu": "Display _MENU_ records"
-				 *        }
-				 *      } );
-				 *    } );
-				 *
-				 *  @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'
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"sLengthMenu": "Show _MENU_ entries",
-		
-		
-				/**
-				 * 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 Loading...
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.loadingRecords
-				 *
-				 *  @example
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "loadingRecords": "Please wait - loading..."
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"sLoadingRecords": "Loading...",
-		
-		
-				/**
-				 * Text which is displayed when the table is processing a user action
-				 * (usually a sort command or similar).
-				 *  @type string
-				 *  @default Processing...
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.processing
-				 *
-				 *  @example
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "processing": "DataTables is currently busy"
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"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": "",
-		
-		
-				/**
-				 * 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 <i>Empty string - i.e. disabled</i>
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.url
-				 *
-				 *  @example
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"sUrl": "",
-		
-		
-				/**
-				 * 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 No matching records found
-				 *
-				 *  @dtopt Language
-				 *  @name DataTable.defaults.language.zeroRecords
-				 *
-				 *  @example
-				 *    $(document).ready( function() {
-				 *      $('#example').dataTable( {
-				 *        "language": {
-				 *          "zeroRecords": "No records to display"
-				 *        }
-				 *      } );
-				 *    } );
-				 */
-				"sZeroRecords": "No matching records found"
-			},
-		
-		
-			/**
-			 * 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>'<' and '>' - div elements</li>
-			 *         <li>'<"class" and '>' - div with a class</li>
-			 *         <li>'<"#id" and '>' - div with an ID</li>
-			 *       </ul>
-			 *     </li>
-			 *     <li>Examples:
-			 *       <ul>
-			 *         <li>'<"wrapper"flipt>'</li>
-			 *         <li>'<lf<t>ip>'</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": '<"top"i>rt<"bottom"flp><"clear">'
-			 *      } );
-			 *    } );
-			 */
-			"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 );
-		
-		
-		
+		"_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,
+	
+		/**
+		 * 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
+		 *
+		 *  @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
+			);
+		},
+	
+	
+		/**
+		 * 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,
+	
+	
 		/**
-		 * 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.
+		 * 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
-		 *  @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
-			},
-		
-		
+		 *  @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": {
 			/**
-			 * Scrolling settings for a table.
+			 * 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
 			 */
-			"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,
-		
+			"oAria": {
 				/**
-				 * 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}.
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"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,
-		
+				"sSortAscending": ": activate to sort column ascending",
+	
 				/**
-				 * 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}.
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"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
+				"sSortDescending": ": activate to sort column descending"
 			},
-		
+	
 			/**
-			 * Browser support parameters
+			 * Pagination string used by DataTables for the built-in pagination
+			 * control types.
 			 *  @namespace
+			 *  @name DataTable.defaults.language.paginate
 			 */
-			"oBrowser": {
+			"oPaginate": {
 				/**
-				 * Indicate if the browser incorrectly calculates width:100% inside a
-				 * scrolling element (IE6/7)
-				 *  @type boolean
-				 *  @default false
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"bScrollOversize": false,
-		
+				"sFirst": "First",
+	
+	
 				/**
-				 * 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
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"bScrollbarLeft": false,
-		
+				"sLast": "Last",
+	
+	
 				/**
-				 * Flag for if `getBoundingClientRect` is fully supported or not
-				 *  @type boolean
-				 *  @default false
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"bBounding": false,
-		
+				"sNext": "Next",
+	
+	
 				/**
-				 * Browser scrollbar width
-				 *  @type integer
-				 *  @default 0
+				 * 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"
+				 *          }
+				 *        }
+				 *      } );
+				 *    } );
 				 */
-				"barWidth": 0
+				"sPrevious": "Previous"
 			},
-		
-		
-			"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": [],
-		
-			/**
-			 * 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
+			 * 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 <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
+			 *  @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"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"bInitialised": false,
-		
+			"sEmptyTable": "No data available in table",
+	
+	
 			/**
-			 * Information about open rows. Each object in the array has the parameters
-			 * 'nTr' and 'nParent'
-			 *  @type array
-			 *  @default []
+			 * 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
+			 *
+			 *  @type string
+			 *  @default Showing _START_ to _END_ of _TOTAL_ entries
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.info
+			 *
+			 *  @example
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "info": "Showing page _PAGE_ of _PAGES_"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"aoOpenRows": [],
-		
+			"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
+	
+	
 			/**
-			 * 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}.
+			 * Display information string for when the table is empty. Typically the
+			 * format of this string should match `info`.
 			 *  @type string
-			 *  @default null
+			 *  @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"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"sDom": null,
-		
+			"sInfoEmpty": "Showing 0 to 0 of 0 entries",
+	
+	
 			/**
-			 * Search delay (in mS)
-			 *  @type integer
-			 *  @default null
+			 * 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"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"searchDelay": null,
-		
+			"sInfoFiltered": "(filtered from _MAX_ total entries)",
+	
+	
 			/**
-			 * 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}.
+			 * 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 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
+			 *  @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."
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"iStateDuration": 0,
-		
+			"sInfoPostFix": "",
+	
+	
 			/**
-			 * 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 []
+			 * 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": "."
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"aoStateSave": [],
-		
+			"sDecimal": "",
+	
+	
 			/**
-			 * 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 []
+			 * 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": "'"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"aoStateLoad": [],
-		
+			"sThousands": ",",
+	
+	
 			/**
-			 * State that was saved. Useful for back reference
-			 *  @type object
-			 *  @default null
+			 * 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 Show _MENU_ entries
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.lengthMenu
+			 *
+			 *  @example
+			 *    // Language change only
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "lengthMenu": "Display _MENU_ records"
+			 *        }
+			 *      } );
+			 *    } );
+			 *
+			 *  @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'
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"oSavedState": null,
-		
+			"sLengthMenu": "Show _MENU_ entries",
+	
+	
 			/**
-			 * State that was loaded. Useful for back reference
-			 *  @type object
-			 *  @default 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 Loading...
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.loadingRecords
+			 *
+			 *  @example
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "loadingRecords": "Please wait - loading..."
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"oLoadedState": null,
-		
+			"sLoadingRecords": "Loading...",
+	
+	
 			/**
-			 * 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}.
+			 * Text which is displayed when the table is processing a user action
+			 * (usually a sort command or similar).
 			 *  @type string
-			 *  @default null
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.processing
+			 *
+			 *  @example
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "processing": "DataTables is currently busy"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"sAjaxSource": null,
-		
+			"sProcessing": "",
+	
+	
 			/**
-			 * 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}.
+			 * 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"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"sAjaxDataProp": null,
-		
+			"sSearch": "Search:",
+	
+	
 			/**
-			 * 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
+			 * Assign a `placeholder` attribute to the search `input` element
+			 *  @type string
+			 *  @default 
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.searchPlaceholder
 			 */
-			"jqXHR": null,
-		
+			"sSearchPlaceholder": "",
+	
+	
 			/**
-			 * JSON returned from the server in the last Ajax request
-			 *  @type object
-			 *  @default undefined
+			 * 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 <i>Empty string - i.e. disabled</i>
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.url
+			 *
+			 *  @example
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"json": undefined,
-		
+			"sUrl": "",
+	
+	
 			/**
-			 * Data submitted as part of the last Ajax request
-			 *  @type object
-			 *  @default undefined
+			 * 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 No matching records found
+			 *
+			 *  @dtopt Language
+			 *  @name DataTable.defaults.language.zeroRecords
+			 *
+			 *  @example
+			 *    $(document).ready( function() {
+			 *      $('#example').dataTable( {
+			 *        "language": {
+			 *          "zeroRecords": "No records to display"
+			 *        }
+			 *      } );
+			 *    } );
 			 */
-			"oAjaxData": undefined,
-		
+			"sZeroRecords": "No matching records found"
+		},
+	
+	
+		/**
+		 * 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>'<' and '>' - div elements</li>
+		 *         <li>'<"class" and '>' - div with a class</li>
+		 *         <li>'<"#id" and '>' - div with an ID</li>
+		 *       </ul>
+		 *     </li>
+		 *     <li>Examples:
+		 *       <ul>
+		 *         <li>'<"wrapper"flipt>'</li>
+		 *         <li>'<lf<t>ip>'</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": '<"top"i>rt<"bottom"flp><"clear">'
+		 *      } );
+		 *    } );
+		 */
+		"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": {
+	
 			/**
-			 * Function to get the server-side data.
+			 * 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 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 []
+			 *  @type boolean
 			 */
-			"aoServerParams": [],
-		
+			"bAutoWidth": null,
+	
 			/**
-			 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
-			 * required).
+			 * 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 string
+			 *  @type boolean
 			 */
-			"sServerMethod": null,
-		
+			"bDeferRender": null,
+	
 			/**
-			 * Format numbers for display.
+			 * 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 function
+			 *  @type boolean
 			 */
-			"fnFormatNumber": null,
-		
+			"bFilter": null,
+	
 			/**
-			 * List of options that can be used for the user selectable length menu.
+			 * 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 []
-			 */
-			"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,
-		
+			"bInfo": null,
+	
 			/**
-			 * Draw index (iDraw) of the last error when parsing the returned data
-			 *  @type int
-			 *  @default -1
+			 * 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
 			 */
-			"iDrawError": -1,
-		
+			"bLengthChange": null,
+	
 			/**
-			 * Paging display length
-			 *  @type int
-			 *  @default 10
+			 * 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
 			 */
-			"_iDisplayLength": 10,
-		
+			"bPaginate": null,
+	
 			/**
-			 * Paging start point - aiDisplay index
-			 *  @type int
-			 *  @default 0
+			 * 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
 			 */
-			"_iDisplayStart": 0,
-		
+			"bProcessing": null,
+	
 			/**
-			 * 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
+			 * 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
 			 */
-			"_iRecordsTotal": 0,
-		
+			"bServerSide": null,
+	
 			/**
-			 * 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.
+			 * Sorting enablement flag.
+			 * Note that this parameter will be set by the initialisation routine. To
+			 * set a default use {@link DataTable.defaults}.
 			 *  @type boolean
-			 *  @default 0
-			 *  @private
 			 */
-			"_iRecordsDisplay": 0,
-		
+			"bSort": null,
+	
 			/**
-			 * The classes to use for the table
-			 *  @type object
-			 *  @default {}
+			 * Multi-column sorting
+			 * Note that this parameter will be set by the initialisation routine. To
+			 * set a default use {@link DataTable.defaults}.
+			 *  @type boolean
 			 */
-			"oClasses": {},
-		
+			"bSortMulti": null,
+	
 			/**
-			 * 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.
+			 * 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
-			 *  @default false
-			 *  @deprecated
 			 */
-			"bFiltered": false,
-		
+			"bSortClasses": null,
+	
 			/**
-			 * 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.
+			 * 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
-			 *  @default false
-			 *  @deprecated
 			 */
-			"bSorted": false,
-		
+			"bStateSave": null
+		},
+	
+	
+		/**
+		 * Scrolling settings for a table.
+		 *  @namespace
+		 */
+		"oScroll": {
 			/**
-			 * 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.
+			 * 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
 			 */
-			"bSortCellsTop": null,
-		
+			"bCollapse": null,
+	
 			/**
-			 * Initialisation object that is used for the table
-			 *  @type object
-			 *  @default null
+			 * Width of the scrollbar for the web-browser's platform. Calculated
+			 * during table initialisation.
+			 *  @type int
+			 *  @default 0
 			 */
-			"oInit": null,
-		
+			"iBarWidth": 0,
+	
 			/**
-			 * Destroy callback functions - for plug-ins to attach themselves to the
-			 * destroy so they can clean up markup and events.
-			 *  @type array
-			 *  @default []
+			 * 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
 			 */
-			"aoDestroyCallback": [],
-		
-		
+			"sX": null,
+	
 			/**
-			 * Get the number of records in the current record set, before filtering
-			 *  @type function
+			 * 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
 			 */
-			"fnRecordsTotal": function ()
-			{
-				return _fnDataSource( this ) == 'ssp' ?
-					this._iRecordsTotal * 1 :
-					this.aiDisplayMaster.length;
-			},
-		
+			"sXInner": null,
+	
 			/**
-			 * Get the number of records in the current record set, after filtering
-			 *  @type function
+			 * 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
 			 */
-			"fnRecordsDisplay": function ()
-			{
-				return _fnDataSource( this ) == 'ssp' ?
-					this._iRecordsDisplay * 1 :
-					this.aiDisplay.length;
-			},
-		
+			"sY": null
+		},
+	
+		/**
+		 * Language information for the table.
+		 *  @namespace
+		 *  @extends DataTable.defaults.oLanguage
+		 */
+		"oLanguage": {
 			/**
-			 * Get the display end point - aiDisplay index
+			 * Information callback function. See
+			 * {@link DataTable.defaults.fnInfoCallback}
 			 *  @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,
-		
+			"fnInfoCallback": null
+		},
+	
+		/**
+		 * Browser support parameters
+		 *  @namespace
+		 */
+		"oBrowser": {
 			/**
-			 * Last applied sort
-			 *  @type array
-			 *  @default []
+			 * Indicate if the browser incorrectly calculates width:100% inside a
+			 * scrolling element (IE6/7)
+			 *  @type boolean
+			 *  @default false
 			 */
-			"aLastSort": [],
-		
+			"bScrollOversize": false,
+	
 			/**
-			 * Stored plug-in instances
-			 *  @type object
-			 *  @default {}
+			 * 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
 			 */
-			"oPlugins": {},
-		
+			"bScrollbarLeft": false,
+	
 			/**
-			 * Function used to get a row's id from the row's data
-			 *  @type function
-			 *  @default null
+			 * Flag for if `getBoundingClientRect` is fully supported or not
+			 *  @type boolean
+			 *  @default false
 			 */
-			"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": [],
+	
+		/**
+		 * 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: [],
+	
+	
 		/**
-		 * Extension object for DataTables that is used to provide all extension
+		 * 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: {},
-		
-		
-			/**
-			 * Element class names
-			 *
-			 *  @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",
-		
-		
+		legacy: {
 			/**
-			 * 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
+			 * Enable / disable DataTables 1.9 compatible server-side processing
+			 * requests
 			 *
-			 *  @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;
+			 *    // 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 && "" == max ) {
-			 *          return true;
-			 *        }
-			 *        else if ( min < version && version < max ) {
-			 *          return true;
+			 *
+			 *        // Check prefixed by currency
+			 *        if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
+			 *          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).
-			 *
-			 * Pagination types (the four built in options and any additional plug-in
-			 * options defined here) can be used through the `paginationType`
-			 * initialisation parameter.
+			 * Type based ordering.
 			 *
-			 * The functions defined take two parameters:
+			 * 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.
 			 *
-			 * 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) == '£' ) {
-				 *          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">…</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">…</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, '&')
+				.replace(/</g, '<')
+				.replace(/>/g, '>')
+				.replace(/"/g, '"') :
+			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, '&')
-					.replace(/</g, '<')
-					.replace(/>/g, '>')
-					.replace(/"/g, '"') :
-				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
@@ -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
@@ -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
@@ -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") {