summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-08-04 20:24:56 +0300
committerGitHub <noreply@github.com>2026-08-04 18:24:56 +0100
commit2aa9c9c10a3cad48a7c6536ff2b6e911aa20b1d1 (patch)
treeded0cb8a4d82c5edd0041b5f036d3f915c5d349e
parent1d1fb3f579dfbe83f0e89a051b2d7ddce9289fac (diff)
downloadvyos-documentation-2aa9c9c10a3cad48a7c6536ff2b6e911aa20b1d1.tar.gz
vyos-documentation-2aa9c9c10a3cad48a7c6536ff2b6e911aa20b1d1.zip
docs: upgrade vendored DataTables 1.11.5 -> 2.3.8 (#2173)
* docs: upgrade vendored DataTables 1.11.5 -> 2.3.8 Swap the vendored single-component DataTables downloader build from 1.11.5 to 2.3.8 (docs/_static/js/datatables.js, docs/_static/css/datatables.css). The only init site, docs/_static/js/tables.js, uses the option-less jQuery-style $('#id').DataTable() form, which is unchanged in 2.x, so neither it nor docs/_templates/layout.html needed edits. jQuery 3.6.0 is still injected ahead of the bundle by sphinxcontrib-jquery and satisfies 2.x. Remove docs/_static/css/DataTables-1.11.5/ (10 sort-icon images). 2.x draws sort indicators in pure CSS: the new datatables.css contains no url() references at all, and the replaced 1.11.5 stylesheet was the only thing in the repo that referenced those files. Migrate custom selectors for the 2.x generated-class renames, each verified against the downloaded bundle rather than assumed: - .dataTables_info -> .dt-info (tables.css, text.css) - .paginate_button -> .dt-paging-button (tables.css, text.css) - #coverage a.paginate_button{,.current,.next,.previous} -> #coverage button.dt-paging-button{...} 2.x renders paging controls as <button> (was <a> in 1.x), so the old rules failed on both element type and class. The .current/.next/ .previous modifiers are still emitted and are retained. The #table-cfgcmd_wrapper / #table-opcmd_wrapper rules are deliberately untouched: only the wrapper *class* changed (dataTables_wrapper -> dt-container); the element id is still <tableId>_wrapper. Drop the CodeQL exclusion (.github/codeql/codeql-config.yml, plus the optional codeql-cfg-path input in .github/workflows/codeql.yml). That config existed solely to paths-ignore the 1.11.5 bundle, which tripped 7 alerts (5x js/incomplete-multi-character-sanitization, 2x js/incomplete-sanitization). Upstream 2.x hardened the implicated helpers - _stripHtml now runs a do/while fixpoint over the <script> strip, and _escapeHtml uses all-global regexes - so those patterns are not expected to recur. Removing the exclusion restores default CodeQL coverage of the file; this PR's own CodeQL run is the empirical test. Provenance (DataTables downloader builder, styling "DataTables default", component set dt only - the 2.x equivalent of the previous #dt/dt-1.11.5): https://cdn.datatables.net/v/dt/dt-2.3.8/datatables.js sha256 184fb4bd0b9a81a955acd608ba94d0643c74271e78c0fae30f1f40b824f88b1d https://cdn.datatables.net/v/dt/dt-2.3.8/datatables.css sha256 e37677437e0fbe4a463aafc83bc4ea8d60986b72050ba9f47f740b41148184ae Verified with a local make html: build succeeded, and coverage.html renders both tables (table-cfgcmd 9095 rows, table-opcmd 3036 rows) with the 2.3.8 assets injected in the correct order after jQuery. 🤖 Generated by [robots](https://vyos.io) * docs: simplify paging-button hover background to one declaration The hover rule carried 'background-color: #E1E4E5 !important' followed by 'background: none'. The important longhand outranks the later normal-importance shorthand, so the gray hover fill did apply — but the pair is confusing and CodeRabbit read it as a dead declaration. Collapse to a single 'background: #E1E4E5 !important', which produces the identical effective state (gray fill, upstream 2.x hover gradient suppressed) in one declaration. 🤖 Generated by [robots](https://vyos.io) * docs: re-target mobile search-input margin at the 2.x sibling DOM DataTables 1.x nested the search <input> inside its <label>; 2.x renders them as siblings inside div.dt-search, so the narrow-viewport descendant rule 'label input' no longer matched and the 10px top margin was silently lost below 576px. Target '.dt-search input' instead. Flagged independently by the implementation pass and both adversarial reviewers (Codex + agy). 🤖 Generated by [robots](https://vyos.io)
-rw-r--r--.github/codeql/codeql-config.yml11
-rw-r--r--.github/workflows/codeql.yml1
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_asc.pngbin160 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_asc.webpbin200 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.pngbin146 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.webpbin174 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_both.pngbin201 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_both.webpbin204 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_desc.pngbin158 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_desc.webpbin206 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.pngbin148 -> 0 bytes
-rw-r--r--docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.webpbin162 -> 0 bytes
-rw-r--r--docs/_static/css/datatables.css949
-rw-r--r--docs/_static/css/tables.css30
-rw-r--r--docs/_static/css/text.css4
-rw-r--r--docs/_static/js/datatables.js17469
16 files changed, 8843 insertions, 9621 deletions
diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml
deleted file mode 100644
index 60563975..00000000
--- a/.github/codeql/codeql-config.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# CodeQL configuration for vyos-documentation.
-#
-# docs/_static/js/datatables.js is vendored stock DataTables 1.11.5 — unmodified
-# third-party code (MIT). CodeQL flags its internal HTML-strip/sort helpers
-# (js/incomplete-multi-character-sanitization, js/incomplete-sanitization), which
-# are display/sort normalization routines, not sanitization boundaries. The file is
-# excluded from analysis rather than hand-patched, so the vendored copy stays
-# byte-identical to upstream and remains trivially re-vendorable on the next bump.
-
-paths-ignore:
- - docs/_static/js/datatables.js
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 5b2714de..499080b4 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -28,4 +28,3 @@ jobs:
uses: vyos/.github/.github/workflows/codeql-analysis.yml@production
with:
languages: "['python', 'javascript-typescript']"
- codeql-cfg-path: ./.github/codeql/codeql-config.yml
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_asc.png b/docs/_static/css/DataTables-1.11.5/images/sort_asc.png
deleted file mode 100644
index e1ba61a8..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_asc.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_asc.webp b/docs/_static/css/DataTables-1.11.5/images/sort_asc.webp
deleted file mode 100644
index eebabad0..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_asc.webp
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.png b/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.png
deleted file mode 100644
index c9fdd8a1..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.webp b/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.webp
deleted file mode 100644
index 847d4f5d..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_asc_disabled.webp
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_both.png b/docs/_static/css/DataTables-1.11.5/images/sort_both.png
deleted file mode 100644
index af5bc7c5..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_both.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_both.webp b/docs/_static/css/DataTables-1.11.5/images/sort_both.webp
deleted file mode 100644
index 1b09597a..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_both.webp
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_desc.png b/docs/_static/css/DataTables-1.11.5/images/sort_desc.png
deleted file mode 100644
index 0e156deb..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_desc.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_desc.webp b/docs/_static/css/DataTables-1.11.5/images/sort_desc.webp
deleted file mode 100644
index 857789c6..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_desc.webp
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.png b/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.png
deleted file mode 100644
index fb11dfe2..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.png
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.webp b/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.webp
deleted file mode 100644
index 3368310f..00000000
--- a/docs/_static/css/DataTables-1.11.5/images/sort_desc_disabled.webp
+++ /dev/null
Binary files differ
diff --git a/docs/_static/css/datatables.css b/docs/_static/css/datatables.css
index 94032981..8da93feb 100644
--- a/docs/_static/css/datatables.css
+++ b/docs/_static/css/datatables.css
@@ -4,39 +4,362 @@
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
- * https://datatables.net/download/#dt/dt-1.11.5
+ * https://datatables.net/download/#dt/dt-2.3.8
*
* Included libraries:
- * DataTables 1.11.5
+ * DataTables 2.3.8
*/
+:root {
+ --dt-row-selected: 13, 110, 253;
+ --dt-row-selected-text: 255, 255, 255;
+ --dt-row-selected-link: 228, 228, 228;
+ --dt-row-stripe: 0, 0, 0;
+ --dt-row-hover: 0, 0, 0;
+ --dt-column-ordering: 0, 0, 0;
+ --dt-header-align-items: center;
+ --dt-header-vertical-align: middle;
+ --dt-html-background: white;
+}
+:root.dark {
+ --dt-html-background: rgb(33, 37, 41);
+}
+
+table.dataTable tbody td.dt-control {
+ text-align: center;
+ cursor: pointer;
+}
+table.dataTable tbody td.dt-control:before {
+ display: inline-block;
+ box-sizing: border-box;
+ content: "";
+ border-top: 5px solid transparent;
+ border-left: 10px solid rgba(0, 0, 0, 0.5);
+ border-bottom: 5px solid transparent;
+ border-right: 0px solid transparent;
+}
+table.dataTable tbody tr.dt-hasChild td.dt-control:before {
+ border-top: 10px solid rgba(0, 0, 0, 0.5);
+ border-left: 5px solid transparent;
+ border-bottom: 0px solid transparent;
+ border-right: 5px solid transparent;
+}
+table.dataTable tfoot:empty {
+ display: none;
+}
+
+html.dark table.dataTable td.dt-control:before,
+:root[data-bs-theme=dark] table.dataTable td.dt-control:before,
+:root[data-theme=dark] table.dataTable td.dt-control:before {
+ border-left-color: rgba(255, 255, 255, 0.5);
+}
+html.dark table.dataTable tr.dt-hasChild td.dt-control:before,
+:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before,
+:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
+ border-top-color: rgba(255, 255, 255, 0.5);
+ border-left-color: transparent;
+}
+
+div.dt-scroll {
+ width: 100%;
+}
+
+div.dt-scroll-body thead tr,
+div.dt-scroll-body tfoot tr {
+ height: 0;
+}
+div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td,
+div.dt-scroll-body tfoot tr th,
+div.dt-scroll-body tfoot tr td {
+ height: 0 !important;
+ padding-top: 0px !important;
+ padding-bottom: 0px !important;
+ border-top-width: 0px !important;
+ border-bottom-width: 0px !important;
+}
+div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing,
+div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,
+div.dt-scroll-body tfoot tr td div.dt-scroll-sizing {
+ height: 0 !important;
+ overflow: hidden !important;
+}
+
+table.dataTable thead > tr > th:active,
+table.dataTable thead > tr > td:active {
+ outline: none;
+}
+table.dataTable thead > tr > th.dt-orderable-asc .dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-orderable-asc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-ordering-asc .dt-column-order:before {
+ position: absolute;
+ display: block;
+ bottom: 50%;
+ content: "\25B2";
+ content: "\25B2"/"";
+}
+table.dataTable thead > tr > th.dt-orderable-desc .dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-orderable-desc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-ordering-desc .dt-column-order:after {
+ position: absolute;
+ display: block;
+ top: 50%;
+ content: "\25BC";
+ content: "\25BC"/"";
+}
+table.dataTable thead > tr > th.dt-orderable-asc .dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc .dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc .dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc .dt-column-order,
+table.dataTable thead > tr > td.dt-orderable-asc .dt-column-order,
+table.dataTable thead > tr > td.dt-orderable-desc .dt-column-order,
+table.dataTable thead > tr > td.dt-ordering-asc .dt-column-order,
+table.dataTable thead > tr > td.dt-ordering-desc .dt-column-order {
+ position: relative;
+ width: 12px;
+ height: 20px;
+}
+table.dataTable thead > tr > th.dt-orderable-asc .dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc .dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc .dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc .dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc .dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc .dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc .dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-orderable-asc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-orderable-asc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-orderable-desc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-orderable-desc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-ordering-asc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-ordering-asc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-ordering-desc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-ordering-desc .dt-column-order:after {
+ left: 0;
+ opacity: 0.125;
+ line-height: 9px;
+ font-size: 0.8em;
+}
+table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc,
+table.dataTable thead > tr > td.dt-orderable-asc,
+table.dataTable thead > tr > td.dt-orderable-desc {
+ cursor: pointer;
+}
+table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover,
+table.dataTable thead > tr > td.dt-orderable-asc:hover,
+table.dataTable thead > tr > td.dt-orderable-desc:hover {
+ outline: 2px solid rgba(0, 0, 0, 0.05);
+ outline-offset: -2px;
+}
+table.dataTable thead > tr > th.dt-ordering-asc .dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc .dt-column-order:after,
+table.dataTable thead > tr > td.dt-ordering-asc .dt-column-order:before,
+table.dataTable thead > tr > td.dt-ordering-desc .dt-column-order:after {
+ opacity: 0.6;
+}
+table.dataTable thead > tr > th.dt-orderable-none:not(.dt-ordering-asc, .dt-ordering-desc) .dt-column-order:empty, table.dataTable thead > tr > th.sorting_desc_disabled .dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled .dt-column-order:before,
+table.dataTable thead > tr > td.dt-orderable-none:not(.dt-ordering-asc, .dt-ordering-desc) .dt-column-order:empty,
+table.dataTable thead > tr > td.sorting_desc_disabled .dt-column-order:after,
+table.dataTable thead > tr > td.sorting_asc_disabled .dt-column-order:before {
+ display: none;
+}
+table.dataTable thead > tr > th:active,
+table.dataTable thead > tr > td:active {
+ outline: none;
+}
+
+table.dataTable thead > tr > th div.dt-column-header,
+table.dataTable thead > tr > th div.dt-column-footer,
+table.dataTable thead > tr > td div.dt-column-header,
+table.dataTable thead > tr > td div.dt-column-footer,
+table.dataTable tfoot > tr > th div.dt-column-header,
+table.dataTable tfoot > tr > th div.dt-column-footer,
+table.dataTable tfoot > tr > td div.dt-column-header,
+table.dataTable tfoot > tr > td div.dt-column-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: var(--dt-header-align-items);
+ gap: 4px;
+}
+table.dataTable thead > tr > th div.dt-column-header .dt-column-title,
+table.dataTable thead > tr > th div.dt-column-footer .dt-column-title,
+table.dataTable thead > tr > td div.dt-column-header .dt-column-title,
+table.dataTable thead > tr > td div.dt-column-footer .dt-column-title,
+table.dataTable tfoot > tr > th div.dt-column-header .dt-column-title,
+table.dataTable tfoot > tr > th div.dt-column-footer .dt-column-title,
+table.dataTable tfoot > tr > td div.dt-column-header .dt-column-title,
+table.dataTable tfoot > tr > td div.dt-column-footer .dt-column-title {
+ flex-grow: 1;
+}
+table.dataTable thead > tr > th div.dt-column-header .dt-column-title:empty,
+table.dataTable thead > tr > th div.dt-column-footer .dt-column-title:empty,
+table.dataTable thead > tr > td div.dt-column-header .dt-column-title:empty,
+table.dataTable thead > tr > td div.dt-column-footer .dt-column-title:empty,
+table.dataTable tfoot > tr > th div.dt-column-header .dt-column-title:empty,
+table.dataTable tfoot > tr > th div.dt-column-footer .dt-column-title:empty,
+table.dataTable tfoot > tr > td div.dt-column-header .dt-column-title:empty,
+table.dataTable tfoot > tr > td div.dt-column-footer .dt-column-title:empty {
+ display: none;
+}
+
+div.dt-scroll-body > table.dataTable > thead > tr > th,
+div.dt-scroll-body > table.dataTable > thead > tr > td {
+ overflow: hidden;
+}
+
+:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover,
+:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover,
+:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover,
+:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover,
+:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover,
+:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover,
+:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover {
+ outline: 2px solid rgba(255, 255, 255, 0.05);
+}
+
+div.dt-processing {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 200px;
+ margin-left: -100px;
+ margin-top: -22px;
+ text-align: center;
+ padding: 2px;
+ z-index: 10;
+}
+div.dt-processing > div:last-child {
+ position: relative;
+ width: 80px;
+ height: 15px;
+ margin: 1em auto;
+}
+div.dt-processing > div:last-child > div {
+ position: absolute;
+ top: 0;
+ width: 13px;
+ height: 13px;
+ border-radius: 50%;
+ background: rgb(13, 110, 253);
+ background: rgb(var(--dt-row-selected));
+ animation-timing-function: cubic-bezier(0, 1, 1, 0);
+}
+div.dt-processing > div:last-child > div:nth-child(1) {
+ left: 8px;
+ animation: datatables-loader-1 0.6s infinite;
+}
+div.dt-processing > div:last-child > div:nth-child(2) {
+ left: 8px;
+ animation: datatables-loader-2 0.6s infinite;
+}
+div.dt-processing > div:last-child > div:nth-child(3) {
+ left: 32px;
+ animation: datatables-loader-2 0.6s infinite;
+}
+div.dt-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,
+table.dataTable td {
+ box-sizing: border-box;
+}
+table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date,
+table.dataTable td.dt-type-numeric,
+table.dataTable td.dt-type-date {
+ text-align: right;
+}
+table.dataTable th.dt-type-numeric div.dt-column-header,
+table.dataTable th.dt-type-numeric div.dt-column-footer, table.dataTable th.dt-type-date div.dt-column-header,
+table.dataTable th.dt-type-date div.dt-column-footer,
+table.dataTable td.dt-type-numeric div.dt-column-header,
+table.dataTable td.dt-type-numeric div.dt-column-footer,
+table.dataTable td.dt-type-date div.dt-column-header,
+table.dataTable td.dt-type-date div.dt-column-footer {
+ flex-direction: row-reverse;
+}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
}
+table.dataTable th.dt-left div.dt-column-header,
+table.dataTable th.dt-left div.dt-column-footer,
+table.dataTable td.dt-left div.dt-column-header,
+table.dataTable td.dt-left div.dt-column-footer {
+ flex-direction: row;
+}
table.dataTable th.dt-center,
-table.dataTable td.dt-center,
-table.dataTable td.dataTables_empty {
+table.dataTable td.dt-center {
text-align: center;
}
table.dataTable th.dt-right,
table.dataTable td.dt-right {
text-align: right;
}
+table.dataTable th.dt-right div.dt-column-header,
+table.dataTable th.dt-right div.dt-column-footer,
+table.dataTable td.dt-right div.dt-column-header,
+table.dataTable td.dt-right div.dt-column-footer {
+ flex-direction: row-reverse;
+}
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
text-align: justify;
}
+table.dataTable th.dt-justify div.dt-column-header,
+table.dataTable th.dt-justify div.dt-column-footer,
+table.dataTable td.dt-justify div.dt-column-header,
+table.dataTable td.dt-justify div.dt-column-footer {
+ flex-direction: row;
+}
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
+table.dataTable th.dt-empty,
+table.dataTable td.dt-empty {
+ text-align: center;
+ vertical-align: top;
+}
+table.dataTable thead th,
+table.dataTable thead td,
+table.dataTable tfoot th,
+table.dataTable tfoot td {
+ text-align: left;
+ vertical-align: var(--dt-header-vertical-align);
+}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
text-align: left;
}
+table.dataTable thead th.dt-head-left div.dt-column-header,
+table.dataTable thead th.dt-head-left div.dt-column-footer,
+table.dataTable thead td.dt-head-left div.dt-column-header,
+table.dataTable thead td.dt-head-left div.dt-column-footer,
+table.dataTable tfoot th.dt-head-left div.dt-column-header,
+table.dataTable tfoot th.dt-head-left div.dt-column-footer,
+table.dataTable tfoot td.dt-head-left div.dt-column-header,
+table.dataTable tfoot td.dt-head-left div.dt-column-footer {
+ flex-direction: row;
+}
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
@@ -49,12 +372,32 @@ table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
text-align: right;
}
+table.dataTable thead th.dt-head-right div.dt-column-header,
+table.dataTable thead th.dt-head-right div.dt-column-footer,
+table.dataTable thead td.dt-head-right div.dt-column-header,
+table.dataTable thead td.dt-head-right div.dt-column-footer,
+table.dataTable tfoot th.dt-head-right div.dt-column-header,
+table.dataTable tfoot th.dt-head-right div.dt-column-footer,
+table.dataTable tfoot td.dt-head-right div.dt-column-header,
+table.dataTable tfoot td.dt-head-right div.dt-column-footer {
+ flex-direction: row-reverse;
+}
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
text-align: justify;
}
+table.dataTable thead th.dt-head-justify div.dt-column-header,
+table.dataTable thead th.dt-head-justify div.dt-column-footer,
+table.dataTable thead td.dt-head-justify div.dt-column-header,
+table.dataTable thead td.dt-head-justify div.dt-column-footer,
+table.dataTable tfoot th.dt-head-justify div.dt-column-header,
+table.dataTable tfoot th.dt-head-justify div.dt-column-footer,
+table.dataTable tfoot td.dt-head-justify div.dt-column-header,
+table.dataTable tfoot td.dt-head-justify div.dt-column-footer {
+ flex-direction: row;
+}
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
@@ -81,30 +424,13 @@ 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;
+
+:root {
+ --dt-row-hover-alpha: 0.035;
+ --dt-row-stripe-alpha: 0.023;
+ --dt-column-ordering-alpha: 0.019;
+ --dt-row-selected-stripe-alpha: 0.923;
+ --dt-row-selected-column-ordering-alpha: 0.919;
}
/*
@@ -113,233 +439,259 @@ table.dataTable tr.dt-hasChild td.dt-control:before {
table.dataTable {
width: 100%;
margin: 0 auto;
- clear: both;
- border-collapse: separate;
border-spacing: 0;
/*
* Header and footer styles
*/
- /*
- * Body styles
- */
}
table.dataTable thead th,
table.dataTable tfoot th {
font-weight: bold;
}
-table.dataTable thead th,
-table.dataTable thead td {
- padding: 10px 18px;
- border-bottom: 1px solid #111;
+table.dataTable > thead > tr > th,
+table.dataTable > thead > tr > td {
+ padding: 10px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.3);
}
-table.dataTable thead th:active,
-table.dataTable thead td:active {
+table.dataTable > thead > tr > th:active,
+table.dataTable > thead > tr > td:active {
outline: none;
}
-table.dataTable tfoot th,
-table.dataTable tfoot td {
- padding: 10px 18px 6px 18px;
- border-top: 1px solid #111;
-}
-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;
- *cursor: hand;
- background-repeat: no-repeat;
- background-position: center right;
-}
-table.dataTable thead .sorting {
- background-image: url("DataTables-1.11.5/images/sort_both.png");
+table.dataTable > tfoot > tr > th,
+table.dataTable > tfoot > tr > td {
+ border-top: 1px solid rgba(0, 0, 0, 0.3);
+ padding: 10px 10px 6px 10px;
}
-table.dataTable thead .sorting_asc {
- background-image: url("DataTables-1.11.5/images/sort_asc.png") !important;
+table.dataTable {
+ /*
+ * Body styles
+ */
}
-table.dataTable thead .sorting_desc {
- background-image: url("DataTables-1.11.5/images/sort_desc.png") !important;
+table.dataTable > tbody > tr {
+ background-color: transparent;
}
-table.dataTable thead .sorting_asc_disabled {
- background-image: url("DataTables-1.11.5/images/sort_asc_disabled.png");
+table.dataTable > tbody > tr:first-child > * {
+ border-top: none;
}
-table.dataTable thead .sorting_desc_disabled {
- background-image: url("DataTables-1.11.5/images/sort_desc_disabled.png");
+table.dataTable > tbody > tr:last-child > * {
+ border-bottom: none;
}
-table.dataTable tbody tr {
- background-color: #ffffff;
+table.dataTable > tbody > tr.selected > * {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.9);
+ color: rgb(255, 255, 255);
+ color: rgb(var(--dt-row-selected-text));
}
-table.dataTable tbody tr.selected {
- background-color: #B0BED9;
+table.dataTable > tbody > tr.selected a {
+ color: rgb(228, 228, 228);
+ color: rgb(var(--dt-row-selected-link));
}
-table.dataTable tbody th,
-table.dataTable tbody td {
+table.dataTable > tbody > tr > th,
+table.dataTable > tbody > tr > td {
padding: 8px 10px;
}
-table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
- border-top: 1px solid #ddd;
+table.dataTable.row-border > tbody > tr > *, table.dataTable.display > tbody > tr > * {
+ border-top: 1px solid rgba(0, 0, 0, 0.15);
}
-table.dataTable.row-border tbody tr:first-child th,
-table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
-table.dataTable.display tbody tr:first-child td {
+table.dataTable.row-border > tbody > tr:first-child > *, table.dataTable.display > tbody > tr:first-child > * {
border-top: none;
}
-table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
- border-top: 1px solid #ddd;
- border-right: 1px solid #ddd;
+table.dataTable.row-border > tbody > tr.selected + tr.selected > td, table.dataTable.display > tbody > tr.selected + tr.selected > td {
+ border-top-color: rgba(13, 110, 253, 0.65);
+ border-top-color: rgba(var(--dt-row-selected), 0.65);
}
-table.dataTable.cell-border tbody tr th:first-child,
-table.dataTable.cell-border tbody tr td:first-child {
- border-left: 1px solid #ddd;
+table.dataTable.cell-border > tbody > tr > * {
+ border-top: 1px solid rgba(0, 0, 0, 0.15);
+ border-right: 1px solid rgba(0, 0, 0, 0.15);
}
-table.dataTable.cell-border tbody tr:first-child th,
-table.dataTable.cell-border tbody tr:first-child td {
- border-top: none;
+table.dataTable.cell-border > tbody > tr > *:first-child {
+ border-left: 1px solid rgba(0, 0, 0, 0.15);
}
-table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
- background-color: #f9f9f9;
+table.dataTable.cell-border > tbody > tr:first-child > * {
+ border-top: 1px solid rgba(0, 0, 0, 0.3);
}
-table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
- background-color: #acbad4;
+table.dataTable.stripe > tbody > tr:nth-child(odd) > *, table.dataTable.display > tbody > tr:nth-child(odd) > * {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.023);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), var(--dt-row-stripe-alpha));
}
-table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
- background-color: #f6f6f6;
+table.dataTable.stripe > tbody > tr:nth-child(odd).selected > *, table.dataTable.display > tbody > tr:nth-child(odd).selected > * {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.923);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), var(--dt-row-selected-stripe-alpha));
}
-table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
- background-color: #aab7d1;
+table.dataTable.hover > tbody > tr:hover > *, table.dataTable.display > tbody > tr:hover > * {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.035);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), var(--dt-row-hover-alpha));
}
-table.dataTable.order-column tbody tr > .sorting_1,
-table.dataTable.order-column tbody tr > .sorting_2,
-table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
-table.dataTable.display tbody tr > .sorting_2,
-table.dataTable.display tbody tr > .sorting_3 {
- background-color: #fafafa;
+table.dataTable.hover > tbody > tr.selected:hover > *, table.dataTable.display > tbody > tr.selected:hover > * {
+ box-shadow: inset 0 0 0 9999px #0d6efd !important;
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 1) !important;
}
-table.dataTable.order-column tbody tr.selected > .sorting_1,
-table.dataTable.order-column tbody tr.selected > .sorting_2,
-table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
-table.dataTable.display tbody tr.selected > .sorting_2,
-table.dataTable.display tbody tr.selected > .sorting_3 {
- background-color: #acbad5;
+table.dataTable.order-column > tbody tr > .sorting_1,
+table.dataTable.order-column > tbody tr > .sorting_2,
+table.dataTable.order-column > tbody tr > .sorting_3, table.dataTable.display > tbody tr > .sorting_1,
+table.dataTable.display > tbody tr > .sorting_2,
+table.dataTable.display > tbody tr > .sorting_3 {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), var(--dt-column-ordering-alpha));
}
-table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
- background-color: #f1f1f1;
+table.dataTable.order-column > tbody tr.selected > .sorting_1,
+table.dataTable.order-column > tbody tr.selected > .sorting_2,
+table.dataTable.order-column > tbody tr.selected > .sorting_3, table.dataTable.display > tbody tr.selected > .sorting_1,
+table.dataTable.display > tbody tr.selected > .sorting_2,
+table.dataTable.display > tbody tr.selected > .sorting_3 {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), var(--dt-row-selected-column-ordering-alpha));
}
-table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
- background-color: #f3f3f3;
+table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_1, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_1 {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.054);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha)));
}
-table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
- background-color: whitesmoke;
+table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_2, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_2 {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.047);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha) - 0.007));
}
-table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
- background-color: #a6b4cd;
+table.dataTable.display > tbody > tr:nth-child(odd) > .sorting_3, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd) > .sorting_3 {
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.039);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-column-ordering), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha) - 0.015));
}
-table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
- background-color: #a8b5cf;
+table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_1 {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.954);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha)));
}
-table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
- background-color: #a9b7d1;
+table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_2 {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.947);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha) - 0.007));
}
-table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
- background-color: #fafafa;
-}
-table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
- background-color: #fcfcfc;
-}
-table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
- background-color: #fefefe;
-}
-table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
- background-color: #acbad5;
-}
-table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
- background-color: #aebcd6;
-}
-table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
- background-color: #afbdd8;
+table.dataTable.display > tbody > tr:nth-child(odd).selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr:nth-child(odd).selected > .sorting_3 {
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.939);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha) - 0.015));
}
table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
- background-color: #eaeaea;
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.082);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha) + var(--dt-row-hover-alpha)));
}
table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
- background-color: #ececec;
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.074);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha) + var(--dt-row-hover-alpha) - 0.007));
}
table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
- background-color: #efefef;
+ box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.062);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), calc(var(--dt-row-stripe-alpha) + var(--dt-column-ordering-alpha) + var(--dt-row-hover-alpha) - 0.015));
}
table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
- background-color: #a2aec7;
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.982);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha)));
}
table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
- background-color: #a3b0c9;
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.974);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha) + var(--dt-row-hover-alpha) - 0.007));
}
table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
- background-color: #a5b2cb;
-}
-table.dataTable.no-footer {
- border-bottom: 1px solid #111;
-}
-table.dataTable.nowrap th, table.dataTable.nowrap td {
- white-space: nowrap;
+ box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.962);
+ box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), calc(var(--dt-row-selected-stripe-alpha) + var(--dt-column-ordering-alpha) + var(--dt-row-hover-alpha) - 0.015));
}
table.dataTable.compact thead th,
-table.dataTable.compact thead td {
- padding: 4px 17px;
-}
+table.dataTable.compact thead td,
table.dataTable.compact tfoot th,
-table.dataTable.compact tfoot td {
- padding: 4px;
-}
+table.dataTable.compact tfoot td,
table.dataTable.compact tbody th,
table.dataTable.compact tbody td {
padding: 4px;
}
-table.dataTable th,
-table.dataTable td {
- box-sizing: content-box;
+div.dt-container div.dt-layout-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin: 0.75em 0;
+}
+div.dt-container div.dt-layout-row div.dt-layout-cell {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-start {
+ justify-content: flex-start;
+ margin-right: auto;
+}
+div.dt-container div.dt-layout-row div.dt-layout-cell.dt-layout-end {
+ justify-content: flex-end;
+ margin-left: auto;
+}
+div.dt-container div.dt-layout-row div.dt-layout-cell:empty {
+ display: none;
}
+@media screen and (max-width: 767px) {
+ div.dt-container div.dt-layout-row:not(.dt-layout-table) {
+ display: block;
+ }
+ div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell {
+ display: block;
+ text-align: center;
+ }
+ div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell > * {
+ margin: 0.5em 0;
+ }
+ div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-start {
+ margin-right: 0;
+ }
+ div.dt-container div.dt-layout-row:not(.dt-layout-table) div.dt-layout-cell.dt-layout-end {
+ margin-left: 0;
+ }
+}
+div.dt-container div.dt-layout-start > *:not(:last-child) {
+ margin-right: 1em;
+}
+div.dt-container div.dt-layout-end > *:not(:first-child) {
+ margin-left: 1em;
+}
+div.dt-container div.dt-layout-full {
+ width: 100%;
+}
+div.dt-container div.dt-layout-full > *:only-child {
+ margin-left: auto;
+ margin-right: auto;
+}
+div.dt-container div.dt-layout-table > div {
+ display: block !important;
+}
+
+@media screen and (max-width: 767px) {
+ div.dt-container div.dt-layout-start > *:not(:last-child) {
+ margin-right: 0;
+ }
+ div.dt-container div.dt-layout-end > *:not(:first-child) {
+ margin-left: 0;
+ }
+}
/*
* Control feature layout
*/
-.dataTables_wrapper {
+div.dt-container {
position: relative;
clear: both;
- margin-bottom: 24px;
}
-.dataTables_wrapper .dataTables_length {
- float: left;
-}
-.dataTables_wrapper .dataTables_length select {
+div.dt-container .dt-search input {
border: 1px solid #aaa;
border-radius: 3px;
padding: 5px;
background-color: transparent;
- padding: 4px;
-}
-.dataTables_wrapper .dataTables_filter {
- float: right;
- text-align: right;
+ color: inherit;
+ margin-left: 3px;
}
-.dataTables_wrapper .dataTables_filter input {
+div.dt-container .dt-input {
border: 1px solid #aaa;
border-radius: 3px;
padding: 5px;
background-color: transparent;
- margin-left: 3px;
+ color: inherit;
}
-.dataTables_wrapper .dataTables_info {
- clear: both;
- float: left;
- padding-top: 0.755em;
-}
-.dataTables_wrapper .dataTables_paginate {
- float: right;
- text-align: right;
- padding-top: 0.25em;
+div.dt-container select.dt-input {
+ padding: 4px;
}
-.dataTables_wrapper .dataTables_paginate .paginate_button {
+div.dt-container .dt-paging .dt-paging-button {
box-sizing: border-box;
display: inline-block;
min-width: 1.5em;
@@ -348,150 +700,169 @@ table.dataTable td {
text-align: center;
text-decoration: none !important;
cursor: pointer;
- *cursor: hand;
- color: #333 !important;
+ color: inherit !important;
border: 1px solid transparent;
border-radius: 2px;
+ background: transparent;
}
-.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
- color: #333 !important;
- border: 1px solid #979797;
- background-color: white;
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc));
- /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%);
- /* Chrome10+,Safari5.1+ */
- background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%);
- /* FF3.6+ */
- background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%);
- /* IE10+ */
- background: -o-linear-gradient(top, white 0%, #dcdcdc 100%);
- /* Opera 11.10+ */
- background: linear-gradient(to bottom, white 0%, #dcdcdc 100%);
- /* W3C */
-}
-.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
+div.dt-container .dt-paging .dt-paging-button.current, div.dt-container .dt-paging .dt-paging-button.current:hover {
+ color: inherit !important;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ background-color: rgba(0, 0, 0, 0.05);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(229.5, 229.5, 229.5, 0.05)), color-stop(100%, rgba(0, 0, 0, 0.05))); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* FF3.6+ */
+ background: -ms-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* IE10+ */
+ background: -o-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* Opera 11.10+ */
+ background: linear-gradient(to bottom, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%); /* W3C */
+}
+div.dt-container .dt-paging .dt-paging-button.disabled, div.dt-container .dt-paging .dt-paging-button.disabled:hover, div.dt-container .dt-paging .dt-paging-button.disabled:active {
cursor: default;
- color: #666 !important;
+ color: rgba(0, 0, 0, 0.5) !important;
border: 1px solid transparent;
background: transparent;
box-shadow: none;
}
-.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
+div.dt-container .dt-paging .dt-paging-button:hover {
color: white !important;
border: 1px solid #111;
- background-color: #585858;
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
- /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
- /* Chrome10+,Safari5.1+ */
- background: -moz-linear-gradient(top, #585858 0%, #111 100%);
- /* FF3.6+ */
- background: -ms-linear-gradient(top, #585858 0%, #111 100%);
- /* IE10+ */
- background: -o-linear-gradient(top, #585858 0%, #111 100%);
- /* Opera 11.10+ */
- background: linear-gradient(to bottom, #585858 0%, #111 100%);
- /* W3C */
-}
-.dataTables_wrapper .dataTables_paginate .paginate_button:active {
+ background-color: #111;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgb(88.4, 88.4, 88.4)), color-stop(100%, #111)); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%); /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%); /* FF3.6+ */
+ background: -ms-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%); /* IE10+ */
+ background: -o-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%); /* Opera 11.10+ */
+ background: linear-gradient(to bottom, rgb(88.4, 88.4, 88.4) 0%, #111 100%); /* W3C */
+}
+div.dt-container .dt-paging .dt-paging-button:active {
outline: none;
- background-color: #2b2b2b;
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
- /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
- /* Chrome10+,Safari5.1+ */
- background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
- /* FF3.6+ */
- background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
- /* IE10+ */
- background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
- /* Opera 11.10+ */
- background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
- /* W3C */
+ background-color: rgb(11.9, 11.9, 11.9);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgb(42.5, 42.5, 42.5)), color-stop(100%, rgb(11.9, 11.9, 11.9))); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%); /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%); /* FF3.6+ */
+ background: -ms-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%); /* IE10+ */
+ background: -o-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%); /* Opera 11.10+ */
+ background: linear-gradient(to bottom, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%); /* W3C */
box-shadow: inset 0 0 3px #111;
}
-.dataTables_wrapper .dataTables_paginate .ellipsis {
+div.dt-container .dt-paging .ellipsis {
padding: 0 1em;
}
-.dataTables_wrapper .dataTables_processing {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 100%;
- height: 40px;
- margin-left: -50%;
- margin-top: -25px;
- padding-top: 20px;
- text-align: center;
- font-size: 1.2em;
- background-color: white;
- background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
- background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
- background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
- background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
- background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
- background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
-}
-.dataTables_wrapper .dataTables_length,
-.dataTables_wrapper .dataTables_filter,
-.dataTables_wrapper .dataTables_info,
-.dataTables_wrapper .dataTables_processing,
-.dataTables_wrapper .dataTables_paginate {
- color: #333;
-}
-.dataTables_wrapper .dataTables_scroll {
+div.dt-container .dt-length,
+div.dt-container .dt-search,
+div.dt-container .dt-info,
+div.dt-container .dt-processing,
+div.dt-container .dt-paging {
+ color: inherit;
+}
+div.dt-container .dataTables_scroll {
clear: both;
}
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
- *margin-top: -1px;
+div.dt-container .dataTables_scroll div.dt-scroll-body {
-webkit-overflow-scrolling: touch;
}
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {
+div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > th, div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > td, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > th, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > td {
vertical-align: middle;
}
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {
+div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > th > div.dataTables_sizing,
+div.dt-container .dataTables_scroll div.dt-scroll-body > table > thead > tr > td > div.dataTables_sizing, div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > th > div.dataTables_sizing,
+div.dt-container .dataTables_scroll div.dt-scroll-body > table > tbody > tr > td > div.dataTables_sizing {
height: 0;
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
}
-.dataTables_wrapper.no-footer .dataTables_scrollBody {
- border-bottom: 1px solid #111;
+div.dt-container.dt-empty-footer tbody > tr:last-child > * {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.3);
+}
+div.dt-container.dt-empty-footer .dt-scroll-body {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.3);
}
-.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,
-.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
+div.dt-container.dt-empty-footer .dt-scroll-body tbody > tr:last-child > * {
border-bottom: none;
}
-.dataTables_wrapper:after {
- visibility: hidden;
- display: block;
- content: "";
- clear: both;
- height: 0;
+
+html.dark {
+ --dt-row-hover: 255, 255, 255;
+ --dt-row-stripe: 255, 255, 255;
+ --dt-column-ordering: 255, 255, 255;
+}
+html.dark table.dataTable > thead > tr > th,
+html.dark table.dataTable > thead > tr > td {
+ border-bottom: 1px solid rgb(89, 91, 94);
+}
+html.dark table.dataTable > thead > tr > th:active,
+html.dark table.dataTable > thead > tr > td:active {
+ outline: none;
+}
+html.dark table.dataTable > tfoot > tr > th,
+html.dark table.dataTable > tfoot > tr > td {
+ border-top: 1px solid rgb(89, 91, 94);
+}
+html.dark table.dataTable.row-border > tbody > tr > *, html.dark table.dataTable.display > tbody > tr > * {
+ border-top: 1px solid rgb(64, 67, 70);
+}
+html.dark table.dataTable.row-border > tbody > tr:first-child > *, html.dark table.dataTable.display > tbody > tr:first-child > * {
+ border-top: none;
+}
+html.dark table.dataTable.row-border > tbody > tr.selected + tr.selected > td, html.dark table.dataTable.display > tbody > tr.selected + tr.selected > td {
+ border-top-color: rgba(13, 110, 253, 0.65);
+ border-top-color: rgba(var(--dt-row-selected), 0.65);
+}
+html.dark table.dataTable.cell-border > tbody > tr > th,
+html.dark table.dataTable.cell-border > tbody > tr > td {
+ border-top: 1px solid rgb(64, 67, 70);
+ border-right: 1px solid rgb(64, 67, 70);
+}
+html.dark table.dataTable.cell-border > tbody > tr > th:first-child,
+html.dark table.dataTable.cell-border > tbody > tr > td:first-child {
+ border-left: 1px solid rgb(64, 67, 70);
+}
+html.dark .dt-container.dt-empty-footer table.dataTable {
+ border-bottom: 1px solid rgb(89, 91, 94);
+}
+html.dark .dt-container .dt-search input,
+html.dark .dt-container .dt-length select {
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ background-color: var(--dt-html-background);
+}
+html.dark .dt-container .dt-paging .dt-paging-button.current, html.dark .dt-container .dt-paging .dt-paging-button.current:hover {
+ border: 1px solid rgb(89, 91, 94);
+ background: rgba(255, 255, 255, 0.15);
+}
+html.dark .dt-container .dt-paging .dt-paging-button.disabled, html.dark .dt-container .dt-paging .dt-paging-button.disabled:hover, html.dark .dt-container .dt-paging .dt-paging-button.disabled:active {
+ color: #666 !important;
+}
+html.dark .dt-container .dt-paging .dt-paging-button:hover {
+ border: 1px solid rgb(53, 53, 53);
+ background: rgb(53, 53, 53);
+}
+html.dark .dt-container .dt-paging .dt-paging-button:active {
+ background: rgb(58.1, 58.1, 58.1);
}
-@media screen and (max-width: 767px) {
- .dataTables_wrapper .dataTables_info,
-.dataTables_wrapper .dataTables_paginate {
- float: none;
- text-align: center;
- }
- .dataTables_wrapper .dataTables_paginate {
- margin-top: 0.5em;
- }
+/*
+ * Overrides for RTL support
+ */
+*[dir=rtl] table.dataTable thead th,
+*[dir=rtl] table.dataTable thead td,
+*[dir=rtl] table.dataTable tfoot th,
+*[dir=rtl] table.dataTable tfoot td {
+ text-align: right;
}
-@media screen and (max-width: 640px) {
- .dataTables_wrapper .dataTables_length,
-.dataTables_wrapper .dataTables_filter {
- float: none;
- text-align: center;
- }
- .dataTables_wrapper .dataTables_filter {
- margin-top: 0.5em;
- }
+*[dir=rtl] table.dataTable th.dt-type-numeric, *[dir=rtl] table.dataTable th.dt-type-date,
+*[dir=rtl] table.dataTable td.dt-type-numeric,
+*[dir=rtl] table.dataTable td.dt-type-date {
+ text-align: left;
+}
+*[dir=rtl] div.dt-container div.dt-layout-cell.dt-start {
+ text-align: right;
+}
+*[dir=rtl] div.dt-container div.dt-layout-cell.dt-end {
+ text-align: left;
+}
+*[dir=rtl] div.dt-container div.dt-search input {
+ margin: 0 3px 0 0;
}
diff --git a/docs/_static/css/tables.css b/docs/_static/css/tables.css
index 7a106d56..1a3817d8 100644
--- a/docs/_static/css/tables.css
+++ b/docs/_static/css/tables.css
@@ -38,27 +38,25 @@
text-align: center;
}
-#coverage a.paginate_button.current,
-#coverage a.paginate_button.next,
-#coverage a.paginate_button.previous {
+#coverage button.dt-paging-button.current,
+#coverage button.dt-paging-button.next,
+#coverage button.dt-paging-button.previous {
color: #FD8F01 !important;
- background-color: none;
background: none;
}
-#coverage a.paginate_button {
+#coverage button.dt-paging-button {
margin-left: 0;
border: unset;
border-radius: 8px;
transition: background-color 250ms linear, color 250ms linear;
}
-#coverage a.paginate_button:hover,
-#coverage a.paginate_button.current:hover,
-#coverage a.paginate_button.next:hover,
-#coverage a.paginate_button.previous:hover {
- background-color: #E1E4E5 !important;
- background: none;
+#coverage button.dt-paging-button:hover,
+#coverage button.dt-paging-button.current:hover,
+#coverage button.dt-paging-button.next:hover,
+#coverage button.dt-paging-button.previous:hover {
+ background: #E1E4E5 !important;
border: unset;
color: #121010 !important;
}
@@ -127,10 +125,8 @@
@media screen and (max-width: 575px) {
#table-cfgcmd_wrapper,
#table-opcmd_wrapper {
- & label {
- & input {
- margin-top: 10px
- }
+ & .dt-search input {
+ margin-top: 10px;
}
}
}
@@ -215,7 +211,7 @@ tbody tr td {
}
}
-.dataTables_info {
+.dt-info {
color: #121010 !important;
font-family: 'Roboto', sans-serif;
font-size: 16px !important;
@@ -223,7 +219,7 @@ tbody tr td {
letter-spacing: -0.5px;
}
-.paginate_button {
+.dt-paging-button {
font-family: 'Archivo', sans-serif;
font-size: 16px !important;
font-weight: 600;
diff --git a/docs/_static/css/text.css b/docs/_static/css/text.css
index f1179534..f9fb1f91 100644
--- a/docs/_static/css/text.css
+++ b/docs/_static/css/text.css
@@ -18,8 +18,8 @@ blockquote > div > dd,
caption.caption-text,
.simple > dt,
div.line-block,
-.paginate_button,
-.dataTables_info,
+.dt-paging-button,
+.dt-info,
#operational-commands label,
.card-body .card-text,
#search-results a {
diff --git a/docs/_static/js/datatables.js b/docs/_static/js/datatables.js
index 34a64607..355bb297 100644
--- a/docs/_static/js/datatables.js
+++ b/docs/_static/js/datatables.js
@@ -4,38 +4,16 @@
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
- * https://datatables.net/download/#dt/dt-1.11.5
+ * https://datatables.net/download/#dt/dt-2.3.8
*
* Included libraries:
- * DataTables 1.11.5
+ * DataTables 2.3.8
*/
-/*! DataTables 1.11.5
- * ©2008-2021 SpryMedia Ltd - datatables.net/license
+/*! DataTables 2.3.8
+ * © SpryMedia Ltd - datatables.net/license
*/
-/**
- * @summary DataTables
- * @description Paginate, search and order HTML tables
- * @version 1.11.5
- * @file jquery.dataTables.js
- * @author SpryMedia Ltd
- * @contact www.datatables.net
- * @copyright Copyright 2008-2021 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
-
-/*jslint evil: true, undef: true, browser: true */
-/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
-
(function( factory ) {
"use strict";
@@ -47,33 +25,45 @@
}
else if ( typeof exports === 'object' ) {
// CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- // CommonJS environments without a window global must pass a
- // root. This will give an error otherwise
- root = window;
- }
+ // jQuery's factory checks for a global window - if it isn't present then it
+ // returns a factory function that expects the window object
+ var jq = require('jquery');
- if ( ! $ ) {
- $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
- require('jquery') :
- require('jquery')( root );
- }
+ if (typeof window === 'undefined') {
+ module.exports = function (root, $) {
+ if ( ! root ) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
- return factory( $, root, root.document );
- };
+ if ( ! $ ) {
+ $ = jq( root );
+ }
+
+ return factory( $, root, root.document );
+ };
+ }
+ else {
+ module.exports = factory( jq, window, window.document );
+ }
}
else {
// Browser
window.DataTable = factory( jQuery, window, document );
}
-}
-(function( $, window, document, undefined ) {
+}(function( $, window, document ) {
"use strict";
var DataTable = function ( selector, options )
{
+ // Check if called with a window or jQuery object for DOM less applications
+ // This is for backwards compatibility
+ if (DataTable.factory(selector, options)) {
+ return DataTable;
+ }
+
// When creating with `new`, create a new DataTable, returning the API instance
if (this instanceof DataTable) {
return $(selector).DataTable(options);
@@ -83,762 +73,6 @@
options = selector;
}
- /**
- * Perform a jQuery selector action on the table's TR elements (from the tbody) and
- * return the resulting jQuery object.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
- * criterion ("applied") or all TR elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {object} jQuery object, filtered by the given selector.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Highlight every second row
- * oTable.$('tr:odd').css('backgroundColor', 'blue');
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to rows with 'Webkit' in them, add a background colour and then
- * // remove the filter, thus highlighting the 'Webkit' rows only.
- * oTable.fnFilter('Webkit');
- * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
- * oTable.fnFilter('');
- * } );
- */
- this.$ = function ( sSelector, oOpts )
- {
- return this.api(true).$( sSelector, oOpts );
- };
-
-
- /**
- * Almost identical to $ in operation, but in this case returns the data for the matched
- * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
- * rather than any descendants, so the data can be obtained for the row/cell. If matching
- * rows are found, the data returned is the original data array/object that was used to
- * create the row (or a generated array if from a DOM source).
- *
- * This method is often useful in-combination with $ where both functions are given the
- * same parameters and the array indexes will match identically.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select elements that meet the current filter
- * criterion ("applied") or all elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the data in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {array} Data for the matched elements. If any elements, as a result of the
- * selector, were not TR, TD or TH elements in the DataTable, they will have a null
- * entry in the array.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the data from the first row in the table
- * var data = oTable._('tr:first');
- *
- * // Do something useful with the data
- * alert( "First cell is: "+data[0] );
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to 'Webkit' and get all data for
- * oTable.fnFilter('Webkit');
- * var data = oTable._('tr', {"search": "applied"});
- *
- * // Do something with the data
- * alert( data.length+" rows matched the search" );
- * } );
- */
- this._ = function ( sSelector, oOpts )
- {
- return this.api(true).rows( sSelector, oOpts ).data();
- };
-
-
- /**
- * Create a DataTables Api instance, with the currently selected tables for
- * the Api's context.
- * @param {boolean} [traditional=false] Set the API instance's context to be
- * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
- * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
- * or if all tables captured in the jQuery object should be used.
- * @return {DataTables.Api}
- */
- this.api = function ( traditional )
- {
- return traditional ?
- new _Api(
- _fnSettingsFromNode( this[ _ext.iApiIndex ] )
- ) :
- new _Api( this );
- };
-
-
- /**
- * Add a single new row or multiple rows of data to the table. Please note
- * that this is suitable for client-side processing only - if you are using
- * server-side processing (i.e. "bServerSide": true), then to add data, you
- * must add it to the data source, i.e. the server-side, through an Ajax call.
- * @param {array|object} data The data to be added to the table. This can be:
- * <ul>
- * <li>1D array of data - add a single row with the data provided</li>
- * <li>2D array of arrays - add multiple rows in a single call</li>
- * <li>object - data object when using <i>mData</i></li>
- * <li>array of objects - multiple data objects when using <i>mData</i></li>
- * </ul>
- * @param {bool} [redraw=true] redraw the table or not
- * @returns {array} An array of integers, representing the list of indexes in
- * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
- * the table.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Global var for counter
- * var giCount = 2;
- *
- * $(document).ready(function() {
- * $('#example').dataTable();
- * } );
- *
- * function fnClickAddRow() {
- * $('#example').dataTable().fnAddData( [
- * giCount+".1",
- * giCount+".2",
- * giCount+".3",
- * giCount+".4" ]
- * );
- *
- * giCount++;
- * }
- */
- this.fnAddData = function( data, redraw )
- {
- var api = this.api( true );
-
- /* Check if we want to add multiple rows or not */
- var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
- api.rows.add( data ) :
- api.row.add( data );
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return rows.flatten().toArray();
- };
-
-
- /**
- * This function will make DataTables recalculate the column sizes, based on the data
- * contained in the table and the sizes applied to the columns (in the DOM, CSS or
- * through the sWidth parameter). This can be useful when the width of the table's
- * parent element changes (for example a window resize).
- * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false
- * } );
- *
- * $(window).on('resize', function () {
- * oTable.fnAdjustColumnSizing();
- * } );
- * } );
- */
- this.fnAdjustColumnSizing = function ( bRedraw )
- {
- var api = this.api( true ).columns.adjust();
- var settings = api.settings()[0];
- var scroll = settings.oScroll;
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw( false );
- }
- else if ( scroll.sX !== "" || scroll.sY !== "" ) {
- /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
- _fnScrollDraw( settings );
- }
- };
-
-
- /**
- * Quickly and simply clear a table
- * @param {bool} [bRedraw=true] redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
- * oTable.fnClearTable();
- * } );
- */
- this.fnClearTable = function( bRedraw )
- {
- var api = this.api( true ).clear();
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- };
-
-
- /**
- * The exact opposite of 'opening' a row, this function will close any rows which
- * are currently 'open'.
- * @param {node} nTr the table row to 'close'
- * @returns {int} 0 on success, or 1 if failed (can't find the row)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnClose = function( nTr )
- {
- this.api( true ).row( nTr ).child.hide();
- };
-
-
- /**
- * Remove a row for the table
- * @param {mixed} target The index of the row from aoData to be deleted, or
- * the TR element you want to delete
- * @param {function|null} [callBack] Callback function
- * @param {bool} [redraw=true] Redraw the table or not
- * @returns {array} The row that was deleted
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately remove the first row
- * oTable.fnDeleteRow( 0 );
- * } );
- */
- this.fnDeleteRow = function( target, callback, redraw )
- {
- var api = this.api( true );
- var rows = api.rows( target );
- var settings = rows.settings()[0];
- var data = settings.aoData[ rows[0][0] ];
-
- rows.remove();
-
- if ( callback ) {
- callback.call( this, settings, data );
- }
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return data;
- };
-
-
- /**
- * Restore the table to it's original state in the DOM by removing all of DataTables
- * enhancements, alterations to the DOM structure of the table and event listeners.
- * @param {boolean} [remove=false] Completely remove the table from the DOM
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * // This example is fairly pointless in reality, but shows how fnDestroy can be used
- * var oTable = $('#example').dataTable();
- * oTable.fnDestroy();
- * } );
- */
- this.fnDestroy = function ( remove )
- {
- this.api( true ).destroy( remove );
- };
-
-
- /**
- * Redraw the table
- * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
- * oTable.fnDraw();
- * } );
- */
- this.fnDraw = function( complete )
- {
- // Note that this isn't an exact match to the old call to _fnDraw - it takes
- // into account the new data, but can hold position.
- this.api( true ).draw( complete );
- };
-
-
- /**
- * Filter the input based on data
- * @param {string} sInput String to filter the table on
- * @param {int|null} [iColumn] Column to limit filtering to
- * @param {bool} [bRegex=false] Treat as regular expression or not
- * @param {bool} [bSmart=true] Perform smart filtering or not
- * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
- * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sometime later - filter...
- * oTable.fnFilter( 'test string' );
- * } );
- */
- this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
- {
- var api = this.api( true );
-
- if ( iColumn === null || iColumn === undefined ) {
- api.search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
- else {
- api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
-
- api.draw();
- };
-
-
- /**
- * Get the data for the whole table, an individual row or an individual cell based on the
- * provided parameters.
- * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
- * a TR node then the data source for the whole row will be returned. If given as a
- * TD/TH cell node then iCol will be automatically calculated and the data for the
- * cell returned. If given as an integer, then this is treated as the aoData internal
- * data index for the row (see fnGetPosition) and the data for that row used.
- * @param {int} [col] Optional column index that you want the data of.
- * @returns {array|object|string} If mRow is undefined, then the data for all rows is
- * returned. If mRow is defined, just data for that row, and is iCol is
- * defined, only data for the designated cell is returned.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Row data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('tr').click( function () {
- * var data = oTable.fnGetData( this );
- * // ... do something with the array / object of data for the row
- * } );
- * } );
- *
- * @example
- * // Individual cell data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('td').click( function () {
- * var sData = oTable.fnGetData( this );
- * alert( 'The cell clicked on had the value of '+sData );
- * } );
- * } );
- */
- this.fnGetData = function( src, col )
- {
- var api = this.api( true );
-
- if ( src !== undefined ) {
- var type = src.nodeName ? src.nodeName.toLowerCase() : '';
-
- return col !== undefined || type == 'td' || type == 'th' ?
- api.cell( src, col ).data() :
- api.row( src ).data() || null;
- }
-
- return api.data().toArray();
- };
-
-
- /**
- * Get an array of the TR nodes that are used in the table's body. Note that you will
- * typically want to use the '$' API method in preference to this as it is more
- * flexible.
- * @param {int} [iRow] Optional row index for the TR element you want
- * @returns {array|node} If iRow is undefined, returns an array of all TR elements
- * in the table's body, or iRow is defined, just the TR element requested.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the nodes from the table
- * var nNodes = oTable.fnGetNodes( );
- * } );
- */
- this.fnGetNodes = function( iRow )
- {
- var api = this.api( true );
-
- return iRow !== undefined ?
- api.row( iRow ).node() :
- api.rows().nodes().flatten().toArray();
- };
-
-
- /**
- * Get the array indexes of a particular cell from it's DOM element
- * and column index including hidden columns
- * @param {node} node this can either be a TR, TD or TH in the table's body
- * @returns {int} If nNode is given as a TR, then a single index is returned, or
- * if given as a cell, an array of [row index, column index (visible),
- * column index (all)] is given.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * $('#example tbody td').click( function () {
- * // Get the position of the current data from the node
- * var aPos = oTable.fnGetPosition( this );
- *
- * // Get the data array for this row
- * var aData = oTable.fnGetData( aPos[0] );
- *
- * // Update the data array and return the value
- * aData[ aPos[1] ] = 'clicked';
- * this.innerHTML = 'clicked';
- * } );
- *
- * // Init DataTables
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnGetPosition = function( node )
- {
- var api = this.api( true );
- var nodeName = node.nodeName.toUpperCase();
-
- if ( nodeName == 'TR' ) {
- return api.row( node ).index();
- }
- else if ( nodeName == 'TD' || nodeName == 'TH' ) {
- var cell = api.cell( node ).index();
-
- return [
- cell.row,
- cell.columnVisible,
- cell.column
- ];
- }
- return null;
- };
-
-
- /**
- * Check to see if a row is 'open' or not.
- * @param {node} nTr the table row to check
- * @returns {boolean} true if the row is currently open, false otherwise
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnIsOpen = function( nTr )
- {
- return this.api( true ).row( nTr ).child.isShown();
- };
-
-
- /**
- * This function will place a new row directly after a row which is currently
- * on display on the page, with the HTML contents that is passed into the
- * function. This can be used, for example, to ask for confirmation that a
- * particular record should be deleted.
- * @param {node} nTr The table row to 'open'
- * @param {string|node|jQuery} mHtml The HTML to put into the row
- * @param {string} sClass Class to give the new TD cell
- * @returns {node} The row opened. Note that if the table row passed in as the
- * first parameter, is not found in the table, this method will silently
- * return.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnOpen = function( nTr, mHtml, sClass )
- {
- return this.api( true )
- .row( nTr )
- .child( mHtml, sClass )
- .show()
- .child()[0];
- };
-
-
- /**
- * Change the pagination - provides the internal logic for pagination in a simple API
- * function. With this function you can have a DataTables table go to the next,
- * previous, first or last pages.
- * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
- * or page number to jump to (integer), note that page 0 is the first page.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnPageChange( 'next' );
- * } );
- */
- this.fnPageChange = function ( mAction, bRedraw )
- {
- var api = this.api( true ).page( mAction );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw(false);
- }
- };
-
-
- /**
- * Show a particular column
- * @param {int} iCol The column whose display should be changed
- * @param {bool} bShow Show (true) or hide (false) the column
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Hide the second column after initialisation
- * oTable.fnSetColumnVis( 1, false );
- * } );
- */
- this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
- {
- var api = this.api( true ).column( iCol ).visible( bShow );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.columns.adjust().draw();
- }
- };
-
-
- /**
- * Get the settings for a particular table for external manipulation
- * @returns {object} DataTables settings object. See
- * {@link DataTable.models.oSettings}
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * var oSettings = oTable.fnSettings();
- *
- * // Show an example parameter from the settings
- * alert( oSettings._iDisplayStart );
- * } );
- */
- this.fnSettings = function()
- {
- return _fnSettingsFromNode( this[_ext.iApiIndex] );
- };
-
-
- /**
- * Sort the table by a particular column
- * @param {int} iCol the data index to sort on. Note that this will not match the
- * 'display index' if you have hidden data entries
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort immediately with columns 0 and 1
- * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
- * } );
- */
- this.fnSort = function( aaSort )
- {
- this.api( true ).order( aaSort ).draw();
- };
-
-
- /**
- * Attach a sort listener to an element for a given column
- * @param {node} nNode the element to attach the sort listener to
- * @param {int} iColumn the column that a click on this node will sort on
- * @param {function} [fnCallback] callback function when sort is run
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort on column 1, when 'sorter' is clicked on
- * oTable.fnSortListener( document.getElementById('sorter'), 1 );
- * } );
- */
- this.fnSortListener = function( nNode, iColumn, fnCallback )
- {
- this.api( true ).order.listener( nNode, iColumn, fnCallback );
- };
-
-
- /**
- * Update a table cell or row - this method will accept either a single value to
- * update the cell with, an array of values with one element for each column or
- * an object in the same format as the original data source. The function is
- * self-referencing in order to make the multi column updates easier.
- * @param {object|array|string} mData Data to update the cell/row with
- * @param {node|int} mRow TR element you want to update or the aoData index
- * @param {int} [iColumn] The column to update, give as null or undefined to
- * update a whole row.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @param {bool} [bAction=true] Perform pre-draw actions or not
- * @returns {int} 0 on success, 1 on error
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
- * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
- * } );
- */
- this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
- {
- var api = this.api( true );
-
- if ( iColumn === undefined || iColumn === null ) {
- api.row( mRow ).data( mData );
- }
- else {
- api.cell( mRow, iColumn ).data( mData );
- }
-
- if ( bAction === undefined || bAction ) {
- api.columns.adjust();
- }
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- return 0;
- };
-
-
- /**
- * Provide a common method for plug-ins to check the version of DataTables being used, in order
- * to ensure compatibility.
- * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
- * formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to the required
- * version, or false if this version of DataTales is not suitable
- * @method
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * alert( oTable.fnVersionCheck( '1.9.0' ) );
- * } );
- */
- this.fnVersionCheck = _ext.fnVersionCheck;
-
-
var _that = this;
var emptyInit = options === undefined;
var len = this.length;
@@ -847,14 +81,11 @@
options = {};
}
- this.oApi = this.internal = _ext.internal;
-
- // Extend with old style plug-in API methods
- for ( var fn in DataTable.ext.internal ) {
- if ( fn ) {
- this[fn] = _fnExternApiFunc(fn);
- }
- }
+ // Method to get DT API instance from jQuery object
+ this.api = function ()
+ {
+ return new _Api( this );
+ };
this.each(function() {
// For each initialisation we want to give it a clean initialisation
@@ -864,21 +95,26 @@
_fnExtend( o, options, true ) :
options;
- /*global oInit,_that,emptyInit*/
- var i=0, iLen, j, jLen, k, kLen;
+
+ var i=0, iLen;
var sId = this.getAttribute( 'id' );
- var bInitHandedOff = false;
var defaults = DataTable.defaults;
var $this = $(this);
-
- /* Sanity check */
+ // Sanity check
if ( this.nodeName.toLowerCase() != 'table' )
{
_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
return;
}
+ // Special case for options
+ if (oInit.on && oInit.on.options) {
+ _fnListener($this, 'options', oInit.on.options);
+ }
+
+ $this.trigger( 'options.dt', oInit );
+
/* Backwards compatibility for the defaults */
_fnCompatOpts( defaults );
_fnCompatCols( defaults.column );
@@ -888,7 +124,7 @@
_fnCamelToHungarian( defaults.column, defaults.column, true );
/* Setting up the initialisation object */
- _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
+ _fnCamelToHungarian( defaults, $.extend( oInit, _fnEscapeObject($this.data()) ), true );
@@ -913,7 +149,7 @@
}
else if ( bDestroy )
{
- s.oInstance.fnDestroy();
+ new DataTable.Api(s).destroy();
break;
}
else
@@ -942,31 +178,42 @@
this.id = sId;
}
+ // Replacing an existing colgroup with our own. Not ideal, but a merge could take a lot of code
+ $this.children('colgroup').remove();
+
/* Create the settings object for this table and set some of the default parameters */
var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
"sDestroyWidth": $this[0].style.width,
"sInstance": sId,
- "sTableId": sId
+ "sTableId": sId,
+ colgroup: $('<colgroup>'),
+ fastData: function (row, column, type) {
+ return _fnGetCellData(oSettings, row, column, type);
+ }
} );
oSettings.nTable = this;
- oSettings.oApi = _that.internal;
oSettings.oInit = oInit;
allSettings.push( oSettings );
+ // Make a single API instance available for internal handling
+ oSettings.api = new _Api( oSettings );
+
// Need to add the instance after the instance after the settings object has been added
// to the settings array, so we can self reference the table instance if more than one
oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
// Backwards compatibility, before we apply all the defaults
_fnCompatOpts( oInit );
- _fnLanguageCompat( oInit.oLanguage );
// If the length menu is given, but the init display length is not, use the length menu
if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
{
- oInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?
- oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
+ oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0])
+ ? oInit.aLengthMenu[0][0]
+ : $.isPlainObject( oInit.aLengthMenu[0] )
+ ? oInit.aLengthMenu[0].value
+ : oInit.aLengthMenu[0];
}
// Apply the defaults and init options to make a single init object will all
@@ -989,26 +236,30 @@
"bDeferRender"
] );
_fnMap( oSettings, oInit, [
- "asStripeClasses",
"ajax",
- "fnServerData",
"fnFormatNumber",
"sServerMethod",
"aaSorting",
"aaSortingFixed",
"aLengthMenu",
"sPaginationType",
- "sAjaxSource",
- "sAjaxDataProp",
"iStateDuration",
- "sDom",
"bSortCellsTop",
"iTabIndex",
+ "sDom",
"fnStateLoadCallback",
"fnStateSaveCallback",
"renderer",
"searchDelay",
"rowId",
+ "caption",
+ "layout",
+ "orderDescReverse",
+ "orderIndicators",
+ "orderHandler",
+ "titleRow",
+ "typeDetect",
+ "columnTitleTag",
[ "iCookieDuration", "iStateDuration" ], // backwards compat
[ "oSearch", "oPreviousSearch" ],
[ "aoSearchCols", "aoPreSearchCols" ],
@@ -1023,28 +274,37 @@
_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
/* Callback functions which are array driven */
- _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
- _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
- _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
+ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback );
+ _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams );
+ _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams );
+ _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded );
+ _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback );
+ _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow );
+ _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback );
+ _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback );
+ _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete );
+ _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback );
oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
+ // Add event listeners
+ if (oInit.on) {
+ Object.keys(oInit.on).forEach(function (key) {
+ _fnListener($this, key, oInit.on[key]);
+ });
+ }
+
/* Browser support detection */
_fnBrowserDetect( oSettings );
var oClasses = oSettings.oClasses;
$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
- $this.addClass( oClasses.sTable );
+ $this.addClass( oClasses.table );
+ if (! oSettings.oFeatures.bPaginate) {
+ oInit.iDisplayStart = 0;
+ }
if ( oSettings.iInitDisplayStart === undefined )
{
@@ -1053,108 +313,49 @@
oSettings._iDisplayStart = oInit.iDisplayStart;
}
- if ( oInit.iDeferLoading !== null )
+ var defer = oInit.iDeferLoading;
+ if ( defer !== null )
{
- oSettings.bDeferLoading = true;
- var tmp = Array.isArray( oInit.iDeferLoading );
- oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
- oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
- }
-
- /* Language definitions */
- var oLanguage = oSettings.oLanguage;
- $.extend( true, oLanguage, oInit.oLanguage );
+ oSettings.deferLoading = true;
- if ( oLanguage.sUrl )
- {
- /* Get the language definitions from a file - because this Ajax call makes the language
- * get async to the remainder of this function we use bInitHandedOff to indicate that
- * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
- */
- $.ajax( {
- dataType: 'json',
- url: oLanguage.sUrl,
- success: function ( json ) {
- _fnCamelToHungarian( defaults.oLanguage, json );
- _fnLanguageCompat( json );
- $.extend( true, oLanguage, json );
-
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
- _fnInitialise( oSettings );
- },
- error: function () {
- // Error occurred loading language file, continue on as best we can
- _fnInitialise( oSettings );
- }
- } );
- bInitHandedOff = true;
- }
- else {
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
- }
-
- /*
- * Stripes
- */
- if ( oInit.asStripeClasses === null )
- {
- oSettings.asStripeClasses =[
- oClasses.sStripeOdd,
- oClasses.sStripeEven
- ];
- }
-
- /* Remove row stripe classes if they are already on the table row */
- var stripeClasses = oSettings.asStripeClasses;
- var rowOne = $this.children('tbody').find('tr').eq(0);
- if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
- return rowOne.hasClass(el);
- } ) ) !== -1 ) {
- $('tbody tr', this).removeClass( stripeClasses.join(' ') );
- oSettings.asDestroyStripes = stripeClasses.slice();
+ var tmp = Array.isArray(defer);
+ oSettings._iRecordsDisplay = tmp ? defer[0] : defer;
+ oSettings._iRecordsTotal = tmp ? defer[1] : defer;
}
/*
* Columns
* See if we should load columns automatically or use defined ones
*/
- var anThs = [];
- var aoColumnsInit;
- var nThead = this.getElementsByTagName('thead');
- if ( nThead.length !== 0 )
- {
- _fnDetectHeader( oSettings.aoHeader, nThead[0] );
- anThs = _fnGetUniqueThs( oSettings );
- }
+ var columnsInit = [];
+ var thead = this.getElementsByTagName('thead');
+ var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] );
- /* If not given a column array, generate one with nulls */
- if ( oInit.aoColumns === null )
- {
- aoColumnsInit = [];
- for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
- {
- aoColumnsInit.push( null );
- }
+ // If we don't have a columns array, then generate one with nulls
+ if ( oInit.aoColumns ) {
+ columnsInit = oInit.aoColumns;
}
- else
- {
- aoColumnsInit = oInit.aoColumns;
+ else if ( initHeaderLayout.length ) {
+ for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) {
+ columnsInit.push( null );
+ }
}
- /* Add the columns */
- for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
- {
- _fnAddColumn( oSettings, anThs ? anThs[i] : null );
+ // Add the columns
+ for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) {
+ _fnAddColumn( oSettings );
}
- /* Apply the column definitions */
- _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
+ // Apply the column definitions
+ _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) {
_fnColumnOptions( oSettings, iCol, oDef );
} );
/* HTML5 attribute detection - build an mData object automatically if the
* attributes are found
*/
+ var rowOne = $this.children('tbody').find('tr:first-child').eq(0);
+
if ( rowOne.length ) {
var a = function ( cell, name ) {
return cell.getAttribute( 'data-'+name ) !== null ? name : null;
@@ -1163,6 +364,10 @@
$( rowOne[0] ).children('th, td').each( function (i, cell) {
var col = oSettings.aoColumns[i];
+ if (! col) {
+ _fnLog( oSettings, 0, 'Incorrect column count', 18 );
+ }
+
if ( col.mData === i ) {
var sort = a( cell, 'sort' ) || a( cell, 'order' );
var filter = a( cell, 'filter' ) || a( cell, 'search' );
@@ -1174,6 +379,7 @@
type: sort !== null ? i+'.@data-'+sort : undefined,
filter: filter !== null ? i+'.@data-'+filter : undefined
};
+ col._isArrayHost = true;
_fnColumnOptions( oSettings, i );
}
@@ -1181,133 +387,709 @@
} );
}
+ // Must be done after everything which can be overridden by the state saving!
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
+
var features = oSettings.oFeatures;
- var loadedInit = function () {
- /*
- * Sorting
- * @todo For modularisation (1.11) this needs to do into a sort start up handler
- */
+ if ( oInit.bStateSave )
+ {
+ features.bStateSave = true;
+ }
- // If aaSorting is not defined, then we use the first indicator in asSorting
- // in case that has been altered, so the default sort reflects that option
- if ( oInit.aaSorting === undefined ) {
- var sorting = oSettings.aaSorting;
- for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
- sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
- }
+ // If aaSorting is not defined, then we use the first indicator in asSorting
+ // in case that has been altered, so the default sort reflects that option
+ if ( oInit.aaSorting === undefined ) {
+ var sorting = oSettings.aaSorting;
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
}
+ }
- /* Do a first pass on the sorting classes (allows any size changes to be taken into
- * account, and also will apply sorting disabled classes if disabled
- */
- _fnSortingClasses( oSettings );
-
- if ( features.bSort ) {
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted ) {
- var aSort = _fnSortFlatten( oSettings );
- var sortedColumns = {};
-
- $.each( aSort, function (i, val) {
- sortedColumns[ val.src ] = val.dir;
- } );
+ // Do a first pass on the sorting classes (allows any size changes to be taken into
+ // account, and also will apply sorting disabled classes if disabled
+ _fnSortingClasses( oSettings );
- _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
- _fnSortAria( oSettings );
- }
- } );
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
+ _fnSortingClasses( oSettings );
}
+ } );
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
- _fnSortingClasses( oSettings );
- }
- }, 'sc' );
+ /*
+ * Table HTML init
+ * Cache the header, body and footer as required, creating them if needed
+ */
+ var caption = $this.children('caption');
- /*
- * Final init
- * Cache the header, body and footer as required, creating them if needed
- */
+ if ( oSettings.caption ) {
+ if ( caption.length === 0 ) {
+ caption = $('<caption/>').prependTo( $this );
+ }
- // Work around for Webkit bug 83867 - store the caption-side before removing from doc
- var captions = $this.children('caption').each( function () {
- this._captionSide = $(this).css('caption-side');
- } );
+ caption.html( oSettings.caption );
+ }
- var thead = $this.children('thead');
- if ( thead.length === 0 ) {
- thead = $('<thead/>').appendTo($this);
- }
- oSettings.nTHead = thead[0];
+ // Store the caption side, so we can remove the element from the document
+ // when creating the element
+ if (caption.length) {
+ caption[0]._captionSide = caption.css('caption-side');
+ oSettings.captionNode = caption[0];
+ }
- var tbody = $this.children('tbody');
- if ( tbody.length === 0 ) {
- tbody = $('<tbody/>').insertAfter(thead);
- }
- oSettings.nTBody = tbody[0];
+ // Place the colgroup element in the correct location for the HTML structure
+ if (caption.length) {
+ oSettings.colgroup.insertAfter(caption);
+ }
+ else {
+ oSettings.colgroup.prependTo(oSettings.nTable);
+ }
- var tfoot = $this.children('tfoot');
- if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
- // If we are a scrolling table, and no footer has been given, then we need to create
- // a tfoot element for the caption element to be appended to
- tfoot = $('<tfoot/>').appendTo($this);
- }
+ if ( thead.length === 0 ) {
+ thead = $('<thead/>').appendTo($this);
+ }
+ oSettings.nTHead = thead[0];
- if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
- $this.addClass( oClasses.sNoFooter );
- }
- else if ( tfoot.length > 0 ) {
- oSettings.nTFoot = tfoot[0];
- _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
- }
+ var tbody = $this.children('tbody');
+ if ( tbody.length === 0 ) {
+ tbody = $('<tbody/>').insertAfter(thead);
+ }
+ oSettings.nTBody = tbody[0];
- /* Check if there is data passing into the constructor */
- if ( oInit.aaData ) {
- for ( i=0 ; i<oInit.aaData.length ; i++ ) {
- _fnAddData( oSettings, oInit.aaData[ i ] );
- }
- }
- else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
- /* Grab the data from the page - only do this when deferred loading or no Ajax
- * source since there is no point in reading the DOM data if we are then going
- * to replace it with Ajax data
- */
- _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
- }
+ var tfoot = $this.children('tfoot');
+ if ( tfoot.length === 0 ) {
+ // If we are a scrolling table, and no footer has been given, then we need to create
+ // a tfoot element for the caption element to be appended to
+ tfoot = $('<tfoot/>').appendTo($this);
+ }
+ oSettings.nTFoot = tfoot[0];
- /* Copy the data index array */
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+ // Copy the data index array
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
- /* Initialisation complete - table can be drawn */
- oSettings.bInitialised = true;
+ // Initialisation complete - table can be drawn
+ oSettings.bInitialised = true;
- /* Check if we need to initialise the table (it might not have been handed off to the
- * language processor)
- */
- if ( bInitHandedOff === false ) {
- _fnInitialise( oSettings );
- }
- };
+ // Language definitions
+ var oLanguage = oSettings.oLanguage;
+ $.extend( true, oLanguage, oInit.oLanguage );
- /* Must be done after everything which can be overridden by the state saving! */
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
+ if ( oLanguage.sUrl ) {
+ // Get the language definitions from a file
+ $.ajax( {
+ dataType: 'json',
+ url: oLanguage.sUrl,
+ success: function ( json ) {
+ _fnCamelToHungarian( defaults.oLanguage, json );
+ $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
- if ( oInit.bStateSave )
- {
- features.bStateSave = true;
- _fnLoadState( oSettings, oInit, loadedInit );
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
+ _fnInitialise( oSettings );
+ },
+ error: function () {
+ // Error occurred loading language file
+ _fnLog( oSettings, 0, 'i18n file loading error', 21 );
+
+ // Continue on as best we can
+ _fnInitialise( oSettings );
+ }
+ } );
}
else {
- loadedInit();
+ _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
+ _fnInitialise( oSettings );
}
-
} );
_that = null;
return this;
};
+
+ /**
+ * 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 = {
+ /**
+ * DataTables build type (expanded by the download builder)
+ *
+ * @type string
+ */
+ builder: "dt/dt-2.3.8",
+
+ /**
+ * 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: {},
+
+
+ /**
+ * ColumnControl buttons and content
+ *
+ * @type object
+ */
+ ccContent: {},
+
+
+ /**
+ * Element class names
+ *
+ * @type object
+ * @default {}
+ */
+ classes: {},
+
+
+ /**
+ * 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",
+
+ /** HTML entity escaping */
+ escape: {
+ /** When reading data-* attributes for initialisation options */
+ attributes: false
+ },
+
+ /**
+ * Legacy so v1 plug-ins don't throw js errors on load
+ */
+ feature: [],
+
+ /**
+ * Feature plug-ins.
+ *
+ * This is an object of callbacks which provide the features for DataTables
+ * to be initialised via the `layout` option.
+ */
+ features: {},
+
+
+ /**
+ * Row searching.
+ *
+ * This method of searching is complimentary to the default type based
+ * searching, and a lot more comprehensive as it allows you complete control
+ * over the searching logic. Each element in this array is a function
+ * (parameters described below) that is called for every row in the table,
+ * and your logic decides if it should be included in the searching data set
+ * or not.
+ *
+ * Searching functions have the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{array|object}` Data for the row to be processed (same as the
+ * original format that was passed in as the data source, or an array
+ * from a DOM data source
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
+ *
+ * And the following return is expected:
+ *
+ * * {boolean} Include the row in the searched result set (true) or not
+ * (false)
+ *
+ * Note that as with the main search ability in DataTables, technically this
+ * is "filtering", since it is subtractive. However, for consistency in
+ * naming we call it searching here.
+ *
+ * @type array
+ * @default []
+ *
+ * @example
+ * // The following example shows custom search being applied to the
+ * // fourth column (i.e. the data[3] index) based on two input values
+ * // from the end-user, matching the data in a certain range.
+ * $.fn.dataTable.ext.search.push(
+ * function( settings, data, dataIndex ) {
+ * var min = document.getElementById('min').value * 1;
+ * var max = document.getElementById('max').value * 1;
+ * var version = data[3] == "-" ? 0 : data[3]*1;
+ *
+ * if ( min == "" && max == "" ) {
+ * return true;
+ * }
+ * else if ( min == "" && version < max ) {
+ * return true;
+ * }
+ * else if ( min < version && "" == max ) {
+ * return true;
+ * }
+ * else if ( min < version && version < max ) {
+ * return true;
+ * }
+ * return false;
+ * }
+ * );
+ */
+ search: [],
+
+
+ /**
+ * Selector extensions
+ *
+ * The `selector` option can be used to extend the options available for the
+ * selector modifier options (`selector-modifier` object data type) that
+ * each of the three built in selector types offer (row, column and cell +
+ * their plural counterparts). For example the Select extension uses this
+ * mechanism to provide an option to select only rows, columns and cells
+ * that have been marked as selected by the end user (`{selected: true}`),
+ * which can be used in conjunction with the existing built in selector
+ * options.
+ *
+ * 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: []
+ },
+
+
+ /**
+ * Legacy configuration options. Enable and disable legacy options that
+ * are available in DataTables.
+ *
+ * @type object
+ */
+ legacy: {
+ /**
+ * Enable / disable DataTables 1.9 compatible server-side processing
+ * requests
+ *
+ * @type boolean
+ * @default null
+ */
+ 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 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: {
+ /**
+ * Automatic column class assignment
+ */
+ className: {},
+
+ /**
+ * Type detection functions.
+ *
+ * The functions defined in this object are used to automatically detect
+ * a column's type, making initialisation of DataTables super easy, even
+ * when complex data is in the table.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{*}` Data from the column cell to be analysed
+ * 2. `{settings}` DataTables settings object. This can be used to
+ * perform context specific type detection - for example detection
+ * based on language settings such as using a comma for a decimal
+ * place. Generally speaking the options from the settings will not
+ * be required
+ *
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Data type detected, or null if unknown (and thus
+ * pass it on to the other type detection functions.
+ *
+ * @type array
+ *
+ * @example
+ * // Currency type detection plug-in:
+ * $.fn.dataTable.ext.type.detect.push(
+ * function ( data, settings ) {
+ * // Check the numeric part
+ * if ( ! data.substring(1).match(/[0-9]/) ) {
+ * return null;
+ * }
+ *
+ * // Check prefixed by currency
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
+ * return 'currency';
+ * }
+ * return null;
+ * }
+ * );
+ */
+ detect: [],
+
+ /**
+ * Automatic renderer assignment
+ */
+ render: {},
+
+
+ /**
+ * 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
+ *
+ * @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,
+
+
+ /**
+ * 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,
+ oStdClasses: _ext.classes,
+ oPagination: _ext.pager
+ } );
+
+
+ $.extend( DataTable.ext.classes, {
+ container: 'dt-container',
+ empty: {
+ row: 'dt-empty'
+ },
+ info: {
+ container: 'dt-info'
+ },
+ layout: {
+ row: 'dt-layout-row',
+ cell: 'dt-layout-cell',
+ tableRow: 'dt-layout-table',
+ tableCell: '',
+ start: 'dt-layout-start',
+ end: 'dt-layout-end',
+ full: 'dt-layout-full'
+ },
+ length: {
+ container: 'dt-length',
+ select: 'dt-input'
+ },
+ order: {
+ canAsc: 'dt-orderable-asc',
+ canDesc: 'dt-orderable-desc',
+ isAsc: 'dt-ordering-asc',
+ isDesc: 'dt-ordering-desc',
+ none: 'dt-orderable-none',
+ position: 'sorting_'
+ },
+ processing: {
+ container: 'dt-processing'
+ },
+ scrolling: {
+ body: 'dt-scroll-body',
+ container: 'dt-scroll',
+ footer: {
+ self: 'dt-scroll-foot',
+ inner: 'dt-scroll-footInner'
+ },
+ header: {
+ self: 'dt-scroll-head',
+ inner: 'dt-scroll-headInner'
+ }
+ },
+ search: {
+ container: 'dt-search',
+ input: 'dt-input'
+ },
+ table: 'dataTable',
+ tbody: {
+ cell: '',
+ row: ''
+ },
+ thead: {
+ cell: '',
+ row: ''
+ },
+ tfoot: {
+ cell: '',
+ row: ''
+ },
+ paging: {
+ active: 'current',
+ button: 'dt-paging-button',
+ container: 'dt-paging',
+ disabled: 'disabled',
+ nav: ''
+ }
+ } );
+
+
/*
* It is useful to have variables which are scoped locally so only the
* DataTables functions can access them and they don't leak into global space.
@@ -1321,7 +1103,6 @@
// Defined else where
// _selector_run
// _selector_opts
- // _selector_first
// _selector_row_indexes
var _ext; // DataTable.ext
@@ -1331,16 +1112,17 @@
var _re_dic = {};
var _re_new_lines = /[\r\n\u2028]/g;
- var _re_html = /<.*?>/g;
+ var _re_html = /<([^>]*>)/g;
+ var _max_str_len = Math.pow(2, 28);
// This is not strict ISO8601 - Date.parse() is quite lax, although
// implementations differ between browsers.
- var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
+ var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
// Escape regular expression special characters
var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
- // http://en.wikipedia.org/wiki/Foreign_exchange_market
+ // https://en.wikipedia.org/wiki/Foreign_exchange_market
// - \u20BD - Russian ruble.
// - \u20a9 - South Korean Won
// - \u20BA - Turkish Lira
@@ -1366,7 +1148,7 @@
};
// Convert from a formatted number with characters other than `.` as the
- // decimal place, to a Javascript number
+ // decimal place, to a JavaScript number
var _numToDecimal = function ( num, decimalPoint ) {
// Cache created regular expressions for speed as this function is called often
if ( ! _re_dic[ decimalPoint ] ) {
@@ -1378,13 +1160,18 @@
};
- var _isNumber = function ( d, decimalPoint, formatted ) {
- var strType = typeof d === 'string';
+ var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) {
+ var type = typeof d;
+ var strType = type === 'string';
+
+ if ( type === 'number' || type === 'bigint') {
+ return true;
+ }
// If empty return immediately so there must be a number if it is a
// formatted string (this stops the string "k", or "kr", etc being detected
// as a formatted number for currency
- if ( _empty( d ) ) {
+ if ( allowEmpty && _empty( d ) ) {
return true;
}
@@ -1405,16 +1192,21 @@
return _empty( d ) || typeof d === 'string';
};
-
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
- if ( _empty( d ) ) {
+ // Is a string a number surrounded by HTML?
+ var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) {
+ if ( allowEmpty && _empty( d ) ) {
return true;
}
+ // input and select strings mean that this isn't just a number
+ if (typeof d === 'string' && d.match(/<(input|select)/i)) {
+ return null;
+ }
+
var html = _isHtml( d );
return ! html ?
null :
- _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
+ _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ?
true :
null;
};
@@ -1422,19 +1214,19 @@
var _pluck = function ( a, prop, prop2 ) {
var out = [];
- var i=0, ien=a.length;
+ var i=0, iLen=a.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
+ for ( ; i<iLen ; i++ ) {
if ( a[i] && a[i][ prop ] ) {
out.push( a[i][ prop ][ prop2 ] );
}
}
}
else {
- for ( ; i<ien ; i++ ) {
+ for ( ; i<iLen ; i++ ) {
if ( a[i] ) {
out.push( a[i][ prop ] );
}
@@ -1450,20 +1242,22 @@
var _pluck_order = function ( a, order, prop, prop2 )
{
var out = [];
- var i=0, ien=order.length;
+ var i=0, iLen=order.length;
// Could have the test in the loop for slightly smaller code, but speed
// is essential here
if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
- if ( a[ order[i] ][ prop ] ) {
+ for ( ; i<iLen ; i++ ) {
+ if ( a[ order[i] ] && a[ order[i] ][ prop ] ) {
out.push( a[ order[i] ][ prop ][ prop2 ] );
}
}
}
else {
- for ( ; i<ien ; i++ ) {
- out.push( a[ order[i] ][ prop ] );
+ for ( ; i<iLen ; i++ ) {
+ if ( a[ order[i] ] ) {
+ out.push( a[ order[i] ][ prop ] );
+ }
}
}
@@ -1497,7 +1291,7 @@
{
var out = [];
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ ) {
if ( a[i] ) { // careful - will remove all falsy values!
out.push( a[i] );
}
@@ -1506,11 +1300,65 @@
return out;
};
+ // Replaceable function in api.util
+ var _stripHtml = function (input, replacement) {
+ if (! input || typeof input !== 'string') {
+ return input;
+ }
+
+ // Irrelevant check to workaround CodeQL's false positive on the regex
+ if (input.length > _max_str_len) {
+ throw new Error('Exceeded max str len');
+ }
+
+ var previous;
- var _stripHtml = function ( d ) {
- return d.replace( _re_html, '' );
+ input = input.replace(_re_html, replacement || ''); // Complete tags
+
+ // Safety for incomplete script tag - use do / while to ensure that
+ // we get all instances
+ do {
+ previous = input;
+ input = input.replace(/<script/i, '');
+ } while (input !== previous);
+
+ return previous;
};
+ // Replaceable function in api.util
+ var _escapeHtml = function ( d ) {
+ if (Array.isArray(d)) {
+ d = d.join(',');
+ }
+
+ return typeof d === 'string' ?
+ d
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;') :
+ d;
+ };
+
+ // Remove diacritics from a string by decomposing it and then removing
+ // non-ascii characters
+ var _normalize = function (str, both) {
+ if (typeof str !== 'string') {
+ return str;
+ }
+
+ // It is faster to just run `normalize` than it is to check if
+ // we need to with a regex! (Check as it isn't available in old
+ // Safari)
+ var res = str.normalize
+ ? str.normalize("NFD")
+ : str;
+
+ // Equally, here we check if a regex is needed or not
+ return res.length !== str.length
+ ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "")
+ : res;
+ }
/**
* Determine if all values in the array are unique. This means we can short
@@ -1529,7 +1377,7 @@
var sorted = src.slice().sort();
var last = sorted[0];
- for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
+ for ( var i=1, iLen=sorted.length ; i<iLen ; i++ ) {
if ( sorted[i] === last ) {
return false;
}
@@ -1550,21 +1398,25 @@
*/
var _unique = function ( src )
{
+ if (Array.from && Set) {
+ return Array.from(new Set(src));
+ }
+
if ( _areAllUnique( src ) ) {
return src.slice();
}
// A faster unique method is to use object keys to identify used values,
// but this doesn't work with arrays or objects, which we must also
- // consider. See jsperf.com/compare-array-unique-versions/4 for more
+ // consider. See jsperf.app/compare-array-unique-versions/4 for more
// information.
var
out = [],
val,
- i, ien=src.length,
+ i, iLen=src.length,
j, k=0;
- again: for ( i=0 ; i<ien ; i++ ) {
+ again: for ( i=0 ; i<iLen ; i++ ) {
val = src[i];
for ( j=0 ; j<k ; j++ ) {
@@ -1591,40 +1443,20 @@
else {
out.push(val);
}
-
+
return out;
}
- var _includes = function (search, start) {
- if (start === undefined) {
- start = 0;
+ // Similar to jQuery's addClass, but use classList.add
+ function _addClass(el, name) {
+ if (name) {
+ name.split(' ').forEach(function (n) {
+ if (n) {
+ // `add` does deduplication, so no need to check `contains`
+ el.classList.add(n);
+ }
+ });
}
-
- return this.indexOf(search, start) !== -1;
- };
-
- // Array.isArray polyfill.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
- if (! Array.isArray) {
- Array.isArray = function(arg) {
- return Object.prototype.toString.call(arg) === '[object Array]';
- };
- }
-
- if (! Array.prototype.includes) {
- Array.prototype.includes = _includes;
- }
-
- // .trim() polyfill
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
- if (!String.prototype.trim) {
- String.prototype.trim = function () {
- return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
- };
- }
-
- if (! String.prototype.includes) {
- String.prototype.includes = _includes;
}
/**
@@ -1639,6 +1471,43 @@
*/
DataTable.util = {
/**
+ * Return a string with diacritic characters decomposed
+ * @param {*} mixed Function or string to normalize
+ * @param {*} both Return original string and the normalized string
+ * @returns String or undefined
+ */
+ diacritics: function (mixed, both) {
+ var type = typeof mixed;
+
+ if (type !== 'function') {
+ return _normalize(mixed, both);
+ }
+ _normalize = mixed;
+ },
+
+ /**
+ * Debounce a function
+ *
+ * @param {function} fn Function to be called
+ * @param {integer} freq Call frequency in mS
+ * @return {function} Wrapped function
+ */
+ debounce: function ( fn, timeout ) {
+ var timer;
+
+ return function () {
+ var that = this;
+ var args = arguments;
+
+ clearTimeout(timer);
+
+ timer = setTimeout( function () {
+ fn.apply(that, args);
+ }, timeout || 250 );
+ };
+ },
+
+ /**
* Throttle the calls to a function. Arguments and context are maintained
* for the throttled function.
*
@@ -1673,7 +1542,6 @@
};
},
-
/**
* Escape a string such that it can be used in a regular expression
*
@@ -1707,9 +1575,10 @@
source( data, 'set', val, meta );
};
}
- else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
- source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
- {
+ else if (
+ typeof source === 'string' && (source.indexOf('.') !== -1 ||
+ source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
+ ) {
// Like the get, we need to get data from a nested object
var setData = function (data, val, src) {
var a = _fnSplitObjNotation( src ), b;
@@ -1825,9 +1694,10 @@
return source( data, type, row, meta );
};
}
- else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
- source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
- {
+ else if (
+ typeof source === 'string' && (source.indexOf('.') !== -1 ||
+ source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
+ ) {
/* If there is a . in the source string then the data source is in a
* nested object so we loop over the data for each level to get the next
* level down. On each loop we test for undefined, and if found immediately
@@ -1882,7 +1752,10 @@
continue;
}
- if ( data === null || data[ a[i] ] === undefined ) {
+ if (data === null || data[ a[i] ] === null) {
+ return null;
+ }
+ else if ( data === undefined || data[ a[i] ] === undefined ) {
return undefined;
}
@@ -1899,11 +1772,39 @@
}
else {
// Array or flat object mapping
- return function (data, type) { // row and meta also passed, but not used
+ return function (data) { // row and meta also passed, but not used
return data[source];
};
}
- }
+ },
+
+ stripHtml: function (mixed, replacement) {
+ var type = typeof mixed;
+
+ if (type === 'function') {
+ _stripHtml = mixed;
+ return;
+ }
+ else if (type === 'string') {
+ return _stripHtml(mixed, replacement);
+ }
+ return mixed;
+ },
+
+ escapeHtml: function (mixed) {
+ var type = typeof mixed;
+
+ if (type === 'function') {
+ _escapeHtml = mixed;
+ return;
+ }
+ else if (type === 'string' || Array.isArray(mixed)) {
+ return _escapeHtml(mixed);
+ }
+ return mixed;
+ },
+
+ unique: _unique
};
@@ -1923,7 +1824,7 @@
newKey,
map = {};
- $.each( o, function (key, val) {
+ $.each( o, function (key) {
match = key.match(/^([^A-Z]+?)([A-Z])/);
if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
@@ -1961,7 +1862,7 @@
var hungarianKey;
- $.each( user, function (key, val) {
+ $.each( user, function (key) {
hungarianKey = src._hungarianMap[ key ];
if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
@@ -1984,57 +1885,6 @@
} );
}
-
- /**
- * Language compatibility - when certain options are given, and others aren't, we
- * need to duplicate the values over, in order to provide backwards compatibility
- * with older language files.
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnLanguageCompat( lang )
- {
- // Note the use of the Hungarian notation for the parameters in this method as
- // this is called after the mapping of camelCase to Hungarian
- var defaults = DataTable.defaults.oLanguage;
-
- // Default mapping
- var defaultDecimal = defaults.sDecimal;
- if ( defaultDecimal ) {
- _addNumericSort( defaultDecimal );
- }
-
- if ( lang ) {
- var zeroRecords = lang.sZeroRecords;
-
- // Backwards compatibility - if there is no sEmptyTable given, then use the same as
- // sZeroRecords - assuming that is given.
- if ( ! lang.sEmptyTable && zeroRecords &&
- defaults.sEmptyTable === "No data available in table" )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
- }
-
- // Likewise with loading records
- if ( ! lang.sLoadingRecords && zeroRecords &&
- defaults.sLoadingRecords === "Loading..." )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
- }
-
- // Old parameter name of the thousands separator mapped onto the new
- if ( lang.sInfoThousands ) {
- lang.sThousands = lang.sInfoThousands;
- }
-
- var decimal = lang.sDecimal;
- if ( decimal && defaultDecimal !== decimal ) {
- _addNumericSort( decimal );
- }
- }
- }
-
-
/**
* Map one parameter onto another
* @param {object} o Object to map
@@ -2075,17 +1925,42 @@
init.scrollX = init.scrollX ? '100%' : '';
}
+ // Objects for ordering
+ if ( typeof init.bSort === 'object' ) {
+ init.orderIndicators = init.bSort.indicators !== undefined ? init.bSort.indicators : true;
+ init.orderHandler = init.bSort.handler !== undefined ? init.bSort.handler : true;
+ init.bSort = true;
+ }
+ else if (init.bSort === false) {
+ init.orderIndicators = false;
+ init.orderHandler = false;
+ }
+ else if (init.bSort === true) {
+ init.orderIndicators = true;
+ init.orderHandler = true;
+ }
+
+ // Which cells are the title cells?
+ if (typeof init.bSortCellsTop === 'boolean') {
+ init.titleRow = init.bSortCellsTop;
+ }
+
// Column search objects are in an array, so it needs to be converted
// element by element
var searchCols = init.aoSearchCols;
if ( searchCols ) {
- for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=searchCols.length ; i<iLen ; i++ ) {
if ( searchCols[i] ) {
_fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
}
}
}
+
+ // Enable search delay if server-side processing is enabled
+ if (init.serverSide && ! init.searchDelay) {
+ init.searchDelay = 400;
+ }
}
@@ -2129,7 +2004,7 @@
.css( {
position: 'fixed',
top: 0,
- left: $(window).scrollLeft()*-1, // allow for scrolling
+ left: -1 * window.pageXOffset, // allow for scrolling
height: 1,
width: 1,
overflow: 'hidden'
@@ -2156,31 +2031,13 @@
var outer = n.children();
var inner = outer.children();
- // Numbers below, in order, are:
- // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
- //
- // IE6 XP: 100 100 100 83
- // IE7 Vista: 100 100 100 83
- // IE 8+ Windows: 83 83 100 83
- // Evergreen Windows: 83 83 100 83
- // Evergreen Mac with scrollbars: 85 85 100 85
- // Evergreen Mac without scrollbars: 100 100 100 100
-
// Get scrollbar width
browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
- // IE6/7 will oversize a width 100% element inside a scrolling element, to
- // include the width of the scrollbar, while other browsers ensure the inner
- // element is contained without forcing scrolling
- browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
-
// In rtl text layout, some browsers (most, but not all) will place the
// scrollbar on the left, rather than the right.
browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
- // IE8- don't provide height and width for getBoundingClientRect
- browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
-
n.remove();
}
@@ -2188,58 +2045,22 @@
settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
}
-
- /**
- * Array.prototype reduce[Right] method, used for browsers which don't support
- * JS 1.6. Done this way to reduce code size, since we iterate either way
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnReduce ( that, fn, init, start, end, inc )
- {
- var
- i = start,
- value,
- isSet = false;
-
- if ( init !== undefined ) {
- value = init;
- isSet = true;
- }
-
- while ( i !== end ) {
- if ( ! that.hasOwnProperty(i) ) {
- continue;
- }
-
- value = isSet ?
- fn( value, that[i], i, that ) :
- that[i];
-
- isSet = true;
- i += inc;
- }
-
- return value;
- }
-
/**
* Add a column to the list used for the table with default values
* @param {object} oSettings dataTables settings object
- * @param {node} nTh The th element for this column
* @memberof DataTable#oApi
*/
- function _fnAddColumn( oSettings, nTh )
+ function _fnAddColumn( oSettings )
{
// Add column to aoColumns array
var oDefaults = DataTable.defaults.column;
var iCol = oSettings.aoColumns.length;
var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
- "nTh": nTh ? nTh : document.createElement('th'),
- "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
"aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
"mData": oDefaults.mData ? oDefaults.mData : iCol,
- idx: iCol
+ idx: iCol,
+ searchFixed: {},
+ colEl: $('<col>').attr('data-dt-column', iCol)
} );
oSettings.aoColumns.push( oCol );
@@ -2248,9 +2069,6 @@
// with only some of the parameters defined, and also not give a default
var searchCols = oSettings.aoPreSearchCols;
searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
-
- // Use the default column options function to initialise classes etc
- _fnColumnOptions( oSettings, iCol, $(nTh).data() );
}
@@ -2264,21 +2082,6 @@
function _fnColumnOptions( oSettings, iCol, oOptions )
{
var oCol = oSettings.aoColumns[ iCol ];
- var oClasses = oSettings.oClasses;
- var th = $(oCol.nTh);
-
- // Try to get width information from the DOM. We can't get it from CSS
- // as we'd need to parse the CSS stylesheet. `width` option can override
- if ( ! oCol.sWidthOrig ) {
- // Width attribute
- oCol.sWidthOrig = th.attr('width') || null;
-
- // Style attribute
- var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
- if ( t ) {
- oCol.sWidthOrig = t[1];
- }
- }
/* User specified column options */
if ( oOptions !== undefined && oOptions !== null )
@@ -2299,20 +2102,25 @@
{
oCol._sManualType = oOptions.sType;
}
-
- // `class` is a reserved word in Javascript, so we need to provide
+
+ // `class` is a reserved word in JavaScript, so we need to provide
// the ability to use a valid name for the camel case input
if ( oOptions.className && ! oOptions.sClass )
{
oOptions.sClass = oOptions.className;
}
- if ( oOptions.sClass ) {
- 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
*/
@@ -2326,7 +2134,17 @@
/* Cache the data get and set functions for speed */
var mDataSrc = oCol.mData;
var mData = _fnGetObjectDataFn( mDataSrc );
- var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
+
+ // The `render` option can be given as an array to access the helper rendering methods.
+ // The first element is the rendering method to use, the rest are the parameters to pass
+ if ( oCol.mRender && Array.isArray( oCol.mRender ) ) {
+ var copy = oCol.mRender.slice();
+ var name = copy.shift();
+
+ oCol.mRender = DataTable.render[name].apply(window, copy);
+ }
+
+ oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
var attrTest = function( src ) {
return typeof src === 'string' && src.indexOf('@') !== -1;
@@ -2339,8 +2157,8 @@
oCol.fnGetData = function (rowData, type, meta) {
var innerData = mData( rowData, type, undefined, meta );
- return mRender && type ?
- mRender( innerData, type, rowData, meta ) :
+ return oCol._render && type ?
+ oCol._render( innerData, type, rowData, meta ) :
innerData;
};
oCol.fnSetData = function ( rowData, val, meta ) {
@@ -2349,7 +2167,7 @@
// Indicate if DataTables should read DOM data as an object or array
// Used in _fnGetRowElements
- if ( typeof mDataSrc !== 'number' ) {
+ if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {
oSettings._rowReadObject = true;
}
@@ -2357,31 +2175,6 @@
if ( !oSettings.oFeatures.bSort )
{
oCol.bSortable = false;
- th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
- }
-
- /* Check that the class assignment is correct for sorting */
- var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
- var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
- if ( !oCol.bSortable || (!bAsc && !bDesc) )
- {
- oCol.sSortingClass = oClasses.sSortableNone;
- oCol.sSortingClassJUI = "";
- }
- else if ( bAsc && !bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableAsc;
- oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
- }
- else if ( !bAsc && bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableDesc;
- oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
- }
- else
- {
- oCol.sSortingClass = oClasses.sSortable;
- oCol.sSortingClassJUI = oClasses.sSortJUI;
}
}
@@ -2394,27 +2187,37 @@
*/
function _fnAdjustColumnSizing ( settings )
{
- /* Not interested in doing column width calculation if auto-width is disabled */
- if ( settings.oFeatures.bAutoWidth !== false )
- {
- var columns = settings.aoColumns;
-
- _fnCalculateColumnWidths( settings );
- for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
- {
- columns[i].nTh.style.width = columns[i].sWidth;
- }
- }
+ _fnCalculateColumnWidths( settings );
+ _fnColumnSizes( settings );
var scroll = settings.oScroll;
- if ( scroll.sY !== '' || scroll.sX !== '')
- {
+ if ( scroll.sY !== '' || scroll.sX !== '') {
_fnScrollDraw( settings );
}
_fnCallbackFire( settings, null, 'column-sizing', [settings] );
}
+ /**
+ * Apply column sizes
+ *
+ * @param {*} settings DataTables settings object
+ */
+ function _fnColumnSizes ( settings )
+ {
+ var cols = settings.aoColumns;
+
+ for (var i=0 ; i<cols.length ; i++) {
+ var width = _fnColumnsSumWidth(settings, [i], false, false);
+
+ cols[i].colEl.css('width', width);
+
+ if (settings.oScroll.sX) {
+ cols[i].colEl.css('min-width', width);
+ }
+ }
+ }
+
/**
* Convert the index of a visible column to the index in the data array (take account
@@ -2445,7 +2248,7 @@
function _fnColumnIndexToVisible( oSettings, iMatch )
{
var aiVis = _fnGetColumns( oSettings, 'bVisible' );
- var iPos = $.inArray( iMatch, aiVis );
+ var iPos = aiVis.indexOf(iMatch);
return iPos !== -1 ? iPos : null;
}
@@ -2457,16 +2260,19 @@
* @returns {int} i the number of visible columns
* @memberof DataTable#oApi
*/
- function _fnVisbleColumns( oSettings )
+ function _fnVisibleColumns( settings )
{
+ var layout = settings.aoHeader;
+ var columns = settings.aoColumns;
var vis = 0;
- // No reduce in IE8, use a loop for now
- $.each( oSettings.aoColumns, function ( i, col ) {
- if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
- vis++;
+ if ( layout.length ) {
+ for ( var i=0, iLen=layout[0].length ; i<iLen ; i++ ) {
+ if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) {
+ vis++;
+ }
}
- } );
+ }
return vis;
}
@@ -2484,7 +2290,7 @@
{
var a = [];
- $.map( oSettings.aoColumns, function(val, i) {
+ oSettings.aoColumns.map( function(val, i) {
if ( val[sParam] ) {
a.push( i );
}
@@ -2493,6 +2299,21 @@
return a;
}
+ /**
+ * Allow the result from a type detection function to be `true` while
+ * translating that into a string. Old type detection functions will
+ * return the type name if it passes. An object store would be better,
+ * but not backwards compatible.
+ *
+ * @param {*} typeDetect Object or function for type detection
+ * @param {*} res Result from the type detection function
+ * @returns Type name or false
+ */
+ function _typeResult (typeDetect, res) {
+ return res === true
+ ? typeDetect._name
+ : res;
+ }
/**
* Calculate the 'type' of a column
@@ -2504,11 +2325,11 @@
var columns = settings.aoColumns;
var data = settings.aoData;
var types = DataTable.ext.type.detect;
- var i, ien, j, jen, k, ken;
- var col, cell, detectedType, cache;
+ var i, iLen, j, jen, k, ken;
+ var col, detectedType, cache;
- // For each column, spin over the
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
+ // For each column, spin over the data type detection functions, seeing if one matches
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
col = columns[i];
cache = [];
@@ -2516,22 +2337,58 @@
col.sType = col._sManualType;
}
else if ( ! col.sType ) {
+ // With SSP type detection can be unreliable and error prone, so we provide a way
+ // to turn it off.
+ if (! settings.typeDetect) {
+ return;
+ }
+
for ( j=0, jen=types.length ; j<jen ; j++ ) {
+ var typeDetect = types[j];
+
+ // There can be either one, or three type detection functions
+ var oneOf = typeDetect.oneOf;
+ var allOf = typeDetect.allOf || typeDetect;
+ var init = typeDetect.init;
+ var one = false;
+
+ detectedType = null;
+
+ // Fast detect based on column assignment
+ if (init) {
+ detectedType = _typeResult(typeDetect, init(settings, col, i));
+
+ if (detectedType) {
+ col.sType = detectedType;
+ break;
+ }
+ }
+
for ( k=0, ken=data.length ; k<ken ; k++ ) {
+ if (! data[k]) {
+ continue;
+ }
+
// Use a cache array so we only need to get the type data
// from the formatter once (when using multiple detectors)
if ( cache[k] === undefined ) {
cache[k] = _fnGetCellData( settings, k, i, 'type' );
}
- detectedType = types[j]( cache[k], settings );
+ // Only one data point in the column needs to match this function
+ if (oneOf && ! one) {
+ one = _typeResult(typeDetect, oneOf( cache[k], settings ));
+ }
+
+ // All data points need to match this function
+ detectedType = _typeResult(typeDetect, allOf( cache[k], settings ));
// If null, then this type can't apply to this column, so
// rather than testing all cells, break out. There is an
// exception for the last type which is `html`. We need to
// scan all rows since it is possible to mix string and HTML
// types
- if ( ! detectedType && j !== types.length-1 ) {
+ if ( ! detectedType && j !== types.length-3 ) {
break;
}
@@ -2545,7 +2402,7 @@
// Type is valid for all data points in the column - use this
// type
- if ( detectedType ) {
+ if ( (oneOf && one && detectedType) || (!oneOf && detectedType) ) {
col.sType = detectedType;
break;
}
@@ -2556,9 +2413,61 @@
col.sType = 'string';
}
}
+
+ // Set class names for header / footer for auto type classes
+ var autoClass = _ext.type.className[col.sType];
+
+ if (autoClass) {
+ _columnAutoClass(settings.aoHeader, i, autoClass);
+ _columnAutoClass(settings.aoFooter, i, autoClass);
+ }
+
+ var renderer = _ext.type.render[col.sType];
+
+ // This can only happen once! There is no way to remove
+ // a renderer. After the first time the renderer has
+ // already been set so createTr will run the renderer itself.
+ if (renderer && ! col._render) {
+ col._render = DataTable.util.get(renderer);
+
+ _columnAutoRender(settings, i);
+ }
}
}
+ /**
+ * Apply an auto detected renderer to data which doesn't yet have
+ * a renderer
+ */
+ function _columnAutoRender(settings, colIdx) {
+ var data = settings.aoData;
+
+ for (var i=0 ; i<data.length ; i++) {
+ if (data[i].nTr) {
+ // We have to update the display here since there is no
+ // invalidation check for the data
+ var display = _fnGetCellData( settings, i, colIdx, 'display' );
+
+ data[i].displayData[colIdx] = display;
+ _fnWriteCell(data[i].anCells[colIdx], display);
+
+ // No need to update sort / filter data since it has
+ // been invalidated and will be re-read with the
+ // renderer now applied
+ }
+ }
+ }
+
+ /**
+ * Apply a class name to a column's header cells
+ */
+ function _columnAutoClass(container, colIdx, className) {
+ container.forEach(function (row) {
+ if (row[colIdx] && row[colIdx].unique) {
+ _addClass(row[colIdx].cell, className);
+ }
+ });
+ }
/**
* Take the column definitions and static columns arrays and calculate how
@@ -2567,15 +2476,24 @@
* @param {object} oSettings dataTables settings object
* @param {array} aoColDefs The aoColumnDefs array that is to be applied
* @param {array} aoCols The aoColumns array that defines columns individually
+ * @param {array} headerLayout Layout for header as it was loaded
* @param {function} fn Callback function - takes two parameters, the calculated
* column index and the definition for that column.
* @memberof DataTable#oApi
*/
- function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
+ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn )
{
var i, iLen, j, jLen, k, kLen, def;
var columns = oSettings.aoColumns;
+ if ( aoCols ) {
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
+ if (aoCols[i] && aoCols[i].name) {
+ columns[i].sName = aoCols[i].name;
+ }
+ }
+ }
+
// Column definitions with aTargets
if ( aoColDefs )
{
@@ -2585,9 +2503,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 ) )
{
@@ -2596,32 +2516,56 @@
for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
{
- if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
+ var target = aTargets[j];
+
+ if ( typeof target === 'number' && target >= 0 )
{
/* Add columns that we don't yet know about */
- while( columns.length <= aTargets[j] )
+ while( columns.length <= target )
{
_fnAddColumn( oSettings );
}
/* Integer, basic index */
- fn( aTargets[j], def );
+ fn( target, def );
}
- else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
+ else if ( typeof target === 'number' && target < 0 )
{
/* Negative integer, right to left column counting */
- fn( columns.length+aTargets[j], def );
+ fn( columns.length+target, def );
}
- else if ( typeof aTargets[j] === 'string' )
+ else if ( typeof target === 'string' )
{
- /* Class name matching on TH element */
- for ( k=0, kLen=columns.length ; k<kLen ; k++ )
- {
- if ( aTargets[j] == "_all" ||
- $(columns[k].nTh).hasClass( aTargets[j] ) )
- {
+ for ( k=0, kLen=columns.length ; k<kLen ; k++ ) {
+ if (target === '_all') {
+ // Apply to all columns
fn( k, def );
}
+ else if (target.indexOf(':name') !== -1) {
+ // Column selector
+ if (columns[k].sName === target.replace(':name', '')) {
+ fn( k, def );
+ }
+ }
+ else {
+ // Cell selector
+ headerLayout.forEach(function (row) {
+ if (row[k]) {
+ var cell = $(row[k].cell);
+
+ // Legacy support. Note that it means that we don't support
+ // an element name selector only, since they are treated as
+ // class names for 1.x compat.
+ if (target.match(/^[a-z][\w-]*$/i)) {
+ target = '.' + target;
+ }
+
+ if (cell.is( target )) {
+ fn( k, def );
+ }
+ }
+ });
+ }
}
}
}
@@ -2629,65 +2573,124 @@
}
// Statically defined columns array
- if ( aoCols )
- {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
- {
+ if ( aoCols ) {
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
fn( i, aoCols[i] );
}
}
}
+
+ /**
+ * Get the width for a given set of columns
+ *
+ * @param {*} settings DataTables settings object
+ * @param {*} targets Columns - comma separated string or array of numbers
+ * @param {*} original Use the original width (true) or calculated (false)
+ * @param {*} incVisible Include visible columns (true) or not (false)
+ * @returns Combined CSS value
+ */
+ function _fnColumnsSumWidth( settings, targets, original, incVisible ) {
+ if ( ! Array.isArray( targets ) ) {
+ targets = _fnColumnsFromHeader( targets );
+ }
+
+ var sum = 0;
+ var unit;
+ var columns = settings.aoColumns;
+
+ for ( var i=0, iLen=targets.length ; i<iLen ; i++ ) {
+ var column = columns[ targets[i] ];
+ var definedWidth = original ?
+ column.sWidthOrig :
+ column.sWidth;
+
+ if ( ! incVisible && column.bVisible === false ) {
+ continue;
+ }
+
+ if ( definedWidth === null || definedWidth === undefined ) {
+ return null; // can't determine a defined width - browser defined
+ }
+ else if ( typeof definedWidth === 'number' ) {
+ unit = 'px';
+ sum += definedWidth;
+ }
+ else {
+ var matched = definedWidth.match(/([\d\.]+)([^\d]*)/);
+
+ if ( matched ) {
+ sum += matched[1] * 1;
+ unit = matched.length === 3 ?
+ matched[2] :
+ 'px';
+ }
+ }
+ }
+
+ return sum + unit;
+ }
+
+ function _fnColumnsFromHeader( cell )
+ {
+ var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column');
+
+ if ( ! attr ) {
+ return [];
+ }
+
+ return attr.split(',').map( function (val) {
+ return val * 1;
+ } );
+ }
/**
* Add a data array to the table, creating DOM node etc. This is the parallel to
- * _fnGatherData, but for adding rows from a Javascript source, rather than a
+ * _fnGatherData, but for adding rows from a JavaScript source, rather than a
* DOM source.
- * @param {object} oSettings dataTables settings object
- * @param {array} aData data array to be added
- * @param {node} [nTr] TR element to add to the table - optional. If not given,
+ * @param {object} settings dataTables settings object
+ * @param {array} data data array to be added
+ * @param {node} [tr] TR element to add to the table - optional. If not given,
* DataTables will create a row automatically
- * @param {array} [anTds] Array of TD|TH elements for the row - must be given
+ * @param {array} [tds] Array of TD|TH elements for the row - must be given
* if nTr is.
* @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
* @memberof DataTable#oApi
*/
- function _fnAddData ( oSettings, aDataIn, nTr, anTds )
+ function _fnAddData ( settings, dataIn, tr, tds )
{
/* Create the object for storing information about this new row */
- var iRow = oSettings.aoData.length;
- var oData = $.extend( true, {}, DataTable.models.oRow, {
- src: nTr ? 'dom' : 'data',
- idx: iRow
+ var rowIdx = settings.aoData.length;
+ var rowModel = $.extend( true, {}, DataTable.models.oRow, {
+ src: tr ? 'dom' : 'data',
+ idx: rowIdx
} );
- oData._aData = aDataIn;
- oSettings.aoData.push( oData );
+ rowModel._aData = dataIn;
+ settings.aoData.push( rowModel );
- /* Create the cells */
- var nTd, sThisType;
- var columns = oSettings.aoColumns;
+ var columns = settings.aoColumns;
- // Invalidate the column types as the new data needs to be revalidated
for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
{
+ // Invalidate the column types as the new data needs to be revalidated
columns[i].sType = null;
}
/* Add to the display array */
- oSettings.aiDisplayMaster.push( iRow );
+ settings.aiDisplayMaster.push( rowIdx );
- var id = oSettings.rowIdFn( aDataIn );
+ var id = settings.rowIdFn( dataIn );
if ( id !== undefined ) {
- oSettings.aIds[ id ] = oData;
+ settings.aIds[ id ] = rowModel;
}
/* Create the DOM information, or register it if already present */
- if ( nTr || ! oSettings.oFeatures.bDeferRender )
+ if ( tr || ! settings.oFeatures.bDeferRender )
{
- _fnCreateTr( oSettings, iRow, nTr, anTds );
+ _fnCreateTr( settings, rowIdx, tr, tds );
}
- return iRow;
+ return rowIdx;
}
@@ -2718,33 +2721,6 @@
/**
- * Take a TR element and convert it to an index in aoData
- * @param {object} oSettings dataTables settings object
- * @param {node} n the TR element to find
- * @returns {int} index if the node is found, null if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToDataIndex( oSettings, n )
- {
- return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
- }
-
-
- /**
- * Take a TD element and convert it into a column data index (not the visible index)
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow The row number the TD/TH can be found in
- * @param {node} n The TD/TH element to find
- * @returns {int} index if the node is found, -1 if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToColumnIndex( oSettings, iRow, n )
- {
- return $.inArray( n, oSettings.aoData[ iRow ].anCells );
- }
-
-
- /**
* Get the data for a given cell from the internal cache, taking into account data mapping
* @param {object} settings dataTables settings object
* @param {int} rowIdx aoData row id
@@ -2762,9 +2738,15 @@
type = 'sort';
}
+ var row = settings.aoData[rowIdx];
+
+ if (! row) {
+ return undefined;
+ }
+
var draw = settings.iDraw;
var col = settings.aoColumns[colIdx];
- var rowData = settings.aoData[rowIdx]._aData;
+ var rowData = row._aData;
var defaultContent = col.sDefaultContent;
var cellData = col.fnGetData( rowData, type, {
settings: settings,
@@ -2772,6 +2754,11 @@
col: colIdx
} );
+ // Allow for a node being returned for non-display types
+ if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) {
+ cellData = cellData.innerHTML;
+ }
+
if ( cellData === undefined ) {
if ( settings.iDrawError != draw && defaultContent === null ) {
_fnLog( settings, 0, "Requested unknown parameter "+
@@ -2798,10 +2785,10 @@
}
if ( type === 'filter' ) {
- var fomatters = DataTable.ext.type.search;
+ var formatters = DataTable.ext.type.search;
- if ( fomatters[ col.sType ] ) {
- cellData = fomatters[ col.sType ]( cellData );
+ if ( formatters[ col.sType ] ) {
+ cellData = formatters[ col.sType ]( cellData );
}
}
@@ -2829,6 +2816,23 @@
} );
}
+ /**
+ * Write a value to a cell
+ * @param {*} td Cell
+ * @param {*} val Value
+ */
+ function _fnWriteCell(td, val)
+ {
+ if (val && typeof val === 'object' && val.nodeName) {
+ $(td)
+ .empty()
+ .append(val);
+ }
+ else {
+ td.innerHTML = val;
+ }
+ }
+
// Private variable that is used to match action syntax in the data property object
var __reArray = /\[.*?\]$/;
@@ -2841,7 +2845,9 @@
*/
function _fnSplitObjNotation( str )
{
- return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
+ var parts = str.match(/(\\.|[^.])+/g) || [''];
+
+ return parts.map( function ( s ) {
return s.replace(/\\\./g, '.');
} );
}
@@ -2893,36 +2899,6 @@
}
- /**
- * Take an array of integers (index array) and remove a target integer (value - not
- * the key!)
- * @param {array} a Index array to target
- * @param {int} iTarget value to find
- * @memberof DataTable#oApi
- */
- function _fnDeleteIndex( a, iTarget, splice )
- {
- var iTargetIndex = -1;
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- if ( a[i] == iTarget )
- {
- iTargetIndex = i;
- }
- else if ( a[i] > iTarget )
- {
- a[i]--;
- }
- }
-
- if ( iTargetIndex != -1 && splice === undefined )
- {
- a.splice( iTargetIndex, 1 );
- }
- }
-
-
/**
* Mark cached data as invalid such that a re-read of the data will occur when
* the cached data is next requested. Also update from the data source object.
@@ -2942,17 +2918,12 @@
function _fnInvalidate( settings, rowIdx, src, colIdx )
{
var row = settings.aoData[ rowIdx ];
- var i, ien;
- var cellWrite = function ( cell, col ) {
- // This is very frustrating, but in IE if you just write directly
- // to innerHTML, and elements that are overwritten are GC'ed,
- // even if there is a reference to them elsewhere
- while ( cell.childNodes.length ) {
- cell.removeChild( cell.firstChild );
- }
+ var i, iLen;
- cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
- };
+ // Remove the cached data for the row
+ row._aSortData = null;
+ row._aFilterData = null;
+ row.displayData = null;
// Are we reading last data from DOM or the data object?
if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
@@ -2965,33 +2936,34 @@
else {
// Reading from data object, update the DOM
var cells = row.anCells;
+ var display = _fnGetRowDisplay(settings, rowIdx);
if ( cells ) {
if ( colIdx !== undefined ) {
- cellWrite( cells[colIdx], colIdx );
+ _fnWriteCell(cells[colIdx], display[colIdx]);
}
else {
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- cellWrite( cells[i], i );
+ for ( i=0, iLen=cells.length ; i<iLen ; i++ ) {
+ _fnWriteCell(cells[i], display[i]);
}
}
}
}
- // For both row and cell invalidation, the cached data for sorting and
- // filtering is nulled out
- row._aSortData = null;
- row._aFilterData = null;
-
- // Invalidate the type for a specific column (if given) or all columns since
- // the data might have changed
+ // Column specific invalidation
var cols = settings.aoColumns;
if ( colIdx !== undefined ) {
+ // Type - the data might have changed
cols[ colIdx ].sType = null;
+
+ // Max length string. Its a fairly cheep recalculation, so not worth
+ // something more complicated
+ cols[ colIdx ].wideStrings = null;
}
else {
- for ( i=0, ien=cols.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=cols.length ; i<iLen ; i++ ) {
cols[i].sType = null;
+ cols[i].wideStrings = null;
}
// Update DataTables special `DT_*` attributes for the row
@@ -3022,7 +2994,7 @@
var
tds = [],
td = row.firstChild,
- name, col, o, i=0, contents,
+ name, col, i=0, contents,
columns = settings.aoColumns,
objectRead = settings._rowReadObject;
@@ -3116,6 +3088,29 @@
cells: tds
};
}
+
+ /**
+ * Render and cache a row's display data for the columns, if required
+ * @returns
+ */
+ function _fnGetRowDisplay (settings, rowIdx) {
+ var rowModal = settings.aoData[rowIdx];
+ var columns = settings.aoColumns;
+
+ if (! rowModal.displayData) {
+ // Need to render and cache
+ rowModal.displayData = [];
+
+ for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) {
+ rowModal.displayData.push(
+ _fnGetCellData( settings, rowIdx, colIdx, 'display' )
+ );
+ }
+ }
+
+ return rowModal.displayData;
+ }
+
/**
* Create a new TR element (and it's TD children) for a row
* @param {object} oSettings dataTables settings object
@@ -3133,7 +3128,8 @@
rowData = row._aData,
cells = [],
nTr, nTd, oCol,
- i, iLen, create;
+ i, iLen, create,
+ trClass = oSettings.oClasses.tbody.row;
if ( row.nTr === null )
{
@@ -3142,6 +3138,8 @@
row.nTr = nTr;
row.anCells = cells;
+ _addClass(nTr, trClass);
+
/* Use a private property on the node to allow reserve mapping from the node
* to the aoData array for fast look up
*/
@@ -3154,35 +3152,43 @@
for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
{
oCol = oSettings.aoColumns[i];
- create = nTrIn ? false : true;
+ create = nTrIn && anTds[i] ? false : true;
nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
+
+ if (! nTd) {
+ _fnLog( oSettings, 0, 'Incorrect column count', 18 );
+ }
+
nTd._DT_CellIndex = {
row: iRow,
column: i
};
cells.push( nTd );
+
+ var display = _fnGetRowDisplay(oSettings, iRow);
// Need to create the HTML if new, or if a rendering function is defined
- if ( create || ((oCol.mRender || oCol.mData !== i) &&
- (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
- )) {
- nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
+ if (
+ create ||
+ (
+ (oCol.mRender || oCol.mData !== i) &&
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
+ )
+ ) {
+ _fnWriteCell(nTd, display[i]);
}
- /* Add user defined class */
- if ( oCol.sClass )
- {
- nTd.className += ' '+oCol.sClass;
- }
+ // column class
+ _addClass(nTd, oCol.sClass);
// Visibility - add or remove as required
- if ( oCol.bVisible && ! nTrIn )
+ if ( oCol.bVisible && create )
{
nTr.appendChild( nTd );
}
- else if ( ! oCol.bVisible && nTrIn )
+ else if ( ! oCol.bVisible && ! create )
{
nTd.parentNode.removeChild( nTd );
}
@@ -3195,7 +3201,10 @@
}
}
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
+ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] );
+ }
+ else {
+ _addClass(row.nTr, trClass);
}
}
@@ -3247,176 +3256,187 @@
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
- function _fnBuildHead( oSettings )
+ function _fnBuildHead( settings, side )
{
- var i, ien, cell, row, column;
- var thead = oSettings.nTHead;
- var tfoot = oSettings.nTFoot;
- var createHeader = $('th, td', thead).length === 0;
- var classes = oSettings.oClasses;
- var columns = oSettings.aoColumns;
-
- if ( createHeader ) {
- row = $('<tr/>').appendTo( thead );
+ var classes = settings.oClasses;
+ var columns = settings.aoColumns;
+ var i, iLen, row;
+ var target = side === 'header'
+ ? settings.nTHead
+ : settings.nTFoot;
+ var titleProp = side === 'header' ? 'sTitle' : side;
+
+ // Footer might be defined
+ if (! target) {
+ return;
}
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- column = columns[i];
- cell = $( column.nTh ).addClass( column.sClass );
+ // If no cells yet and we have content for them, then create
+ if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) {
+ row = $('tr', target);
- if ( createHeader ) {
- cell.appendTo( row );
+ // Add a row if needed
+ if (! row.length) {
+ row = $('<tr/>').appendTo(target)
}
- // 1.11 move into sorting
- if ( oSettings.oFeatures.bSort ) {
- cell.addClass( column.sSortingClass );
-
- if ( column.bSortable !== false ) {
- cell
- .attr( 'tabindex', oSettings.iTabIndex )
- .attr( 'aria-controls', oSettings.sTableId );
+ // Add the number of cells needed to make up to the number of columns
+ if (row.length === 1) {
+ var cellCount = 0;
+
+ $('td, th', row).each(function () {
+ cellCount += this.colSpan;
+ });
- _fnSortAttachListener( oSettings, column.nTh, i );
+ for ( i=cellCount, iLen=columns.length ; i<iLen ; i++ ) {
+ $('<th/>')
+ .html( columns[i][titleProp] || '' )
+ .appendTo( row );
}
}
+ }
- if ( column.sTitle != cell[0].innerHTML ) {
- cell.html( column.sTitle );
- }
+ var detected = _fnDetectHeader( settings, target, true );
- _fnRenderer( oSettings, 'header' )(
- oSettings, cell, column, classes
- );
+ if (side === 'header') {
+ settings.aoHeader = detected;
+ $('tr', target).addClass(classes.thead.row);
}
-
- if ( createHeader ) {
- _fnDetectHeader( oSettings.aoHeader, thead );
+ else {
+ settings.aoFooter = detected;
+ $('tr', target).addClass(classes.tfoot.row);
}
- /* Deal with the footer - add classes if required */
- $(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );
- $(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );
-
- // Cache the footer cells. Note that we only take the cells from the first
- // row in the footer. If there is more than one row the user wants to
- // interact with, they need to use the table().foot() method. Note also this
- // allows cells to be used for multiple columns using colspan
- if ( tfoot !== null ) {
- var cells = oSettings.aoFooter[0];
-
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- column = columns[i];
- column.nTf = cells[i].cell;
-
- if ( column.sClass ) {
- $(column.nTf).addClass( column.sClass );
- }
- }
- }
+ // Every cell needs to be passed through the renderer
+ $(target).children('tr').children('th, td')
+ .each( function () {
+ _fnRenderer( settings, side )(
+ settings, $(this), classes
+ );
+ } );
}
-
/**
- * Draw the header (or footer) element based on the column visibility states. The
- * methodology here is to use the layout array from _fnDetectHeader, modified for
- * the instantaneous column visibility, to construct the new layout. The grid is
- * traversed over cell at a time in a rows x columns grid fashion, although each
- * cell insert can cover multiple elements in the grid - which is tracks using the
- * aApplied array. Cell inserts in the grid will only occur where there isn't
- * already a cell in that position.
- * @param {object} oSettings dataTables settings object
- * @param array {objects} aoSource Layout array from _fnDetectHeader
- * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
- * @memberof DataTable#oApi
+ * Build a layout structure for a header or footer
+ *
+ * @param {*} settings DataTables settings
+ * @param {*} source Source layout array
+ * @param {*} incColumns What columns should be included
+ * @returns Layout array in column index order
*/
- function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
+ function _fnHeaderLayout( settings, source, incColumns )
{
- var i, iLen, j, jLen, k, kLen, n, nLocalTr;
- var aoLocal = [];
- var aApplied = [];
- var iColumns = oSettings.aoColumns.length;
- var iRowspan, iColspan;
+ var row, column, cell;
+ var local = [];
+ var structure = [];
+ var columns = settings.aoColumns;
+ var columnCount = columns.length;
+ var rowspan, colspan;
- if ( ! aoSource )
- {
+ if ( ! source ) {
return;
}
- if ( bIncludeHidden === undefined )
- {
- bIncludeHidden = false;
+ // Default is to work on only visible columns
+ if ( ! incColumns ) {
+ incColumns = _range(columnCount)
+ .filter(function (idx) {
+ return columns[idx].bVisible;
+ });
}
- /* Make a copy of the master layout array, but without the visible columns in it */
- for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
- {
- aoLocal[i] = aoSource[i].slice();
- aoLocal[i].nTr = aoSource[i].nTr;
-
- /* Remove any columns which are currently hidden */
- for ( j=iColumns-1 ; j>=0 ; j-- )
- {
- if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
- {
- aoLocal[i].splice( j, 1 );
- }
- }
+ // Make a copy of the master layout array, but with only the columns we want
+ for ( row=0 ; row<source.length ; row++ ) {
+ // Remove any columns we haven't selected
+ local[row] = source[row].slice().filter(function (cell, i) {
+ return incColumns.includes(i);
+ });
- /* Prep the applied array - it needs an element for each row */
- aApplied.push( [] );
+ // Prep the structure array - it needs an element for each row
+ structure.push( [] );
}
- for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
- {
- nLocalTr = aoLocal[i].nTr;
+ for ( row=0 ; row<local.length ; row++ ) {
+ for ( column=0 ; column<local[row].length ; column++ ) {
+ rowspan = 1;
+ colspan = 1;
- /* All cells are going to be replaced, so empty out the row */
- if ( nLocalTr )
- {
- while( (n = nLocalTr.firstChild) )
- {
- nLocalTr.removeChild( n );
+ // Check to see if there is already a cell (row/colspan) covering our target
+ // insert point. If there is, then there is nothing to do.
+ if ( structure[row][column] === undefined ) {
+ cell = local[row][column].cell;
+
+ // Expand for rowspan
+ while (
+ local[row+rowspan] !== undefined &&
+ local[row][column].cell == local[row+rowspan][column].cell
+ ) {
+ structure[row+rowspan][column] = null;
+ rowspan++;
+ }
+
+ // And for colspan
+ while (
+ local[row][column+colspan] !== undefined &&
+ local[row][column].cell == local[row][column+colspan].cell
+ ) {
+ // Which also needs to go over rows
+ for ( var k=0 ; k<rowspan ; k++ ) {
+ structure[row+k][column+colspan] = null;
+ }
+
+ colspan++;
+ }
+
+ var titleSpan = $('.dt-column-title', cell);
+
+ structure[row][column] = {
+ cell: cell,
+ colspan: colspan,
+ rowspan: rowspan,
+ title: titleSpan.length
+ ? titleSpan.html()
+ : $(cell).html()
+ };
}
}
+ }
- for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
- {
- iRowspan = 1;
- iColspan = 1;
+ return structure;
+ }
- /* Check to see if there is already a cell (row/colspan) covering our target
- * insert point. If there is, then there is nothing to do.
- */
- if ( aApplied[i][j] === undefined )
- {
- nLocalTr.appendChild( aoLocal[i][j].cell );
- aApplied[i][j] = 1;
- /* Expand the cell to cover as many rows as needed */
- while ( aoLocal[i+iRowspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
- {
- aApplied[i+iRowspan][j] = 1;
- iRowspan++;
- }
+ /**
+ * Draw the header (or footer) element based on the column visibility states.
+ *
+ * @param object oSettings dataTables settings object
+ * @param array aoSource Layout array from _fnDetectHeader
+ * @memberof DataTable#oApi
+ */
+ function _fnDrawHead( settings, source )
+ {
+ var layout = _fnHeaderLayout(settings, source);
+ var tr, n;
- /* Expand the cell to cover as many columns as needed */
- while ( aoLocal[i][j+iColspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
- {
- /* Must update the applied array over the rows for the columns */
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aApplied[i+k][j+iColspan] = 1;
- }
- iColspan++;
- }
+ for ( var row=0 ; row<source.length ; row++ ) {
+ tr = source[row].row;
+
+ // All cells are going to be replaced, so empty out the row
+ // Can't use $().empty() as that kills event handlers
+ if (tr) {
+ while( (n = tr.firstChild) ) {
+ tr.removeChild( n );
+ }
+ }
- /* Do the actual expansion in the DOM */
- $(aoLocal[i][j].cell)
- .attr('rowspan', iRowspan)
- .attr('colspan', iColspan);
+ for ( var column=0 ; column<layout[row].length ; column++ ) {
+ var point = layout[row][column];
+
+ if (point) {
+ $(point.cell)
+ .appendTo(tr)
+ .attr('rowspan', point.rowspan)
+ .attr('colspan', point.colspan);
}
}
}
@@ -3436,7 +3456,7 @@
/* Provide a pre-callback function which can be used to cancel the draw is false is returned */
var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
- if ( $.inArray( false, aPreDraw ) !== -1 )
+ if ( aPreDraw.indexOf(false) !== -1 )
{
_fnProcessingDisplay( oSettings, false );
return;
@@ -3444,20 +3464,19 @@
var anRows = [];
var iRowCount = 0;
- var asStripeClasses = oSettings.asStripeClasses;
- var iStripes = asStripeClasses.length;
- var oLang = oSettings.oLanguage;
var bServerSide = _fnDataSource( oSettings ) == 'ssp';
var aiDisplay = oSettings.aiDisplay;
var iDisplayStart = oSettings._iDisplayStart;
var iDisplayEnd = oSettings.fnDisplayEnd();
+ var columns = oSettings.aoColumns;
+ var body = $(oSettings.nTBody);
oSettings.bDrawing = true;
/* Server-side processing draw intercept */
- if ( oSettings.bDeferLoading )
+ if ( oSettings.deferLoading )
{
- oSettings.bDeferLoading = false;
+ oSettings.deferLoading = false;
oSettings.iDraw++;
_fnProcessingDisplay( oSettings, false );
}
@@ -3467,6 +3486,11 @@
}
else if ( !oSettings.bDestroying && !ajaxComplete)
{
+ // Show loading message for server-side processing
+ if (oSettings.iDraw === 0) {
+ body.empty().append(_emptyRow(oSettings));
+ }
+
_fnAjaxUpdate( oSettings );
return;
}
@@ -3480,6 +3504,14 @@
{
var iDataIndex = aiDisplay[j];
var aoData = oSettings.aoData[ iDataIndex ];
+
+ // Row has been deleted - can't be displayed
+ if (aoData === null)
+ {
+ continue;
+ }
+
+ // Row node hasn't been created yet
if ( aoData.nTr === null )
{
_fnCreateTr( oSettings, iDataIndex );
@@ -3487,15 +3519,13 @@
var nRow = aoData.nTr;
- /* Remove the old striping classes and then add the new one */
- if ( iStripes !== 0 )
- {
- var sStripe = asStripeClasses[ iRowCount % iStripes ];
- if ( aoData._sRowStripe != sStripe )
- {
- $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
- aoData._sRowStripe = sStripe;
- }
+ // Add various classes as needed
+ for (var i=0 ; i<columns.length ; i++) {
+ var col = columns[i];
+ var td = aoData.anCells[i];
+
+ _addClass(td, _ext.type.className[col.sType]); // auto class
+ _addClass(td, oSettings.oClasses.tbody.cell); // all cells
}
// Row callback functions - might want to manipulate the row
@@ -3510,23 +3540,7 @@
}
else
{
- /* Table is empty - create a row with an empty message in it */
- var sZero = oLang.sZeroRecords;
- if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
- {
- sZero = oLang.sLoadingRecords;
- }
- else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
- {
- sZero = oLang.sEmptyTable;
- }
-
- anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
- .append( $('<td />', {
- 'valign': 'top',
- 'colSpan': _fnVisbleColumns( oSettings ),
- 'class': oSettings.oClasses.sRowEmpty
- } ).html( sZero ) )[0];
+ anRows[ 0 ] = _emptyRow(oSettings);
}
/* Header and footer callbacks */
@@ -3536,13 +3550,21 @@
_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
- var body = $(oSettings.nTBody);
+ // replaceChildren is faster, but only became widespread in 2020,
+ // so a fall back in jQuery is provided for older browsers.
+ if (body[0].replaceChildren) {
+ body[0].replaceChildren.apply(body[0], anRows);
+ }
+ else {
+ body.children().detach();
+ body.append( $(anRows) );
+ }
- body.children().detach();
- body.append( $(anRows) );
+ // Empty table needs a specific class
+ $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0);
/* Call all required callback functions for the end of a draw */
- _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
+ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true );
/* Draw is complete, sorting and filtering must be as well */
oSettings.bSorted = false;
@@ -3558,28 +3580,38 @@
* the paging is reset to the first page
* @memberof DataTable#oApi
*/
- function _fnReDraw( settings, holdPosition )
+ function _fnReDraw( settings, holdPosition, recompute )
{
var
features = settings.oFeatures,
sort = features.bSort,
filter = features.bFilter;
- if ( sort ) {
- _fnSort( settings );
- }
+ if (recompute === undefined || recompute === true) {
+ // Resolve any column types that are unknown due to addition or invalidation
+ _fnColumnTypes( settings );
- if ( filter ) {
- _fnFilterComplete( settings, settings.oPreviousSearch );
- }
- else {
- // No filtering, so we want to just use the display master
- settings.aiDisplay = settings.aiDisplayMaster.slice();
+ if ( sort ) {
+ _fnSort( settings );
+ }
+
+ if ( filter ) {
+ _fnFilterComplete( settings, settings.oPreviousSearch );
+ }
+ else {
+ // No filtering, so we want to just use the display master
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+ }
}
if ( holdPosition !== true ) {
settings._iDisplayStart = 0;
}
+ else {
+ // Keep position, but make sure that there is actually data to display,
+ // otherwise we need to rewind a bit (e.g. if rows were deleted)
+ _fnLengthOverflow(settings);
+ }
// Let any modules know about the draw hold position state (used by
// scrolling internally)
@@ -3587,272 +3619,549 @@
_fnDraw( settings );
- settings._drawHold = false;
+ settings.api.one('draw', function () {
+ settings._drawHold = false;
+ });
+ }
+
+
+ /*
+ * Table is empty - create a row with an empty message in it
+ */
+ function _emptyRow ( settings ) {
+ var oLang = settings.oLanguage;
+ var zero = oLang.sZeroRecords;
+ var dataSrc = _fnDataSource( settings );
+
+ // Make use of the fact that settings.json is only set once the initial data has
+ // been loaded. Show loading when that isn't the case
+ if ((dataSrc === 'ssp' || dataSrc === 'ajax') && ! settings.json) {
+ zero = oLang.sLoadingRecords;
+ }
+ else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
+ {
+ zero = oLang.sEmptyTable;
+ }
+
+ return $( '<tr/>' )
+ .append( $('<td />', {
+ 'colSpan': _fnVisibleColumns( settings ),
+ 'class': settings.oClasses.empty.row
+ } ).html( zero ) )[0];
+ }
+
+
+ /**
+ * Expand the layout items into an object for the rendering function
+ */
+ function _layoutItems (row, align, items) {
+ if ( Array.isArray(items)) {
+ for (var i=0 ; i<items.length ; i++) {
+ _layoutItems(row, align, items[i]);
+ }
+
+ return;
+ }
+
+ var rowCell = row[align];
+
+ // If it is an object, then there can be multiple features contained in it
+ if ( $.isPlainObject( items ) ) {
+ // A feature plugin cannot be named "features" due to this check
+ if (items.features) {
+ if (items.rowId) {
+ row.id = items.rowId;
+ }
+ if (items.rowClass) {
+ row.className = items.rowClass;
+ }
+
+ rowCell.id = items.id;
+ rowCell.className = items.className;
+
+ _layoutItems(row, align, items.features);
+ }
+ else {
+ Object.keys(items).map(function (key) {
+ rowCell.contents.push( {
+ feature: key,
+ opts: items[key]
+ });
+ });
+ }
+ }
+ else {
+ rowCell.contents.push(items);
+ }
+ }
+
+ /**
+ * Find, or create a layout row
+ */
+ function _layoutGetRow(rows, rowNum, align) {
+ var row;
+
+ // Find existing rows
+ for (var i=0; i<rows.length; i++) {
+ row = rows[i];
+
+ if (row.rowNum === rowNum) {
+ // full is on its own, but start and end share a row
+ if (
+ (align === 'full' && row.full) ||
+ ((align === 'start' || align === 'end') && (row.start || row.end))
+ ) {
+ if (! row[align]) {
+ row[align] = {
+ contents: []
+ };
+ }
+
+ return row;
+ }
+ }
+ }
+
+ // If we get this far, then there was no match, create a new row
+ row = {
+ rowNum: rowNum
+ };
+
+ row[align] = {
+ contents: []
+ };
+
+ rows.push(row);
+
+ return row;
+ }
+
+ /**
+ * Convert a `layout` object given by a user to the object structure needed
+ * for the renderer. This is done twice, once for above and once for below
+ * the table. Ordering must also be considered.
+ *
+ * @param {*} settings DataTables settings object
+ * @param {*} layout Layout object to convert
+ * @param {string} side `top` or `bottom`
+ * @returns Converted array structure - one item for each row.
+ */
+ function _layoutArray ( settings, layout, side ) {
+ var rows = [];
+
+ // Split out into an array
+ $.each( layout, function ( pos, items ) {
+ if (items === null) {
+ return;
+ }
+
+ var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/);
+ var rowNum = parts[2]
+ ? parts[2] * 1
+ : 0;
+ var align = parts[3]
+ ? parts[3].toLowerCase()
+ : 'full';
+
+ // Filter out the side we aren't interested in
+ if (parts[1] !== side) {
+ return;
+ }
+
+ // Get or create the row we should attach to
+ var row = _layoutGetRow(rows, rowNum, align);
+
+ _layoutItems(row, align, items);
+ });
+
+ // Order by item identifier
+ rows.sort( function ( a, b ) {
+ var order1 = a.rowNum;
+ var order2 = b.rowNum;
+
+ // If both in the same row, then the row with `full` comes first
+ if (order1 === order2) {
+ var ret = a.full && ! b.full ? -1 : 1;
+
+ return side === 'bottom'
+ ? ret * -1
+ : ret;
+ }
+
+ return order2 - order1;
+ } );
+
+ // Invert for below the table
+ if ( side === 'bottom' ) {
+ rows.reverse();
+ }
+
+ for (var row = 0; row<rows.length; row++) {
+ delete rows[row].rowNum;
+
+ _layoutResolve(settings, rows[row]);
+ }
+
+ return rows;
+ }
+
+
+ /**
+ * Convert the contents of a row's layout object to nodes that can be inserted
+ * into the document by a renderer. Execute functions, look up plug-ins, etc.
+ *
+ * @param {*} settings DataTables settings object
+ * @param {*} row Layout object for this row
+ */
+ function _layoutResolve( settings, row ) {
+ var getFeature = function (feature, opts) {
+ if ( ! _ext.features[ feature ] ) {
+ _fnLog( settings, 0, 'Unknown feature: '+ feature );
+ }
+
+ return _ext.features[ feature ].apply( this, [settings, opts] );
+ };
+
+ var resolve = function ( item ) {
+ if (! row[ item ]) {
+ return;
+ }
+
+ var line = row[ item ].contents;
+
+ for ( var i=0, iLen=line.length ; i<iLen ; i++ ) {
+ if ( ! line[i] ) {
+ continue;
+ }
+ else if ( typeof line[i] === 'string' ) {
+ line[i] = getFeature( line[i], null );
+ }
+ else if ( $.isPlainObject(line[i]) ) {
+ // If it's an object, it just has feature and opts properties from
+ // the transform in _layoutArray
+ line[i] = getFeature(line[i].feature, line[i].opts);
+ }
+ else if ( typeof line[i].node === 'function' ) {
+ line[i] = line[i].node( settings );
+ }
+ else if ( typeof line[i] === 'function' ) {
+ var inst = line[i]( settings );
+
+ line[i] = typeof inst.node === 'function' ?
+ inst.node() :
+ inst;
+ }
+ }
+ };
+
+ resolve('start');
+ resolve('end');
+ resolve('full');
}
/**
* Add the options to the page HTML for the table
- * @param {object} oSettings dataTables settings object
+ * @param {object} settings DataTables settings object
* @memberof DataTable#oApi
*/
- function _fnAddOptionsHtml ( oSettings )
+ function _fnAddOptionsHtml ( settings )
{
- var classes = oSettings.oClasses;
- var table = $(oSettings.nTable);
- var holding = $('<div/>').insertBefore( table ); // Holding element for speed
- var features = oSettings.oFeatures;
-
- // All DataTables are wrapped in a div
- var insert = $('<div/>', {
- id: oSettings.sTableId+'_wrapper',
- 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
- } );
+ var classes = settings.oClasses;
+ var table = $(settings.nTable);
- oSettings.nHolding = holding[0];
- oSettings.nTableWrapper = insert[0];
- oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
+ // Wrapper div around everything DataTables controls
+ var insert = $('<div/>')
+ .attr({
+ id: settings.sTableId+'_wrapper',
+ 'class': classes.container
+ })
+ .insertBefore(table);
- /* Loop over the user set positioning and place the elements as needed */
- var aDom = oSettings.sDom.split('');
- var featureNode, cOption, nNewNode, cNext, sAttr, j;
- for ( var i=0 ; i<aDom.length ; i++ )
- {
+ settings.nTableWrapper = insert[0];
+
+ if (settings.sDom) {
+ // Legacy
+ _fnLayoutDom(settings, settings.sDom, insert);
+ }
+ else {
+ var top = _layoutArray( settings, settings.layout, 'top' );
+ var bottom = _layoutArray( settings, settings.layout, 'bottom' );
+ var renderer = _fnRenderer( settings, 'layout' );
+
+ // Everything above - the renderer will actually insert the contents into the document
+ top.forEach(function (item) {
+ renderer( settings, insert, item );
+ });
+
+ // The table - always the center of attention
+ renderer( settings, insert, {
+ full: {
+ table: true,
+ contents: [ _fnFeatureHtmlTable(settings) ]
+ }
+ } );
+
+ // Everything below
+ bottom.forEach(function (item) {
+ renderer( settings, insert, item );
+ });
+ }
+
+ // Processing floats on top, so it isn't an inserted feature
+ _processingHtml( settings );
+ }
+
+ /**
+ * Draw the table with the legacy DOM property
+ * @param {*} settings DT settings object
+ * @param {*} dom DOM string
+ * @param {*} insert Insert point
+ */
+ function _fnLayoutDom( settings, dom, insert )
+ {
+ var parts = dom.match(/(".*?")|('.*?')|./g);
+ var featureNode, option, newNode, next, attr;
+
+ for ( var i=0 ; i<parts.length ; i++ ) {
featureNode = null;
- cOption = aDom[i];
+ option = parts[i];
- if ( cOption == '<' )
- {
- /* New container div */
- nNewNode = $('<div/>')[0];
+ if ( option == '<' ) {
+ // New container div
+ newNode = $('<div/>');
- /* Check to see if we should append an id and/or a class name to the container */
- cNext = aDom[i+1];
- if ( cNext == "'" || cNext == '"' )
- {
- sAttr = "";
- j = 2;
- while ( aDom[i+j] != cNext )
- {
- sAttr += aDom[i+j];
- j++;
- }
+ // Check to see if we should append an id and/or a class name to the container
+ next = parts[i+1];
- /* Replace jQuery UI constants @todo depreciated */
- if ( sAttr == "H" )
- {
- sAttr = classes.sJUIHeader;
- }
- else if ( sAttr == "F" )
- {
- sAttr = classes.sJUIFooter;
- }
+ if ( next[0] == "'" || next[0] == '"' ) {
+ attr = next.replace(/['"]/g, '');
+
+ var id = '', className;
/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
* breaks the string into parts and applies them as needed
*/
- if ( sAttr.indexOf('.') != -1 )
- {
- var aSplit = sAttr.split('.');
- nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
- nNewNode.className = aSplit[1];
+ if ( attr.indexOf('.') != -1 ) {
+ var split = attr.split('.');
+
+ id = split[0];
+ className = split[1];
}
- else if ( sAttr.charAt(0) == "#" )
- {
- nNewNode.id = sAttr.substr(1, sAttr.length-1);
+ else if ( attr[0] == "#" ) {
+ id = attr;
}
- else
- {
- nNewNode.className = sAttr;
+ else {
+ className = attr;
}
- i += j; /* Move along the position array */
+ newNode
+ .attr('id', id.substring(1))
+ .addClass(className);
+
+ i++; // Move along the position array
}
- insert.append( nNewNode );
- insert = $(nNewNode);
+ insert.append( newNode );
+ insert = newNode;
}
- else if ( cOption == '>' )
- {
- /* End container div */
+ else if ( option == '>' ) {
+ // End container div
insert = insert.parent();
}
- // @todo Move options into their own plugins?
- else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
- {
- /* Length */
- featureNode = _fnFeatureHtmlLength( oSettings );
- }
- else if ( cOption == 'f' && features.bFilter )
- {
- /* Filter */
- featureNode = _fnFeatureHtmlFilter( oSettings );
- }
- else if ( cOption == 'r' && features.bProcessing )
- {
- /* pRocessing */
- featureNode = _fnFeatureHtmlProcessing( oSettings );
- }
- else if ( cOption == 't' )
- {
- /* Table */
- featureNode = _fnFeatureHtmlTable( oSettings );
+ else if ( option == 't' ) {
+ // Table
+ featureNode = _fnFeatureHtmlTable( settings );
}
- else if ( cOption == 'i' && features.bInfo )
- {
- /* Info */
- featureNode = _fnFeatureHtmlInfo( oSettings );
- }
- else if ( cOption == 'p' && features.bPaginate )
- {
- /* Pagination */
- featureNode = _fnFeatureHtmlPaginate( oSettings );
- }
- else if ( DataTable.ext.feature.length !== 0 )
+ else
{
- /* Plug-in features */
- var aoFeatures = DataTable.ext.feature;
- for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
- {
- if ( cOption == aoFeatures[k].cFeature )
- {
- featureNode = aoFeatures[k].fnInit( oSettings );
- break;
+ DataTable.ext.feature.forEach(function(feature) {
+ if ( option == feature.cFeature ) {
+ featureNode = feature.fnInit( settings );
}
- }
+ });
}
- /* Add to the 2D features array */
- if ( featureNode )
- {
- var aanFeatures = oSettings.aanFeatures;
-
- if ( ! aanFeatures[cOption] )
- {
- aanFeatures[cOption] = [];
- }
-
- aanFeatures[cOption].push( featureNode );
+ // Add to the display
+ if ( featureNode ) {
insert.append( featureNode );
}
}
-
- /* Built our DOM structure - replace the holding div with what we want */
- holding.replaceWith( insert );
- oSettings.nHolding = null;
}
/**
* Use the DOM source to create up an array of header cells. The idea here is to
* create a layout grid (array) of rows x columns, which contains a reference
- * to the cell that that point in the grid (regardless of col/rowspan), such that
+ * to the cell at that point in the grid (regardless of col/rowspan), such that
* any column / row could be removed and the new grid constructed
- * @param array {object} aLayout Array to store the calculated layout in
- * @param {node} nThead The header/footer element for the table
+ * @param {node} thead The header/footer element for the table
+ * @returns {array} Calculated layout array
* @memberof DataTable#oApi
*/
- function _fnDetectHeader ( aLayout, nThead )
+ function _fnDetectHeader ( settings, thead, write )
{
- var nTrs = $(nThead).children('tr');
- var nTr, nCell;
- var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
- var bUnique;
- var fnShiftCol = function ( a, i, j ) {
+ var columns = settings.aoColumns;
+ var rows = $(thead).children('tr');
+ var row, cell;
+ var i, k, l, iLen, shifted, column, colspan, rowspan;
+ var titleRow = settings.titleRow;
+ var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
+ var layout = [];
+ var unique;
+ var shift = function ( a, i, j ) {
var k = a[i];
- while ( k[j] ) {
+ while ( k[j] ) {
j++;
}
return j;
};
- aLayout.splice( 0, aLayout.length );
-
- /* We know how many rows there are in the layout - so prep it */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- aLayout.push( [] );
+ // We know how many rows there are in the layout - so prep it
+ for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
+ layout.push( [] );
}
- /* Calculate a layout array */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- nTr = nTrs[i];
- iColumn = 0;
-
- /* For every cell in the row... */
- nCell = nTr.firstChild;
- while ( nCell ) {
- if ( nCell.nodeName.toUpperCase() == "TD" ||
- nCell.nodeName.toUpperCase() == "TH" )
- {
- /* Get the col and rowspan attributes from the DOM and sanitise them */
- iColspan = nCell.getAttribute('colspan') * 1;
- iRowspan = nCell.getAttribute('rowspan') * 1;
- iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
- iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
-
- /* There might be colspan cells already in this row, so shift our target
- * accordingly
- */
- iColShifted = fnShiftCol( aLayout, i, iColumn );
+ for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
+ row = rows[i];
+ column = 0;
- /* Cache calculation for unique columns */
- bUnique = iColspan === 1 ? true : false;
+ // For every cell in the row..
+ cell = row.firstChild;
+ while ( cell ) {
+ if (
+ cell.nodeName.toUpperCase() == 'TD' ||
+ cell.nodeName.toUpperCase() == 'TH'
+ ) {
+ var cols = [];
+ var jqCell = $(cell);
+
+ // Get the col and rowspan attributes from the DOM and sanitise them
+ colspan = cell.getAttribute('colspan') * 1;
+ rowspan = cell.getAttribute('rowspan') * 1;
+ colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan;
+ rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan;
+
+ // There might be colspan cells already in this row, so shift our target
+ // accordingly
+ shifted = shift( layout, i, column );
+
+ // Cache calculation for unique columns
+ unique = colspan === 1 ?
+ true :
+ false;
+
+ // Perform header setup
+ if ( write ) {
+ if (unique) {
+ // Allow column options to be set from HTML attributes
+ _fnColumnOptions( settings, shifted, _fnEscapeObject(jqCell.data()) );
+
+ // Get the width for the column. This can be defined from the
+ // width attribute, style attribute or `columns.width` option
+ var columnDef = columns[shifted];
+ var width = cell.getAttribute('width') || null;
+ var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/);
+ if ( t ) {
+ width = t[1];
+ }
- /* If there is col / rowspan, copy the information into the layout grid */
- for ( l=0 ; l<iColspan ; l++ )
- {
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aLayout[i+k][iColShifted+l] = {
- "cell": nCell,
- "unique": bUnique
- };
- aLayout[i+k].nTr = nTr;
+ columnDef.sWidthOrig = columnDef.sWidth || width;
+
+ if (isHeader) {
+ // Column title handling - can be user set, or read from the DOM
+ // This happens before the render, so the original is still in place
+ if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
+ if (
+ (titleRow === true && i === 0) || // top row
+ (titleRow === false && i === rows.length -1) || // bottom row
+ (titleRow === i) || // specific row
+ (titleRow === null)
+ ) {
+ cell.innerHTML = columnDef.sTitle;
+ }
+ }
+
+ if (! columnDef.sTitle && unique) {
+ columnDef.sTitle = _stripHtml(cell.innerHTML);
+ columnDef.autoTitle = true;
+ }
+ }
+ else {
+ // Footer specific operations
+ if (columnDef.footer) {
+ cell.innerHTML = columnDef.footer;
+ }
+ }
+
+ // Fall back to the aria-label attribute on the table header if no ariaTitle is
+ // provided.
+ if (! columnDef.ariaTitle) {
+ columnDef.ariaTitle = jqCell.attr("aria-label") || columnDef.sTitle;
+ }
+
+ // Column specific class names
+ if ( columnDef.className ) {
+ jqCell.addClass( columnDef.className );
+ }
+ }
+
+ // Wrap the column title so we can write to it in future
+ if ( $('.dt-column-title', cell).length === 0) {
+ $(document.createElement(settings.columnTitleTag))
+ .addClass('dt-column-title')
+ .append(cell.childNodes)
+ .appendTo(cell);
+ }
+
+ if (
+ settings.orderIndicators &&
+ isHeader &&
+ jqCell.filter(':not([data-dt-order=disable])').length !== 0 &&
+ jqCell.parent(':not([data-dt-order=disable])').length !== 0 &&
+ $('.dt-column-order', cell).length === 0
+ ) {
+ $(document.createElement(settings.columnTitleTag))
+ .addClass('dt-column-order')
+ .appendTo(cell);
+ }
+
+ // We need to wrap the elements in the header in another element to use flexbox
+ // layout for those elements
+ var headerFooter = isHeader ? 'header' : 'footer';
+
+ if ( $('div.dt-column-' + headerFooter, cell).length === 0) {
+ $('<div>')
+ .addClass('dt-column-' + headerFooter)
+ .append(cell.childNodes)
+ .appendTo(cell);
}
}
- }
- nCell = nCell.nextSibling;
- }
- }
- }
+ // If there is col / rowspan, copy the information into the layout grid
+ for ( l=0 ; l<colspan ; l++ ) {
+ for ( k=0 ; k<rowspan ; k++ ) {
+ layout[i+k][shifted+l] = {
+ cell: cell,
+ unique: unique
+ };
- /**
- * Get an array of unique th elements, one for each column
- * @param {object} oSettings dataTables settings object
- * @param {node} nHeader automatically detect the layout from this node - optional
- * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
- * @returns array {node} aReturn list of unique th's
- * @memberof DataTable#oApi
- */
- function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
- {
- var aReturn = [];
- if ( !aLayout )
- {
- aLayout = oSettings.aoHeader;
- if ( nHeader )
- {
- aLayout = [];
- _fnDetectHeader( aLayout, nHeader );
- }
- }
+ layout[i+k].row = row;
+ }
- for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
- {
- for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
- {
- if ( aLayout[i][j].unique &&
- (!aReturn[j] || !oSettings.bSortCellsTop) )
- {
- aReturn[j] = aLayout[i][j].cell;
+ cols.push( shifted+l );
+ }
+
+ // Assign an attribute so spanning cells can still be identified
+ // as belonging to a column
+ cell.setAttribute('data-dt-column', _unique(cols).join(','));
}
+
+ cell = cell.nextSibling;
}
}
- return aReturn;
+ return layout;
}
/**
@@ -3886,72 +4195,49 @@
* DataTables - may be augmented by developer callbacks
* @param {function} fn Callback function to run when data is obtained
*/
- function _fnBuildAjax( oSettings, data, fn )
- {
- // Compatibility with 1.9-, allow fnServerData and event to manipulate
- _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
-
- // Convert to object based for 1.10+ if using the old array scheme which can
- // come from server-side processing or serverParams
- if ( data && Array.isArray(data) ) {
- var tmp = {};
- var rbracket = /(.*?)\[\]$/;
-
- $.each( data, function (key, val) {
- var match = val.name.match(rbracket);
-
- if ( match ) {
- // Support for arrays
- var name = match[0];
-
- if ( ! tmp[ name ] ) {
- tmp[ name ] = [];
- }
- tmp[ name ].push( val.value );
- }
- else {
- tmp[val.name] = val.value;
- }
- } );
- data = tmp;
- }
-
+ function _fnBuildAjax(oSettings, data, fn) {
var ajaxData;
var ajax = oSettings.ajax;
var instance = oSettings.oInstance;
- var callback = function ( json ) {
- var status = oSettings.jqXHR
- ? oSettings.jqXHR.status
- : null;
+ var callback = function (json) {
+ var status = oSettings.jqXHR ? oSettings.jqXHR.status : null;
- if ( json === null || (typeof status === 'number' && status == 204 ) ) {
+ if (json === null || (typeof status === 'number' && status == 204)) {
json = {};
- _fnAjaxDataSrc( oSettings, json, [] );
+ _fnAjaxDataSrc(oSettings, json, []);
}
var error = json.error || json.sError;
- if ( error ) {
- _fnLog( oSettings, 0, error );
+ if (error) {
+ _fnLog(oSettings, 0, error);
+ }
+
+ // Microsoft often wrap JSON as a string in another JSON object
+ // Let's handle that automatically
+ if (json.d && typeof json.d === 'string') {
+ try {
+ json = JSON.parse(json.d);
+ } catch (e) {
+ // noop
+ }
}
oSettings.json = json;
- _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
- fn( json );
+ _fnCallbackFire(oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true);
+ fn(json);
};
- if ( $.isPlainObject( ajax ) && ajax.data )
- {
+ if ($.isPlainObject(ajax) && ajax.data) {
ajaxData = ajax.data;
- var newData = typeof ajaxData === 'function' ?
- ajaxData( data, oSettings ) : // fn can manipulate data or return
- ajaxData; // an object object or array to merge
+ var newData =
+ typeof ajaxData === 'function'
+ ? ajaxData(data, oSettings) // fn can manipulate data or return
+ : ajaxData; // an object or array to merge
// If the function returned something, use that alone
- data = typeof ajaxData === 'function' && newData ?
- newData :
- $.extend( true, data, newData );
+ data = typeof ajaxData === 'function' && newData ? newData : $.extend(true, data, newData);
// Remove the data property as we've resolved it already and don't want
// jQuery to do it again (it is restored at the end of the function)
@@ -3959,197 +4245,156 @@
}
var baseAjax = {
- "data": data,
- "success": callback,
- "dataType": "json",
- "cache": false,
- "type": oSettings.sServerMethod,
- "error": function (xhr, error, thrown) {
- var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
-
- if ( $.inArray( true, ret ) === -1 ) {
- if ( error == "parsererror" ) {
- _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
+ url: typeof ajax === 'string' ? ajax : '',
+ data: data,
+ success: callback,
+ dataType: 'json',
+ cache: false,
+ type: oSettings.sServerMethod,
+ error: function (xhr, error) {
+ var ret = _fnCallbackFire(
+ oSettings,
+ null,
+ 'xhr',
+ [oSettings, null, oSettings.jqXHR],
+ true
+ );
+
+ if (ret.indexOf(true) === -1) {
+ if (error == 'parsererror') {
+ _fnLog(oSettings, 0, 'Invalid JSON response', 1);
}
- else if ( xhr.readyState === 4 ) {
- _fnLog( oSettings, 0, 'Ajax error', 7 );
+ else if (xhr.readyState === 4) {
+ _fnLog(oSettings, 0, 'Ajax error', 7);
}
}
- _fnProcessingDisplay( oSettings, false );
+ _fnProcessingDisplay(oSettings, false);
}
};
+ // If `ajax` option is an object, extend and override our default base
+ if ($.isPlainObject(ajax)) {
+ $.extend(baseAjax, ajax);
+ }
+
// Store the data submitted for the API
oSettings.oAjaxData = data;
// Allow plug-ins and external processes to modify the data
- _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
+ _fnCallbackFire(oSettings, null, 'preXhr', [oSettings, data, baseAjax], true);
- if ( oSettings.fnServerData )
- {
- // DataTables 1.9- compatibility
- oSettings.fnServerData.call( instance,
- oSettings.sAjaxSource,
- $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
- return { name: key, value: val };
- } ),
- callback,
- oSettings
- );
- }
- else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
- {
- // DataTables 1.9- compatibility
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
- url: ajax || oSettings.sAjaxSource
- } ) );
+ // Custom Ajax option to submit the parameters as a JSON string
+ if (baseAjax.submitAs === 'json' && typeof data === 'object') {
+ baseAjax.data = JSON.stringify(data);
+
+ if (!baseAjax.contentType) {
+ baseAjax.contentType = 'application/json; charset=utf-8';
+ }
}
- else if ( typeof ajax === 'function' )
- {
+
+ if (typeof ajax === 'function') {
// Is a function - let the caller define what needs to be done
- oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
+ oSettings.jqXHR = ajax.call(instance, data, callback, oSettings);
}
- else
- {
+ else if (ajax.url === '') {
+ // No url, so don't load any data. Just apply an empty data array
+ // to the object for the callback.
+ var empty = {};
+
+ _fnAjaxDataSrc(oSettings, empty, []);
+ callback(empty);
+ }
+ else {
// Object to extend the base settings
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
+ oSettings.jqXHR = $.ajax(baseAjax);
+ }
- // Restore for next time around
+ // Restore for next time around
+ if (ajaxData) {
ajax.data = ajaxData;
}
}
-
/**
* Update the table using an Ajax call
* @param {object} settings dataTables settings object
* @returns {boolean} Block the table drawing or not
* @memberof DataTable#oApi
*/
- function _fnAjaxUpdate( settings )
- {
+ function _fnAjaxUpdate(settings) {
settings.iDraw++;
- _fnProcessingDisplay( settings, true );
+ _fnProcessingDisplay(settings, true);
- _fnBuildAjax(
- settings,
- _fnAjaxParameters( settings ),
- function(json) {
- _fnAjaxUpdateDraw( settings, json );
- }
- );
+ _fnBuildAjax(settings, _fnAjaxParameters(settings), function (json) {
+ _fnAjaxUpdateDraw(settings, json);
+ });
}
-
/**
* Build up the parameters in an object needed for a server-side processing
- * request. Note that this is basically done twice, is different ways - a modern
- * method which is used by default in DataTables 1.10 which uses objects and
- * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
- * the sAjaxSource option is used in the initialisation, or the legacyAjax
- * option is set.
+ * request.
* @param {object} oSettings dataTables settings object
* @returns {bool} block the table drawing or not
* @memberof DataTable#oApi
*/
- function _fnAjaxParameters( settings )
- {
- var
- columns = settings.aoColumns,
- columnCount = columns.length,
+ function _fnAjaxParameters(settings) {
+ var columns = settings.aoColumns,
features = settings.oFeatures,
preSearch = settings.oPreviousSearch,
preColSearch = settings.aoPreSearchCols,
- i, data = [], dataProp, column, columnSearch,
- sort = _fnSortFlatten( settings ),
- displayStart = settings._iDisplayStart,
- displayLength = features.bPaginate !== false ?
- settings._iDisplayLength :
- -1;
-
- var param = function ( name, value ) {
- data.push( { 'name': name, 'value': value } );
- };
+ colData = function (idx, prop) {
+ return typeof columns[idx][prop] === 'function' ? 'function' : columns[idx][prop];
+ };
- // DataTables 1.9- compatible method
- param( 'sEcho', settings.iDraw );
- param( 'iColumns', columnCount );
- param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
- param( 'iDisplayStart', displayStart );
- param( 'iDisplayLength', displayLength );
-
- // DataTables 1.10+ method
- var d = {
- draw: settings.iDraw,
- columns: [],
- order: [],
- start: displayStart,
- length: displayLength,
- search: {
- value: preSearch.sSearch,
- regex: preSearch.bRegex
+ return {
+ draw: settings.iDraw,
+ columns: columns.map(function (column, i) {
+ return {
+ data: colData(i, 'mData'),
+ name: column.sName,
+ searchable: column.bSearchable,
+ orderable: column.bSortable,
+ search: {
+ value: preColSearch[i].search,
+ regex: preColSearch[i].regex,
+ fixed: Object.keys(column.searchFixed)
+ .map(function (name) {
+ return {
+ name: name,
+ term: typeof column.searchFixed[name] !== 'function'
+ ? column.searchFixed[name].toString()
+ : 'function'
+ };
+ })
+ }
+ };
+ }),
+ order: _fnSortFlatten(settings).map(function (val) {
+ return {
+ column: val.col,
+ dir: val.dir,
+ name: colData(val.col, 'sName')
+ };
+ }),
+ start: settings._iDisplayStart,
+ length: features.bPaginate ? settings._iDisplayLength : -1,
+ search: {
+ value: preSearch.search,
+ regex: preSearch.regex,
+ fixed: Object.keys(settings.searchFixed)
+ .map(function (name) {
+ return {
+ name: name,
+ term: typeof settings.searchFixed[name] !== 'function'
+ ? settings.searchFixed[name].toString()
+ : 'function'
+ };
+ })
}
};
-
- for ( i=0 ; i<columnCount ; i++ ) {
- column = columns[i];
- columnSearch = preColSearch[i];
- dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
-
- d.columns.push( {
- data: dataProp,
- name: column.sName,
- searchable: column.bSearchable,
- orderable: column.bSortable,
- search: {
- value: columnSearch.sSearch,
- regex: columnSearch.bRegex
- }
- } );
-
- param( "mDataProp_"+i, dataProp );
-
- if ( features.bFilter ) {
- param( 'sSearch_'+i, columnSearch.sSearch );
- param( 'bRegex_'+i, columnSearch.bRegex );
- param( 'bSearchable_'+i, column.bSearchable );
- }
-
- if ( features.bSort ) {
- param( 'bSortable_'+i, column.bSortable );
- }
- }
-
- if ( features.bFilter ) {
- param( 'sSearch', preSearch.sSearch );
- param( 'bRegex', preSearch.bRegex );
- }
-
- if ( features.bSort ) {
- $.each( sort, function ( i, val ) {
- d.order.push( { column: val.col, dir: val.dir } );
-
- param( 'iSortCol_'+i, val.col );
- param( 'sSortDir_'+i, val.dir );
- } );
-
- param( 'iSortingCols', sort.length );
- }
-
- // If the legacy.ajax parameter is null, then we automatically decide which
- // form to use, based on sAjaxSource
- var legacy = DataTable.ext.legacy.ajax;
- if ( legacy === null ) {
- return settings.sAjaxSource ? data : d;
- }
-
- // Otherwise, if legacy has been specified then we use that to decide on the
- // form
- return legacy ? data : d;
}
-
/**
* Data the data from the server (nuking the old) and redraw the table
* @param {object} oSettings dataTables settings object
@@ -4161,237 +4406,171 @@
* @param {string} [json.sColumns] Column ordering (sName, comma separated)
* @memberof DataTable#oApi
*/
- function _fnAjaxUpdateDraw ( settings, json )
- {
- // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
- // Support both
- var compat = function ( old, modern ) {
- return json[old] !== undefined ? json[old] : json[modern];
- };
-
- var data = _fnAjaxDataSrc( settings, json );
- var draw = compat( 'sEcho', 'draw' );
- var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
- var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
+ function _fnAjaxUpdateDraw(settings, json) {
+ var data = _fnAjaxDataSrc(settings, json);
+ var draw = _fnAjaxDataSrcParam(settings, 'draw', json);
+ var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json);
+ var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json);
- if ( draw !== undefined ) {
+ if (draw !== undefined) {
// Protect against out of sequence returns
- if ( draw*1 < settings.iDraw ) {
+ if (draw * 1 < settings.iDraw) {
return;
}
settings.iDraw = draw * 1;
}
// No data in returned object, so rather than an array, we show an empty table
- if ( ! data ) {
+ if (!data) {
data = [];
}
- _fnClearTable( settings );
- settings._iRecordsTotal = parseInt(recordsTotal, 10);
+ _fnClearTable(settings);
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- _fnAddData( settings, data[i] );
+ for (var i = 0, iLen = data.length; i < iLen; i++) {
+ _fnAddData(settings, data[i]);
}
settings.aiDisplay = settings.aiDisplayMaster.slice();
- _fnDraw( settings, true );
-
- if ( ! settings._bInitComplete ) {
- _fnInitComplete( settings, json );
- }
-
- _fnProcessingDisplay( settings, false );
+ _fnColumnTypes(settings);
+ _fnDraw(settings, true);
+ _fnInitComplete(settings);
+ _fnProcessingDisplay(settings, false);
}
-
/**
* Get the data from the JSON data source to use for drawing a table. Using
* `_fnGetObjectDataFn` allows the data to be sourced from a property of the
* source object, or from a processing function.
- * @param {object} oSettings dataTables settings object
+ * @param {object} settings dataTables settings object
* @param {object} json Data source object / array from the server
* @return {array} Array of data to use
*/
- function _fnAjaxDataSrc ( oSettings, json, write )
- {
- var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
- oSettings.ajax.dataSrc :
- oSettings.sAjaxDataProp; // Compatibility with 1.9-.
+ function _fnAjaxDataSrc(settings, json, write) {
+ var dataProp = 'data';
+
+ if ($.isPlainObject(settings.ajax) && settings.ajax.dataSrc !== undefined) {
+ // Could in inside a `dataSrc` object, or not!
+ var dataSrc = settings.ajax.dataSrc;
- if ( ! write ) {
- if ( dataSrc === 'data' ) {
+ // string, function and object are valid types
+ if (typeof dataSrc === 'string' || typeof dataSrc === 'function') {
+ dataProp = dataSrc;
+ }
+ else if (dataSrc.data !== undefined) {
+ dataProp = dataSrc.data;
+ }
+ }
+
+ if (!write) {
+ if (dataProp === 'data') {
// If the default, then we still want to support the old style, and safely ignore
// it if possible
- return json.aaData || json[dataSrc];
+ return json.aaData || json[dataProp];
}
- return dataSrc !== "" ?
- _fnGetObjectDataFn( dataSrc )( json ) :
- json;
+ return dataProp !== '' ? _fnGetObjectDataFn(dataProp)(json) : json;
}
// set
- _fnSetObjectDataFn( dataSrc )( json, write );
+ _fnSetObjectDataFn(dataProp)(json, write);
}
/**
- * Generate the node required for filtering text
- * @returns {node} Filter control element
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
+ * Very similar to _fnAjaxDataSrc, but for the other SSP properties
+ * @param {*} settings DataTables settings object
+ * @param {*} param Target parameter
+ * @param {*} json JSON data
+ * @returns Resolved value
*/
- function _fnFeatureHtmlFilter ( settings )
- {
- var classes = settings.oClasses;
- var tableId = settings.sTableId;
- var language = settings.oLanguage;
- var previousSearch = settings.oPreviousSearch;
- var features = settings.aanFeatures;
- var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
-
- var str = language.sSearch;
- str = str.match(/_INPUT_/) ?
- str.replace('_INPUT_', input) :
- str+input;
-
- var filter = $('<div/>', {
- 'id': ! features.f ? tableId+'_filter' : null,
- 'class': classes.sFilter
- } )
- .append( $('<label/>' ).append( str ) );
+ function _fnAjaxDataSrcParam(settings, param, json) {
+ var dataSrc = $.isPlainObject(settings.ajax) ? settings.ajax.dataSrc : null;
- var searchFn = function(event) {
- /* Update all other filter input elements for the new display */
- var n = features.f;
- var val = !this.value ? "" : this.value; // mental IE8 fix :-(
- if(previousSearch.return && event.key !== "Enter") {
- return;
- }
- /* Now do the filter */
- if ( val != previousSearch.sSearch ) {
- _fnFilterComplete( settings, {
- "sSearch": val,
- "bRegex": previousSearch.bRegex,
- "bSmart": previousSearch.bSmart ,
- "bCaseInsensitive": previousSearch.bCaseInsensitive,
- "return": previousSearch.return
- } );
-
- // Need to redraw, without resorting
- settings._iDisplayStart = 0;
- _fnDraw( settings );
- }
- };
+ if (dataSrc && dataSrc[param]) {
+ // Get from custom location
+ return _fnGetObjectDataFn(dataSrc[param])(json);
+ }
- var searchDelay = settings.searchDelay !== null ?
- settings.searchDelay :
- _fnDataSource( settings ) === 'ssp' ?
- 400 :
- 0;
+ // else - Default behaviour
+ var old = '';
- var jqFilter = $('input', filter)
- .val( previousSearch.sSearch )
- .attr( 'placeholder', language.sSearchPlaceholder )
- .on(
- 'keyup.DT search.DT input.DT paste.DT cut.DT',
- searchDelay ?
- _fnThrottle( searchFn, searchDelay ) :
- searchFn
- )
- .on( 'mouseup', function(e) {
- // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
- // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
- // checks the value to see if it has changed. In other browsers it won't have.
- setTimeout( function () {
- searchFn.call(jqFilter[0], e);
- }, 10);
- } )
- .on( 'keypress.DT', function(e) {
- /* Prevent form submission */
- if ( e.keyCode == 13 ) {
- return false;
- }
- } )
- .attr('aria-controls', tableId);
-
- // Update the input elements whenever the table is filtered
- $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
- if ( settings === s ) {
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame...
- try {
- if ( jqFilter[0] !== document.activeElement ) {
- jqFilter.val( previousSearch.sSearch );
- }
- }
- catch ( e ) {}
- }
- } );
+ // Legacy support
+ if (param === 'draw') {
+ old = 'sEcho';
+ }
+ else if (param === 'recordsTotal') {
+ old = 'iTotalRecords';
+ }
+ else if (param === 'recordsFiltered') {
+ old = 'iTotalDisplayRecords';
+ }
- return filter[0];
+ return json[old] !== undefined ? json[old] : json[param];
}
/**
* Filter the table using both the global filter and column based filtering
- * @param {object} oSettings dataTables settings object
- * @param {object} oSearch search information
- * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
+ * @param {object} settings dataTables settings object
+ * @param {object} input search information
* @memberof DataTable#oApi
*/
- function _fnFilterComplete ( oSettings, oInput, iForce )
+ function _fnFilterComplete ( settings, input )
{
- var oPrevSearch = oSettings.oPreviousSearch;
- var aoPrevSearch = oSettings.aoPreSearchCols;
- var fnSaveFilter = function ( oFilter ) {
- /* Save the filtering values */
- oPrevSearch.sSearch = oFilter.sSearch;
- oPrevSearch.bRegex = oFilter.bRegex;
- oPrevSearch.bSmart = oFilter.bSmart;
- oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
- oPrevSearch.return = oFilter.return;
- };
- var fnRegex = function ( o ) {
- // Backwards compatibility with the bEscapeRegex option
- return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
- };
-
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo As per sort - can this be moved into an event handler?
- _fnColumnTypes( oSettings );
+ var columnsSearch = settings.aoPreSearchCols;
- /* In server-side processing all filtering is done by the server, so no point hanging around here */
- if ( _fnDataSource( oSettings ) != 'ssp' )
+ // In server-side processing all filtering is done by the server, so no point hanging around here
+ if ( _fnDataSource( settings ) != 'ssp' )
{
- /* Global filter */
- _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive, oInput.return );
- fnSaveFilter( oInput );
+ // Check if any of the rows were invalidated
+ _fnFilterData( settings );
- /* Now do the individual column filter */
- for ( var i=0 ; i<aoPrevSearch.length ; i++ )
+ // Start from the full data set
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+
+ // Global filter first
+ _fnFilter( settings.aiDisplay, settings, input.search, input );
+
+ $.each(settings.searchFixed, function (name, term) {
+ _fnFilter(settings.aiDisplay, settings, term, {});
+ });
+
+ // Then individual column filters
+ for ( var i=0 ; i<columnsSearch.length ; i++ )
{
- _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
- aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
+ var col = columnsSearch[i];
+
+ _fnFilter(
+ settings.aiDisplay,
+ settings,
+ col.search,
+ col,
+ i
+ );
+
+ $.each(settings.aoColumns[i].searchFixed, function (name, term) {
+ _fnFilter(settings.aiDisplay, settings, term, {}, i);
+ });
}
- /* Custom filtering */
- _fnFilterCustom( oSettings );
- }
- else
- {
- fnSaveFilter( oInput );
+ // And finally global filtering
+ _fnFilterCustom( settings );
}
- /* Tell the draw function we have been filtering */
- oSettings.bFiltered = true;
- _fnCallbackFire( oSettings, null, 'search', [oSettings] );
+ // Tell the draw function we have been filtering
+ settings.bFiltered = true;
+
+ _fnCallbackFire( settings, null, 'search', [settings] );
}
/**
* Apply custom filtering functions
+ *
+ * This is legacy now that we have named functions, but it is widely used
+ * from 1.x, so it is not yet deprecated.
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
*/
@@ -4401,7 +4580,7 @@
var displayRows = settings.aiDisplay;
var row, rowIdx;
- for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=filters.length ; i<iLen ; i++ ) {
var rows = [];
// Loop over each row and see if it should be included
@@ -4417,97 +4596,49 @@
// So the array reference doesn't break set the results into the
// existing array
displayRows.length = 0;
- $.merge( displayRows, rows );
+ _fnArrayApply(displayRows, rows);
}
}
/**
- * Filter the table on a per-column basis
- * @param {object} oSettings dataTables settings object
- * @param {string} sInput string to filter on
- * @param {int} iColumn column to filter
- * @param {bool} bRegex treat search string as a regular expression or not
- * @param {bool} bSmart use smart filtering or not
- * @param {bool} bCaseInsensitive Do case insensitive matching or not
- * @memberof DataTable#oApi
+ * Filter the data table based on user input and draw the table
*/
- function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
+ function _fnFilter( searchRows, settings, input, options, column )
{
- if ( searchStr === '' ) {
+ if ( input === '' ) {
return;
}
- var data;
- var out = [];
- var display = settings.aiDisplay;
- var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
+ var i = 0;
+ var matched = [];
- for ( var i=0 ; i<display.length ; i++ ) {
- data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
+ // Search term can be a function, regex or string - if a string we apply our
+ // smart filtering regex (assuming the options require that)
+ var searchFunc = typeof input === 'function' ? input : null;
+ var rpSearch = input instanceof RegExp
+ ? input
+ : searchFunc
+ ? null
+ : _fnFilterCreateSearch( input, options );
- if ( rpSearch.test( data ) ) {
- out.push( display[i] );
- }
- }
-
- settings.aiDisplay = out;
- }
-
-
- /**
- * Filter the data table based on user input and draw the table
- * @param {object} settings dataTables settings object
- * @param {string} input string to filter on
- * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
- * @param {bool} regex treat as a regular expression or not
- * @param {bool} smart perform smart filtering or not
- * @param {bool} caseInsensitive Do case insensitive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
- {
- var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
- var prevSearch = settings.oPreviousSearch.sSearch;
- var displayMaster = settings.aiDisplayMaster;
- var display, invalidated, i;
- var filtered = [];
+ // Then for each row, does the test pass. If not, lop the row from the array
+ for (i=0 ; i<searchRows.length ; i++) {
+ var row = settings.aoData[ searchRows[i] ];
+ var data = column === undefined
+ ? row._sFilterRow
+ : row._aFilterData[ column ];
- // Need to take account of custom filtering functions - always filter
- if ( DataTable.ext.search.length !== 0 ) {
- force = true;
- }
-
- // Check if any of the rows were invalidated
- invalidated = _fnFilterData( settings );
-
- // If the input is blank - we just want the full data set
- if ( input.length <= 0 ) {
- settings.aiDisplay = displayMaster.slice();
- }
- else {
- // New search - start from the master array
- if ( invalidated ||
- force ||
- regex ||
- prevSearch.length > input.length ||
- input.indexOf(prevSearch) !== 0 ||
- settings.bSorted // On resort, the display master needs to be
- // re-filtered since indexes will have changed
- ) {
- settings.aiDisplay = displayMaster.slice();
+ if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) {
+ matched.push(searchRows[i]);
}
+ }
- // Search the display array
- display = settings.aiDisplay;
+ // Mutate the searchRows array
+ searchRows.length = matched.length;
- for ( i=0 ; i<display.length ; i++ ) {
- if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
- filtered.push( display[i] );
- }
- }
-
- settings.aiDisplay = filtered;
+ for (i=0 ; i<matched.length ; i++) {
+ searchRows[i] = matched[i];
}
}
@@ -4517,37 +4648,96 @@
* @param {string} sSearch string to search for
* @param {bool} bRegex treat as a regular expression or not
* @param {bool} bSmart perform smart filtering or not
- * @param {bool} bCaseInsensitive Do case insensitive matching or not
+ * @param {bool} bCaseInsensitive Do case-insensitive matching or not
* @returns {RegExp} constructed object
* @memberof DataTable#oApi
*/
- function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
+ function _fnFilterCreateSearch( search, inOpts )
{
- search = regex ?
+ var not = [];
+ var options = $.extend({}, {
+ boundary: false,
+ caseInsensitive: true,
+ exact: false,
+ regex: false,
+ smart: true
+ }, inOpts);
+
+ if (typeof search !== 'string') {
+ search = search.toString();
+ }
+
+ // Remove diacritics if normalize is set up to do so
+ search = _normalize(search);
+
+ if (options.exact) {
+ return new RegExp(
+ '^'+_fnEscapeRegex(search)+'$',
+ options.caseInsensitive ? 'i' : ''
+ );
+ }
+
+ search = options.regex ?
search :
_fnEscapeRegex( search );
- if ( smart ) {
+ if ( options.smart ) {
/* For smart filtering we want to allow the search to work regardless of
* word order. We also want double quoted text to be preserved, so word
- * order is important - a la google. So this is what we want to
- * generate:
+ * order is important - a la google. And a negative look around for
+ * finding rows which don't contain a given string.
+ *
+ * So this is the sort of thing we want to generate:
*
* ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
*/
- var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
+ var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''];
+ var a = parts.map( function ( word ) {
+ var negative = false;
+ var m;
+
+ // Determine if it is a "does not include"
+ if ( word.charAt(0) === '!' ) {
+ negative = true;
+ word = word.substring(1);
+ }
+
+ // Strip the quotes from around matched phrases
if ( word.charAt(0) === '"' ) {
- var m = word.match( /^"(.*)"$/ );
+ m = word.match( /^"(.*)"$/ );
word = m ? m[1] : word;
}
+ else if ( word.charAt(0) === '\u201C' ) {
+ // Smart quote match (iPhone users)
+ m = word.match( /^\u201C(.*)\u201D$/ );
+ word = m ? m[1] : word;
+ }
+
+ // For our "not" case, we need to modify the string that is
+ // allowed to match at the end of the expression.
+ if (negative) {
+ if (word.length > 1) {
+ not.push('(?!'+word+')');
+ }
+
+ word = '';
+ }
- return word.replace('"', '');
+ return word.replace(/"/g, '');
} );
- search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
+ var match = not.length
+ ? not.join('')
+ : '';
+
+ var boundary = options.boundary
+ ? '\\b'
+ : '';
+
+ search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$';
}
- return new RegExp( search, caseInsensitive ? 'i' : '' );
+ return new RegExp( search, options.caseInsensitive ? 'i' : '' );
}
@@ -4566,12 +4756,17 @@
function _fnFilterData ( settings )
{
var columns = settings.aoColumns;
+ var data = settings.aoData;
var column;
- var i, j, ien, jen, filterData, cellData, row;
+ var j, jen, filterData, cellData, row;
var wasInvalidated = false;
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
+ for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
+ if (! data[rowIdx]) {
+ continue;
+ }
+
+ row = data[rowIdx];
if ( ! row._aFilterData ) {
filterData = [];
@@ -4580,10 +4775,9 @@
column = columns[j];
if ( column.bSearchable ) {
- cellData = _fnGetCellData( settings, i, j, 'filter' );
+ cellData = _fnGetCellData( settings, rowIdx, j, 'filter' );
- // Search in DataTables 1.10 is string based. In 1.11 this
- // should be altered to also allow strict type checking.
+ // Search in DataTables is string based
if ( cellData === null ) {
cellData = '';
}
@@ -4599,7 +4793,7 @@
// If it looks like there is an HTML entity in the string,
// attempt to decode it so sorting works as expected. Note that
// we could use a single line of jQuery to do this, but the DOM
- // method used here is much faster http://jsperf.com/html-decode
+ // method used here is much faster https://jsperf.com/html-decode
if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
__filter_div.innerHTML = cellData;
cellData = __filter_div_textContent ?
@@ -4625,244 +4819,127 @@
/**
- * Convert from the internal Hungarian notation to camelCase for external
- * interaction
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToCamel ( obj )
- {
- return {
- search: obj.sSearch,
- smart: obj.bSmart,
- regex: obj.bRegex,
- caseInsensitive: obj.bCaseInsensitive
- };
- }
-
-
-
- /**
- * Convert from camelCase notation to the internal Hungarian. We could use the
- * Hungarian convert function here, but this is cleaner
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToHung ( obj )
- {
- return {
- sSearch: obj.search,
- bSmart: obj.smart,
- bRegex: obj.regex,
- bCaseInsensitive: obj.caseInsensitive
- };
- }
-
- /**
- * Generate the node required for the info display
- * @param {object} oSettings dataTables settings object
- * @returns {node} Information element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlInfo ( settings )
- {
- var
- tid = settings.sTableId,
- nodes = settings.aanFeatures.i,
- n = $('<div/>', {
- 'class': settings.oClasses.sInfo,
- 'id': ! nodes ? tid+'_info' : null
- } );
-
- if ( ! nodes ) {
- // Update display on each draw
- settings.aoDrawCallback.push( {
- "fn": _fnUpdateInfo,
- "sName": "information"
- } );
-
- n
- .attr( 'role', 'status' )
- .attr( 'aria-live', 'polite' );
-
- // Table is described by our info div
- $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
- }
-
- return n[0];
- }
-
-
- /**
- * Update the information elements in the display
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnUpdateInfo ( settings )
- {
- /* Show information about the table */
- var nodes = settings.aanFeatures.i;
- if ( nodes.length === 0 ) {
- return;
- }
-
- var
- lang = settings.oLanguage,
- start = settings._iDisplayStart+1,
- end = settings.fnDisplayEnd(),
- max = settings.fnRecordsTotal(),
- total = settings.fnRecordsDisplay(),
- out = total ?
- lang.sInfo :
- lang.sInfoEmpty;
-
- if ( total !== max ) {
- /* Record set after filtering */
- out += ' ' + lang.sInfoFiltered;
- }
-
- // Convert the macros
- out += lang.sInfoPostFix;
- out = _fnInfoMacros( settings, out );
-
- var callback = lang.fnInfoCallback;
- if ( callback !== null ) {
- out = callback.call( settings.oInstance,
- settings, start, end, max, total, out
- );
- }
-
- $(nodes).html( out );
- }
-
-
- function _fnInfoMacros ( settings, str )
- {
- // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
- // internally
- var
- formatter = settings.fnFormatNumber,
- start = settings._iDisplayStart+1,
- len = settings._iDisplayLength,
- vis = settings.fnRecordsDisplay(),
- all = len === -1;
-
- return str.
- replace(/_START_/g, formatter.call( settings, start ) ).
- replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
- replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
- replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
- replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
- replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
- }
-
-
-
- /**
* Draw the table for the first time, adding all required features
* @param {object} settings dataTables settings object
* @memberof DataTable#oApi
*/
function _fnInitialise ( settings )
{
- var i, iLen, iAjaxStart=settings.iInitDisplayStart;
- var columns = settings.aoColumns, column;
- var features = settings.oFeatures;
- var deferLoading = settings.bDeferLoading; // value modified by the draw
+ var i;
+ var init = settings.oInit;
+ var deferLoading = settings.deferLoading;
+ var dataSrc = _fnDataSource( settings );
- /* Ensure that the table data is fully initialised */
+ // Ensure that the table data is fully initialised
if ( ! settings.bInitialised ) {
setTimeout( function(){ _fnInitialise( settings ); }, 200 );
return;
}
- /* Show the display HTML options */
- _fnAddOptionsHtml( settings );
+ // Build the header / footer for the table
+ _fnBuildHead( settings, 'header' );
+ _fnBuildHead( settings, 'footer' );
- /* Build and draw the header / footer for the table */
- _fnBuildHead( settings );
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
+ // Load the table's state (if needed) and then render around it and draw
+ _fnLoadState( settings, init, function () {
+ // Then draw the header / footer
+ _fnDrawHead( settings, settings.aoHeader );
+ _fnDrawHead( settings, settings.aoFooter );
- /* Okay to show that something is going on now */
- _fnProcessingDisplay( settings, true );
+ // Cache the paging start point, as the first redraw will reset it
+ var iAjaxStart = settings.iInitDisplayStart
- /* Calculate sizes for columns */
- if ( features.bAutoWidth ) {
- _fnCalculateColumnWidths( settings );
- }
+ // Local data load
+ // Check if there is data passing into the constructor
+ if ( init.aaData ) {
+ for ( i=0 ; i<init.aaData.length ; i++ ) {
+ _fnAddData( settings, init.aaData[ i ] );
+ }
+ }
+ else if ( deferLoading || dataSrc == 'dom' ) {
+ // Grab the data from the page
+ _fnAddTr( settings, $(settings.nTBody).children('tr') );
+ }
- for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
- column = columns[i];
+ // Filter not yet applied - copy the display master
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
- if ( column.sWidth ) {
- column.nTh.style.width = _fnStringToCss( column.sWidth );
- }
- }
+ // Enable features
+ _fnAddOptionsHtml( settings );
+ _fnSortInit( settings );
- _fnCallbackFire( settings, null, 'preInit', [settings] );
+ _colGroup( settings );
- // If there is default sorting required - let's do it. The sort function
- // will do the drawing for us. Otherwise we draw the table regardless of the
- // Ajax source - this allows the table to look initialised for Ajax sourcing
- // data (show 'loading' message possibly)
- _fnReDraw( settings );
+ /* Okay to show that something is going on now */
+ _fnProcessingDisplay( settings, true );
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
- var dataSrc = _fnDataSource( settings );
- if ( dataSrc != 'ssp' || deferLoading ) {
- // if there is an ajax source load the data
- if ( dataSrc == 'ajax' ) {
- _fnBuildAjax( settings, [], function(json) {
- var aData = _fnAjaxDataSrc( settings, json );
-
- // Got the data - add it to the table
- for ( i=0 ; i<aData.length ; i++ ) {
- _fnAddData( settings, aData[i] );
- }
+ _fnCallbackFire( settings, null, 'preInit', [settings], true );
+
+ // If there is default sorting required - let's do it. The sort function
+ // will do the drawing for us. Otherwise we draw the table regardless of the
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
+ // data (show 'loading' message possibly)
+ _fnReDraw( settings );
+
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
+ if ( dataSrc != 'ssp' || deferLoading ) {
+ // if there is an ajax source load the data
+ if ( dataSrc == 'ajax' ) {
+ _fnBuildAjax( settings, {}, function(json) {
+ var aData = _fnAjaxDataSrc( settings, json );
- // Reset the init display for cookie saving. We've already done
- // a filter, and therefore cleared it before. So we need to make
- // it appear 'fresh'
- settings.iInitDisplayStart = iAjaxStart;
+ // Got the data - add it to the table
+ for ( i=0 ; i<aData.length ; i++ ) {
+ _fnAddData( settings, aData[i] );
+ }
- _fnReDraw( settings );
+ // Reset the init display for cookie saving. We've already done
+ // a filter, and therefore cleared it before. So we need to make
+ // it appear 'fresh'
+ settings.iInitDisplayStart = iAjaxStart;
+ _fnReDraw( settings );
+ _fnProcessingDisplay( settings, false );
+ _fnInitComplete( settings );
+ }, settings );
+ }
+ else {
+ _fnInitComplete( settings );
_fnProcessingDisplay( settings, false );
- _fnInitComplete( settings, json );
- }, settings );
- }
- else {
- _fnProcessingDisplay( settings, false );
- _fnInitComplete( settings );
+ }
}
- }
+ } );
}
/**
* Draw the table for the first time, adding all required features
- * @param {object} oSettings dataTables settings object
- * @param {object} [json] JSON from the server that completed the table, if using Ajax source
- * with client-side processing (optional)
+ * @param {object} settings dataTables settings object
* @memberof DataTable#oApi
*/
- function _fnInitComplete ( settings, json )
+ function _fnInitComplete ( settings )
{
- settings._bInitComplete = true;
+ if (settings._bInitComplete) {
+ return;
+ }
- // When data was added after the initialisation (data or Ajax) we need to
- // calculate the column sizing
- if ( json || settings.oInit.aaData ) {
- _fnAdjustColumnSizing( settings );
+ var args = [settings, settings.json];
+
+ // If the footer element is empty after initialisation, then remove it
+ let tfoot = $(settings.tfoot);
+ if (tfoot.children().length === 0) {
+ tfoot.remove();
}
- _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
- _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
- }
+ settings._bInitComplete = true;
+ // Table is fully set up and we have data, so calculate the
+ // column widths
+ _fnAdjustColumnSizing( settings );
+
+ _fnCallbackFire( settings, null, 'plugin-init', args, true );
+ _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true );
+ }
function _fnLengthChange ( settings, val )
{
@@ -4875,131 +4952,6 @@
_fnCallbackFire( settings, null, 'length', [settings, len] );
}
-
- /**
- * Generate the node required for user display length changing
- * @param {object} settings dataTables settings object
- * @returns {node} Display length feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlLength ( settings )
- {
- var
- classes = settings.oClasses,
- tableId = settings.sTableId,
- menu = settings.aLengthMenu,
- d2 = Array.isArray( menu[0] ),
- lengths = d2 ? menu[0] : menu,
- language = d2 ? menu[1] : menu;
-
- var select = $('<select/>', {
- 'name': tableId+'_length',
- 'aria-controls': tableId,
- 'class': classes.sLengthSelect
- } );
-
- for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
- select[0][ i ] = new Option(
- typeof language[i] === 'number' ?
- settings.fnFormatNumber( language[i] ) :
- language[i],
- lengths[i]
- );
- }
-
- var div = $('<div><label/></div>').addClass( classes.sLength );
- if ( ! settings.aanFeatures.l ) {
- div[0].id = tableId+'_length';
- }
-
- div.children().append(
- settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
- );
-
- // Can't use `select` variable as user might provide their own and the
- // reference is broken by the use of outerHTML
- $('select', div)
- .val( settings._iDisplayLength )
- .on( 'change.DT', function(e) {
- _fnLengthChange( settings, $(this).val() );
- _fnDraw( settings );
- } );
-
- // Update node value whenever anything changes the table's length
- $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
- if ( settings === s ) {
- $('select', div).val( len );
- }
- } );
-
- return div[0];
- }
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Note that most of the paging logic is done in
- * DataTable.ext.pager
- */
-
- /**
- * Generate the node required for default pagination
- * @param {object} oSettings dataTables settings object
- * @returns {node} Pagination feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlPaginate ( settings )
- {
- var
- type = settings.sPaginationType,
- plugin = DataTable.ext.pager[ type ],
- modern = typeof plugin === 'function',
- redraw = function( settings ) {
- _fnDraw( settings );
- },
- node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
- features = settings.aanFeatures;
-
- if ( ! modern ) {
- plugin.fnInit( settings, node, redraw );
- }
-
- /* Add a draw callback for the pagination on first instance, to update the paging display */
- if ( ! features.p )
- {
- node.id = settings.sTableId+'_paginate';
-
- settings.aoDrawCallback.push( {
- "fn": function( settings ) {
- if ( modern ) {
- var
- start = settings._iDisplayStart,
- len = settings._iDisplayLength,
- visRecords = settings.fnRecordsDisplay(),
- all = len === -1,
- page = all ? 0 : Math.ceil( start / len ),
- pages = all ? 1 : Math.ceil( visRecords / len ),
- buttons = plugin(page, pages),
- i, ien;
-
- for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
- _fnRenderer( settings, 'pageButton' )(
- settings, features.p[i], i, buttons, page, pages
- );
- }
- }
- else {
- plugin.fnUpdate( settings, redraw );
- }
- },
- "sName": "pagination"
- } );
- }
-
- return node;
- }
-
-
/**
* Alter the display settings to change the page
* @param {object} settings DataTables settings object
@@ -5041,7 +4993,7 @@
if ( start < 0 )
{
- start = 0;
+ start = 0;
}
}
else if ( action == "next" )
@@ -5055,6 +5007,10 @@
{
start = Math.floor( (records-1) / len) * len;
}
+ else if ( action === 'ellipsis' )
+ {
+ return;
+ }
else
{
_fnLog( settings, 0, "Unknown paging action: "+action, 5 );
@@ -5063,52 +5019,88 @@
var changed = settings._iDisplayStart !== start;
settings._iDisplayStart = start;
- if ( changed ) {
- _fnCallbackFire( settings, null, 'page', [settings] );
+ _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] );
- if ( redraw ) {
- _fnDraw( settings );
- }
+ if ( changed && redraw ) {
+ _fnDraw( settings );
}
return changed;
}
-
/**
* Generate the node required for the processing node
- * @param {object} settings dataTables settings object
- * @returns {node} Processing element
- * @memberof DataTable#oApi
+ * @param {object} settings DataTables settings object
*/
- function _fnFeatureHtmlProcessing ( settings )
+ function _processingHtml ( settings )
{
- return $('<div/>', {
- 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
- 'class': settings.oClasses.sProcessing
- } )
- .html( settings.oLanguage.sProcessing )
- .insertBefore( settings.nTable )[0];
+ var table = settings.nTable;
+ var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== '';
+
+ if ( settings.oFeatures.bProcessing ) {
+ var n = $('<div/>', {
+ 'id': settings.sTableId + '_processing',
+ 'class': settings.oClasses.processing.container,
+ 'role': 'status'
+ } )
+ .html( settings.oLanguage.sProcessing )
+ .append('<div><div></div><div></div><div></div><div></div></div>');
+
+ // Different positioning depending on if scrolling is enabled or not
+ if (scrolling) {
+ n.prependTo( $('div.dt-scroll', settings.nTableWrapper) );
+ }
+ else {
+ n.insertBefore( table );
+ }
+
+ $(table).on( 'processing.dt.DT', function (e, s, show) {
+ n.css( 'display', show ? 'block' : 'none' );
+ } );
+ }
}
/**
* Display or hide the processing indicator
- * @param {object} settings dataTables settings object
+ * @param {object} settings DataTables settings object
* @param {bool} show Show the processing indicator (true) or not (false)
- * @memberof DataTable#oApi
*/
function _fnProcessingDisplay ( settings, show )
{
- if ( settings.oFeatures.bProcessing ) {
- $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
+ // Ignore cases when we are still redrawing
+ if (settings.bDrawing && show === false) {
+ return;
}
_fnCallbackFire( settings, null, 'processing', [settings, show] );
}
/**
+ * Show the processing element if an action takes longer than a given time
+ *
+ * @param {*} settings DataTables settings object
+ * @param {*} enable Do (true) or not (false) async processing (local feature enablement)
+ * @param {*} run Function to run
+ */
+ function _fnProcessingRun( settings, enable, run ) {
+ if (! enable) {
+ // Immediate execution, synchronous
+ run();
+ }
+ else {
+ _fnProcessingDisplay(settings, true);
+
+ // Allow the processing display to show if needed
+ setTimeout(function () {
+ run();
+
+ _fnProcessingDisplay(settings, false);
+ }, 0);
+ }
+ }
+ /**
* Add any control elements for the table - specifically scrolling
* @param {object} settings dataTables settings object
* @returns {node} Node to add to the DOM
@@ -5127,9 +5119,9 @@
var scrollX = scroll.sX;
var scrollY = scroll.sY;
- var classes = settings.oClasses;
- var caption = table.children('caption');
- var captionSide = caption.length ? caption[0]._captionSide : null;
+ var classes = settings.oClasses.scrolling;
+ var caption = settings.captionNode;
+ var captionSide = caption ? caption._captionSide : null;
var headerClone = $( table[0].cloneNode(false) );
var footerClone = $( table[0].cloneNode(false) );
var footer = table.children('tfoot');
@@ -5158,9 +5150,9 @@
* table - scroll foot table
* tfoot - tfoot
*/
- var scroller = $( _div, { 'class': classes.sScrollWrapper } )
+ var scroller = $( _div, { 'class': classes.container } )
.append(
- $(_div, { 'class': classes.sScrollHead } )
+ $(_div, { 'class': classes.header.self } )
.css( {
overflow: 'hidden',
position: 'relative',
@@ -5168,7 +5160,7 @@
width: scrollX ? size(scrollX) : '100%'
} )
.append(
- $(_div, { 'class': classes.sScrollHeadInner } )
+ $(_div, { 'class': classes.header.inner } )
.css( {
'box-sizing': 'content-box',
width: scroll.sXInner || '100%'
@@ -5185,7 +5177,7 @@
)
)
.append(
- $(_div, { 'class': classes.sScrollBody } )
+ $(_div, { 'class': classes.body } )
.css( {
position: 'relative',
overflow: 'auto',
@@ -5196,14 +5188,14 @@
if ( footer ) {
scroller.append(
- $(_div, { 'class': classes.sScrollFoot } )
+ $(_div, { 'class': classes.footer.self } )
.css( {
overflow: 'hidden',
border: 0,
width: scrollX ? size(scrollX) : '100%'
} )
.append(
- $(_div, { 'class': classes.sScrollFootInner } )
+ $(_div, { 'class': classes.footer.inner } )
.append(
footerClone
.removeAttr('id')
@@ -5223,17 +5215,26 @@
var scrollFoot = footer ? children[2] : null;
// When the body is scrolled, then we also want to scroll the headers
- if ( scrollX ) {
- $(scrollBody).on( 'scroll.DT', function (e) {
- var scrollLeft = this.scrollLeft;
+ $(scrollBody).on( 'scroll.DT', function () {
+ var scrollLeft = this.scrollLeft;
- scrollHead.scrollLeft = scrollLeft;
+ scrollHead.scrollLeft = scrollLeft;
- if ( footer ) {
- scrollFoot.scrollLeft = scrollLeft;
- }
- } );
- }
+ if ( footer ) {
+ scrollFoot.scrollLeft = scrollLeft;
+ }
+ } );
+
+ // When focus is put on the header cells, we might need to scroll the body
+ $('th, td', scrollHead).on('focus', function () {
+ var scrollLeft = scrollHead.scrollLeft;
+
+ scrollBody.scrollLeft = scrollLeft;
+
+ if ( footer ) {
+ scrollBody.scrollLeft = scrollLeft;
+ }
+ });
$(scrollBody).css('max-height', scrollY);
if (! scroll.bCollapse) {
@@ -5245,10 +5246,7 @@
settings.nScrollFoot = scrollFoot;
// On redraw - align columns
- settings.aoDrawCallback.push( {
- "fn": _fnScrollDraw,
- "sName": "scrolling"
- } );
+ settings.aoDrawCallback.push(_fnScrollDraw);
return scroller[0];
}
@@ -5262,8 +5260,8 @@
* Welcome to the most horrible function DataTables. The process that this
* function follows is basically:
* 1. Re-create the table inside the scrolling div
- * 2. Take live measurements from the DOM
- * 3. Apply the measurements to align the columns
+ * 2. Correct colgroup > col values if needed
+ * 3. Copy colgroup > col over to header and footer
* 4. Clean up
*
* @param {object} settings dataTables settings object
@@ -5275,43 +5273,20 @@
// to try and keep the minimised size as small as possible
var
scroll = settings.oScroll,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- scrollY = scroll.sY,
barWidth = scroll.iBarWidth,
divHeader = $(settings.nScrollHead),
- divHeaderStyle = divHeader[0].style,
divHeaderInner = divHeader.children('div'),
- divHeaderInnerStyle = divHeaderInner[0].style,
divHeaderTable = divHeaderInner.children('table'),
divBodyEl = settings.nScrollBody,
divBody = $(divBodyEl),
- divBodyStyle = divBodyEl.style,
divFooter = $(settings.nScrollFoot),
divFooterInner = divFooter.children('div'),
divFooterTable = divFooterInner.children('table'),
header = $(settings.nTHead),
table = $(settings.nTable),
- tableEl = table[0],
- tableStyle = tableEl.style,
- footer = settings.nTFoot ? $(settings.nTFoot) : null,
+ footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null,
browser = settings.oBrowser,
- ie67 = browser.bScrollOversize,
- dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
- headerTrgEls, footerTrgEls,
- headerSrcEls, footerSrcEls,
- headerCopy, footerCopy,
- headerWidths=[], footerWidths=[],
- headerContent=[], footerContent=[],
- idx, correction, sanityWidth,
- zeroOut = function(nSizer) {
- var style = nSizer.style;
- style.paddingTop = "0";
- style.paddingBottom = "0";
- style.borderTopWidth = "0";
- style.borderBottomWidth = "0";
- style.height = 0;
- };
+ headerCopy, footerCopy;
// If the scrollbar visibility has changed from the last draw, we need to
// adjust the column sizes as the table width will have changed to account
@@ -5327,212 +5302,129 @@
settings.scrollBarVis = scrollBarVis;
}
- /*
- * 1. Re-create the table inside the scrolling div
- */
-
+ // 1. Re-create the table inside the scrolling div
// Remove the old minimised thead and tfoot elements in the inner table
table.children('thead, tfoot').remove();
- if ( footer ) {
- 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');
- }
-
// Clone the current header and footer elements and then place it into the inner table
headerCopy = header.clone().prependTo( table );
- headerTrgEls = header.find('tr'); // original header is in its own table
- headerSrcEls = headerCopy.find('tr');
headerCopy.find('th, td').removeAttr('tabindex');
-
-
- /*
- * 2. Take live measurements from the DOM - do not alter the DOM itself!
- */
-
- // Remove old sizing and apply the calculated column widths
- // Get the unique column headers in the newly created (cloned) header. We want to apply the
- // calculated sizes to this header
- if ( ! scrollX )
- {
- divBodyStyle.width = '100%';
- divHeader[0].style.width = '100%';
- }
-
- $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
- idx = _fnVisibleToColumnIndex( settings, i );
- el.style.width = settings.aoColumns[idx].sWidth;
- } );
+ headerCopy.find('[id]').removeAttr('id');
if ( footer ) {
- _fnApplyToChildren( function(n) {
- n.style.width = "";
- }, footerSrcEls );
- }
-
- // Size the table as a whole
- sanityWidth = table.outerWidth();
- if ( scrollX === "" ) {
- // No x scrolling
- tableStyle.width = "100%";
-
- // IE7 will make the width of the table when 100% include the scrollbar
- // - which is shouldn't. When there is a scrollbar we need to take this
- // into account.
- if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
+ footerCopy = footer.clone().prependTo( table );
+ footerCopy.find('[id]').removeAttr('id');
+ }
+
+ // 2. Correct colgroup > col values if needed
+ // It is possible that the cell sizes are smaller than the content, so we need to
+ // correct colgroup>col for such cases. This can happen if the auto width detection
+ // uses a cell which has a longer string, but isn't the widest! For example
+ // "Chief Executive Officer (CEO)" is the longest string in the demo, but
+ // "Systems Administrator" is actually the widest string since it doesn't collapse.
+ // Note the use of translating into a column index to get the `col` element. This
+ // is because of Responsive which might remove `col` elements, knocking the alignment
+ // of the indexes out.
+ if (settings.aiDisplay.length) {
+ // Get the column sizes from the first row in the table. This should really be a
+ // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year
+ // browser support
+ var firstTr = null;
+ var start = _fnDataSource( settings ) !== 'ssp'
+ ? settings._iDisplayStart
+ : 0;
+
+ for (i=start ; i<start + settings.aiDisplay.length ; i++) {
+ var idx = settings.aiDisplay[i];
+ var tr = settings.aoData[idx].nTr;
+
+ if (tr) {
+ firstTr = tr;
+ break;
+ }
}
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
- else if ( scrollXInner !== "" ) {
- // legacy x scroll inner has been given - use it
- tableStyle.width = _fnStringToCss(scrollXInner);
-
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
-
- // Hidden header should have zero height, so remove padding and borders. Then
- // set the width based on the real headers
-
- // Apply all styles in one pass
- _fnApplyToChildren( zeroOut, headerSrcEls );
-
- // Read all widths in next pass
- _fnApplyToChildren( function(nSizer) {
- var style = window.getComputedStyle ?
- window.getComputedStyle(nSizer).width :
- _fnStringToCss( $(nSizer).width() );
+ if (firstTr) {
+ var colSizes = $(firstTr).children('th, td').map(function (vis) {
+ return {
+ idx: _fnVisibleToColumnIndex(settings, vis),
+ width: $(this).outerWidth()
+ };
+ });
- headerContent.push( nSizer.innerHTML );
- headerWidths.push( style );
- }, headerSrcEls );
+ // Check against what the colgroup > col is set to and correct if needed
+ for (var i=0 ; i<colSizes.length ; i++) {
+ var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0];
+ var colWidth = colEl.style.width.replace('px', '');
- // Apply all widths in final pass
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = headerWidths[i];
- }, headerTrgEls );
+ if (colWidth !== colSizes[i].width) {
+ colEl.style.width = colSizes[i].width + 'px';
- $(headerSrcEls).css('height', 0);
+ if (scroll.sX) {
+ colEl.style.minWidth = colSizes[i].width + 'px';
+ }
+ }
+ }
+ }
+ }
- /* Same again with the footer if we have one */
- if ( footer )
- {
- _fnApplyToChildren( zeroOut, footerSrcEls );
+ // 3. Copy the colgroup over to the header and footer
+ divHeaderTable
+ .find('colgroup')
+ .remove();
- _fnApplyToChildren( function(nSizer) {
- footerContent.push( nSizer.innerHTML );
- footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
- }, footerSrcEls );
+ divHeaderTable.append(settings.colgroup.clone());
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = footerWidths[i];
- }, footerTrgEls );
+ if ( footer ) {
+ divFooterTable
+ .find('colgroup')
+ .remove();
- $(footerSrcEls).height(0);
+ divFooterTable.append(settings.colgroup.clone());
}
-
- /*
- * 3. Apply the measurements
- */
-
// "Hide" the header and footer that we used for the sizing. We need to keep
// the content of the cell so that the width applied to the header and body
- // both match, but we want to hide it completely. We want to also fix their
- // width to what they currently are
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
- nSizer.childNodes[0].style.height = "0";
- nSizer.childNodes[0].style.overflow = "hidden";
- nSizer.style.width = headerWidths[i];
- }, headerSrcEls );
-
- if ( footer )
- {
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
- nSizer.childNodes[0].style.height = "0";
- nSizer.childNodes[0].style.overflow = "hidden";
- nSizer.style.width = footerWidths[i];
- }, footerSrcEls );
- }
-
- // Sanity check that the table is of a sensible width. If not then we are going to get
- // misalignment - try to prevent this by not allowing the table to shrink below its min width
- if ( Math.round(table.outerWidth()) < Math.round(sanityWidth) )
- {
- // The min width depends upon if we have a vertical scrollbar visible or not */
- correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")) ?
- sanityWidth+barWidth :
- sanityWidth;
-
- // IE6/7 are a law unto themselves...
- if ( ie67 && (divBodyEl.scrollHeight >
- divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( correction-barWidth );
- }
-
- // And give the user a warning that we've stopped the table getting too small
- if ( scrollX === "" || scrollXInner !== "" ) {
- _fnLog( settings, 1, 'Possible column misalignment', 6 );
- }
- }
- else
- {
- correction = '100%';
- }
-
- // Apply to the container elements
- divBodyStyle.width = _fnStringToCss( correction );
- divHeaderStyle.width = _fnStringToCss( correction );
+ // both match, but we want to hide it completely.
+ $('th, td', headerCopy).each(function () {
+ $(this.childNodes).wrapAll('<div class="dt-scroll-sizing" />');
+ });
if ( footer ) {
- settings.nScrollFoot.style.width = _fnStringToCss( correction );
+ $('th, td', footerCopy).each(function () {
+ $(this.childNodes).wrapAll('<div class="dt-scroll-sizing" />');
+ });
}
+ // 4. Clean up
+ // Figure out if there are scrollbar present - if so then we need the header and footer to
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
+ var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
+ var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
- /*
- * 4. Clean up
- */
- if ( ! scrollY ) {
- /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
- * the scrollbar height from the visible display, rather than adding it on. We need to
- * set the height in order to sort this. Don't want to do it in any other browsers.
- */
- if ( ie67 ) {
- divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
- }
- }
-
- /* Finally set the width's of the header and footer tables */
- var iOuterWidth = table.outerWidth();
- divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
- divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
+ // Set the width's of the header and footer tables
+ var outerWidth = table.outerWidth();
- // Figure out if there are scrollbar present - if so then we need a the header and footer to
- // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
- var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
- var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
- divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
+ divHeaderTable.css('width', _fnStringToCss( outerWidth ));
+ divHeaderInner
+ .css('width', _fnStringToCss( outerWidth ))
+ .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
if ( footer ) {
- divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
+ divFooterTable.css('width', _fnStringToCss( outerWidth ));
+ divFooterInner
+ .css('width', _fnStringToCss( outerWidth ))
+ .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
}
// Correct DOM ordering for colgroup - comes before the thead
- table.children('colgroup').insertBefore( table.children('thead') );
+ table.children('colgroup').prependTo(table);
+
+ // Remove tabindex from the hidden row elements
+ table.find('thead, tfoot').find('[tabindex]').removeAttr('tabindex');
+ table.find('thead, tfoot').find('role').removeAttr('role');
- /* Adjust the position of the header in case we loose the y-scrollbar */
+ // Adjust the position of the header in case we loose the y-scrollbar
divBody.trigger('scroll');
// If sorting or filtering has occurred, jump the scrolling back to the top
@@ -5542,148 +5434,99 @@
}
}
-
-
/**
- * Apply a given function to the display child nodes of an element array (typically
- * TD children of TR rows
- * @param {function} fn Method to apply to the objects
- * @param array {nodes} an1 List of elements to look through for display children
- * @param array {nodes} an2 Another list (identical structure to the first) - optional
+ * Calculate the width of columns for the table
+ * @param {object} settings dataTables settings object
* @memberof DataTable#oApi
*/
- function _fnApplyToChildren( fn, an1, an2 )
+ function _fnCalculateColumnWidths ( settings )
{
- var index=0, i=0, iLen=an1.length;
- var nNode1, nNode2;
-
- while ( i < iLen ) {
- nNode1 = an1[i].firstChild;
- nNode2 = an2 ? an2[i].firstChild : null;
-
- while ( nNode1 ) {
- if ( nNode1.nodeType === 1 ) {
- if ( an2 ) {
- fn( nNode1, nNode2, index );
- }
- else {
- fn( nNode1, index );
- }
-
- index++;
- }
-
- nNode1 = nNode1.nextSibling;
- nNode2 = an2 ? nNode2.nextSibling : null;
- }
-
- i++;
+ // Not interested in doing column width calculation if auto-width is disabled
+ if (! settings.oFeatures.bAutoWidth) {
+ return;
}
- }
-
-
- var __re_html_remove = /<.*?>/g;
-
-
- /**
- * Calculate the width of columns for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateColumnWidths ( oSettings )
- {
var
- table = oSettings.nTable,
- columns = oSettings.aoColumns,
- scroll = oSettings.oScroll,
+ table = settings.nTable,
+ columns = settings.aoColumns,
+ scroll = settings.oScroll,
scrollY = scroll.sY,
scrollX = scroll.sX,
scrollXInner = scroll.sXInner,
- columnCount = columns.length,
- visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
- headerCells = $('th', oSettings.nTHead),
+ visibleColumns = _fnGetColumns( settings, 'bVisible' ),
tableWidthAttr = table.getAttribute('width'), // from DOM element
tableContainer = table.parentNode,
- userInputs = false,
- i, column, columnIdx, width, outerWidth,
- browser = oSettings.oBrowser,
- ie67 = browser.bScrollOversize;
-
+ i, j, column, columnIdx;
+
var styleWidth = table.style.width;
+ var containerWidth = _fnWrapperWidth(settings);
+
+ // Don't re-run for the same width as the last time
+ if (containerWidth === settings.containerWidth) {
+ return false;
+ }
+
+ settings.containerWidth = containerWidth;
+
+ // If there is no width applied as a CSS style or as an attribute, we assume that
+ // the width is intended to be 100%, which is usually is in CSS, but it is very
+ // difficult to correctly parse the rules to get the final result.
+ if ( ! styleWidth && ! tableWidthAttr) {
+ table.style.width = '100%';
+ styleWidth = '100%';
+ }
+
if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
tableWidthAttr = styleWidth;
}
- /* Convert any user input sizes into pixel sizes */
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
+ // Let plug-ins know that we are doing a recalc, in case they have changed any of the
+ // visible columns their own way (e.g. Responsive uses display:none).
+ _fnCallbackFire(
+ settings,
+ null,
+ 'column-calc',
+ {visible: visibleColumns},
+ false
+ );
- if ( column.sWidth !== null ) {
- column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
+ // Construct a worst case table with the widest, assign any user defined
+ // widths, then insert it into the DOM and allow the browser to do all
+ // the hard work of calculating table widths
+ var tmpTable = $(table.cloneNode())
+ .css( 'visibility', 'hidden' )
+ .css( 'margin', 0 )
+ .removeAttr( 'id' );
- userInputs = true;
- }
- }
+ // Clean up the table body
+ tmpTable.append('<tbody/>')
- /* If the number of columns in the DOM equals the number that we have to
- * process in DataTables, then we can use the offsets that are created by
- * the web- browser. No custom sizes can be set in order for this to happen,
- * nor scrolling used
- */
- if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
- columnCount == _fnVisbleColumns( oSettings ) &&
- columnCount == headerCells.length
- ) {
- for ( i=0 ; i<columnCount ; i++ ) {
- var colIdx = _fnVisibleToColumnIndex( oSettings, i );
+ // Clone the table header and footer - we can't use the header / footer
+ // from the cloned table, since if scrolling is active, the table's
+ // real header and footer are contained in different table tags
+ tmpTable
+ .append( $(settings.nTHead).clone() )
+ .append( $(settings.nTFoot).clone() );
- if ( colIdx !== null ) {
- columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
- }
- }
- }
- else
- {
- // Otherwise construct a single row, worst case, table with the widest
- // node in the data, assign any user defined widths, then insert it into
- // the DOM and allow the browser to do all the hard work of calculating
- // table widths
- var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
- .css( 'visibility', 'hidden' )
- .removeAttr( 'id' );
-
- // Clean up the table body
- tmpTable.find('tbody tr').remove();
- var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
-
- // Clone the table header and footer - we can't use the header / footer
- // from the cloned table, since if scrolling is active, the table's
- // real header and footer are contained in different table tags
- tmpTable.find('thead, tfoot').remove();
- tmpTable
- .append( $(oSettings.nTHead).clone() )
- .append( $(oSettings.nTFoot).clone() );
-
- // Remove any assigned widths from the footer (from scrolling)
- tmpTable.find('tfoot th, tfoot td').css('width', '');
-
- // Apply custom sizing to the cloned header
- headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
-
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
-
- headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
- _fnStringToCss( column.sWidthOrig ) :
- '';
+ // Remove any assigned widths from the footer (from scrolling)
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
+
+ // Apply custom sizing to the cloned header
+ tmpTable.find('thead th, thead td').each( function () {
+ // Get the `width` from the header layout
+ var width = _fnColumnsSumWidth( settings, this, true, false );
+
+ if ( width ) {
+ this.style.width = width;
// For scrollX we need to force the column width otherwise the
// browser will collapse it. If this width is smaller than the
// width the column requires, then it will have no effect
- if ( column.sWidthOrig && scrollX ) {
- $( headerCells[i] ).append( $('<div/>').css( {
- width: column.sWidthOrig,
+ if ( scrollX ) {
+ this.style.minWidth = width;
+
+ $( this ).append( $('<div/>').css( {
+ width: width,
margin: 0,
padding: 0,
border: 0,
@@ -5691,97 +5534,111 @@
} ) );
}
}
+ else {
+ this.style.width = '';
+ }
+ } );
- // Find the widest cell for each column and put it into the table
- if ( oSettings.aoData.length ) {
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- columnIdx = visibleColumns[i];
- column = columns[ columnIdx ];
+ // Get the widest strings for each of the visible columns and add them to
+ // our table to create a "worst case"
+ var longestData = [];
- $( _fnGetWidestNode( oSettings, columnIdx ) )
- .clone( false )
- .append( column.sContentPadding )
- .appendTo( tr );
- }
- }
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ longestData.push(_fnGetWideStrings(settings, visibleColumns[i]));
+ }
- // Tidy the temporary table - remove name attributes so there aren't
- // duplicated in the dom (radio elements for example)
- $('[name]', tmpTable).removeAttr('name');
+ if (longestData.length) {
+ for ( i=0 ; i<longestData[0].length ; i++ ) {
+ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
- // Table has been built, attach to the document so we can work with it.
- // A holding element is used, positioned at the top of the container
- // with minimal height, so it has no effect on if the container scrolls
- // or not. Otherwise it might trigger scrolling when it actually isn't
- // needed
- var holder = $('<div/>').css( scrollX || scrollY ?
- {
- position: 'absolute',
- top: 0,
- left: 0,
- height: 1,
- right: 0,
- overflow: 'hidden'
- } :
- {}
- )
- .append( tmpTable )
- .appendTo( tableContainer );
+ for ( j=0 ; j<visibleColumns.length ; j++ ) {
+ columnIdx = visibleColumns[j];
+ column = columns[ columnIdx ];
- // When scrolling (X or Y) we want to set the width of the table as
- // appropriate. However, when not scrolling leave the table width as it
- // is. This results in slightly different, but I think correct behaviour
- if ( scrollX && scrollXInner ) {
- tmpTable.width( scrollXInner );
- }
- else if ( scrollX ) {
- tmpTable.css( 'width', 'auto' );
- tmpTable.removeAttr('width');
+ var longest = longestData[j][i] || '';
+ var autoClass = _ext.type.className[column.sType];
+ var padding = column.sContentPadding || (scrollX ? '-' : '');
+ var text = longest + padding;
+ var insert = longest.indexOf('<') === -1 && longest.indexOf('&') === -1
+ ? document.createTextNode(text)
+ : text
- // If there is no width attribute or style, then allow the table to
- // collapse
- if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
- tmpTable.width( tableContainer.clientWidth );
+ $('<td/>')
+ .addClass(autoClass)
+ .addClass(column.sClass)
+ .append(insert)
+ .appendTo(tr);
}
}
- else if ( scrollY ) {
- tmpTable.width( tableContainer.clientWidth );
- }
- else if ( tableWidthAttr ) {
- tmpTable.width( tableWidthAttr );
- }
+ }
- // Get the width of each column in the constructed table - we need to
- // know the inner width (so it can be assigned to the other table's
- // cells) and the outer width so we can calculate the full width of the
- // table. This is safe since DataTables requires a unique cell for each
- // column, but if ever a header can span multiple columns, this will
- // need to be modified.
- var total = 0;
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- var cell = $(headerCells[i]);
- var border = cell.outerWidth() - cell.width();
+ // Tidy the temporary table - remove name attributes so there aren't
+ // duplicated in the dom (radio elements for example)
+ $('[name]', tmpTable).removeAttr('name');
- // Use getBounding... where possible (not IE8-) because it can give
- // sub-pixel accuracy, which we then want to round up!
- var bounding = browser.bBounding ?
- Math.ceil( headerCells[i].getBoundingClientRect().width ) :
- cell.outerWidth();
+ // Table has been built, attach to the document so we can work with it.
+ // A holding element is used, positioned at the top of the container
+ // with minimal height, so it has no effect on if the container scrolls
+ // or not. Otherwise it might trigger scrolling when it actually isn't
+ // needed
+ var holder = $('<div/>').css( scrollX || scrollY ?
+ {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ height: 1,
+ right: 0,
+ overflow: 'hidden'
+ } :
+ {}
+ )
+ .append( tmpTable )
+ .appendTo( tableContainer );
- // Total is tracked to remove any sub-pixel errors as the outerWidth
- // of the table might not equal the total given here (IE!).
- total += bounding;
+ // When scrolling (X or Y) we want to set the width of the table as
+ // appropriate. However, when not scrolling leave the table width as it
+ // is. This results in slightly different, but I think correct behaviour
+ if ( scrollX && scrollXInner ) {
+ tmpTable.width( scrollXInner );
+ }
+ else if ( scrollX ) {
+ tmpTable.css( 'width', 'auto' );
+ tmpTable.removeAttr('width');
- // Width for each column to use
- columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
+ // If there is no width attribute or style, then allow the table to
+ // collapse
+ if ( tmpTable.outerWidth() < tableContainer.clientWidth && tableWidthAttr ) {
+ tmpTable.outerWidth( tableContainer.clientWidth );
}
+ }
+ else if ( scrollY ) {
+ tmpTable.outerWidth( tableContainer.clientWidth );
+ }
+ else if ( tableWidthAttr ) {
+ tmpTable.outerWidth( tableWidthAttr );
+ }
+
+ // Get the width of each column in the constructed table
+ var total = 0;
+ var bodyCells = tmpTable.find('tbody tr').eq(0).children();
+
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ // Use getBounding for sub-pixel accuracy, which we then want to round up!
+ var bounding = bodyCells[i].getBoundingClientRect().width;
- table.style.width = _fnStringToCss( total );
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
+ // of the table might not equal the total given here
+ total += bounding;
- // Finished with the table - ditch it
- holder.remove();
+ // Width for each column to use
+ columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding );
}
+ table.style.width = _fnStringToCss( total );
+
+ // Finished with the table - ditch it
+ holder.remove();
+
// If there is a width attr, we want to attach an event listener which
// allows the table sizing to automatically adjust when the window is
// resized. Use the width attr rather than CSS, since we can't know if the
@@ -5790,106 +5647,158 @@
table.style.width = _fnStringToCss( tableWidthAttr );
}
- if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
- var bindResize = function () {
- $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
- _fnAdjustColumnSizing( oSettings );
- } ) );
- };
+ if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) {
+ var resize = DataTable.util.throttle( function () {
+ var newWidth = _fnWrapperWidth(settings);
+
+ // Don't do it if destroying or the container width is 0
+ if (! settings.bDestroying && newWidth !== 0) {
+ _fnAdjustColumnSizing( settings );
+ }
+ } );
- // IE6/7 will crash if we bind a resize event handler on page load.
- // To be removed in 1.11 which drops IE6/7 support
- if ( ie67 ) {
- setTimeout( bindResize, 1000 );
+ // For browsers that support it (~2020 onwards for wide support) we can watch for the
+ // container changing width.
+ if (window.ResizeObserver) {
+ // This is a tricky beast - if the element is visible when `.observe()` is called,
+ // then the callback is immediately run. Which we don't want. If the element isn't
+ // visible, then it isn't run, but we want it to run when it is then made visible.
+ // This flag allows the above to be satisfied.
+ var first = $(settings.nTableWrapper).is(':visible');
+
+ // Use an empty div to attach the observer so it isn't impacted by height changes
+ var resizer = $('<div>')
+ .css({
+ width: '100%',
+ height: 0
+ })
+ .addClass('dt-autosize')
+ .appendTo(settings.nTableWrapper);
+
+ settings.resizeObserver = new ResizeObserver(function (e) {
+ if (first) {
+ first = false;
+ }
+ else {
+ resize();
+ }
+ });
+
+ settings.resizeObserver.observe(resizer[0]);
}
else {
- bindResize();
+ // For old browsers, the best we can do is listen for a window resize
+ $(window).on('resize.DT-'+settings.sInstance, resize);
}
- oSettings._reszEvt = true;
+ settings._reszEvt = true;
}
}
-
/**
- * Throttle the calls to a function. Arguments and context are maintained for
- * the throttled function
- * @param {function} fn Function to be called
- * @param {int} [freq=200] call frequency in mS
- * @returns {function} wrapped function
- * @memberof DataTable#oApi
- */
- var _fnThrottle = DataTable.util.throttle;
-
-
- /**
- * Convert a CSS unit width to pixels (e.g. 2em)
- * @param {string} width width to be converted
- * @param {node} parent parent to get the with for (required for relative widths) - optional
- * @returns {int} width in pixels
- * @memberof DataTable#oApi
+ * Get the width of the DataTables wrapper element
+ *
+ * @param {*} settings DataTables settings object
+ * @returns Width
*/
- function _fnConvertToWidth ( width, parent )
- {
- if ( ! width ) {
- return 0;
- }
-
- var n = $('<div/>')
- .css( 'width', _fnStringToCss( width ) )
- .appendTo( parent || document.body );
-
- var val = n[0].offsetWidth;
- n.remove();
-
- return val;
+ function _fnWrapperWidth(settings) {
+ return $(settings.nTableWrapper).is(':visible')
+ ? $(settings.nTableWrapper).width()
+ : 0;
}
-
/**
- * Get the widest node
+ * Get the widest strings for each column.
+ *
+ * It is very difficult to determine what the widest string actually is due to variable character
+ * width and kerning. Doing an exact calculation with the DOM or even Canvas would kill performance
+ * and this is a critical point, so we use two techniques to determine a collection of the longest
+ * strings from the column, which will likely contain the widest strings:
+ *
+ * 1) Get the top three longest strings from the column
+ * 2) Get the top three widest words (i.e. an unbreakable phrase)
+ *
* @param {object} settings dataTables settings object
* @param {int} colIdx column of interest
- * @returns {node} widest table node
+ * @returns {string[]} Array of the longest strings
* @memberof DataTable#oApi
*/
- function _fnGetWidestNode( settings, colIdx )
+ function _fnGetWideStrings( settings, colIdx )
{
- var idx = _fnGetMaxLenString( settings, colIdx );
- if ( idx < 0 ) {
- return null;
- }
+ var column = settings.aoColumns[colIdx];
+
+ // Do we need to recalculate (i.e. was invalidated), or just use the cached data?
+ if (! column.wideStrings) {
+ var allStrings = [];
+ var collection = [];
+
+ // Create an array with the string information for the column
+ for ( var i=0, iLen=settings.aiDisplayMaster.length ; i<iLen ; i++ ) {
+ var rowIdx = settings.aiDisplayMaster[i];
+ var data = _fnGetRowDisplay(settings, rowIdx)[colIdx];
+
+ var cellString = data && typeof data === 'object' && data.nodeType
+ ? data.innerHTML
+ : data+'';
+
+ // Remove id / name attributes from elements so they
+ // don't interfere with existing elements
+ cellString = cellString
+ .replace(/id=".*?"/g, '')
+ .replace(/name=".*?"/g, '');
+
+ // Don't want script, dialog or template tags in the width
+ // calculations as they are hidden content
+ cellString = cellString
+ .replace(/<script[\s\S]*?<\/script>/gi, ' ')
+ .replace(/<dialog[\s\S]*?<\/dialog>/gi, ' ')
+ .replace(/<template[\s\S]*?<\/template>/gi, ' ');
+
+ var noHtml = _stripHtml(cellString, ' ')
+ .replace( /&nbsp;/g, ' ' );
+
+ // The length is calculated on the text only, but we keep the HTML
+ // in the string so it can be used in the calculation table
+ collection.push({
+ str: cellString,
+ len: noHtml.length
+ });
- var data = settings.aoData[ idx ];
- return ! data.nTr ? // Might not have been created when deferred rendering
- $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
- data.anCells[ colIdx ];
- }
+ allStrings.push(noHtml);
+ }
+ // Order and then cut down to the size we need
+ collection
+ .sort(function (a, b) {
+ return b.len - a.len;
+ })
+ .splice(3);
- /**
- * Get the maximum strlen for each data column
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {string} max string length for each column
- * @memberof DataTable#oApi
- */
- function _fnGetMaxLenString( settings, colIdx )
- {
- var s, max=-1, maxIdx = -1;
+ column.wideStrings = collection.map(function (item) {
+ return item.str;
+ });
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
- s = s.replace( __re_html_remove, '' );
- s = s.replace( /&nbsp;/g, ' ' );
+ // Longest unbroken string
+ let parts = allStrings.join(' ').split(' ');
- if ( s.length > max ) {
- max = s.length;
- maxIdx = i;
+ parts.sort(function (a, b) {
+ return b.length - a.length;
+ });
+
+ if (parts.length) {
+ column.wideStrings.push(parts[0]);
+ }
+
+ if (parts.length > 1) {
+ column.wideStrings.push(parts[1]);
+ }
+
+ if (parts.length > 2) {
+ column.wideStrings.push(parts[3]);
}
}
- return maxIdx;
+ return column.wideStrings;
}
@@ -5917,68 +5826,233 @@
s;
}
+ /**
+ * Re-insert the `col` elements for current visibility
+ *
+ * @param {*} settings DT settings
+ */
+ function _colGroup( settings ) {
+ var cols = settings.aoColumns;
+
+ settings.colgroup.empty();
+
+ for (i=0 ; i<cols.length ; i++) {
+ if (cols[i].bVisible) {
+ settings.colgroup.append(cols[i].colEl);
+ }
+ }
+ }
+
+
+ function _fnSortInit( settings ) {
+ var target = settings.nTHead;
+ var headerRows = target.querySelectorAll('tr');
+ var titleRow = settings.titleRow;
+ var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
+
+ // Legacy support for `orderCellsTop`
+ if (titleRow === true) {
+ target = headerRows[0];
+ }
+ else if (titleRow === false) {
+ target = headerRows[ headerRows.length - 1 ];
+ }
+ else if (titleRow !== null) {
+ target = headerRows[titleRow];
+ }
+ // else - all rows
+
+ if (settings.orderHandler) {
+ _fnSortAttachListener(
+ settings,
+ target,
+ target === settings.nTHead
+ ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
+ : 'th'+notSelector+', td'+notSelector
+ );
+ }
+
+ // Need to resolve the user input array into our internal structure
+ var order = [];
+ _fnSortResolve( settings, order, settings.aaSorting );
+
+ settings.aaSorting = order;
+ }
+
+
+ function _fnSortAttachListener(settings, node, selector, column, callback) {
+ _fnBindAction( node, selector, function (e) {
+ var run = false;
+ var columns = column === undefined
+ ? _fnColumnsFromHeader( e.target )
+ : typeof column === 'function'
+ ? column()
+ : Array.isArray(column)
+ ? column
+ : [column];
+
+ if ( columns.length ) {
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) {
+ var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey );
+
+ if (ret !== false) {
+ run = true;
+ }
+
+ // If the first entry is no sort, then subsequent
+ // sort columns are ignored
+ if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') {
+ break;
+ }
+ }
+
+ if (run) {
+ _fnProcessingRun(settings, true, function () {
+ _fnSort( settings );
+ _fnSortDisplay( settings, settings.aiDisplay );
+
+ _fnReDraw( settings, false, false );
+
+ if (callback) {
+ callback();
+ }
+ });
+ }
+ }
+ } );
+ }
+
+ /**
+ * Sort the display array to match the master's order
+ * @param {*} settings
+ */
+ function _fnSortDisplay(settings, display) {
+ if (display.length < 2) {
+ return;
+ }
+
+ var master = settings.aiDisplayMaster;
+ var masterMap = {};
+ var map = {};
+ var i;
+
+ // Rather than needing an `indexOf` on master array, we can create a map
+ for (i=0 ; i<master.length ; i++) {
+ masterMap[master[i]] = i;
+ }
+
+ // And then cache what would be the indexOf from the display
+ for (i=0 ; i<display.length ; i++) {
+ map[display[i]] = masterMap[display[i]];
+ }
+
+ display.sort(function(a, b){
+ // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);`
+ return map[a] - map[b];
+ });
+ }
+
+
+ function _fnSortResolve (settings, nestedSort, sort) {
+ var push = function ( a ) {
+ if ($.isPlainObject(a)) {
+ if (a.idx !== undefined) {
+ // Index based ordering
+ nestedSort.push([a.idx, a.dir]);
+ }
+ else if (a.name) {
+ // Name based ordering
+ var cols = _pluck( settings.aoColumns, 'sName');
+ var idx = cols.indexOf(a.name);
+
+ if (idx !== -1) {
+ nestedSort.push([idx, a.dir]);
+ }
+ }
+ }
+ else {
+ // Plain column index and direction pair
+ nestedSort.push(a);
+ }
+ };
+
+ if ( $.isPlainObject(sort) ) {
+ // Object
+ push(sort);
+ }
+ else if ( sort.length && typeof sort[0] === 'number' ) {
+ // 1D array
+ push(sort);
+ }
+ else if ( sort.length ) {
+ // 2D array
+ for (var z=0; z<sort.length; z++) {
+ push(sort[z]); // Object or array
+ }
+ }
+ }
function _fnSortFlatten ( settings )
{
var
- i, iLen, k, kLen,
+ i, k, kLen,
aSort = [],
- aiOrig = [],
+ extSort = DataTable.ext.type.order,
aoColumns = settings.aoColumns,
aDataSort, iCol, sType, srcCol,
fixed = settings.aaSortingFixed,
fixedObj = $.isPlainObject( fixed ),
- nestedSort = [],
- add = function ( a ) {
- if ( a.length && ! Array.isArray( a[0] ) ) {
- // 1D array
- nestedSort.push( a );
- }
- else {
- // 2D array
- $.merge( nestedSort, a );
- }
- };
+ nestedSort = [];
+
+ if ( ! settings.oFeatures.bSort ) {
+ return aSort;
+ }
// Build the sort array, with pre-fix and post-fix options if they have been
// specified
if ( Array.isArray( fixed ) ) {
- add( fixed );
+ _fnSortResolve( settings, nestedSort, fixed );
}
if ( fixedObj && fixed.pre ) {
- add( fixed.pre );
+ _fnSortResolve( settings, nestedSort, fixed.pre );
}
- add( settings.aaSorting );
+ _fnSortResolve( settings, nestedSort, settings.aaSorting );
if (fixedObj && fixed.post ) {
- add( fixed.post );
+ _fnSortResolve( settings, nestedSort, fixed.post );
}
for ( i=0 ; i<nestedSort.length ; i++ )
{
srcCol = nestedSort[i][0];
- aDataSort = aoColumns[ srcCol ].aDataSort;
- for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
- {
- iCol = aDataSort[k];
- sType = aoColumns[ iCol ].sType || 'string';
+ if ( aoColumns[ srcCol ] ) {
+ aDataSort = aoColumns[ srcCol ].aDataSort;
- if ( nestedSort[i]._idx === undefined ) {
- nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
- }
+ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
+ {
+ iCol = aDataSort[k];
+ sType = aoColumns[ iCol ].sType || 'string';
- aSort.push( {
- src: srcCol,
- col: iCol,
- dir: nestedSort[i][1],
- index: nestedSort[i]._idx,
- type: sType,
- formatter: DataTable.ext.type.order[ sType+"-pre" ]
- } );
+ if ( nestedSort[i]._idx === undefined ) {
+ nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]);
+ }
+
+ if ( nestedSort[i][1] ) {
+ aSort.push( {
+ src: srcCol,
+ col: iCol,
+ dir: nestedSort[i][1],
+ index: nestedSort[i]._idx,
+ type: sType,
+ formatter: extSort[ sType+"-pre" ],
+ sorter: extSort[ sType+"-"+nestedSort[i][1] ]
+ } );
+ }
+ }
}
}
@@ -5989,38 +6063,44 @@
* Change the order of the table
* @param {object} oSettings dataTables settings object
* @memberof DataTable#oApi
- * @todo This really needs split up!
*/
- function _fnSort ( oSettings )
+ function _fnSort ( oSettings, col, dir )
{
var
- i, ien, iLen, j, jLen, k, kLen,
- sDataType, nTh,
+ i, iLen,
aiOrig = [],
- oExtSort = DataTable.ext.type.order,
+ extSort = DataTable.ext.type.order,
aoData = oSettings.aoData,
- aoColumns = oSettings.aoColumns,
- aDataSort, data, iCol, sType, oSort,
- formatters = 0,
sortCol,
displayMaster = oSettings.aiDisplayMaster,
aSort;
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo Can this be moved into a 'data-ready' handler which is called when
- // data is going to be used in the table?
- _fnColumnTypes( oSettings );
-
- aSort = _fnSortFlatten( oSettings );
+ // Make sure the columns all have types defined
+ _fnColumnTypes(oSettings);
+
+ // Allow a specific column to be sorted, which will _not_ alter the display
+ // master
+ if (col !== undefined) {
+ var srcCol = oSettings.aoColumns[col];
+
+ aSort = [{
+ src: col,
+ col: col,
+ dir: dir,
+ index: 0,
+ type: srcCol.sType,
+ formatter: extSort[ srcCol.sType+"-pre" ],
+ sorter: extSort[ srcCol.sType+"-"+dir ]
+ }];
+ displayMaster = displayMaster.slice();
+ }
+ else {
+ aSort = _fnSortFlatten( oSettings );
+ }
- for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=aSort.length ; i<iLen ; i++ ) {
sortCol = aSort[i];
- // Track if we can use the fast sort algorithm
- if ( sortCol.formatter ) {
- formatters++;
- }
-
// Load the data needed for the sort, for each cell
_fnSortData( oSettings, sortCol.col );
}
@@ -6028,137 +6108,89 @@
/* No sorting required if server-side or no sorting array */
if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
{
- // Create a value - key array of the current row positions such that we can use their
- // current position during the sort, if values match, in order to perform stable sorting
+ // Reset the initial positions on each pass so we get a stable sort
for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
- aiOrig[ displayMaster[i] ] = i;
+ aiOrig[ i ] = i;
+ }
+
+ // If the first sort is desc, then reverse the array to preserve original
+ // order, just in reverse
+ if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) {
+ aiOrig.reverse();
}
/* Do the sort - here we want multi-column sorting based on a given data source (column)
* and sorting function (from oSort) in a certain direction. It's reasonably complex to
- * follow on it's own, but this is what we want (example two column sorting):
+ * follow on its own, but this is what we want (example two column sorting):
* fnLocalSorting = function(a,b){
- * var iTest;
- * iTest = oSort['string-asc']('data11', 'data12');
- * if (iTest !== 0)
- * return iTest;
- * iTest = oSort['numeric-desc']('data21', 'data22');
- * if (iTest !== 0)
- * return iTest;
+ * var test;
+ * test = oSort['string-asc']('data11', 'data12');
+ * if (test !== 0)
+ * return test;
+ * test = oSort['numeric-desc']('data21', 'data22');
+ * if (test !== 0)
+ * return test;
* return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
* }
* Basically we have a test for each sorting column, if the data in that column is equal,
* test the next column. If all columns match, then we use a numeric sort on the row
* positions in the original data array to provide a stable sort.
- *
- * Note - I know it seems excessive to have two sorting methods, but the first is around
- * 15% faster, so the second is only maintained for backwards compatibility with sorting
- * methods which do not have a pre-sort formatting function.
*/
- if ( formatters === aSort.length ) {
- // All sort types have formatting functions
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, test, sort,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
+ displayMaster.sort( function ( a, b ) {
+ var
+ x, y, k, test, sort,
+ len=aSort.length,
+ dataA = aoData[a]._aSortData,
+ dataB = aoData[b]._aSortData;
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
+ for ( k=0 ; k<len ; k++ ) {
+ sort = aSort[k];
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
+ // Data, which may have already been through a `-pre` function
+ x = dataA[ sort.col ];
+ y = dataB[ sort.col ];
+
+ if (sort.sorter) {
+ // If there is a custom sorter (`-asc` or `-desc`) for this
+ // data type, use it
+ test = sort.sorter(x, y);
- test = x<y ? -1 : x>y ? 1 : 0;
if ( test !== 0 ) {
- return sort.dir === 'asc' ? test : -test;
+ return test;
}
}
+ else {
+ // Otherwise, use generic sorting
+ test = x<y ? -1 : x>y ? 1 : 0;
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- else {
- // Depreciated - remove in 1.11 (providing a plug-in option)
- // Not all sort types have formatting methods, so we have to call their sorting
- // methods.
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, l, test, sort, fn,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
-
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
-
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
- test = fn( x, y );
if ( test !== 0 ) {
- return test;
+ return sort.dir === 'asc' ? test : -test;
}
}
+ }
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- }
-
- /* Tell the draw function that we have sorted the data */
- oSettings.bSorted = true;
- }
-
-
- function _fnSortAria ( settings )
- {
- var label;
- var nextSort;
- var columns = settings.aoColumns;
- var aSort = _fnSortFlatten( settings );
- var oAria = settings.oLanguage.oAria;
-
- // ARIA attributes - need to loop all columns, to update all (removing old
- // attributes as needed)
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- var col = columns[i];
- var asSorting = col.asSorting;
- var sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, "" );
- var th = col.nTh;
-
- // IE7 is throwing an error when setting these properties with jQuery's
- // attr() and removeAttr() methods...
- th.removeAttribute('aria-sort');
+ x = aiOrig[a];
+ y = aiOrig[b];
- /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
- if ( col.bSortable ) {
- if ( aSort.length > 0 && aSort[0].col == i ) {
- th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
- nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
- }
- else {
- nextSort = asSorting[0];
- }
+ return x<y ? -1 : x>y ? 1 : 0;
+ } );
+ }
+ else if ( aSort.length === 0 ) {
+ // Apply index order
+ displayMaster.sort(function (x, y) {
+ return x<y ? -1 : x>y ? 1 : 0;
+ });
+ }
- label = sTitle + ( nextSort === "asc" ?
- oAria.sSortAscending :
- oAria.sSortDescending
- );
- }
- else {
- label = sTitle;
- }
+ if (col === undefined) {
+ // Tell the draw function that we have sorted the data
+ oSettings.bSorted = true;
+ oSettings.sortDetails = aSort;
- th.setAttribute('aria-label', label);
+ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
}
+
+ return displayMaster;
}
@@ -6167,12 +6199,12 @@
* @param {object} settings dataTables settings object
* @param {node} attachTo node to attach the handler to
* @param {int} colIdx column sorting index
- * @param {boolean} [append=false] Append the requested sort to the existing
- * sort if true (i.e. multi-column sort)
+ * @param {int} addIndex Counter
+ * @param {boolean} [shift=false] Shift click add
* @param {function} [callback] callback function
* @memberof DataTable#oApi
*/
- function _fnSortListener ( settings, colIdx, append, callback )
+ function _fnSortAdd ( settings, colIdx, addIndex, shift )
{
var col = settings.aoColumns[ colIdx ];
var sorting = settings.aaSorting;
@@ -6181,7 +6213,7 @@
var next = function ( a, overflow ) {
var idx = a._idx;
if ( idx === undefined ) {
- idx = $.inArray( a[1], asSorting );
+ idx = asSorting.indexOf(a[1]);
}
return idx+1 < asSorting.length ?
@@ -6191,15 +6223,19 @@
0;
};
+ if ( ! col.bSortable ) {
+ return false;
+ }
+
// Convert to 2D array if needed
if ( typeof sorting[0] === 'number' ) {
sorting = settings.aaSorting = [ sorting ];
}
// If appending the sort then we are multi-column sorting
- if ( append && settings.oFeatures.bSortMulti ) {
+ if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) {
// Are we already doing some kind of sort on this column?
- var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
+ var sortIdx = _pluck(sorting, '0').indexOf(colIdx);
if ( sortIdx !== -1 ) {
// Yes, modify the sort
@@ -6209,7 +6245,7 @@
nextSortIdx = 0; // can't remove sorting completely
}
- if ( nextSortIdx === null ) {
+ if ( nextSortIdx === null || asSorting[ nextSortIdx ] === '' ) {
sorting.splice( sortIdx, 1 );
}
else {
@@ -6217,11 +6253,18 @@
sorting[sortIdx]._idx = nextSortIdx;
}
}
- else {
- // No sort on this column yet
+ else if (shift) {
+ // No sort on this column yet, being added by shift click
+ // add it as itself
sorting.push( [ colIdx, asSorting[0], 0 ] );
sorting[sorting.length-1]._idx = 0;
}
+ else {
+ // No sort on this column yet, being added from a colspan
+ // so add with same direction as first column
+ sorting.push( [ colIdx, sorting[0][1], 0 ] );
+ sorting[sorting.length-1]._idx = 0;
+ }
}
else if ( sorting.length && sorting[0][0] == colIdx ) {
// Single column - already sorting on this column, modify the sort
@@ -6237,54 +6280,6 @@
sorting.push( [ colIdx, asSorting[0] ] );
sorting[0]._idx = 0;
}
-
- // Run the sort by calling a full redraw
- _fnReDraw( settings );
-
- // callback used for async user interaction
- if ( typeof callback == 'function' ) {
- callback( settings );
- }
- }
-
-
- /**
- * Attach a sort handler (click) to a node
- * @param {object} settings dataTables settings object
- * @param {node} attachTo node to attach the handler to
- * @param {int} colIdx column sorting index
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
- {
- var col = settings.aoColumns[ colIdx ];
-
- _fnBindAction( attachTo, {}, function (e) {
- /* If the column is not sortable - don't to anything */
- if ( col.bSortable === false ) {
- return;
- }
-
- // If processing is enabled use a timeout to allow the processing
- // display to be shown - otherwise to it synchronously
- if ( settings.oFeatures.bProcessing ) {
- _fnProcessingDisplay( settings, true );
-
- setTimeout( function() {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
-
- // In server-side processing, the draw callback will remove the
- // processing display
- if ( _fnDataSource( settings ) !== 'ssp' ) {
- _fnProcessingDisplay( settings, false );
- }
- }, 0 );
- }
- else {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
- }
- } );
}
@@ -6297,14 +6292,14 @@
function _fnSortingClasses( settings )
{
var oldSort = settings.aLastSort;
- var sortClass = settings.oClasses.sSortColumn;
+ var sortClass = settings.oClasses.order.position;
var sort = _fnSortFlatten( settings );
var features = settings.oFeatures;
- var i, ien, colIdx;
+ var i, iLen, colIdx;
if ( features.bSort && features.bSortClasses ) {
// Remove old sorting classes
- for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=oldSort.length ; i<iLen ; i++ ) {
colIdx = oldSort[i].src;
// Remove column sorting
@@ -6313,7 +6308,7 @@
}
// Add new column sorting
- for ( i=0, ien=sort.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=sort.length ; i<iLen ; i++ ) {
colIdx = sort[i].src;
$( _pluck( settings.aoData, 'anCells', colIdx ) )
@@ -6327,48 +6322,54 @@
// Get the data to sort a column, be it from cache, fresh (populating the
// cache), or from a sort formatter
- function _fnSortData( settings, idx )
+ function _fnSortData( settings, colIdx )
{
// Custom sorting function - provided by the sort data type
- var column = settings.aoColumns[ idx ];
+ var column = settings.aoColumns[ colIdx ];
var customSort = DataTable.ext.order[ column.sSortDataType ];
var customData;
if ( customSort ) {
- customData = customSort.call( settings.oInstance, settings, idx,
- _fnColumnIndexToVisible( settings, idx )
+ customData = customSort.call( settings.oInstance, settings, colIdx,
+ _fnColumnIndexToVisible( settings, colIdx )
);
}
// Use / populate cache
var row, cellData;
var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
+ var data = settings.aoData;
+
+ for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
+ // Sparse array
+ if (! data[rowIdx]) {
+ continue;
+ }
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
+ row = data[rowIdx];
if ( ! row._aSortData ) {
row._aSortData = [];
}
- if ( ! row._aSortData[idx] || customSort ) {
+ if ( ! row._aSortData[colIdx] || customSort ) {
cellData = customSort ?
- customData[i] : // If there was a custom sort function, use data from there
- _fnGetCellData( settings, i, idx, 'sort' );
+ customData[rowIdx] : // If there was a custom sort function, use data from there
+ _fnGetCellData( settings, rowIdx, colIdx, 'sort' );
- row._aSortData[ idx ] = formatter ?
- formatter( cellData ) :
+ row._aSortData[ colIdx ] = formatter ?
+ formatter( cellData, settings ) :
cellData;
}
}
}
-
/**
- * Save the state of a table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
+ * State information for a table
+ *
+ * @param {*} settings
+ * @returns State object
*/
function _fnSaveState ( settings )
{
@@ -6376,17 +6377,28 @@
return;
}
+ // Sort state saving uses [[idx, order]] structure.
+ var sorting = [];
+ _fnSortResolve(settings, sorting, settings.aaSorting );
+
/* Store the interesting variables */
+ var columns = settings.aoColumns;
var state = {
time: +new Date(),
start: settings._iDisplayStart,
length: settings._iDisplayLength,
- order: $.extend( true, [], settings.aaSorting ),
- search: _fnSearchToCamel( settings.oPreviousSearch ),
- columns: $.map( settings.aoColumns, function ( col, i ) {
+ order: sorting.map(function (sort) {
+ // If a column name is available, use it
+ return columns[sort[0]] && columns[sort[0]].sName
+ ? [ columns[sort[0]].sName, sort[1] ]
+ : sort.slice();
+ } ),
+ search: $.extend({}, settings.oPreviousSearch),
+ columns: settings.aoColumns.map( function ( col, i ) {
return {
+ name: col.sName,
visible: col.bVisible,
- search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
+ search: $.extend({}, settings.aoPreSearchCols[i])
};
} )
};
@@ -6408,7 +6420,7 @@
* @param {function} callback Callback to execute when the state has been loaded
* @memberof DataTable#oApi
*/
- function _fnLoadState ( settings, oInit, callback )
+ function _fnLoadState ( settings, init, callback )
{
if ( ! settings.oFeatures.bStateSave ) {
callback();
@@ -6430,8 +6442,10 @@
}
function _fnImplementState ( settings, s, callback) {
- var i, ien;
+ var i, iLen;
var columns = settings.aoColumns;
+ var currentNames = _pluck(settings.aoColumns, 'sName');
+
settings._bLoadingState = true;
// When StateRestore was introduced the state could now be implemented at any time
@@ -6444,15 +6458,6 @@
return;
}
- // Allow custom and plug-in manipulation functions to alter the saved data set and
- // cancelling of loading by returning false
- var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
- if ( $.inArray( false, abStateLoad ) !== -1 ) {
- settings._bLoadingState = false;
- callback();
- return;
- }
-
// Reject old data
var duration = settings.iStateDuration;
if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
@@ -6461,8 +6466,10 @@
return;
}
- // Number of columns have changed - all bets are off, no restore of settings
- if ( s.columns && columns.length !== s.columns.length ) {
+ // Allow custom and plug-in manipulation functions to alter the saved data set and
+ // cancelling of loading by returning false
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
+ if ( abStateLoad.indexOf(false) !== -1 ) {
settings._bLoadingState = false;
callback();
return;
@@ -6471,90 +6478,134 @@
// Store the saved state so it might be accessed at any time
settings.oLoadedState = $.extend( true, {}, s );
- // Restore key features - todo - for 1.11 this needs to be done by
- // subscribed events
+ // This is needed for ColReorder, which has to happen first to allow all
+ // the stored indexes to be usable. It is not publicly documented.
+ _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true );
+
+ // 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
if ( s.start !== undefined ) {
if(api === null) {
settings._iDisplayStart = s.start;
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 ) {
settings.aaSorting = [];
$.each( s.order, function ( i, col ) {
- settings.aaSorting.push( col[0] >= columns.length ?
- [ 0, col[1] ] :
- col
- );
+ var set = [ col[0], col[1] ];
+
+ // A column name was stored and should be used for restore
+ if (typeof col[0] === 'string') {
+ // Find the name from the current list of column names
+ var idx = currentNames.indexOf(col[0]);
+
+ if (idx < 0) {
+ // If the column was not found ignore it and continue
+ return;
+ }
+
+ set[0] = idx;
+ }
+ else if (set[0] >= columns.length) {
+ // If the column index is out of bounds ignore it and continue
+ return;
+ }
+
+ settings.aaSorting.push(set);
} );
}
// Search
if ( s.search !== undefined ) {
- $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
+ $.extend( settings.oPreviousSearch, s.search );
}
// Columns
if ( s.columns ) {
- for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
- var col = s.columns[i];
-
- // Visibility
- if ( col.visible !== undefined ) {
- // If the api is defined, the table has been initialised so we need to use it rather than internal settings
- if (api) {
- // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
- api.column(i).visible(col.visible, false);
+ var set = s.columns;
+ var incoming = _pluck(s.columns, 'name');
+
+ // Check if it is a 2.2 style state object with a `name` property for the columns, and if
+ // the name was defined. If so, then create a new array that will map the state object
+ // given, to the current columns (don't bother if they are already matching tho).
+ if (incoming.join('').length && incoming.join('') !== currentNames.join('')) {
+ set = [];
+
+ // For each column, try to find the name in the incoming array
+ for (i=0 ; i<currentNames.length ; i++) {
+ if (currentNames[i] != '') {
+ var idx = incoming.indexOf(currentNames[i]);
+
+ if (idx >= 0) {
+ set.push(s.columns[idx]);
+ }
+ else {
+ // No matching column name in the state's columns, so this might be a new
+ // column and thus can't have a state already.
+ set.push({});
+ }
}
else {
- columns[i].bVisible = col.visible;
+ // If no name, but other columns did have a name, then there is no knowing
+ // where this one came from originally so it can't be restored.
+ set.push({});
+ }
+ }
+ }
+
+ // If the number of columns to restore is different from current, then all bets are off.
+ if (set.length === columns.length) {
+ for ( i=0, iLen=set.length ; i<iLen ; i++ ) {
+ var col = set[i];
+
+ // Visibility
+ if ( col.visible !== undefined ) {
+ // If the api is defined, the table has been initialised so we need to use it rather than internal settings
+ if (api) {
+ // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
+ api.column(i).visible(col.visible, false);
+ }
+ else {
+ columns[i].bVisible = col.visible;
+ }
+ }
+
+ // Search
+ if ( col.search !== undefined ) {
+ $.extend( settings.aoPreSearchCols[i], col.search );
}
}
- // Search
- if ( col.search !== undefined ) {
- $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
+ // If the api is defined then we need to adjust the columns once the visibility has been changed
+ if (api) {
+ api.one('draw', function () {
+ api.columns.adjust();
+ });
}
}
-
- // If the api is defined then we need to adjust the columns once the visibility has been changed
- if (api) {
- api.columns.adjust();
- }
}
settings._bLoadingState = false;
_fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
callback();
- };
-
-
- /**
- * Return the settings object for a particular table
- * @param {node} table table we are using as a dataTable
- * @returns {object} Settings object - or null if not found
- * @memberof DataTable#oApi
- */
- function _fnSettingsFromNode ( table )
- {
- var settings = DataTable.settings;
- var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
-
- return idx !== -1 ?
- settings[ idx ] :
- null;
}
-
/**
* Log an error message
* @param {object} settings dataTables settings object
@@ -6570,7 +6621,7 @@
if ( tn ) {
msg += '. For more information about this error, please see '+
- 'http://datatables.net/tn/'+tn;
+ 'https://datatables.net/tn/'+tn;
}
if ( ! level ) {
@@ -6579,7 +6630,7 @@
var type = ext.sErrMode || ext.errMode;
if ( settings ) {
- _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
+ _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true );
}
if ( type == 'alert' ) {
@@ -6653,7 +6704,7 @@
var val;
for ( var prop in extender ) {
- if ( extender.hasOwnProperty(prop) ) {
+ if ( Object.prototype.hasOwnProperty.call(extender, prop) ) {
val = extender[prop];
if ( $.isPlainObject( val ) ) {
@@ -6676,51 +6727,46 @@
/**
- * Bind an event handers to allow a click or return key to activate the callback.
+ * Bind an event handler to allow a click or return key to activate the callback.
* This is good for accessibility since a return on the keyboard will have the
* same effect as a click, if the element has focus.
* @param {element} n Element to bind the action to
- * @param {object} oData Data object to pass to the triggered function
+ * @param {object|string} selector Selector (for delegated events) or data object
+ * to pass to the triggered function
* @param {function} fn Callback function for when the event is triggered
* @memberof DataTable#oApi
*/
- function _fnBindAction( n, oData, fn )
+ function _fnBindAction( n, selector, fn )
{
$(n)
- .on( 'click.DT', oData, function (e) {
- $(n).trigger('blur'); // Remove focus outline for mouse users
+ .on( 'click.DT', selector, function (e) {
+ fn(e);
+ } )
+ .on( 'keypress.DT', selector, function (e){
+ if ( e.which === 13 ) {
+ e.preventDefault();
fn(e);
- } )
- .on( 'keypress.DT', oData, function (e){
- if ( e.which === 13 ) {
- e.preventDefault();
- fn(e);
- }
- } )
- .on( 'selectstart.DT', function () {
- /* Take the brutal approach to cancelling text selection */
- return false;
- } );
+ }
+ } )
+ .on( 'selectstart.DT', selector, function () {
+ // Don't want a double click resulting in text selection
+ return false;
+ } );
}
/**
* Register a callback function. Easily allows a callback function to be added to
* an array store of callback functions that can then all be called together.
- * @param {object} oSettings dataTables settings object
- * @param {string} sStore Name of the array storage for the callbacks in oSettings
+ * @param {object} settings dataTables settings object
+ * @param {string} store Name of the array storage for the callbacks in oSettings
* @param {function} fn Function to be called back
- * @param {string} sName Identifying name for the callback (i.e. a label)
* @memberof DataTable#oApi
*/
- function _fnCallbackReg( oSettings, sStore, fn, sName )
+ function _fnCallbackReg( settings, store, fn )
{
- if ( fn )
- {
- oSettings[sStore].push( {
- "fn": fn,
- "sName": sName
- } );
+ if ( fn ) {
+ settings[store].push(fn);
}
}
@@ -6737,22 +6783,33 @@
* null no trigger is fired
* @param {array} args Array of arguments to pass to the callback function /
* trigger
+ * @param {boolean} [bubbles] True if the event should bubble
* @memberof DataTable#oApi
*/
- function _fnCallbackFire( settings, callbackArr, eventName, args )
+ function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles )
{
var ret = [];
if ( callbackArr ) {
- ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
- return val.fn.apply( settings.oInstance, args );
+ ret = settings[callbackArr].slice().reverse().map( function (val) {
+ return val.apply( settings.oInstance, args );
} );
}
- if ( eventName !== null ) {
+ if ( eventName !== null) {
var e = $.Event( eventName+'.dt' );
+ var table = $(settings.nTable);
+
+ // Expose the DataTables API on the event object for easy access
+ e.dt = settings.api;
- $(settings.nTable).trigger( e, args );
+ table[bubbles ? 'trigger' : 'triggerHandler']( e, args );
+
+ // If not yet attached to the document, trigger the event
+ // on the body directly to sort of simulate the bubble
+ if (bubbles && table.parents('body').length === 0) {
+ $('body').trigger( e, args );
+ }
ret.push( e.result );
}
@@ -6820,12 +6877,97 @@
if ( settings.oFeatures.bServerSide ) {
return 'ssp';
}
- else if ( settings.ajax || settings.sAjaxSource ) {
+ else if ( settings.ajax ) {
return 'ajax';
}
return 'dom';
}
+ /**
+ * Common replacement for language strings
+ *
+ * @param {*} settings DT settings object
+ * @param {*} str String with values to replace
+ * @param {*} entries Plural number for _ENTRIES_ - can be undefined
+ * @returns String
+ */
+ function _fnMacros ( settings, str, entries )
+ {
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is
+ // used only internally
+ var
+ formatter = settings.fnFormatNumber,
+ start = settings._iDisplayStart+1,
+ len = settings._iDisplayLength,
+ vis = settings.fnRecordsDisplay(),
+ max = settings.fnRecordsTotal(),
+ all = len === -1;
+
+ return str.
+ replace(/_START_/g, formatter.call( settings, start ) ).
+ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
+ replace(/_MAX_/g, formatter.call( settings, max ) ).
+ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
+ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
+ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ).
+ replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ).
+ replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ).
+ replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
+ }
+
+ /**
+ * Add elements to an array as quickly as possible, but stack safe.
+ *
+ * @param {*} arr Array to add the data to
+ * @param {*} data Data array that is to be added
+ * @returns
+ */
+ function _fnArrayApply(arr, data) {
+ if (! data) {
+ return;
+ }
+
+ // Chrome can throw a max stack error if apply is called with
+ // too large an array, but apply is faster.
+ if (data.length < 10000) {
+ arr.push.apply(arr, data);
+ }
+ else {
+ for (i=0 ; i<data.length ; i++) {
+ arr.push(data[i]);
+ }
+ }
+ }
+
+ /**
+ * Add one or more listeners to the table
+ *
+ * @param {*} that JQ for the table
+ * @param {*} name Event name
+ * @param {*} src Listener(s)
+ */
+ function _fnListener(that, name, src) {
+ if (!Array.isArray(src)) {
+ src = [src];
+ }
+
+ for (i=0 ; i<src.length ; i++) {
+ that.on(name + '.dt', src[i]);
+ }
+ }
+
+ /**
+ * Escape HTML entities in strings, in an object
+ */
+ function _fnEscapeObject(obj) {
+ if (DataTable.ext.escape.attributes) {
+ $.each(obj, function (key, val) {
+ obj[key] = _escapeHtml(val);
+ })
+ }
+
+ return obj;
+ }
@@ -6901,20 +7043,18 @@
{
var idx, jq;
var settings = DataTable.settings;
- var tables = $.map( settings, function (el, i) {
- return el.nTable;
- } );
+ var tables = _pluck(settings, 'nTable');
if ( ! mixed ) {
return [];
}
- else if ( mixed.nTable && mixed.oApi ) {
+ else if ( mixed.nTable && mixed.oFeatures ) {
// DataTables settings object
return [ mixed ];
}
else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
// Table node
- idx = $.inArray( mixed, tables );
+ idx = tables.indexOf(mixed);
return idx !== -1 ? [ settings[idx] ] : null;
}
else if ( mixed && typeof mixed.settings === 'function' ) {
@@ -6922,18 +7062,17 @@
}
else if ( typeof mixed === 'string' ) {
// jQuery selector
- jq = $(mixed);
+ jq = $(mixed).get();
}
else if ( mixed instanceof $ ) {
// jQuery object (also DataTables instance)
- jq = mixed;
+ jq = mixed.get();
}
if ( jq ) {
- return jq.map( function(i) {
- idx = $.inArray( this, tables );
- return idx !== -1 ? settings[idx] : null;
- } ).toArray();
+ return settings.filter(function (v, idx) {
+ return jq.includes(tables[idx]);
+ });
}
};
@@ -6990,7 +7129,7 @@
*
* @example
* // Initialisation as a constructor
- * var api = new $.fn.DataTable.Api( 'table.dataTable' );
+ * var api = new DataTable.Api( 'table.dataTable' );
*/
_Api = function ( context, data )
{
@@ -6998,6 +7137,7 @@
return new _Api( context, data );
}
+ var i;
var settings = [];
var ctxSettings = function ( o ) {
var a = _toSettings( o );
@@ -7007,7 +7147,7 @@
};
if ( Array.isArray( context ) ) {
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
+ for ( i=0 ; i<context.length ; i++ ) {
ctxSettings( context[i] );
}
}
@@ -7016,12 +7156,12 @@
}
// Remove duplicates
- this.context = _unique( settings );
+ this.context = settings.length > 1
+ ? _unique( settings )
+ : settings;
// Initial data
- if ( data ) {
- $.merge( this, data );
- }
+ _fnArrayApply(this, data);
// selector
this.selector = {
@@ -7043,29 +7183,22 @@
return this.count() !== 0;
},
-
- concat: __arrayProto.concat,
-
-
context: [], // array of table settings objects
-
count: function ()
{
return this.flatten().length;
},
-
each: function ( fn )
{
- for ( var i=0, ien=this.length ; i<ien; i++ ) {
+ for ( var i=0, iLen=this.length ; i<iLen; i++ ) {
fn.call( this, this[i], i, this );
}
return this;
},
-
eq: function ( idx )
{
var ctx = this.context;
@@ -7075,51 +7208,37 @@
null;
},
-
filter: function ( fn )
{
- var a = [];
-
- if ( __arrayProto.filter ) {
- a = __arrayProto.filter.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- if ( fn.call( this, this[i], i, this ) ) {
- a.push( this[i] );
- }
- }
- }
+ var a = __arrayProto.filter.call( this, fn, this );
return new _Api( this.context, a );
},
-
flatten: function ()
{
var a = [];
+
return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
},
+ get: function ( idx )
+ {
+ return this[ idx ];
+ },
join: __arrayProto.join,
-
- indexOf: __arrayProto.indexOf || function (obj, start)
- {
- for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
- if ( this[i] === obj ) {
- return i;
- }
- }
- return -1;
+ includes: function ( find ) {
+ return this.indexOf( find ) === -1 ? false : true;
},
+ indexOf: __arrayProto.indexOf,
+
iterator: function ( flatten, type, fn, alwaysNew ) {
var
a = [], ret,
- i, ien, j, jen,
+ i, iLen, j, jen,
context = this.context,
rows, items, item,
selector = this.selector;
@@ -7132,7 +7251,7 @@
flatten = false;
}
- for ( i=0, ien=context.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=context.length ; i<iLen ; i++ ) {
var apiInst = new _Api( context[i] );
if ( type === 'table' ) {
@@ -7150,7 +7269,7 @@
a.push( ret );
}
}
- else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
+ else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
// columns and rows share the same structure.
// 'this' is an array of column indexes for each context
items = this[i];
@@ -7187,110 +7306,94 @@
return this;
},
-
- lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
- {
- // Bit cheeky...
- return this.indexOf.apply( this.toArray.reverse(), arguments );
- },
-
+ lastIndexOf: __arrayProto.lastIndexOf,
length: 0,
-
map: function ( fn )
{
- var a = [];
-
- if ( __arrayProto.map ) {
- a = __arrayProto.map.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- a.push( fn.call( this, this[i], i ) );
- }
- }
+ var a = __arrayProto.map.call( this, fn, this );
return new _Api( this.context, a );
},
-
pluck: function ( prop )
{
+ var fn = DataTable.util.get(prop);
+
return this.map( function ( el ) {
- return el[ prop ];
+ return fn(el);
} );
},
pop: __arrayProto.pop,
-
push: __arrayProto.push,
+ reduce: __arrayProto.reduce,
- // Does not return an API instance
- reduce: __arrayProto.reduce || function ( fn, init )
- {
- return _fnReduce( this, fn, init, 0, this.length, 1 );
- },
-
-
- reduceRight: __arrayProto.reduceRight || function ( fn, init )
- {
- return _fnReduce( this, fn, init, this.length-1, -1, -1 );
- },
-
+ reduceRight: __arrayProto.reduceRight,
reverse: __arrayProto.reverse,
-
// Object with rows, columns and opts
selector: null,
-
shift: __arrayProto.shift,
-
slice: function () {
return new _Api( this.context, this );
},
-
- sort: __arrayProto.sort, // ? name - order?
-
+ sort: __arrayProto.sort,
splice: __arrayProto.splice,
-
toArray: function ()
{
return __arrayProto.slice.call( this );
},
-
to$: function ()
{
return $( this );
},
-
toJQuery: function ()
{
return $( this );
},
-
unique: function ()
{
- return new _Api( this.context, _unique(this) );
+ return new _Api( this.context, _unique(this.toArray()) );
},
-
unshift: __arrayProto.unshift
} );
+ function _api_scope( scope, fn, struct ) {
+ return function () {
+ var ret = fn.apply( scope || this, arguments );
+
+ // Method extension
+ _Api.extend( ret, ret, struct.methodExt );
+ return ret;
+ };
+ }
+
+ function _api_find( src, name ) {
+ for ( var i=0, iLen=src.length ; i<iLen ; i++ ) {
+ if ( src[i].name === name ) {
+ return src[i];
+ }
+ }
+ return null;
+ }
+
+ window.__apiStruct = __apiStruct;
+
_Api.extend = function ( scope, obj, ext )
{
// Only extend API instances and static properties of the API
@@ -7299,24 +7402,19 @@
}
var
- i, ien,
- struct,
- methodScoping = function ( scope, fn, struc ) {
- return function () {
- var ret = fn.apply( scope, arguments );
-
- // Method extension
- _Api.extend( ret, ret, struc.methodExt );
- return ret;
- };
- };
+ i, iLen,
+ struct;
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=ext.length ; i<iLen ; i++ ) {
struct = ext[i];
+ if (struct.name === '__proto__') {
+ continue;
+ }
+
// Value
obj[ struct.name ] = struct.type === 'function' ?
- methodScoping( scope, struct.val, struct ) :
+ _api_scope( scope, struct.val, struct ) :
struct.type === 'object' ?
{} :
struct.val;
@@ -7328,17 +7426,6 @@
}
};
-
- // @todo - Is there need for an augment function?
- // _Api.augment = function ( inst, name )
- // {
- // // Find src object in the structure from the name
- // var parts = name.split('.');
-
- // _Api.extend( inst, obj );
- // };
-
-
// [
// {
// name: 'data' -- string - Property name
@@ -7362,6 +7449,7 @@
// }
// ]
+
_Api.register = _api_register = function ( name, val )
{
if ( Array.isArray( name ) ) {
@@ -7372,27 +7460,18 @@
}
var
- i, ien,
+ i, iLen,
heir = name.split('.'),
struct = __apiStruct,
key, method;
- var find = function ( src, name ) {
- for ( var i=0, ien=src.length ; i<ien ; i++ ) {
- if ( src[i].name === name ) {
- return src[i];
- }
- }
- return null;
- };
-
- for ( i=0, ien=heir.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=heir.length ; i<iLen ; i++ ) {
method = heir[i].indexOf('()') !== -1;
key = method ?
heir[i].replace('()', '') :
heir[i];
- var src = find( struct, key );
+ var src = _api_find( struct, key );
if ( ! src ) {
src = {
name: key,
@@ -7404,7 +7483,7 @@
struct.push( src );
}
- if ( i === ien-1 ) {
+ if ( i === iLen-1 ) {
src.val = val;
src.type = typeof val === 'function' ?
'function' :
@@ -7458,9 +7537,17 @@
var __table_selector = function ( selector, a )
{
if ( Array.isArray(selector) ) {
- return $.map( selector, function (item) {
- return __table_selector(item, a);
- } );
+ var result = [];
+
+ selector.forEach(function (sel) {
+ var inner = __table_selector(sel, a);
+
+ _fnArrayApply(result, inner);
+ });
+
+ return result.filter( function (item) {
+ return item;
+ });
}
// Integer is used to pick out a table by index
@@ -7469,15 +7556,15 @@
}
// Perform a jQuery selector on the table nodes
- var nodes = $.map( a, function (el, i) {
+ var nodes = a.map( function (el) {
return el.nTable;
} );
return $(nodes)
.filter( selector )
- .map( function (i) {
+ .map( function () {
// Need to translate back from the table node to the settings
- var idx = $.inArray( this, nodes );
+ var idx = nodes.indexOf(this);
return a[ idx ];
} )
.toArray();
@@ -7514,41 +7601,119 @@
tables;
} );
+ // Common methods, combined to reduce size
+ [
+ ['nodes', 'node', 'nTable'],
+ ['body', 'body', 'nTBody'],
+ ['header', 'header', 'nTHead'],
+ ['footer', 'footer', 'nTFoot'],
+ ].forEach(function (item) {
+ _api_registerPlural(
+ 'tables().' + item[0] + '()',
+ 'table().' + item[1] + '()' ,
+ function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx[item[2]];
+ }, 1 );
+ }
+ );
+ });
- _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTable;
- }, 1 );
- } );
+ // Structure methods
+ [
+ ['header', 'aoHeader'],
+ ['footer', 'aoFooter'],
+ ].forEach(function (item) {
+ _api_register( 'table().' + item[0] + '.structure()' , function (selector) {
+ var indexes = this.columns(selector).indexes().flatten().toArray();
+ var ctx = this.context[0];
+ var structure = _fnHeaderLayout(ctx, ctx[item[1]], indexes);
+ // The structure is in column index order - but from this method we want the return to be
+ // in the columns() selector API order. In order to do that we need to map from one form
+ // to the other
+ var orderedIndexes = indexes.slice().sort(function (a, b) {
+ return a - b;
+ });
- _api_registerPlural( 'tables().body()', 'table().body()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTBody;
- }, 1 );
- } );
+ return structure.map(function (row) {
+ return indexes.map(function (colIdx) {
+ return row[orderedIndexes.indexOf(colIdx)];
+ });
+ });
+ });
+ });
- _api_registerPlural( 'tables().header()', 'table().header()' , function () {
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
return this.iterator( 'table', function ( ctx ) {
- return ctx.nTHead;
+ return ctx.nTableWrapper;
}, 1 );
} );
+ _api_register( 'tables().every()', function ( fn ) {
+ var that = this;
- _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTFoot;
- }, 1 );
- } );
+ return this.iterator('table', function (s, i) {
+ fn.call(that.table(i), i);
+ });
+ });
+ _api_register( 'caption()', function ( value, side ) {
+ var context = this.context;
+
+ // Getter - return existing node's content
+ if ( value === undefined ) {
+ var caption = context[0].captionNode;
+
+ return caption && context.length ?
+ caption.innerHTML :
+ null;
+ }
- _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
return this.iterator( 'table', function ( ctx ) {
- return ctx.nTableWrapper;
+ var table = $(ctx.nTable);
+ var caption = $(ctx.captionNode);
+ var container = $(ctx.nTableWrapper);
+
+ // Create the node if it doesn't exist yet
+ if ( ! caption.length ) {
+ caption = $('<caption/>').html( value );
+ ctx.captionNode = caption[0];
+
+ // If side isn't set, we need to insert into the document to let the
+ // CSS decide so we can read it back, otherwise there is no way to
+ // know if the CSS would put it top or bottom for scrolling
+ if (! side) {
+ table.prepend(caption);
+
+ side = caption.css('caption-side');
+ }
+ }
+
+ caption.html( value );
+
+ if ( side ) {
+ caption.css( 'caption-side', side );
+ caption[0]._captionSide = side;
+ }
+
+ if (container.find('div.dataTables_scroll').length) {
+ var selector = (side === 'top' ? 'Head' : 'Foot');
+
+ container.find('div.dataTables_scroll'+ selector +' table').prepend(caption);
+ }
+ else {
+ table.prepend(caption);
+ }
}, 1 );
} );
+ _api_register( 'caption.node()', function () {
+ var ctx = this.context;
+
+ return ctx.length ? ctx[0].captionNode : null;
+ } );
/**
@@ -7622,7 +7787,7 @@
* * `recordsDisplay` - Data set length once the current filtering criterion
* are applied.
*/
- _api_register( 'page.info()', function ( action ) {
+ _api_register( 'page.info()', function () {
if ( this.context.length === 0 ) {
return undefined;
}
@@ -7660,7 +7825,7 @@
*/
_api_register( 'page.len()', function ( len ) {
// Note that we can't call this function 'length()' because `length`
- // is a Javascript property of functions which defines how many arguments
+ // is a JavaScript property of functions which defines how many arguments
// the function expects.
if ( len === undefined ) {
return this.context.length !== 0 ?
@@ -7699,15 +7864,16 @@
}
// Trigger xhr
- _fnBuildAjax( settings, [], function( json ) {
+ _fnBuildAjax( settings, {}, function( json ) {
_fnClearTable( settings );
var data = _fnAjaxDataSrc( settings, json );
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=data.length ; i<iLen ; i++ ) {
_fnAddData( settings, data[i] );
}
_fnReDraw( settings, holdPosition );
+ _fnInitComplete( settings );
_fnProcessingDisplay( settings, false );
} );
}
@@ -7784,11 +7950,9 @@
}
ctx = ctx[0];
- return ctx.ajax ?
- $.isPlainObject( ctx.ajax ) ?
- ctx.ajax.url :
- ctx.ajax :
- ctx.sAjaxSource;
+ return $.isPlainObject( ctx.ajax ) ?
+ ctx.ajax.url :
+ ctx.ajax;
}
// set
@@ -7799,9 +7963,6 @@
else {
settings.ajax = url;
}
- // No need to consider sAjaxSource here since DataTables gives priority
- // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
- // value of `sAjaxSource` redundant.
} );
} );
@@ -7830,7 +7991,7 @@
{
var
out = [], res,
- a, i, ien, j, jen,
+ i, iLen,
selectorType = typeof selector;
// Can't just check for isArray here, as an API or jQuery instance might be
@@ -7839,25 +8000,23 @@
selector = [ selector ];
}
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
- // Only split on simple strings - complex expressions will be jQuery selectors
- a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
- selector[i].split(',') :
- [ selector[i] ];
+ for ( i=0, iLen=selector.length ; i<iLen ; i++ ) {
+ res = selectFn( typeof selector[i] === 'string' ? selector[i].trim() : selector[i] );
- for ( j=0, jen=a.length ; j<jen ; j++ ) {
- res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
+ // Remove empty items
+ res = res.filter( function (item) {
+ return item !== null && item !== undefined;
+ });
- if ( res && res.length ) {
- out = out.concat( res );
- }
+ if ( res && res.length ) {
+ out = out.concat( res );
}
}
// selector extensions
var ext = _ext.selector[ type ];
if ( ext.length ) {
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=ext.length ; i<iLen ; i++ ) {
out = ext[i]( settings, opts, out );
}
}
@@ -7879,6 +8038,7 @@
}
return $.extend( {
+ columnOrder: 'implied',
search: 'none',
order: 'current',
page: 'all'
@@ -7886,24 +8046,24 @@
};
- var _selector_first = function ( inst )
+ // Reduce the API instance to the first item found
+ var _selector_first = function ( old )
{
- // Reduce the API instance to the first item found
- for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
- if ( inst[i].length > 0 ) {
- // Assign the first element to the first item in the instance
- // and truncate the instance and context
- inst[0] = inst[i];
- inst[0].length = 1;
- inst.length = 1;
- inst.context = [ inst.context[i] ];
+ var inst = new _Api(old.context[0]);
- return inst;
- }
+ // Use a push rather than passing to the constructor, since it will
+ // merge arrays down automatically, which isn't what is wanted here
+ if (old.length) {
+ inst.push( old[0] );
+ }
+
+ inst.selector = old.selector;
+
+ // Limit to a single row / column / cell
+ if (inst.length && inst[0].length > 1) {
+ inst[0].splice(1);
}
- // Not found - return an empty instance
- inst.length = 0;
return inst;
};
@@ -7911,7 +8071,7 @@
var _selector_row_indexes = function ( settings, opts )
{
var
- i, ien, tmp, a=[],
+ i, iLen, tmp, a=[],
displayFiltered = settings.aiDisplay,
displayMaster = settings.aiDisplayMaster;
@@ -7929,11 +8089,12 @@
[] :
_range( 0, displayMaster.length );
}
- else if ( page == 'current' ) {
+
+ if ( page == 'current' ) {
// Current page implies that order=current and filter=applied, since it is
// fairly senseless otherwise, regardless of what order and search actually
// are
- for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
+ for ( i=settings._iDisplayStart, iLen=settings.fnDisplayEnd() ; i<iLen ; i++ ) {
a.push( displayFiltered[i] );
}
}
@@ -7948,24 +8109,28 @@
// O(n+m) solution by creating a hash map
var displayFilteredMap = {};
- for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=displayFiltered.length ; i<iLen ; i++ ) {
displayFilteredMap[displayFiltered[i]] = null;
}
- a = $.map( displayMaster, function (el) {
- return ! displayFilteredMap.hasOwnProperty(el) ?
- el :
- null;
- } );
+ displayMaster.forEach(function (item) {
+ if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) {
+ a.push(item);
+ }
+ });
}
}
else if ( order == 'index' || order == 'original' ) {
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=settings.aoData.length ; i<iLen ; i++ ) {
+ if (! settings.aoData[i]) {
+ continue;
+ }
+
if ( search == 'none' ) {
a.push( i );
}
else { // applied | removed
- tmp = $.inArray( i, displayFiltered );
+ tmp = displayFiltered.indexOf(i);
if ((tmp === -1 && search == 'removed') ||
(tmp >= 0 && search == 'applied') )
@@ -7975,6 +8140,25 @@
}
}
}
+ else if ( typeof order === 'number' ) {
+ // Order the rows by the given column
+ var ordered = _fnSort(settings, order, 'asc');
+
+ if (search === 'none') {
+ a = ordered;
+ }
+ else { // applied | removed
+ for (i=0; i<ordered.length; i++) {
+ tmp = displayFiltered.indexOf(ordered[i]);
+
+ if ((tmp === -1 && search == 'removed') ||
+ (tmp >= 0 && search == 'applied') )
+ {
+ a.push( ordered[i] );
+ }
+ }
+ }
+ }
return a;
};
@@ -7995,7 +8179,6 @@
var rows;
var run = function ( sel ) {
var selInt = _intVal( sel );
- var i, ien;
var aoData = settings.aoData;
// Short cut - selector is a number and no options provided (default is
@@ -8009,7 +8192,7 @@
rows = _selector_row_indexes( settings, opts );
}
- if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
+ if ( selInt !== null && rows.indexOf(selInt) !== -1 ) {
// Selector - integer
return [ selInt ];
}
@@ -8020,7 +8203,7 @@
// Selector - function
if ( typeof sel === 'function' ) {
- return $.map( rows, function (idx) {
+ return rows.map( function (idx) {
var row = aoData[ idx ];
return sel( idx, row._aData, row.nTr ) ? idx : null;
} );
@@ -8086,7 +8269,13 @@
.toArray();
};
- return _selector_run( 'row', selector, run, settings, opts );
+ var matched = _selector_run( 'row', selector, run, settings, opts );
+
+ if (opts.order === 'current' || opts.order === 'applied') {
+ _fnSortDisplay(settings, matched);
+ }
+
+ return matched;
};
@@ -8149,7 +8338,7 @@
var context = this.context;
// `iterator` will drop undefined values, but in this case we want them
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=context.length ; i<iLen ; i++ ) {
for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
a.push( (hash === true ? '#' : '' )+ id );
@@ -8160,38 +8349,15 @@
} );
_api_registerPlural( 'rows().remove()', 'row().remove()', function () {
- var that = this;
-
- this.iterator( 'row', function ( settings, row, thatIdx ) {
+ this.iterator( 'row', function ( settings, row ) {
var data = settings.aoData;
var rowData = data[ row ];
- var i, ien, j, jen;
- var loopRow, loopCells;
-
- data.splice( row, 1 );
-
- // Update the cached indexes
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- loopRow = data[i];
- loopCells = loopRow.anCells;
-
- // Rows
- if ( loopRow.nTr !== null ) {
- loopRow.nTr._DT_RowIndex = i;
- }
-
- // Cells
- if ( loopCells !== null ) {
- for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
- loopCells[j]._DT_CellIndex.row = i;
- }
- }
- }
// Delete from the display arrays
- _fnDeleteIndex( settings.aiDisplayMaster, row );
- _fnDeleteIndex( settings.aiDisplay, row );
- _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
+ var idx = settings.aiDisplayMaster.indexOf(row);
+ if (idx !== -1) {
+ settings.aiDisplayMaster.splice(idx, 1);
+ }
// For server-side processing tables - subtract the deleted row from the count
if ( settings._iRecordsDisplay > 0 ) {
@@ -8206,12 +8372,8 @@
if ( id !== undefined ) {
delete settings.aIds[ id ];
}
- } );
- this.iterator( 'table', function ( settings ) {
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- settings.aoData[i].idx = i;
- }
+ data[row] = null;
} );
return this;
@@ -8220,10 +8382,10 @@
_api_register( 'rows.add()', function ( rows ) {
var newRows = this.iterator( 'table', function ( settings ) {
- var row, i, ien;
+ var row, i, iLen;
var out = [];
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
row = rows[i];
if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
@@ -8240,7 +8402,7 @@
// Return an Api.rows() extended instance, so rows().nodes() etc can be used
var modRows = this.rows( -1 );
modRows.pop();
- $.merge( modRows, newRows );
+ _fnArrayApply(modRows, newRows);
return modRows;
} );
@@ -8262,7 +8424,7 @@
if ( data === undefined ) {
// Get
- return ctx.length && this.length ?
+ return ctx.length && this.length && this[0].length ?
ctx[0].aoData[ this[0] ]._aData :
undefined;
}
@@ -8286,9 +8448,15 @@
_api_register( 'row().node()', function () {
var ctx = this.context;
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ].nTr || null :
- null;
+ if (ctx.length && this.length && this[0].length) {
+ var row = ctx[0].aoData[ this[0] ];
+
+ if (row && row.nTr) {
+ return row.nTr;
+ }
+ }
+
+ return null;
} );
@@ -8314,34 +8482,48 @@
$(document).on('plugin-init.dt', function (e, context) {
var api = new _Api( context );
- api.on( 'stateSaveParams', function ( e, settings, d ) {
+ api.on( 'stateSaveParams.DT', function ( e, settings, d ) {
// This could be more compact with the API, but it is a lot faster as a simple
// internal loop
var idFn = settings.rowIdFn;
- var data = settings.aoData;
+ var rows = settings.aiDisplayMaster;
var ids = [];
- for (var i=0 ; i<data.length ; i++) {
- if (data[i]._detailsShow) {
- ids.push( '#' + idFn(data[i]._aData) );
+ for (var i=0 ; i<rows.length ; i++) {
+ var rowIdx = rows[i];
+ var data = settings.aoData[rowIdx];
+
+ if (data._detailsShow) {
+ ids.push( '#' + idFn(data._aData) );
}
}
d.childRows = ids;
- })
+ });
- var loaded = api.state.loaded();
+ // For future state loads (e.g. with StateRestore)
+ api.on( 'stateLoaded.DT', function (e, settings, state) {
+ __details_state_load( api, state );
+ });
- if ( loaded && loaded.childRows ) {
+ // And the initial load state
+ __details_state_load( api, api.state.loaded() );
+ });
+
+ var __details_state_load = function (api, state)
+ {
+ if ( state && state.childRows ) {
api
- .rows( $.map(loaded.childRows, function (id){
- return id.replace(/:/g, '\\:')
+ .rows( state.childRows.map(function (id) {
+ // Escape any `:` characters from the row id. Accounts for
+ // already escaped characters.
+ return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:");
}) )
.every( function () {
- _fnCallbackFire( context, null, 'requestChild', [ this ] )
+ _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] )
});
}
- });
+ }
var __details_add = function ( ctx, row, data, klass )
{
@@ -8350,7 +8532,7 @@
var addRow = function ( r, k ) {
// Recursion to allow for arrays of jQuery objects
if ( Array.isArray( r ) || r instanceof $ ) {
- for ( var i=0, ien=r.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=r.length ; i<iLen ; i++ ) {
addRow( r[i], k );
}
return;
@@ -8359,15 +8541,18 @@
// If we get a TR element, then just add it directly - up to the dev
// to add the correct number of columns etc
if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
+ r.setAttribute( 'data-dt-row', row.idx );
rows.push( r );
}
else {
// Otherwise create a row with a wrapper
- var created = $('<tr><td></td></tr>').addClass( k );
+ var created = $('<tr><td></td></tr>')
+ .attr( 'data-dt-row', row.idx )
+ .addClass( k );
+
$('td', created)
.addClass( k )
- .html( r )
- [0].colSpan = _fnVisbleColumns( ctx );
+ .html( r )[0].colSpan = _fnVisibleColumns( ctx );
rows.push( created[0] );
}
@@ -8405,7 +8590,7 @@
var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
if ( row && row._details ) {
- row._details.remove();
+ row._details.detach();
row._detailsShow = undefined;
row._details = undefined;
@@ -8448,7 +8633,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;
@@ -8472,20 +8657,26 @@
} );
// Column visibility change - update the colspan
- api.on( colvisEvent, function ( e, ctx, idx, vis ) {
+ api.on( colvisEvent, function ( e, ctx ) {
if ( settings !== ctx ) {
return;
}
// Update the colspan for the details rows (note, only if it already has
// a colspan)
- var row, visible = _fnVisbleColumns( ctx );
+ var row, visible = _fnVisibleColumns( ctx );
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ for ( var i=0, iLen=data.length ; i<iLen ; i++ ) {
row = data[i];
- if ( row._details ) {
- row._details.children('td[colspan]').attr('colspan', visible );
+ if ( row && row._details ) {
+ row._details.each(function () {
+ var el = $(this).children('td');
+
+ if (el.length == 1) {
+ el.attr('colspan', visible);
+ }
+ });
}
}
} );
@@ -8496,8 +8687,8 @@
return;
}
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- if ( data[i]._details ) {
+ for ( var i=0, iLen=data.length ; i<iLen ; i++ ) {
+ if ( data[i] && data[i]._details ) {
__details_remove( api, i );
}
}
@@ -8519,9 +8710,9 @@
if ( data === undefined ) {
// get
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ]._details :
- undefined;
+ return ctx.length && this.length && ctx[0].aoData[ this[0] ]
+ ? ctx[0].aoData[ this[0] ]._details
+ : undefined;
}
else if ( data === true ) {
// show
@@ -8543,7 +8734,7 @@
_api_register( [
_child_obj+'.show()',
_child_mth+'.show()' // only when `child()` was called with parameters (without
- ], function ( show ) { // it returns an object and this method is not executed)
+ ], function () { // it returns an object and this method is not executed)
__details_display( this, true );
return this;
} );
@@ -8570,7 +8761,7 @@
_api_register( _child_obj+'.isShown()', function () {
var ctx = this.context;
- if ( ctx.length && this.length ) {
+ if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) {
// _detailsShown as false or undefined will fall through to return false
return ctx[0].aoData[ this[0] ]._detailsShow || false;
}
@@ -8593,27 +8784,77 @@
// can be an array of these items, comma separated list, or an array of comma
// separated lists
- var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
+ var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/;
// r1 and r2 are redundant - but it means that the parameters match for the
// iterator callback in columns().data()
- var __columnData = function ( settings, column, r1, r2, rows ) {
+ var __columnData = function ( settings, column, r1, r2, rows, type ) {
var a = [];
- for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
- a.push( _fnGetCellData( settings, rows[row], column ) );
+ for ( var row=0, iLen=rows.length ; row<iLen ; row++ ) {
+ a.push( _fnGetCellData( settings, rows[row], column, type ) );
}
return a;
};
+ var __column_header = function ( settings, column, row ) {
+ var header = settings.aoHeader;
+ var titleRow = settings.titleRow;
+ var target = null;
+
+ if (row !== undefined) {
+ target = row;
+ }
+ else if (titleRow === true) { // legacy orderCellsTop support
+ target = 0;
+ }
+ else if (titleRow === false) {
+ target = header.length - 1;
+ }
+ else if (titleRow !== null) {
+ target = titleRow;
+ }
+ else {
+ // Automatic - find the _last_ unique cell from the top that is not empty (last for
+ // backwards compatibility)
+ for (var i=0 ; i<header.length ; i++) {
+ if (header[i][column].unique && $('.dt-column-title', header[i][column].cell).text()) {
+ target = i;
+ }
+ }
+
+ if (target === null) {
+ target = 0;
+ }
+ }
+
+ return header[target][column].cell;
+ };
+
+ var __column_header_cells = function (header) {
+ var out = [];
+
+ for (var i=0 ; i<header.length ; i++) {
+ for (var j=0 ; j<header[i].length ; j++) {
+ var cell = header[i][j].cell;
+
+ if (!out.includes(cell)) {
+ out.push(cell);
+ }
+ }
+ }
+
+ return out;
+ }
+
var __column_selector = function ( settings, selector, opts )
{
var
columns = settings.aoColumns,
- names = _pluck( columns, 'sName' ),
- nodes = _pluck( columns, 'nTh' );
-
+ names, titles,
+ nodes = __column_header_cells(settings.aoHeader);
+
var run = function ( s ) {
var selInt = _intVal( s );
@@ -8634,13 +8875,13 @@
if ( typeof s === 'function' ) {
var rows = _selector_row_indexes( settings, opts );
- return $.map( columns, function (col, idx) {
+ return columns.map(function (col, idx) {
return s(
idx,
__columnData( settings, idx, 0, 0, rows ),
- nodes[ idx ]
+ __column_header( settings, idx )
) ? idx : null;
- } );
+ });
}
// jQuery or string selector
@@ -8652,24 +8893,62 @@
switch( match[2] ) {
case 'visIdx':
case 'visible':
- var idx = parseInt( match[1], 10 );
- // Visible index given, convert to column index
- if ( idx < 0 ) {
- // Counting from the right
- var visColumns = $.map( columns, function (col,i) {
- return col.bVisible ? i : null;
- } );
- return [ visColumns[ visColumns.length + idx ] ];
+ // Selector is a column index
+ if (match[1] && match[1].match(/^\d+$/)) {
+ var idx = parseInt( match[1], 10 );
+
+ // Visible index given, convert to column index
+ if ( idx < 0 ) {
+ // Counting from the right
+ var visColumns = columns.map( function (col,i) {
+ return col.bVisible ? i : null;
+ } );
+ return [ visColumns[ visColumns.length + idx ] ];
+ }
+ // Counting from the left
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
}
- // Counting from the left
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
+
+ return columns.map( function (col, idx) {
+ // Not visible, can't match
+ if (! col.bVisible) {
+ return null;
+ }
+
+ if (col.responsiveVisible === false) {
+ return null;
+ }
+
+ // Selector
+ if (match[1]) {
+ return $(nodes[idx]).filter(match[1]).length > 0 ? idx : null;
+ }
+
+ // `:visible` on its own
+ return idx;
+ } );
case 'name':
+ // Don't get names, unless needed, and only get once if it is
+ if (!names) {
+ names = _pluck( columns, 'sName' );
+ }
+
// match by name. `names` is column index complete and in order
- return $.map( names, function (name, i) {
+ return names.map( function (name, i) {
return name === match[1] ? i : null;
} );
+ case 'title':
+ if (!titles) {
+ titles = _pluck( columns, 'sTitle' );
+ }
+
+ // match by column title
+ return titles.map( function (title, i) {
+ return title === match[1] ? i : null;
+ } );
+
default:
return [];
}
@@ -8684,9 +8963,12 @@
var jqResult = $( nodes )
.filter( s )
.map( function () {
- return $.inArray( this, nodes ); // `nodes` is column index complete and in order
+ return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order
} )
- .toArray();
+ .toArray()
+ .sort(function (a, b) {
+ return a - b;
+ });
if ( jqResult.length || ! s.nodeName ) {
return jqResult;
@@ -8700,7 +8982,11 @@
[];
};
- return _selector_run( 'column', selector, run, settings, opts );
+ var selected = _selector_run( 'column', selector, run, settings, opts );
+
+ return opts.columnOrder && opts.columnOrder === 'index'
+ ? selected.sort(function (a, b) { return a - b; })
+ : selected; // implied
};
@@ -8709,7 +8995,7 @@
cols = settings.aoColumns,
col = cols[ column ],
data = settings.aoData,
- row, cells, i, ien, tr;
+ cells, i, iLen, tr;
// Get
if ( vis === undefined ) {
@@ -8719,21 +9005,23 @@
// Set
// No change
if ( col.bVisible === vis ) {
- return;
+ return false;
}
if ( vis ) {
// Insert column
// Need to decide if we should use appendChild or insertBefore
- var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
+ var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1);
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- tr = data[i].nTr;
- cells = data[i].anCells;
+ for ( i=0, iLen=data.length ; i<iLen ; i++ ) {
+ if (data[i]) {
+ tr = data[i].nTr;
+ cells = data[i].anCells;
- if ( tr ) {
- // insertBefore can act like appendChild if 2nd arg is null
- tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
+ if ( tr ) {
+ // insertBefore can act like appendChild if 2nd arg is null
+ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
+ }
}
}
}
@@ -8744,6 +9032,10 @@
// Common actions
col.bVisible = vis;
+
+ _colGroup(settings);
+
+ return true;
};
@@ -8770,15 +9062,21 @@
return inst;
} );
- _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTh;
+ _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) {
+ return this.iterator( 'column', function (settings, column) {
+ return __column_header(settings, column, row);
}, 1 );
} );
- _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
+ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) {
return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTf;
+ var footer = settings.aoFooter;
+
+ if (! footer.length) {
+ return null;
+ }
+
+ return settings.aoFooter[row !== undefined ? row : 0][column].cell;
}, 1 );
} );
@@ -8786,6 +9084,12 @@
return this.iterator( 'column-rows', __columnData, 1 );
} );
+ _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) {
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
+ return __columnData( settings, column, i, j, rows, type );
+ }, 1 );
+ } );
+
_api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
return this.iterator( 'column', function ( settings, column ) {
return settings.aoColumns[column].mData;
@@ -8800,19 +9104,72 @@
}, 1 );
} );
+ _api_registerPlural( 'columns().init()', 'column().init()', function () {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column];
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().names()', 'column().name()', function () {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column].sName;
+ }, 1 );
+ } );
+
_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
}, 1 );
} );
+ _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) {
+ return this.iterator( 'column', function ( settings, column ) {
+ // Argument shifting
+ if (typeof title === 'number') {
+ row = title;
+ title = undefined;
+ }
+
+ var span = $('.dt-column-title', this.column(column).header(row));
+
+ if (title !== undefined) {
+ span.html(title);
+ return this;
+ }
+
+ return span.html();
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().types()', 'column().type()', function () {
+ return this.iterator( 'column', function ( settings, column ) {
+ var colObj = settings.aoColumns[column]
+ var type = colObj.sType;
+
+ // If the type was invalidated, then resolve it. This actually does
+ // all columns at the moment. Would only happen once if getting all
+ // column's data types.
+ if (! type) {
+ _fnColumnTypes(settings);
+
+ type = colObj.sType;
+ }
+
+ return type;
+ }, 1 );
+ } );
+
_api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
var that = this;
+ var changed = [];
var ret = this.iterator( 'column', function ( settings, column ) {
if ( vis === undefined ) {
return settings.aoColumns[ column ].bVisible;
} // else
- __setColumnVis( settings, column, vis );
+
+ if (__setColumnVis( settings, column, vis )) {
+ changed.push(column);
+ }
} );
// Group the column visibility changes
@@ -8825,17 +9182,19 @@
// Update colspan for no records display. Child rows and extensions will use their own
// listeners to do this - only need to update the empty table item here
if ( ! settings.aiDisplay.length ) {
- $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
+ $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisibleColumns(settings));
}
_fnSaveState( settings );
// Second loop once the first is done for events
that.iterator( 'column', function ( settings, column ) {
- _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
+ if (changed.includes(column)) {
+ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
+ }
} );
- if ( calc === undefined || calc ) {
+ if ( changed.length && (calc === undefined || calc) ) {
that.columns.adjust();
}
});
@@ -8844,6 +9203,28 @@
return ret;
} );
+ _api_registerPlural( 'columns().widths()', 'column().width()', function () {
+ // Injects a fake row into the table for just a moment so the widths can
+ // be read, regardless of colspan in the header and rows being present in
+ // the body
+ var columns = this.columns(':visible').count();
+ var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>');
+
+ $(this.table().body()).append(row);
+
+ var widths = row.children().map(function () {
+ return $(this).outerWidth();
+ });
+
+ row.remove();
+
+ return this.iterator( 'column', function ( settings, column ) {
+ var visIdx = _fnColumnIndexToVisible( settings, column );
+
+ return visIdx !== null ? widths[visIdx] : 0;
+ }, 1);
+ } );
+
_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
return this.iterator( 'column', function ( settings, column ) {
return type === 'visible' ?
@@ -8854,6 +9235,10 @@
_api_register( 'columns.adjust()', function () {
return this.iterator( 'table', function ( settings ) {
+ // Force a column sizing to happen with a manual call - otherwise it can skip
+ // if the size hasn't changed
+ settings.containerWidth = -1;
+
_fnAdjustColumnSizing( settings );
}, 1 );
} );
@@ -8883,7 +9268,7 @@
var allCells = $(_flatten( [], cells ));
var row;
var columns = settings.aoColumns.length;
- var a, i, ien, j, o, host;
+ var a, i, iLen, j, o, host;
var run = function ( s ) {
var fnSelector = typeof s === 'function';
@@ -8892,7 +9277,7 @@
// All cells and function selectors
a = [];
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
+ for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
row = rows[i];
for ( j=0 ; j<columns ; j++ ) {
@@ -8922,7 +9307,7 @@
// Selector - index
if ( $.isPlainObject( s ) ) {
// Valid cell index and its in the array of selectable rows
- return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
+ return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ?
[s] :
[];
}
@@ -8934,7 +9319,7 @@
return { // use a new object, in case someone changes the values
row: el._DT_CellIndex.row,
column: el._DT_CellIndex.column
- };
+ };
} )
.toArray();
@@ -8997,12 +9382,12 @@
// Row + column selector
var columns = this.columns( columnSelector, internalOpts );
var rows = this.rows( rowSelector, internalOpts );
- var i, ien, j, jen;
+ var i, iLen, j, jen;
var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
var a = [];
- for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
+ for ( i=0, iLen=rows[idx].length ; i<iLen ; i++ ) {
for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
a.push( {
row: rows[idx][i],
@@ -9139,6 +9524,7 @@
*/
_api_register( 'order()', function ( order, dir ) {
var ctx = this.context;
+ var args = Array.prototype.slice.call( arguments );
if ( order === undefined ) {
// get
@@ -9152,14 +9538,17 @@
// Simple column / direction passed in
order = [ [ order, dir ] ];
}
- else if ( order.length && ! Array.isArray( order[0] ) ) {
+ else if ( args.length > 1 ) {
// Arguments passed in (list of 1D arrays)
- order = Array.prototype.slice.call( arguments );
+ order = args;
}
// otherwise a 2D array was passed in
return this.iterator( 'table', function ( settings ) {
- settings.aaSorting = order.slice();
+ var resolved = [];
+ _fnSortResolve(settings, resolved, order);
+
+ settings.aaSorting = resolved;
} );
} );
@@ -9176,7 +9565,7 @@
*/
_api_register( 'order.listener()', function ( node, column, callback ) {
return this.iterator( 'table', function ( settings ) {
- _fnSortAttachListener( settings, node, column, callback );
+ _fnSortAttachListener(settings, node, {}, column, callback);
} );
} );
@@ -9206,18 +9595,45 @@
], function ( dir ) {
var that = this;
- return this.iterator( 'table', function ( settings, i ) {
- var sort = [];
+ if ( ! dir ) {
+ return this.iterator( 'column', function ( settings, idx ) {
+ var sort = _fnSortFlatten( settings );
+
+ for ( var i=0, iLen=sort.length ; i<iLen ; i++ ) {
+ if ( sort[i].col === idx ) {
+ return sort[i].dir;
+ }
+ }
- $.each( that[i], function (j, col) {
- sort.push( [ col, dir ] );
+ return null;
+ }, 1 );
+ }
+ else {
+ return this.iterator( 'table', function ( settings, i ) {
+ settings.aaSorting = that[i].map( function (col) {
+ return [ col, dir ];
+ } );
} );
+ }
+ } );
- settings.aaSorting = sort;
- } );
+ _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) {
+ return this.iterator( 'column', function ( settings, idx ) {
+ var col = settings.aoColumns[idx];
+
+ return directions ?
+ col.asSorting :
+ col.bSortable;
+ }, 1 );
} );
+ _api_register( 'processing()', function ( show ) {
+ return this.iterator( 'table', function ( ctx ) {
+ _fnProcessingDisplay( ctx, show );
+ } );
+ } );
+
_api_register( 'search()', function ( input, regex, smart, caseInsen ) {
var ctx = this.context;
@@ -9225,7 +9641,7 @@
if ( input === undefined ) {
// get
return ctx.length !== 0 ?
- ctx[0].oPreviousSearch.sSearch :
+ ctx[0].oPreviousSearch.search :
undefined;
}
@@ -9235,15 +9651,48 @@
return;
}
- _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } ), 1 );
+ if (typeof regex === 'object') {
+ // New style options to pass to the search builder
+ _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, {
+ search: input
+ } ) );
+ }
+ else {
+ // Compat for the old options
+ _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, {
+ search: input,
+ regex: regex === null ? false : regex,
+ smart: smart === null ? true : smart,
+ caseInsensitive: caseInsen === null ? true : caseInsen
+ } ) );
+ }
} );
} );
+ _api_register( 'search.fixed()', function ( name, search ) {
+ var ret = this.iterator( true, 'table', function ( settings ) {
+ var fixed = settings.searchFixed;
+
+ if (! name) {
+ return Object.keys(fixed);
+ }
+ else if (search === undefined) {
+ return fixed[name];
+ }
+ else if (search === null) {
+ delete fixed[name];
+ }
+ else {
+ fixed[name] = search;
+ }
+
+ return this;
+ } );
+
+ return name !== undefined && search === undefined
+ ? ret[0]
+ : ret;
+ } );
_api_registerPlural(
'columns().search()',
@@ -9254,7 +9703,7 @@
if ( input === undefined ) {
// get
- return preSearch[ column ].sSearch;
+ return preSearch[ column ].search;
}
// set
@@ -9262,26 +9711,78 @@
return;
}
- $.extend( preSearch[ column ], {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } );
+ if (typeof regex === 'object') {
+ // New style options to pass to the search builder
+ $.extend( preSearch[ column ], regex, {
+ search: input
+ } );
+ }
+ else {
+ // Old style (with not all options available)
+ $.extend( preSearch[ column ], {
+ search: input,
+ regex: regex === null ? false : regex,
+ smart: smart === null ? true : smart,
+ caseInsensitive: caseInsen === null ? true : caseInsen
+ } );
+ }
- _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
+ _fnFilterComplete( settings, settings.oPreviousSearch );
} );
}
);
+ _api_register([
+ 'columns().search.fixed()',
+ 'column().search.fixed()'
+ ],
+ function ( name, search ) {
+ var ret = this.iterator( true, 'column', function ( settings, colIdx ) {
+ var fixed = settings.aoColumns[colIdx].searchFixed;
+
+ if (! name) {
+ return Object.keys(fixed);
+ }
+ else if (search === undefined) {
+ return fixed[name] || null;
+ }
+ else if (search === null) {
+ delete fixed[name];
+ }
+ else {
+ fixed[name] = search;
+ }
+
+ return this;
+ } );
+
+ return name !== undefined && search === undefined
+ ? ret[0]
+ : ret;
+ }
+ );
/*
* State API methods
*/
- _api_register( 'state()', function () {
- return this.context.length ?
- this.context[0].oSavedState :
- null;
+ _api_register( 'state()', function ( set, ignoreTime ) {
+ // getter
+ if ( ! set ) {
+ return this.context.length ?
+ this.context[0].oSavedState :
+ null;
+ }
+
+ var setMutate = $.extend( true, {}, set );
+
+ // setter
+ return this.iterator( 'table', function ( settings ) {
+ if ( ignoreTime !== false ) {
+ setMutate.time = +new Date() + 100;
+ }
+
+ _fnImplementState( settings, setMutate, function(){} );
+ } );
} );
@@ -9306,7 +9807,108 @@
} );
} );
+ // Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js
+ var __bootstrap;
+ var __foundation;
+ /**
+ * Set the libraries that DataTables uses, or the global objects.
+ * Note that the arguments can be either way around (legacy support)
+ * and the second is optional. See docs.
+ */
+ DataTable.use = function (arg1, arg2) {
+ // Reverse arguments for legacy support
+ var module = typeof arg1 === 'string'
+ ? arg2
+ : arg1;
+ var type = typeof arg2 === 'string'
+ ? arg2
+ : arg1;
+
+ // Getter
+ if (module === undefined && typeof type === 'string') {
+ switch (type) {
+ case 'lib':
+ case 'jq':
+ return $;
+
+ case 'win':
+ return window;
+
+ case 'datetime':
+ return DataTable.DateTime;
+
+ case 'luxon':
+ return __luxon;
+
+ case 'moment':
+ return __moment;
+
+ case 'bootstrap':
+ // Use local if set, otherwise try window, which could be undefined
+ return __bootstrap || window.bootstrap;
+
+ case 'foundation':
+ // Ditto
+ return __foundation || window.Foundation;
+
+ default:
+ return null;
+ }
+ }
+
+ // Setter
+ if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
+ $ = module;
+ }
+ else if (type === 'win' || (module && module.document)) {
+ window = module;
+ document = module.document;
+ }
+ else if (type === 'datetime' || (module && module.type === 'DateTime')) {
+ DataTable.DateTime = module;
+ }
+ else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
+ __luxon = module;
+ }
+ else if (type === 'moment' || (module && module.isMoment)) {
+ __moment = module;
+ }
+ else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal'))
+ {
+ // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
+ __bootstrap = module;
+ }
+ else if (type === 'foundation' || (module && module.Reveal)) {
+ __foundation = module;
+ }
+ }
+
+ /**
+ * CommonJS factory function pass through. This will check if the arguments
+ * given are a window object or a jQuery object. If so they are set
+ * accordingly.
+ * @param {*} root Window
+ * @param {*} jq jQUery
+ * @returns {boolean} Indicator
+ */
+ DataTable.factory = function (root, jq) {
+ var is = false;
+
+ // Test if the first parameter is a window object
+ if (root && root.document) {
+ window = root;
+ document = root.document;
+ }
+
+ // Test if the second parameter is a jQuery object
+ if (jq && jq.fn && jq.fn.jquery) {
+ $ = jq;
+ is = true;
+ }
+
+ return is;
+ }
/**
* Provide a common method for plug-ins to check the version of DataTables being
@@ -9314,6 +9916,8 @@
*
* @param {string} version Version string to check for, in the format "X.Y.Z".
* Note that the formats "X" and "X.Y" are also acceptable.
+ * @param {string} [version2=current DataTables version] As above, but optional.
+ * If not given the current DataTables version will be used.
* @returns {boolean} true if this version of DataTables is greater or equal to
* the required version, or false if this version of DataTales is not
* suitable
@@ -9323,9 +9927,11 @@
* @example
* alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
*/
- DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
+ DataTable.versionCheck = function( version, version2 )
{
- var aThis = DataTable.version.split('.');
+ var aThis = version2 ?
+ version2.split('.') :
+ DataTable.version.split('.');
var aThat = version.split('.');
var iThis, iThat;
@@ -9361,7 +9967,7 @@
* $('#example').dataTable();
* }
*/
- DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
+ DataTable.isDataTable = function ( table )
{
var t = $(table).get(0);
var is = false;
@@ -9399,7 +10005,7 @@
* $(table).DataTable().columns.adjust();
* } );
*/
- DataTable.tables = DataTable.fnTables = function ( visible )
+ DataTable.tables = function ( visible )
{
var api = false;
@@ -9408,11 +10014,15 @@
visible = visible.visible;
}
- var a = $.map( DataTable.settings, function (o) {
- if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
+ var a = DataTable.settings
+ .filter( function (o) {
+ return !visible || (visible && $(o.nTable).is(':visible'))
+ ? true
+ : false;
+ } )
+ .map( function (o) {
return o.nTable;
- }
- } );
+ });
return api ?
new _Api( a ) :
@@ -9458,7 +10068,7 @@
var args = Array.prototype.slice.call(arguments);
// Add the `dt` namespace automatically if it isn't already present
- args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
+ args[0] = args[0].split( /\s/ ).map( function ( e ) {
return ! e.match(/\.dt\b/) ?
e+'.dt' :
e;
@@ -9478,6 +10088,13 @@
} );
+ _api_register( 'error()', function (msg) {
+ return this.iterator( 'table', function ( settings ) {
+ _fnLog( settings, 0, msg );
+ } );
+ } );
+
+
_api_register( 'settings()', function () {
return new _Api( this.context, this.context );
} );
@@ -9496,11 +10113,44 @@
} );
+ _api_register( 'trigger()', function ( name, args, bubbles ) {
+ return this.iterator( 'table', function ( settings ) {
+ return _fnCallbackFire( settings, null, name, args, bubbles );
+ } ).flatten();
+ } );
+
+
+ _api_register( 'ready()', function ( fn ) {
+ var ctx = this.context;
+
+ // Get status of first table
+ if (! fn) {
+ return ctx.length
+ ? (ctx[0]._bInitComplete || false)
+ : null;
+ }
+
+ // Function to run either once the table becomes ready or
+ // immediately if it is already ready.
+ return this.tables().every(function () {
+ var api = this;
+
+ if (this.context[0]._bInitComplete) {
+ fn.call(api);
+ }
+ else {
+ this.on('init.dt.DT', function () {
+ fn.call(api);
+ });
+ }
+ } );
+ } );
+
+
_api_register( 'destroy()', function ( remove ) {
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;
@@ -9509,21 +10159,26 @@
var jqTable = $(table);
var jqTbody = $(tbody);
var jqWrapper = $(settings.nTableWrapper);
- var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
- var i, ien;
+ var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } );
+ var orderClasses = classes.order;
// Flag to note that the table is currently being destroyed - no action
// should be taken
settings.bDestroying = true;
// Fire off the destroy callbacks for plug-ins etc
- _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
+ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true );
// If not being removed from the document, make all columns visible
if ( ! remove ) {
new _Api( settings ).columns().visible( true );
}
+ // Container width change listener
+ if (settings.resizeObserver) {
+ settings.resizeObserver.disconnect();
+ }
+
// Blitz all `DT` namespaced events (these are internal events, the
// lowercase, `dt` events are user subscribed and they are responsible
// for removing them
@@ -9541,20 +10196,39 @@
jqTable.append( tfoot );
}
+ // Clean up the header / footer
+ cleanHeader(thead, 'header');
+ cleanHeader(tfoot, 'footer');
+ settings.colgroup.remove();
+
settings.aaSorting = [];
settings.aaSortingFixed = [];
_fnSortingClasses( settings );
- $( rows ).removeClass( settings.asStripeClasses.join(' ') );
-
- $('th, td', thead).removeClass( classes.sSortable+' '+
- classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
+ $(jqTable).find('th, td').removeClass(
+ $.map(DataTable.ext.type.className, function (v) {
+ return v;
+ }).join(' ')
);
+ $('th, td', thead)
+ .removeClass(
+ orderClasses.none + ' ' +
+ orderClasses.canAsc + ' ' +
+ orderClasses.canDesc + ' ' +
+ orderClasses.isAsc + ' ' +
+ orderClasses.isDesc
+ )
+ .css('width', '')
+ .removeAttr('aria-sort');
+
// Add the TR elements back into the table in their original order
jqTbody.children().detach();
jqTbody.append( rows );
+ var orig = settings.nTableWrapper.parentNode;
+ var insertBefore = settings.nTableWrapper.nextSibling;
+
// Remove the DataTables generated nodes, events and classes
var removedMethod = remove ? 'remove' : 'detach';
jqTable[ removedMethod ]();
@@ -9563,28 +10237,17 @@
// If we need to reattach the table to the document
if ( ! remove && orig ) {
// insertBefore acts like appendChild if !arg[1]
- orig.insertBefore( table, settings.nTableReinsertBefore );
+ orig.insertBefore( table, insertBefore );
// Restore the width of the original table - was read from the style property,
// so we can restore directly to that
jqTable
.css( 'width', settings.sDestroyWidth )
- .removeClass( classes.sTable );
-
- // If the were originally stripe classes - then we add them back here.
- // Note this is not fool proof (for example if not all rows had stripe
- // classes - but it's a good effort without getting carried away
- ien = settings.asDestroyStripes.length;
-
- if ( ien ) {
- jqTbody.children().each( function (i) {
- $(this).addClass( settings.asDestroyStripes[i % ien] );
- } );
- }
+ .removeClass( classes.table );
}
/* Remove the settings object from the settings array */
- var idx = $.inArray( settings, DataTable.settings );
+ var idx = DataTable.settings.indexOf(settings);
if ( idx !== -1 ) {
DataTable.settings.splice( idx, 1 );
}
@@ -9597,26 +10260,20 @@
_api_register( type+'s().every()', function ( fn ) {
var opts = this.selector.opts;
var api = this;
+ var inst;
+ var counter = 0;
- return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
- // Rows and columns:
- // arg1 - index
- // arg2 - table counter
- // arg3 - loop counter
- // arg4 - undefined
- // Cells:
- // arg1 - row index
- // arg2 - column index
- // arg3 - table counter
- // arg4 - loop counter
- fn.call(
- api[ type ](
- arg1,
- type==='cell' ? arg2 : opts,
- type==='cell' ? opts : undefined
- ),
- arg1, arg2, arg3, arg4
- );
+ return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) {
+ inst = api[ type ](selectedIdx, opts);
+
+ if (type === 'cell') {
+ fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter);
+ }
+ else {
+ fn.call(inst, selectedIdx, tableIdx, counter);
+ }
+
+ counter++;
} );
} );
} );
@@ -9632,23 +10289,41 @@
resolved = def;
}
- if ( plural !== undefined && $.isPlainObject( resolved ) ) {
- resolved = resolved[ plural ] !== undefined ?
- resolved[ plural ] :
- resolved._;
+ if ( $.isPlainObject( resolved ) ) {
+ resolved = plural !== undefined && resolved[ plural ] !== undefined
+ ? resolved[ plural ]
+ : plural === false
+ ? resolved
+ : resolved._;
}
- return resolved.replace( '%d', plural ); // nb: plural might be undefined,
- } );
+ return typeof resolved === 'string'
+ ? resolved.replace( '%d', plural ) // nb: plural might be undefined,
+ : resolved;
+ } );
+
+ // Needed for header and footer, so pulled into its own function
+ function cleanHeader(node, className) {
+ $(node).find('.dt-column-order').remove();
+ $(node).find('.dt-column-title').each(function () {
+ var title = $(this).html();
+ $(this).parent().parent().append(title);
+ $(this).remove();
+ });
+ $(node).find('div.dt-column-' + className).remove();
+
+ $('th, td', node).removeAttr('data-dt-column');
+ }
+
/**
* Version string for plug-ins to check compatibility. Allowed format is
* `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
- * only for non-release builds. See http://semver.org/ for more information.
+ * only for non-release builds. See https://semver.org/ for more information.
* @member
* @type string
* @default Version number
*/
- DataTable.version = "1.11.5";
+ DataTable.version = "2.3.8";
/**
* Private data store, containing all of the settings objects that are
@@ -9681,5169 +10356,2793 @@
*/
DataTable.models.oSearch = {
/**
- * Flag to indicate if the filtering should be case insensitive or not
- * @type boolean
- * @default true
+ * Flag to whether or not the filtering should be case-insensitive
*/
- "bCaseInsensitive": true,
+ "caseInsensitive": true,
/**
* Applied search term
- * @type string
- * @default <i>Empty string</i>
*/
- "sSearch": "",
+ "search": "",
/**
* Flag to indicate if the search term should be interpreted as a
* regular expression (true) or not (false) and therefore and special
* regex characters escaped.
- * @type boolean
- * @default false
*/
- "bRegex": false,
+ "regex": false,
/**
* Flag to indicate if DataTables is to use its smart filtering or not.
- * @type boolean
- * @default true
*/
- "bSmart": true,
+ "smart": true,
/**
* Flag to indicate if DataTables should only trigger a search when
* the return key is pressed.
- * @type boolean
- * @default false
*/
"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
*/
- 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.
+ * Array of TD elements for each row. This is null until the row has been
+ * created.
+ */
+ "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.
+ */
+ "_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.
+ */
+ "_aSortData": null,
+
+ /**
+ * Per cell filtering data cache. As per the sort data cache, used to
+ * increase the performance of the filtering in DataTables
+ */
+ "_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)
+ */
+ "_sFilterRow": null,
+
+ /**
+ * 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.
+ */
+ "src": null,
+
+ /**
+ * Index in the aoData array. This saves an indexOf lookup when we have the
+ * object, but want to know the index
+ */
+ "idx": -1,
+
+ /**
+ * Cached display value
+ */
+ displayData: 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
+ */
+ DataTable.models.oColumn = {
+ /**
+ * Column index.
+ */
+ "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).
+ */
+ "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.
+ */
+ "asSorting": null,
+
+ /**
+ * Flag to indicate if the column is searchable, and thus should be included
+ * in the filtering or not.
+ */
+ "bSearchable": null,
+
+ /**
+ * Flag to indicate if the column is sortable or not.
+ */
+ "bSortable": null,
+
+ /**
+ * Flag to indicate if the column is currently visible in the table or not
+ */
+ "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.
+ */
+ "_sManualType": null,
+
+ /**
+ * Flag to indicate if HTML5 data attributes should be used as the data
+ * source for filtering or sorting. True is either are.
+ */
+ "_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.
+ */
+ "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
+ */
+ "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
+ */
+ "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.
+ */
+ "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.
+ */
+ "mRender": null,
+
+ /**
+ * The class to apply to all TD elements in the table's TBODY for the column
+ */
+ "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.
+ */
+ "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).
+ */
+ "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).
+ */
+ "sName": null,
+
+ /**
+ * Custom sorting data type - defines which of the available plug-ins in
+ * afnSortData the custom sorting will use - if any is defined.
+ */
+ "sSortDataType": 'std',
+
+ /**
+ * Class to be applied to the header element when sorting on this column
+ */
+ "sSortingClass": null,
+
+ /**
+ * Title of the column - what is seen in the TH element (nTh).
+ */
+ "sTitle": null,
+
+ /**
+ * Column sorting and filtering type
+ */
+ "sType": null,
+
+ /**
+ * Width of the column
+ */
+ "sWidth": null,
+
+ /**
+ * Width of the column when it was first "encountered"
+ */
+ "sWidthOrig": null,
+
+ /** Cached longest strings from a column */
+ wideStrings: null,
+
+ /**
+ * Store for named searches
+ */
+ searchFixed: 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.
+ */
+ "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').
+ */
+ "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.
+ */
+ "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`
+ * --------
*
- * 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
+ * As an object, the parameters in the object are passed to
+ * [jQuery.ajax](https://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.
+ *
+ * * `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
*/
- 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).
+ "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').
*
- * 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).
+ * Note that the `pageLength` property will be automatically set to the
+ * first value given in this array, unless `pageLength` is also provided.
*/
-
+ "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).
+ */
+ "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>
+ */
+ "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.
+ */
+ "aoSearchCols": [],
+
+
/**
- * Initialisation options that can be given to DataTables at initialisation
+ * 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`.
+ */
+ "bAutoWidth": true,
+
+
+ /**
+ * Deferred rendering can provide DataTables with a huge speed boost when you
+ * are using an Ajax or JS data source for the table. This option, when set to
+ * true, will cause DataTables to defer the creation of the table elements for
+ * each row until they are needed for a draw - saving a significant amount of
* time.
- * @namespace
*/
- DataTable.defaults = {
- /**
- * An array of data to use for the table, passed in at initialisation which
- * will be used in preference to any data which is already in the DOM. This is
- * particularly useful for constructing tables purely in Javascript, for
- * example with a custom Ajax call.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.data
- *
- * @example
- * // Using a 2D array data source
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
- * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
- * ],
- * "columns": [
- * { "title": "Engine" },
- * { "title": "Browser" },
- * { "title": "Platform" },
- * { "title": "Version" },
- * { "title": "Grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using an array of objects as a data source (`data`)
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 4.0",
- * "platform": "Win 95+",
- * "version": 4,
- * "grade": "X"
- * },
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 5.0",
- * "platform": "Win 95+",
- * "version": 5,
- * "grade": "C"
- * }
- * ],
- * "columns": [
- * { "title": "Engine", "data": "engine" },
- * { "title": "Browser", "data": "browser" },
- * { "title": "Platform", "data": "platform" },
- * { "title": "Version", "data": "version" },
- * { "title": "Grade", "data": "grade" }
- * ]
- * } );
- * } );
- */
- "aaData": null,
-
-
- /**
- * If ordering is enabled, then DataTables will perform a first pass sort on
- * initialisation. You can define which column(s) the sort is performed
- * upon, and the sorting direction, with this variable. The `sorting` array
- * should contain an array for each column to be sorted initially containing
- * the column's index and a direction string ('asc' or 'desc').
- * @type array
- * @default [[0,'asc']]
- *
- * @dtopt Option
- * @name DataTable.defaults.order
- *
- * @example
- * // Sort by 3rd column first, and then 4th column
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": [[2,'asc'], [3,'desc']]
- * } );
- * } );
- *
- * // No initial sorting
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": []
- * } );
- * } );
- */
- "aaSorting": [[0,'asc']],
-
-
- /**
- * This parameter is basically identical to the `sorting` parameter, but
- * cannot be overridden by user interaction with the table. What this means
- * is that you could have a column (visible or hidden) which the sorting
- * will always be forced on first - any sorting after that (from the user)
- * will then be performed as required. This can be useful for grouping rows
- * together.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.orderFixed
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderFixed": [[0,'asc']]
- * } );
- * } )
- */
- "aaSortingFixed": [],
-
-
- /**
- * DataTables can be instructed to load data to display in the table from a
- * Ajax source. This option defines how that Ajax call is made and where to.
- *
- * The `ajax` property has three different modes of operation, depending on
- * how it is defined. These are:
- *
- * * `string` - Set the URL from where the data should be loaded from.
- * * `object` - Define properties for `jQuery.ajax`.
- * * `function` - Custom data get function
- *
- * `string`
- * --------
- *
- * As a string, the `ajax` property simply defines the URL from which
- * DataTables will load data.
- *
- * `object`
- * --------
- *
- * As an object, the parameters in the object are passed to
- * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
- * of the Ajax request. DataTables has a number of default parameters which
- * you can override using this option. Please refer to the jQuery
- * documentation for a full description of the options available, although
- * the following parameters provide additional options in DataTables or
- * require special consideration:
- *
- * * `data` - As with jQuery, `data` can be provided as an object, but it
- * can also be used as a function to manipulate the data DataTables sends
- * to the server. The function takes a single parameter, an object of
- * parameters with the values that DataTables has readied for sending. An
- * object may be returned which will be merged into the DataTables
- * defaults, or you can add the items to the object that was passed in and
- * not return anything from the function. This supersedes `fnServerParams`
- * from DataTables 1.9-.
- *
- * * `dataSrc` - By default DataTables will look for the property `data` (or
- * `aaData` for compatibility with DataTables 1.9-) when obtaining data
- * from an Ajax source or for server-side processing - this parameter
- * allows that property to be changed. You can use Javascript dotted
- * object notation to get a data source for multiple levels of nesting, or
- * it my be used as a function. As a function it takes a single parameter,
- * the JSON returned from the server, which can be manipulated as
- * required, with the returned value being that used by DataTables as the
- * data source for the table. This supersedes `sAjaxDataProp` from
- * DataTables 1.9-.
- *
- * * `success` - Should not be overridden it is used internally in
- * DataTables. To manipulate / transform the data returned by the server
- * use `ajax.dataSrc`, or use `ajax` as a function (see below).
- *
- * `function`
- * ----------
- *
- * As a function, making the Ajax call is left up to yourself allowing
- * complete control of the Ajax request. Indeed, if desired, a method other
- * than Ajax could be used to obtain the required data, such as Web storage
- * or an AIR database.
- *
- * The function is given four parameters and no return is required. The
- * parameters are:
- *
- * 1. _object_ - Data to send to the server
- * 2. _function_ - Callback function that must be executed when the required
- * data has been obtained. That data should be passed into the callback
- * as the only parameter
- * 3. _object_ - DataTables settings object for the table
- *
- * Note that this supersedes `fnServerData` from DataTables 1.9-.
- *
- * @type string|object|function
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.ajax
- * @since 1.10.0
- *
- * @example
- * // Get JSON data from a file via Ajax.
- * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
- * $('#example').dataTable( {
- * "ajax": "data.json"
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to change
- * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": "tableData"
- * }
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to read data
- * // from a plain array rather than an array in an object
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": ""
- * }
- * } );
- *
- * @example
- * // Manipulate the data returned from the server - add a link to data
- * // (note this can, should, be done using `render` for the column - this
- * // is just a simple example of how the data can be manipulated).
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": function ( json ) {
- * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
- * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
- * }
- * return json;
- * }
- * }
- * } );
- *
- * @example
- * // Add data to the request
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "data": function ( d ) {
- * return {
- * "extra_search": $('#extra').val()
- * };
- * }
- * }
- * } );
- *
- * @example
- * // Send request as POST
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "type": "POST"
- * }
- * } );
- *
- * @example
- * // Get the data from localStorage (could interface with a form for
- * // adding, editing and removing rows).
- * $('#example').dataTable( {
- * "ajax": function (data, callback, settings) {
- * callback(
- * JSON.parse( localStorage.getItem('dataTablesData') )
- * );
- * }
- * } );
- */
- "ajax": null,
-
-
- /**
- * This parameter allows you to readily specify the entries in the length drop
- * down menu that DataTables shows when pagination is enabled. It can be
- * either a 1D array of options which will be used for both the displayed
- * option and the value, or a 2D array which will use the array in the first
- * position as the value, and the array in the second position as the
- * displayed options (useful for language strings such as 'All').
- *
- * Note that the `pageLength` property will be automatically set to the
- * first value given in this array, unless `pageLength` is also provided.
- * @type array
- * @default [ 10, 25, 50, 100 ]
- *
- * @dtopt Option
- * @name DataTable.defaults.lengthMenu
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
- * } );
- * } );
- */
- "aLengthMenu": [ 10, 25, 50, 100 ],
-
-
- /**
- * The `columns` option in the initialisation parameter allows you to define
- * details about the way individual columns behave. For a full list of
- * column options that can be set, please see
- * {@link DataTable.defaults.column}. Note that if you use `columns` to
- * define your columns, you must have an entry in the array for every single
- * column that you have in your table (these can be null if you don't which
- * to specify any options).
- * @member
- *
- * @name DataTable.defaults.column
- */
- "aoColumns": null,
-
- /**
- * Very similar to `columns`, `columnDefs` allows you to target a specific
- * column, multiple columns, or all columns, using the `targets` property of
- * each object in the array. This allows great flexibility when creating
- * tables, as the `columnDefs` arrays can be of any length, targeting the
- * columns you specifically want. `columnDefs` may use any of the column
- * options available: {@link DataTable.defaults.column}, but it _must_
- * have `targets` defined in each object in the array. Values in the `targets`
- * array may be:
- * <ul>
- * <li>a string - class name will be matched on the TH for the column</li>
- * <li>0 or a positive integer - column index counting from the left</li>
- * <li>a negative integer - column index counting from the right</li>
- * <li>the string "_all" - all columns (i.e. assign a default)</li>
- * </ul>
- * @member
- *
- * @name DataTable.defaults.columnDefs
- */
- "aoColumnDefs": null,
-
-
- /**
- * Basically the same as `search`, this parameter defines the individual column
- * filtering state at initialisation time. The array must be of the same size
- * as the number of columns, and each element be an object with the parameters
- * `search` and `escapeRegex` (the latter is optional). 'null' is also
- * accepted and the default will be used.
- * @type array
- * @default []
- *
- * @dtopt Option
- * @name DataTable.defaults.searchCols
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchCols": [
- * null,
- * { "search": "My filter" },
- * null,
- * { "search": "^[0-9]", "escapeRegex": false }
- * ]
- * } );
- * } )
- */
- "aoSearchCols": [],
-
-
- /**
- * An array of CSS classes that should be applied to displayed rows. This
- * array may be of any length, and DataTables will apply each class
- * sequentially, looping when required.
- * @type array
- * @default null <i>Will take the values determined by the `oClasses.stripe*`
- * options</i>
- *
- * @dtopt Option
- * @name DataTable.defaults.stripeClasses
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
- * } );
- * } )
- */
- "asStripeClasses": null,
-
-
- /**
- * Enable or disable automatic column width calculation. This can be disabled
- * as an optimisation (it takes some time to calculate the widths) if the
- * tables widths are passed in using `columns`.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.autoWidth
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "autoWidth": false
- * } );
- * } );
- */
- "bAutoWidth": true,
-
-
- /**
- * Deferred rendering can provide DataTables with a huge speed boost when you
- * are using an Ajax or JS data source for the table. This option, when set to
- * true, will cause DataTables to defer the creation of the table elements for
- * each row until they are needed for a draw - saving a significant amount of
- * time.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.deferRender
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajax": "sources/arrays.txt",
- * "deferRender": true
- * } );
- * } );
- */
- "bDeferRender": false,
-
-
- /**
- * Replace a DataTable which matches the given selector and replace it with
- * one which has the properties of the new initialisation object passed. If no
- * table matches the selector, then the new DataTable will be constructed as
- * per normal.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.destroy
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "srollY": "200px",
- * "paginate": false
- * } );
- *
- * // Some time later....
- * $('#example').dataTable( {
- * "filter": false,
- * "destroy": true
- * } );
- * } );
- */
- "bDestroy": false,
-
-
- /**
- * Enable or disable filtering of data. Filtering in DataTables is "smart" in
- * that it allows the end user to input multiple words (space separated) and
- * will match a row containing those words, even if not in the order that was
- * specified (this allow matching across multiple columns). Note that if you
- * wish to use filtering in DataTables this must remain 'true' - to remove the
- * default filtering input box and retain filtering abilities, please use
- * {@link DataTable.defaults.dom}.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.searching
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "searching": false
- * } );
- * } );
- */
- "bFilter": true,
-
-
- /**
- * Enable or disable the table information display. This shows information
- * about the data that is currently visible on the page, including information
- * about filtered data if that action is being performed.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.info
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "info": false
- * } );
- * } );
- */
- "bInfo": true,
-
-
- /**
- * Allows the end user to select the size of a formatted page from a select
- * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.lengthChange
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "lengthChange": false
- * } );
- * } );
- */
- "bLengthChange": true,
-
-
- /**
- * Enable or disable pagination.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.paging
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "paging": false
- * } );
- * } );
- */
- "bPaginate": true,
-
-
- /**
- * Enable or disable the display of a 'processing' indicator when the table is
- * being processed (e.g. a sort). This is particularly useful for tables with
- * large amounts of data where it can take a noticeable amount of time to sort
- * the entries.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.processing
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "processing": true
- * } );
- * } );
- */
- "bProcessing": false,
-
-
- /**
- * Retrieve the DataTables object for the given selector. Note that if the
- * table has already been initialised, this parameter will cause DataTables
- * to simply return the object that has already been set up - it will not take
- * account of any changes you might have made to the initialisation object
- * passed to DataTables (setting this parameter to true is an acknowledgement
- * that you understand this). `destroy` can be used to reinitialise a table if
- * you need.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.retrieve
- *
- * @example
- * $(document).ready( function() {
- * initTable();
- * tableActions();
- * } );
- *
- * function initTable ()
- * {
- * return $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false,
- * "retrieve": true
- * } );
- * }
- *
- * function tableActions ()
- * {
- * var table = initTable();
- * // perform API operations with oTable
- * }
- */
- "bRetrieve": false,
-
-
- /**
- * When vertical (y) scrolling is enabled, DataTables will force the height of
- * the table's viewport to the given height at all times (useful for layout).
- * However, this can look odd when filtering data down to a small data set,
- * and the footer is left "floating" further down. This parameter (when
- * enabled) will cause DataTables to collapse the table's viewport down when
- * the result set will fit within the given Y height.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollCollapse
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200",
- * "scrollCollapse": true
- * } );
- * } );
- */
- "bScrollCollapse": false,
-
-
- /**
- * Configure DataTables to use server-side processing. Note that the
- * `ajax` parameter must also be given in order to give DataTables a
- * source to obtain the required data for each draw.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @dtopt Server-side
- * @name DataTable.defaults.serverSide
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "xhr.php"
- * } );
- * } );
- */
- "bServerSide": false,
-
-
- /**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the `sortable` option for each column.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.ordering
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "ordering": false
- * } );
- * } );
- */
- "bSort": true,
-
-
- /**
- * Enable or display DataTables' ability to sort multiple columns at the
- * same time (activated by shift-click by the user).
- * @type boolean
- * @default true
- *
- * @dtopt Options
- * @name DataTable.defaults.orderMulti
- *
- * @example
- * // Disable multiple column sorting ability
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderMulti": false
- * } );
- * } );
- */
- "bSortMulti": true,
-
-
- /**
- * Allows control over whether DataTables should use the top (true) unique
- * cell that is found for a single column, or the bottom (false - default).
- * This is useful when using complex headers.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.orderCellsTop
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderCellsTop": true
- * } );
- * } );
- */
- "bSortCellsTop": false,
-
-
- /**
- * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
- * `sorting\_3` to the columns which are currently being sorted on. This is
- * presented as a feature switch as it can increase processing time (while
- * classes are removed and added) so for large data sets you might want to
- * turn this off.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.orderClasses
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderClasses": false
- * } );
- * } );
- */
- "bSortClasses": true,
-
-
- /**
- * Enable or disable state saving. When enabled HTML5 `localStorage` will be
- * used to save table display information such as pagination information,
- * display length, filtering and sorting. As such when the end user reloads
- * the page the display display will match what thy had previously set up.
- *
- * Due to the use of `localStorage` the default state saving is not supported
- * in IE6 or 7. If state saving is required in those browsers, use
- * `stateSaveCallback` to provide a storage solution such as cookies.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.stateSave
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "stateSave": true
- * } );
- * } );
- */
- "bStateSave": false,
-
-
- /**
- * This function is called when a TR element is created (and all TD child
- * elements have been inserted), or registered if using a DOM source, allowing
- * manipulation of the TR element (adding classes etc).
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} dataIndex The index of this row in the internal aoData array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.createdRow
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "createdRow": function( row, data, dataIndex ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" )
- * {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnCreatedRow": null,
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify any aspect you want about the created DOM.
- * @type function
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.drawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "drawCallback": function( settings ) {
- * alert( 'DataTables has redrawn the table' );
- * }
- * } );
- * } );
- */
- "fnDrawCallback": null,
-
-
- /**
- * Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' event.
- * @type function
- * @param {node} foot "TR" element for the footer
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.footerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "footerCallback": function( tfoot, data, start, end, display ) {
- * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
- * }
- * } );
- * } )
- */
- "fnFooterCallback": null,
-
-
- /**
- * When rendering large numbers in the information element for the table
- * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
- * to have a comma separator for the 'thousands' units (e.g. 1 million is
- * rendered as "1,000,000") to help readability for the end user. This
- * function will override the default method DataTables uses.
- * @type function
- * @member
- * @param {int} toFormat number to be formatted
- * @returns {string} formatted string for DataTables to show the number
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.formatNumber
- *
- * @example
- * // Format a number using a single quote for the separator (note that
- * // this can also be done with the language.thousands option)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "formatNumber": function ( toFormat ) {
- * return toFormat.toString().replace(
- * /\B(?=(\d{3})+(?!\d))/g, "'"
- * );
- * };
- * } );
- * } );
- */
- "fnFormatNumber": function ( toFormat ) {
- return toFormat.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g,
- this.oLanguage.sThousands
+ "bDeferRender": true,
+
+
+ /**
+ * 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.
+ */
+ "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}.
+ */
+ "bFilter": true,
+
+ /**
+ * Used only for compatibility with DT1
+ * @deprecated
+ */
+ "bInfo": true,
+
+ /**
+ * Used only for compatibility with DT1
+ * @deprecated
+ */
+ "bLengthChange": true,
+
+ /**
+ * Enable or disable pagination.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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.
+ */
+ "bServerSide": false,
+
+
+ /**
+ * Enable or disable sorting of columns. Sorting of individual columns can be
+ * disabled by the `sortable` option for each column.
+ */
+ "bSort": true,
+
+
+ /**
+ * Enable or display DataTables' ability to sort multiple columns at the
+ * same time (activated by shift-click by the user).
+ */
+ "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.
+ */
+ "bSortCellsTop": null,
+
+
+ /** Specify which row is the title row in the header. Replacement for bSortCellsTop */
+ titleRow: null,
+
+
+ /**
+ * 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.
+ */
+ "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 will match what thy had previously set up.
+ */
+ "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).
+ */
+ "fnCreatedRow": null,
+
+
+ /**
+ * This function is called on every 'draw' event, and allows you to
+ * dynamically modify any aspect you want about the created DOM.
+ */
+ "fnDrawCallback": null,
+
+
+ /**
+ * Identical to fnHeaderCallback() but for the table footer this function
+ * allows you to modify the table footer on every 'draw' event.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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).
+ */
+ "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.
+ */
+ "fnRowCallback": 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.
+ */
+ "fnStateLoadCallback": function ( settings ) {
+ try {
+ return JSON.parse(
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname
+ )
);
- },
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify the header row. This can be used to calculate and
- * display useful information about the table.
- * @type function
- * @param {node} head "TR" element for the header
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.headerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "fheaderCallback": function( head, data, start, end, display ) {
- * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
- * }
- * } );
- * } )
- */
- "fnHeaderCallback": null,
-
-
- /**
- * The information element can be used to convey information about the current
- * state of the table. Although the internationalisation options presented by
- * DataTables are quite capable of dealing with most customisations, there may
- * be times where you wish to customise the string further. This callback
- * allows you to do exactly that.
- * @type function
- * @param {object} oSettings DataTables settings object
- * @param {int} start Starting position in data for the draw
- * @param {int} end End position in data for the draw
- * @param {int} max Total number of rows in the table (regardless of
- * filtering)
- * @param {int} total Total number of rows in the data set, after filtering
- * @param {string} pre The string that DataTables has formatted using it's
- * own rules
- * @returns {string} The string to be displayed in the information element.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.infoCallback
- *
- * @example
- * $('#example').dataTable( {
- * "infoCallback": function( settings, start, end, max, total, pre ) {
- * return start +" to "+ end;
- * }
- * } );
- */
- "fnInfoCallback": null,
-
-
- /**
- * Called when the table has been initialised. Normally DataTables will
- * initialise sequentially and there will be no need for this function,
- * however, this does not hold true when using external language information
- * since that is obtained using an async XHR call.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.initComplete
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "initComplete": function(settings, json) {
- * alert( 'DataTables has finished its initialisation.' );
- * }
- * } );
- * } )
- */
- "fnInitComplete": null,
-
-
- /**
- * Called at the very start of each table draw and can be used to cancel the
- * draw by returning false, any other return (including undefined) results in
- * the full draw occurring).
- * @type function
- * @param {object} settings DataTables settings object
- * @returns {boolean} False will cancel the draw, anything else (including no
- * return) will allow it to complete.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.preDrawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "preDrawCallback": function( settings ) {
- * if ( $('#test').val() == 1 ) {
- * return false;
- * }
- * }
- * } );
- * } );
- */
- "fnPreDrawCallback": null,
-
-
- /**
- * This function allows you to 'post process' each row after it have been
- * generated for each table draw, but before it is rendered on screen. This
- * function might be used for setting the row class name etc.
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} displayIndex The display index for the current table draw
- * @param {int} displayIndexFull The index of the data in the full list of
- * rows (after filtering)
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.rowCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" ) {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnRowCallback": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * This parameter allows you to override the default function which obtains
- * the data from the server so something more suitable for your application.
- * For example you could use POST data, or pull information from a Gears or
- * AIR database.
- * @type function
- * @member
- * @param {string} source HTTP source to obtain the data from (`ajax`)
- * @param {array} data A key/value pair object containing the data to send
- * to the server
- * @param {function} callback to be called on completion of the data get
- * process that will draw the data on the page.
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverData
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerData": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * It is often useful to send extra data to the server when making an Ajax
- * request - for example custom filtering information, and this callback
- * function makes it trivial to send extra information to the server. The
- * passed in parameter is the data set that has been constructed by
- * DataTables, and you can add to this or modify it as you require.
- * @type function
- * @param {array} data Data array (array of objects which are name/value
- * pairs) that has been constructed by DataTables and will be sent to the
- * server. In the case of Ajax sourced data with server-side processing
- * this will be an empty array, for server-side processing there will be a
- * significant number of parameters!
- * @returns {undefined} Ensure that you modify the data array passed in,
- * as this is passed by reference.
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverParams
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerParams": null,
-
-
- /**
- * Load the table state. With this function you can define from where, and how, the
- * state of a table is loaded. By default DataTables will load from `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} callback Callback that can be executed when done. It
- * should be passed the loaded state object.
- * @return {object} The DataTables state object to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadCallback": function (settings, callback) {
- * $.ajax( {
- * "url": "/state_load",
- * "dataType": "json",
- * "success": function (json) {
- * callback( json );
- * }
- * } );
- * }
- * } );
- * } );
- */
- "fnStateLoadCallback": function ( settings ) {
- try {
- return JSON.parse(
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname
- )
- );
- } catch (e) {
- return {};
- }
- },
-
-
- /**
- * Callback which allows modification of the saved state prior to loading that state.
- * This callback is called when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state. Note that for
- * plug-in authors, you should use the `stateLoadParams` event to load parameters for
- * a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that is to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadParams
- *
- * @example
- * // Remove a saved filter, so filtering is never loaded
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- *
- * @example
- * // Disallow state loading by returning false
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * return false;
- * }
- * } );
- * } );
- */
- "fnStateLoadParams": null,
-
-
- /**
- * Callback that is called when the state has been loaded from the state saving method
- * and the DataTables settings object has been modified as a result of the loaded state.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that was loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoaded
- *
- * @example
- * // Show an alert with the filtering value that was saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoaded": function (settings, data) {
- * alert( 'Saved filter was: '+data.oSearch.sSearch );
- * }
- * } );
- * } );
- */
- "fnStateLoaded": null,
-
-
- /**
- * Save the table state. This function allows you to define where and how the state
- * information for the table is stored By default DataTables will use `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveCallback": function (settings, data) {
- * // Send an Ajax request to the server with the state object
- * $.ajax( {
- * "url": "/state_save",
- * "data": data,
- * "dataType": "json",
- * "method": "POST"
- * "success": function () {}
- * } );
- * }
- * } );
- * } );
- */
- "fnStateSaveCallback": function ( settings, data ) {
- try {
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname,
- JSON.stringify( data )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the state to be saved. Called when the table
- * has changed state a new state save is required. This method allows modification of
- * the state saving object prior to actually doing the save, including addition or
- * other state properties or modification. Note that for plug-in authors, you should
- * use the `stateSaveParams` event to save parameters for a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveParams
- *
- * @example
- * // Remove a saved filter, so filtering is never saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- */
- "fnStateSaveParams": null,
-
-
- /**
- * Duration for which the saved state information is considered valid. After this period
- * has elapsed the state will be returned to the default.
- * Value is given in seconds.
- * @type int
- * @default 7200 <i>(2 hours)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.stateDuration
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateDuration": 60*60*24; // 1 day
- * } );
- * } )
- */
- "iStateDuration": 7200,
-
-
- /**
- * When enabled DataTables will not make a request to the server for the first
- * page draw - rather it will use the data already on the page (no sorting etc
- * will be applied to it), thus saving on an XHR at load time. `deferLoading`
- * is used to indicate that deferred loading is required, but it is also used
- * to tell DataTables how many records there are in the full table (allowing
- * the information element and pagination to be displayed correctly). In the case
- * where a filtering is applied to the table on initial load, this can be
- * indicated by giving the parameter as an array, where the first element is
- * the number of records available after filtering and the second element is the
- * number of records without filtering (allowing the table information element
- * to be shown correctly).
- * @type int | array
- * @default null
- *
- * @dtopt Options
- * @name DataTable.defaults.deferLoading
- *
- * @example
- * // 57 records available in the table, no filtering applied
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": 57
- * } );
- * } );
- *
- * @example
- * // 57 records after filtering, 100 without filtering (an initial filter applied)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": [ 57, 100 ],
- * "search": {
- * "search": "my_filter"
- * }
- * } );
- * } );
- */
- "iDeferLoading": null,
-
-
- /**
- * Number of rows to display on a single page when using pagination. If
- * feature enabled (`lengthChange`) then the end user will be able to override
- * this to a custom setting using a pop-up menu.
- * @type int
- * @default 10
- *
- * @dtopt Options
- * @name DataTable.defaults.pageLength
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pageLength": 50
- * } );
- * } )
- */
- "iDisplayLength": 10,
-
-
- /**
- * Define the starting point for data display when using DataTables with
- * pagination. Note that this parameter is the number of records, rather than
- * the page number, so if you have 10 records per page and want to start on
- * the third page, it should be "20".
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.displayStart
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "displayStart": 20
- * } );
- * } )
- */
- "iDisplayStart": 0,
-
-
- /**
- * By default DataTables allows keyboard navigation of the table (sorting, paging,
- * and filtering) by adding a `tabindex` attribute to the required elements. This
- * allows you to tab through the controls and press the enter key to activate them.
- * The tabindex is default 0, meaning that the tab follows the flow of the document.
- * You can overrule this using this parameter if you wish. Use a value of -1 to
- * disable built-in keyboard navigation.
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.tabIndex
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "tabIndex": 1
- * } );
- * } );
- */
- "iTabIndex": 0,
-
-
- /**
- * Classes that DataTables assigns to the various components and features
- * that it adds to the HTML table. This allows classes to be configured
- * during initialisation in addition to through the static
- * {@link DataTable.ext.oStdClasses} object).
- * @namespace
- * @name DataTable.defaults.classes
- */
- "oClasses": {},
-
-
+ } catch (e) {
+ return {};
+ }
+ },
+
+
+ /**
+ * Callback which allows modification of the saved state prior to loading that state.
+ * This callback is called when the table is loading state from the stored data, but
+ * prior to the settings object being modified by the saved state. Note that for
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
+ * a plug-in.
+ */
+ "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.
+ */
+ "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.
+ */
+ "fnStateSaveCallback": function ( settings, data ) {
+ try {
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname,
+ JSON.stringify( data )
+ );
+ } catch (e) {
+ // noop
+ }
+ },
+
+
+ /**
+ * 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.
+ */
+ "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.
+ */
+ "iStateDuration": 7200,
+
+
+ /**
+ * 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.
+ */
+ "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".
+ */
+ "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.
+ */
+ "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).
+ */
+ "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.
+ */
+ "oLanguage": {
/**
- * All strings that DataTables uses in the user interface that it creates
- * are defined in this object, allowing you to modified them individually or
- * completely replace them all as required.
- * @namespace
- * @name DataTable.defaults.language
+ * 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).
*/
- "oLanguage": {
+ "oAria": {
/**
- * 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
+ * ARIA label that is added to the table headers when the column may be sorted
*/
- "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",
-
-
+ "orderable": ": Activate to sort",
+
/**
- * 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..."
- * }
- * } );
- * } );
+ * ARIA label that is added to the table headers when the column is currently being sorted
*/
- "sLoadingRecords": "Loading...",
-
-
+ "orderableReverse": ": Activate to invert sorting",
+
/**
- * 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"
- * }
- * } );
- * } );
+ * ARIA label that is added to the table headers when the column is currently being
+ * sorted and next step is to remove sorting
*/
- "sProcessing": "Processing...",
-
-
+ "orderableRemove": ": Activate to remove sorting",
+
+ paginate: {
+ first: 'First',
+ last: 'Last',
+ next: 'Next',
+ previous: 'Previous',
+ number: ''
+ }
+ },
+
+ /**
+ * Pagination string used by DataTables for the built-in pagination
+ * control types.
+ */
+ "oPaginate": {
/**
- * 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"
- * }
- * } );
- * } );
+ * Label and character for first page button («)
*/
- "sSearch": "Search:",
-
-
+ "sFirst": "\u00AB",
+
/**
- * Assign a `placeholder` attribute to the search `input` element
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.searchPlaceholder
+ * Last page button (»)
*/
- "sSearchPlaceholder": "",
-
-
+ "sLast": "\u00BB",
+
/**
- * 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"
- * }
- * } );
- * } );
+ * Next page button (›)
*/
- "sUrl": "",
-
-
+ "sNext": "\u203A",
+
/**
- * 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"
- * }
- * } );
- * } );
+ * Previous page button (‹)
*/
- "sZeroRecords": "No matching records found"
+ "sPrevious": "\u2039",
},
-
-
- /**
- * This parameter allows you to have define the global filtering state at
- * initialisation time. As an object the `search` parameter must be
- * defined, but all other parameters are optional. When `regex` is true,
- * the search string will be treated as a regular expression, when false
- * (default) it will be treated as a straight string. When `smart`
- * DataTables will use it's smart filtering methods (to word match at
- * any point in the data), when false this will not be done.
- * @namespace
- * @extends DataTable.models.oSearch
- *
- * @dtopt Options
- * @name DataTable.defaults.search
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "search": {"search": "Initial search"}
- * } );
- * } )
- */
- "oSearch": $.extend( {}, DataTable.models.oSearch ),
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * By default DataTables will look for the property `data` (or `aaData` for
- * compatibility with DataTables 1.9-) when obtaining data from an Ajax
- * source or for server-side processing - this parameter allows that
- * property to be changed. You can use Javascript dotted object notation to
- * get a data source for multiple levels of nesting.
- * @type string
- * @default data
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxDataProp
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxDataProp": "data",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * You can instruct DataTables to load data from an external
- * source using this parameter (use aData if you want to pass data in you
- * already have). Simply provide a url a JSON object can be obtained from.
- * @type string
- * @default null
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxSource
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxSource": null,
-
-
- /**
- * This initialisation variable allows you to specify exactly where in the
- * DOM you want DataTables to inject the various controls it adds to the page
- * (for example you might want the pagination controls at the top of the
- * table). DIV elements (with or without a custom class) can also be added to
- * aid styling. The follow syntax is used:
- * <ul>
- * <li>The following options are allowed:
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * </li>
- * <li>The following constants are allowed:
- * <ul>
- * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
- * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
- * </ul>
- * </li>
- * <li>The following syntax is expected:
- * <ul>
- * <li>'&lt;' and '&gt;' - div elements</li>
- * <li>'&lt;"class" and '&gt;' - div with a class</li>
- * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
- * </ul>
- * </li>
- * <li>Examples:
- * <ul>
- * <li>'&lt;"wrapper"flipt&gt;'</li>
- * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
- * </ul>
- * </li>
- * </ul>
- * @type string
- * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
- * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.dom
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
- * } );
- * } );
- */
- "sDom": "lfrtip",
-
-
- /**
- * Search delay option. This will throttle full table searches that use the
- * DataTables provided search input element (it does not effect calls to
- * `dt-api search()`, providing a delay before the search is made.
- * @type integer
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.searchDelay
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchDelay": 200
- * } );
- * } )
- */
- "searchDelay": null,
-
-
- /**
- * DataTables features six different built-in options for the buttons to
- * display for pagination control:
- *
- * * `numbers` - Page number buttons only
- * * `simple` - 'Previous' and 'Next' buttons only
- * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
- * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
- * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
- * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
- *
- * Further methods can be added using {@link DataTable.ext.oPagination}.
- * @type string
- * @default simple_numbers
- *
- * @dtopt Options
- * @name DataTable.defaults.pagingType
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pagingType": "full_numbers"
- * } );
- * } )
- */
- "sPaginationType": "simple_numbers",
-
-
- /**
- * Enable horizontal scrolling. When a table is too wide to fit into a
- * certain layout, or you have a large number of columns in the table, you
- * can enable x-scrolling to show the table in a viewport, which can be
- * scrolled. This property can be `true` which will allow the table to
- * scroll horizontally when needed, or any CSS unit, or a number (in which
- * case it will be treated as a pixel measurement). Setting as simply `true`
- * is recommended.
- * @type boolean|string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollX
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": true,
- * "scrollCollapse": true
- * } );
- * } );
- */
- "sScrollX": "",
-
-
- /**
- * This property can be used to force a DataTable to use more width than it
- * might otherwise do when x-scrolling is enabled. For example if you have a
- * table which requires to be well spaced, this parameter is useful for
- * "over-sizing" the table, and thus forcing scrolling. This property can by
- * any CSS unit, or a number (in which case it will be treated as a pixel
- * measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollXInner
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": "100%",
- * "scrollXInner": "110%"
- * } );
- * } );
- */
- "sScrollXInner": "",
-
-
- /**
- * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
- * to the given height, and enable scrolling for any data which overflows the
- * current viewport. This can be used as an alternative to paging to display
- * a lot of data in a small area (although paging and scrolling can both be
- * enabled at the same time). This property can be any CSS unit, or a number
- * (in which case it will be treated as a pixel measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollY
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false
- * } );
- * } );
- */
- "sScrollY": "",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * Set the HTTP method that is used to make the Ajax call for server-side
- * processing or Ajax sourced data.
- * @type string
- * @default GET
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.serverMethod
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sServerMethod": "GET",
-
-
- /**
- * DataTables makes use of renderers when displaying HTML elements for
- * a table. These renderers can be added or modified by plug-ins to
- * generate suitable mark-up for a site. For example the Bootstrap
- * integration plug-in for DataTables uses a paging button renderer to
- * display pagination buttons in the mark-up required by Bootstrap.
- *
- * For further information about the renderers available see
- * DataTable.ext.renderer
- * @type string|object
- * @default null
- *
- * @name DataTable.defaults.renderer
- *
- */
- "renderer": null,
-
-
- /**
- * Set the data property name that DataTables should use to get a row's id
- * to set as the `id` property in the node.
- * @type string
- * @default DT_RowId
- *
- * @name DataTable.defaults.rowId
- */
- "rowId": "DT_RowId"
- };
-
- _fnHungarianMap( DataTable.defaults );
-
-
-
- /*
- * Developer note - See note in model.defaults.js about the use of Hungarian
- * notation and camel case.
- */
-
- /**
- * Column options that can be given to DataTables at initialisation time.
- * @namespace
- */
- DataTable.defaults.column = {
- /**
- * Define which column(s) an order will occur on for this column. This
- * allows a column's ordering to take multiple columns into account when
- * doing a sort or use the data from a different column. For example first
- * name / last name columns make sense to do a multi-column sort over the
- * two columns.
- * @type array|int
- * @default null <i>Takes the value of the column index automatically</i>
- *
- * @name DataTable.defaults.column.orderData
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
- * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
- * { "orderData": 2, "targets": [ 2 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderData": [ 0, 1 ] },
- * { "orderData": [ 1, 0 ] },
- * { "orderData": 2 },
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "aDataSort": null,
- "iDataSort": -1,
-
-
- /**
- * You can control the default ordering direction, and even alter the
- * behaviour of the sort handler (i.e. only allow ascending ordering etc)
- * using this parameter.
- * @type array
- * @default [ 'asc', 'desc' ]
- *
- * @name DataTable.defaults.column.orderSequence
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
- * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
- * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * { "orderSequence": [ "asc" ] },
- * { "orderSequence": [ "desc", "asc", "asc" ] },
- * { "orderSequence": [ "desc" ] },
- * null
- * ]
- * } );
- * } );
- */
- "asSorting": [ 'asc', 'desc' ],
-
-
+
/**
- * Enable or disable filtering on the data in this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.searchable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "searchable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "searchable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
+ * Plural object for the data type the table is showing
*/
- "bSearchable": true,
-
-
+ entries: {
+ _: "entries",
+ 1: "entry"
+ },
+
/**
- * 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
- * ] } );
- * } );
+ * Page length options
*/
- "bSortable": true,
-
-
+ lengthLabels: {
+ '-1': 'All'
+ },
+
/**
- * 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
- * ] } );
- * } );
+ * 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).
*/
- "bVisible": true,
-
-
+ "sEmptyTable": "No data available in table",
+
+
/**
- * 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
+ * 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:
*
- * @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')
- * }
- * }
- * } ]
- * });
- * } );
+ * * `\_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
*/
- "fnCreatedCell": null,
-
-
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_",
+
+
/**
- * 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
+ * Display information string for when the table is empty. Typically the
+ * format of this string should match `info`.
*/
-
-
+ "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_",
+
+
/**
- * 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[, ]"
- * } ]
- * } );
- * } );
- *
+ * 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.
*/
- "mData": null,
-
-
+ "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)",
+
+
/**
- * 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>';
- * }
- * } ]
- * } );
- * } );
+ * 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.
*/
- "mRender": null,
-
-
+ "sInfoPostFix": "",
+
+
/**
- * 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
+ * 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.
*
- * @example
- * // Make the first column use TH cells
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "cellType": "th"
- * } ]
- * } );
- * } );
+ * 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.
*/
- "sCellType": "td",
-
-
+ "sDecimal": "",
+
+
/**
- * 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
- * ]
- * } );
- * } );
+ * 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.
*/
- "sClass": "",
-
+ "sThousands": ",",
+
+
/**
- * 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"
- * }
- * ]
- * } );
- * } );
+ * 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.
*/
- "sContentPadding": "",
-
-
+ "sLengthMenu": "_MENU_ _ENTRIES_ per page",
+
+
/**
- * 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"
- * }
- * ]
- * } );
- * } );
+ * 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 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.
*/
- "sDefaultContent": null,
-
-
+ "sLoadingRecords": "Loading...",
+
+
/**
- * This parameter is only used in DataTables' server-side processing. It can
- * be exceptionally useful to know what columns are being displayed on the
- * client side, and to map these to database fields. When defined, the names
- * also allow DataTables to reorder information from the server if it comes
- * back in an unexpected order (i.e. if you switch your columns around on the
- * client-side, your server-side code does not also need updating).
- * @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" }
- * ]
- * } );
- * } );
+ * Text which is displayed when the table is processing a user action
+ * (usually a sort command or similar).
*/
- "sName": "",
-
-
+ "sProcessing": "",
+
+
/**
- * Defines a data source type for the ordering which can be used to read
- * real-time information from the table (updating the internally cached
- * version) prior to ordering. This allows ordering to occur on user
- * editable elements such as form inputs.
- * @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" }
- * ]
- * } );
- * } );
+ * 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.
*/
- "sSortDataType": "std",
-
-
+ "sSearch": "Search:",
+
+
/**
- * The title of this column.
+ * Assign a `placeholder` attribute to the search `input` element
* @type string
- * @default null <i>Derived from the 'TH' value for this column in the
- * original HTML table.</i>
+ * @default
*
- * @name DataTable.defaults.column.title
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "title": "My column title", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "title": "My column title" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
+ * @dtopt Language
+ * @name DataTable.defaults.language.searchPlaceholder
*/
- "sTitle": null,
-
-
+ "sSearchPlaceholder": "",
+
+
/**
- * The type allows you to specify how the data for this column will be
- * ordered. Four types (string, numeric, date and html (which will strip
- * HTML tags before ordering)) are currently available. Note that only date
- * formats understood by Javascript's Date() object will be accepted as type
- * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
- * 'numeric', 'date' or 'html' (by default). Further types can be adding
- * through plug-ins.
- * @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
- * ]
- * } );
- * } );
+ * 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.
*/
- "sType": null,
-
-
+ "sUrl": "",
+
+
/**
- * Defining the width of the column, this parameter may take any CSS value
- * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
- * been given a specific width through this interface ensuring that the table
- * remains readable.
- * @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
- * ]
- * } );
- * } );
+ * 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).
*/
- "sWidth": null
- };
-
- _fnHungarianMap( DataTable.defaults.column );
-
-
-
+ "sZeroRecords": "No matching records found"
+ },
+
+
+ /** The initial data order is reversed when `desc` ordering */
+ orderDescReverse: true,
+
+
+ /**
+ * 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.
+ */
+ "oSearch": $.extend( {}, DataTable.models.oSearch ),
+
+
+ /**
+ * Table and control layout. This replaces the legacy `dom` option.
+ */
+ layout: {
+ topStart: 'pageLength',
+ topEnd: 'search',
+ bottomStart: 'info',
+ bottomEnd: 'paging'
+ },
+
+
+ /**
+ * Legacy DOM layout option
+ */
+ "sDom": null,
+
+
/**
- * 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.
+ * 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.
+ */
+ "searchDelay": null,
+
+
+ /**
+ * DataTables features six different built-in options for the buttons to
+ * display for pagination control:
*
- * 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": {
-
- /**
- * Flag to say if DataTables should automatically try to calculate the
- * optimum table and columns widths (true) or not (false).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bAutoWidth": null,
-
- /**
- * Delay the creation of TR and TD elements until they are actually
- * needed by a driven page draw. This can give a significant speed
- * increase for Ajax source and Javascript source data, but makes no
- * difference at all for DOM and server-side processing tables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bDeferRender": null,
-
- /**
- * Enable filtering on the table or not. Note that if this is disabled
- * then there is no filtering at all on the table, including fnFilter.
- * To just remove the filtering input use sDom and remove the 'f' option.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bFilter": null,
-
- /**
- * Table information element (the 'Showing x of y records' div) enable
- * flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bInfo": null,
-
- /**
- * Present a user control allowing the end user to change the page size
- * when pagination is enabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bLengthChange": null,
-
- /**
- * Pagination enabled or not. Note that if this is disabled then length
- * changing must also be disabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bPaginate": null,
-
- /**
- * Processing indicator enable flag whenever DataTables is enacting a
- * user request - typically an Ajax request for server-side processing.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bProcessing": null,
-
- /**
- * Server-side processing enabled flag - when enabled DataTables will
- * get all data from the server for every draw - there is no filtering,
- * sorting or paging done on the client-side.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bServerSide": null,
-
- /**
- * Sorting enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSort": null,
-
- /**
- * Multi-column sorting
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortMulti": null,
-
- /**
- * Apply a class to the columns which are being sorted to provide a
- * visual highlight or not. This can slow things down when enabled since
- * there is a lot of DOM interaction.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortClasses": null,
-
- /**
- * State saving enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bStateSave": null
- },
-
-
- /**
- * Scrolling settings for a table.
- * @namespace
- */
- "oScroll": {
- /**
- * When the table is shorter in height than sScrollY, collapse the
- * table container down to the height of the table (when true).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bCollapse": null,
-
- /**
- * Width of the scrollbar for the web-browser's platform. Calculated
- * during table initialisation.
- * @type int
- * @default 0
- */
- "iBarWidth": 0,
-
- /**
- * Viewport width for horizontal scrolling. Horizontal scrolling is
- * disabled if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sX": null,
-
- /**
- * Width to expand the table to when using x-scrolling. Typically you
- * should not need to use this.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @deprecated
- */
- "sXInner": null,
-
- /**
- * Viewport height for vertical scrolling. Vertical scrolling is disabled
- * if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sY": null
- },
-
- /**
- * Language information for the table.
- * @namespace
- * @extends DataTable.defaults.oLanguage
- */
- "oLanguage": {
- /**
- * Information callback function. See
- * {@link DataTable.defaults.fnInfoCallback}
- * @type function
- * @default null
- */
- "fnInfoCallback": null
- },
-
- /**
- * Browser support parameters
- * @namespace
- */
- "oBrowser": {
- /**
- * Indicate if the browser incorrectly calculates width:100% inside a
- * scrolling element (IE6/7)
- * @type boolean
- * @default false
- */
- "bScrollOversize": false,
-
- /**
- * Determine if the vertical scrollbar is on the right or left of the
- * scrolling container - needed for rtl language layout, although not
- * all browsers move the scrollbar (Safari).
- * @type boolean
- * @default false
- */
- "bScrollbarLeft": false,
-
- /**
- * Flag for if `getBoundingClientRect` is fully supported or not
- * @type boolean
- * @default false
- */
- "bBounding": false,
-
- /**
- * Browser scrollbar width
- * @type integer
- * @default 0
- */
- "barWidth": 0
- },
-
-
- "ajax": null,
-
-
- /**
- * Array referencing the nodes which are used for the features. The
- * parameters of this object match what is allowed by sDom - i.e.
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * @type array
- * @default []
- */
- "aanFeatures": [],
-
- /**
- * Store data information - see {@link DataTable.models.oRow} for detailed
- * information.
- * @type array
- * @default []
- */
- "aoData": [],
-
- /**
- * Array of indexes which are in the current display (after filtering etc)
- * @type array
- * @default []
- */
- "aiDisplay": [],
-
- /**
- * Array of indexes for display - no filtering
- * @type array
- * @default []
- */
- "aiDisplayMaster": [],
-
- /**
- * Map of row ids to data indexes
- * @type object
- * @default {}
- */
- "aIds": {},
-
- /**
- * Store information about each column that is in use
- * @type array
- * @default []
- */
- "aoColumns": [],
-
- /**
- * Store information about the table's header
- * @type array
- * @default []
- */
- "aoHeader": [],
-
- /**
- * Store information about the table's footer
- * @type array
- * @default []
- */
- "aoFooter": [],
-
- /**
- * 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": [],
-
+ * * `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
+ */
+ "sPaginationType": "",
+
+
+ /**
+ * 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.
+ */
+ "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).
+ */
+ "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).
+ */
+ "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.
+ */
+ "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
+ */
+ "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.
+ */
+ "rowId": "DT_RowId",
+
+
+ /**
+ * Caption value
+ */
+ "caption": null,
+
+
+ /**
+ * For server-side processing - use the data from the DOM for the first draw
+ */
+ iDeferLoading: null,
+
+ /** Event listeners */
+ on: null,
+
+ /** Title wrapper element type */
+ columnTitleTag: 'span'
+ };
+
+ _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.
+ */
+ "aDataSort": null,
+ "iDataSort": -1,
+
+ ariaTitle: '',
+
+
+ /**
+ * 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.
+ */
+ "asSorting": [ 'asc', 'desc', '' ],
+
+
+ /**
+ * Enable or disable filtering on the data in this column.
+ */
+ "bSearchable": true,
+
+
+ /**
+ * Enable or disable ordering on this column.
+ */
+ "bSortable": true,
+
+
+ /**
+ * Enable or disable the display of this column.
+ */
+ "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.
+ */
+ "fnCreatedCell": null,
+
+
+ /**
+ * 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.
+ */
+ "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 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.
+ */
+ "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).
+ */
+ "sCellType": "td",
+
+
+ /**
+ * Class to give to each cell in this column.
+ */
+ "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!
+ */
+ "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).
+ */
+ "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).
+ */
+ "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.
+ */
+ "sSortDataType": "std",
+
+
+ /**
+ * The title of this column.
+ */
+ "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.
+ */
+ "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.
+ */
+ "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.
+ */
+ DataTable.models.oSettings = {
+ /**
+ * Primary features of DataTables and their enablement state.
+ */
+ "oFeatures": {
+
/**
- * 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>
+ * 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 array
- * @todo These inner arrays should really be objects
*/
- "aaSorting": null,
-
+ "bAutoWidth": null,
+
/**
- * Sorting that is always applied to the table (i.e. prefixed in front of
- * aaSorting).
+ * Delay the creation of TR and TD elements until they are actually
+ * needed by a driven page draw. This can give a significant speed
+ * increase for Ajax source and JavaScript source data, but makes no
+ * difference at all for DOM and server-side processing tables.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
*/
- "aaSortingFixed": [],
-
+ "bDeferRender": null,
+
/**
- * Classes to use for the striping of a table.
+ * Enable filtering on the table or not. Note that if this is disabled
+ * then there is no filtering at all on the table, including fnFilter.
+ * To just remove the filtering input use sDom and remove the 'f' option.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "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,
-
+ "bFilter": null,
+
/**
- * Permanent ref to the tbody element
- * @type node
- * @default null
+ * Used only for compatibility with DT1
+ * @deprecated
*/
- "nTBody": null,
-
+ "bInfo": true,
+
/**
- * Cache the wrapper node (contains all DataTables controlled elements)
- * @type node
- * @default null
+ * Used only for compatibility with DT1
+ * @deprecated
*/
- "nTableWrapper": null,
-
+ "bLengthChange": true,
+
/**
- * Indicate if when using server-side processing the loading of data
- * should be deferred until the second draw.
+ * Pagination enabled or not. Note that if this is disabled then length
+ * changing must also be disabled.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type boolean
- * @default false
*/
- "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": [],
-
+ "bPaginate": null,
+
/**
- * Dictate the positioning of DataTables' control elements - see
- * {@link DataTable.model.oInit.sDom}.
+ * Processing indicator enable flag whenever DataTables is enacting a
+ * user request - typically an Ajax request for server-side processing.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
*/
- "sDom": null,
-
- /**
- * Search delay (in mS)
- * @type integer
- * @default null
- */
- "searchDelay": null,
-
+ "bProcessing": null,
+
/**
- * Which type of pagination should be used.
+ * Server-side processing enabled flag - when enabled DataTables will
+ * get all data from the server for every draw - there is no filtering,
+ * sorting or paging done on the client-side.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default two_button
*/
- "sPaginationType": "two_button",
-
+ "bServerSide": null,
+
/**
- * The state duration (for `stateSave`) in seconds.
+ * Sorting enablement flag.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type int
- * @default 0
- */
- "iStateDuration": 0,
-
- /**
- * Array of callback functions for state saving. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the JSON string to save that has been thus far created. Returns
- * a JSON string to be inserted into a json object
- * (i.e. '"param": [ 0, 1, 2]')</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateSave": [],
-
- /**
- * Array of callback functions for state loading. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the object stored. May return false to cancel state loading</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
*/
- "aoStateLoad": [],
-
- /**
- * State that was saved. Useful for back reference
- * @type object
- * @default null
- */
- "oSavedState": null,
-
- /**
- * State that was loaded. Useful for back reference
- * @type object
- * @default null
- */
- "oLoadedState": null,
-
+ "bSort": null,
+
/**
- * Source url for AJAX data for the table.
+ * Multi-column sorting
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
*/
- "sAjaxSource": null,
-
+ "bSortMulti": null,
+
/**
- * Property from a given object from which to read the table data from. This
- * can be an empty string (when not server-side processing), in which case
- * it is assumed an an array is given directly.
+ * Apply a class to the columns which are being sorted to provide a
+ * visual highlight or not. This can slow things down when enabled since
+ * there is a lot of DOM interaction.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sAjaxDataProp": null,
-
- /**
- * The last jQuery XHR object that was used for server-side data gathering.
- * This can be used for working with the XHR information in one of the
- * callbacks
- * @type object
- * @default null
- */
- "jqXHR": null,
-
- /**
- * JSON returned from the server in the last Ajax request
- * @type object
- * @default undefined
- */
- "json": undefined,
-
- /**
- * Data submitted as part of the last Ajax request
- * @type object
- * @default undefined
*/
- "oAjaxData": undefined,
-
+ "bSortClasses": null,
+
/**
- * Function to get the server-side data.
+ * State saving enablement flag.
* 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": [],
-
+ "bStateSave": null
+ },
+
+
+ /**
+ * Scrolling settings for a table.
+ */
+ "oScroll": {
/**
- * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
- * required).
+ * 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 string
*/
- "sServerMethod": null,
-
+ "bCollapse": 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
+ * Width of the scrollbar for the web-browser's platform. Calculated
+ * during table initialisation.
*/
- "fnFormatNumber": null,
-
+ "iBarWidth": 0,
+
/**
- * List of options that can be used for the user selectable length menu.
+ * 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 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,
-
+ "sX": 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.
- * @type boolean
- * @default false
+ * 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}.
* @deprecated
*/
- "bSorted": false,
-
+ "sXInner": null,
+
/**
- * 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.
+ * 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 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,
-
+ "sY": null
+ },
+
+ /**
+ * Language information for the table.
+ */
+ "oLanguage": {
/**
- * Last applied sort
- * @type array
- * @default []
+ * Information callback function. See
+ * {@link DataTable.defaults.fnInfoCallback}
*/
- "aLastSort": [],
-
+ "fnInfoCallback": null
+ },
+
+ /**
+ * Browser support parameters
+ */
+ "oBrowser": {
/**
- * 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).
*/
- "oPlugins": {},
-
+ "bScrollbarLeft": false,
+
/**
- * Function used to get a row's id from the row's data
- * @type function
- * @default null
+ * Browser scrollbar width
*/
- "rowIdFn": null,
-
- /**
- * Data location where to store a row's id
- * @type string
- * @default null
- */
- "rowId": null
- };
-
+ "barWidth": 0
+ },
+
+
+ "ajax": 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
+ * 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>
*/
-
-
+ "aanFeatures": [],
+
/**
- * 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
+ * Store data information - see {@link DataTable.models.oRow} for detailed
+ * information.
*/
- 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:"dt/dt-1.11.5",
-
-
- /**
- * Error reporting.
- *
- * How should DataTables report an error. Can take the value 'alert',
- * 'throw', 'none' or a function.
- *
- * @type string|function
- * @default alert
- */
- errMode: "alert",
-
-
- /**
- * Feature plug-ins.
- *
- * This is an array of objects which describe the feature plug-ins that are
- * available to DataTables. These feature plug-ins are then available for
- * use through the `dom` initialisation option.
- *
- * Each feature plug-in is described by an object which must have the
- * following properties:
- *
- * * `fnInit` - function that is used to initialise the plug-in,
- * * `cFeature` - a character so the feature can be enabled by the `dom`
- * instillation option. This is case sensitive.
- *
- * The `fnInit` function has the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- *
- * And the following return is expected:
- *
- * * {node|null} The element which contains your feature. Note that the
- * return may also be void if your plug-in does not require to inject any
- * DOM elements into DataTables control (`dom`) - for example this might
- * be useful when developing a plug-in which allows table control via
- * keyboard entry
- *
- * @type array
- *
- * @example
- * $.fn.dataTable.ext.features.push( {
- * "fnInit": function( oSettings ) {
- * return new TableTools( { "oDTSettings": oSettings } );
- * },
- * "cFeature": "T"
- * } );
- */
- feature: [],
-
-
- /**
- * Row searching.
- *
- * This method of searching is complimentary to the default type based
- * searching, and a lot more comprehensive as it allows you complete control
- * over the searching logic. Each element in this array is a function
- * (parameters described below) that is called for every row in the table,
- * and your logic decides if it should be included in the searching data set
- * or not.
- *
- * Searching functions have the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{array|object}` Data for the row to be processed (same as the
- * original format that was passed in as the data source, or an array
- * from a DOM data source
- * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
- * can be useful to retrieve the `TR` element if you need DOM interaction.
- *
- * And the following return is expected:
- *
- * * {boolean} Include the row in the searched result set (true) or not
- * (false)
- *
- * Note that as with the main search ability in DataTables, technically this
- * is "filtering", since it is subtractive. However, for consistency in
- * naming we call it searching here.
- *
- * @type array
- * @default []
- *
- * @example
- * // The following example shows custom search being applied to the
- * // fourth column (i.e. the data[3] index) based on two input values
- * // from the end-user, matching the data in a certain range.
- * $.fn.dataTable.ext.search.push(
- * function( settings, data, dataIndex ) {
- * var min = document.getElementById('min').value * 1;
- * var max = document.getElementById('max').value * 1;
- * var version = data[3] == "-" ? 0 : data[3]*1;
- *
- * if ( min == "" && max == "" ) {
- * return true;
- * }
- * else if ( min == "" && version < max ) {
- * return true;
- * }
- * else if ( min < version && "" == max ) {
- * return true;
- * }
- * else if ( min < version && version < max ) {
- * return true;
- * }
- * return false;
- * }
- * );
- */
- search: [],
-
-
- /**
- * Selector extensions
- *
- * The `selector` option can be used to extend the options available for the
- * selector modifier options (`selector-modifier` object data type) that
- * each of the three built in selector types offer (row, column and cell +
- * their plural counterparts). For example the Select extension uses this
- * mechanism to provide an option to select only rows, columns and cells
- * that have been marked as selected by the end user (`{selected: true}`),
- * which can be used in conjunction with the existing built in selector
- * options.
- *
- * 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: []
- },
-
-
- /**
- * 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.
- *
- * @type object
- * @default {}
- */
- internal: {},
-
-
- /**
- * Legacy configuration options. Enable and disable legacy options that
- * are available in DataTables.
- *
- * @type object
- */
- legacy: {
- /**
- * Enable / disable DataTables 1.9 compatible server-side processing
- * requests
- *
- * @type boolean
- * @default null
- */
- 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: {
- /**
- * Type detection functions.
- *
- * The functions defined in this object are used to automatically detect
- * a column's type, making initialisation of DataTables super easy, even
- * when complex data is in the table.
- *
- * The functions defined take two parameters:
- *
- * 1. `{*}` Data from the column cell to be analysed
- * 2. `{settings}` DataTables settings object. This can be used to
- * perform context specific type detection - for example detection
- * based on language settings such as using a comma for a decimal
- * place. Generally speaking the options from the settings will not
- * be required
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Data type detected, or null if unknown (and thus
- * pass it on to the other type detection functions.
- *
- * @type array
- *
- * @example
- * // Currency type detection plug-in:
- * $.fn.dataTable.ext.type.detect.push(
- * function ( data, settings ) {
- * // Check the numeric part
- * if ( ! data.substring(1).match(/[0-9]/) ) {
- * return null;
- * }
- *
- * // Check prefixed by currency
- * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- detect: [],
-
-
- /**
- * Type based search formatting.
- *
- * The type based searching functions can be used to pre-format the
- * data to be search on. For example, it can be used to strip HTML
- * tags or to de-format telephone numbers for numeric only searching.
- *
- * Note that is a search is not defined for a column of a given type,
- * no search formatting will be performed.
- *
- * Pre-processing of searching data plug-ins - When you assign the sType
- * for a column (or have it automatically detected for you by DataTables
- * or a type detection plug-in), you will typically be using this for
- * custom sorting, but it can also be used to provide custom searching
- * by allowing you to pre-processing the data and returning the data in
- * the format that should be searched upon. This is done by adding
- * functions this object with a parameter name which matches the sType
- * for that target column. This is the corollary of <i>afnSortData</i>
- * for searching data.
- *
- * The functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for searching
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Formatted string that will be used for the searching.
- *
- * @type object
- * @default {}
- *
- * @example
- * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
- * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
- * }
- */
- search: {},
-
-
- /**
- * Type based ordering.
- *
- * The column type tells DataTables what ordering to apply to the table
- * when a column is sorted upon. The order for each type that is defined,
- * is defined by the functions available in this object.
- *
- * Each ordering option can be described by three properties added to
- * this object:
- *
- * * `{type}-pre` - Pre-formatting function
- * * `{type}-asc` - Ascending order function
- * * `{type}-desc` - Descending order function
- *
- * All three can be used together, only `{type}-pre` or only
- * `{type}-asc` and `{type}-desc` together. It is generally recommended
- * that only `{type}-pre` is used, as this provides the optimal
- * implementation in terms of speed, although the others are provided
- * for compatibility with existing Javascript sort functions.
- *
- * `{type}-pre`: Functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for ordering
- *
- * And return:
- *
- * * `{*}` Data to be sorted upon
- *
- * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
- * functions, taking two parameters:
- *
- * 1. `{*}` Data to compare to the second parameter
- * 2. `{*}` Data to compare to the first parameter
- *
- * And returning:
- *
- * * `{*}` Ordering match: <0 if first parameter should be sorted lower
- * than the second parameter, ===0 if the two parameters are equal and
- * >0 if the first parameter should be sorted height than the second
- * parameter.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Numeric ordering of formatted numbers with a pre-formatter
- * $.extend( $.fn.dataTable.ext.type.order, {
- * "string-pre": function(x) {
- * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
- * return parseFloat( a );
- * }
- * } );
- *
- * @example
- * // Case-sensitive string ordering, with no pre-formatting method
- * $.extend( $.fn.dataTable.ext.order, {
- * "string-case-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-case-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- */
- order: {}
- },
-
- /**
- * Unique DataTables instance counter
- *
- * @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
- */
- 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 ) {
+ "aoData": [],
+
+ /**
+ * Array of indexes which are in the current display (after filtering etc)
+ */
+ "aiDisplay": [],
+
+ /**
+ * Array of indexes for display - no filtering
+ */
+ "aiDisplayMaster": [],
+
+ /**
+ * Map of row ids to data indexes
+ */
+ "aIds": {},
+
+ /**
+ * Store information about each column that is in use
+ */
+ "aoColumns": [],
+
+ /**
+ * Store information about the table's header
+ */
+ "aoHeader": [],
+
+ /**
+ * Store information about the table's footer
+ */
+ "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}.
+ */
+ "oPreviousSearch": {},
+
+ /**
+ * Store for named searches
+ */
+ searchFixed: {},
+
+ /**
+ * 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.
+ */
+ "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}.
+ */
+ "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}.
+ */
+ "aaSortingFixed": [],
+
+ /**
+ * If restoring a table - we should restore its width
+ */
+ "sDestroyWidth": 0,
+
+ /**
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
+ */
+ "aoRowCallback": [],
+
+ /**
+ * Callback functions for the header on each draw.
+ */
+ "aoHeaderCallback": [],
+
+ /**
+ * Callback function for the footer on each draw.
+ */
+ "aoFooterCallback": [],
+
+ /**
+ * Array of callback functions for draw callback functions
+ */
+ "aoDrawCallback": [],
+
+ /**
+ * Array of callback functions for row created function
+ */
+ "aoRowCreatedCallback": [],
+
+ /**
+ * Callback functions for just before the table is redrawn. A return of
+ * false will be used to cancel the draw.
+ */
+ "aoPreDrawCallback": [],
+
+ /**
+ * Callback functions for when the table has been initialised.
+ */
+ "aoInitComplete": [],
+
+
+ /**
+ * Callbacks for modifying the settings to be stored for state saving, prior to
+ * saving state.
+ */
+ "aoStateSaveParams": [],
+
+ /**
+ * Callbacks for modifying the settings that have been stored for state saving
+ * prior to using the stored values to restore the state.
+ */
+ "aoStateLoadParams": [],
+
+ /**
+ * Callbacks for operating on the settings object once the saved state has been
+ * loaded
+ */
+ "aoStateLoaded": [],
+
+ /**
+ * Cache the table ID for quick access
+ */
+ "sTableId": "",
+
+ /**
+ * The TABLE node for the main table
+ */
+ "nTable": null,
+
+ /**
+ * Permanent ref to the thead element
+ */
+ "nTHead": null,
+
+ /**
+ * Permanent ref to the tfoot element - if it exists
+ */
+ "nTFoot": null,
+
+ /**
+ * Permanent ref to the tbody element
+ */
+ "nTBody": null,
+
+ /**
+ * Cache the wrapper node (contains all DataTables controlled elements)
+ */
+ "nTableWrapper": null,
+
+ /**
+ * Indicate if all required information has been read in
+ */
+ "bInitialised": false,
+
+ /**
+ * Information about open rows. Each object in the array has the parameters
+ * 'nTr' and 'nParent'
+ */
+ "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}.
+ */
+ "sDom": null,
+
+ /**
+ * Search delay (in mS)
+ */
+ "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}.
+ */
+ "sPaginationType": "two_button",
+
+ /**
+ * Number of paging controls on the page. Only used for backwards compatibility
+ */
+ pagingControls: 0,
+
+ /**
+ * 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}.
+ */
+ "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>
+ */
+ "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>
+ */
+ "aoStateLoad": [],
+
+ /**
+ * State that was saved. Useful for back reference
+ */
+ "oSavedState": null,
+
+ /**
+ * State that was loaded. Useful for back reference
+ */
+ "oLoadedState": null,
+
+ /**
+ * Note if draw should be blocked while getting data
+ */
+ "bAjaxDataGet": true,
+
+ /**
+ * 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
+ */
+ "jqXHR": null,
+
+ /**
+ * JSON returned from the server in the last Ajax request
+ */
+ "json": undefined,
+
+ /**
+ * Data submitted as part of the last Ajax request
+ */
+ "oAjaxData": undefined,
+
+ /**
+ * 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}.
+ */
+ "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}.
+ */
+ "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}.
+ */
+ "aLengthMenu": null,
+
+ /**
+ * Counter for the draws that the table does. Also used as a tracker for
+ * server-side processing
+ */
+ "iDraw": 0,
+
+ /**
+ * Indicate if a redraw is being done - useful for Ajax
+ */
+ "bDrawing": false,
+
+ /**
+ * Draw index (iDraw) of the last error when parsing the returned data
+ */
+ "iDrawError": -1,
+
+ /**
+ * Paging display length
+ */
+ "_iDisplayLength": 10,
+
+ /**
+ * Paging start point - aiDisplay index
+ */
+ "_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.
+ */
+ "_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.
+ */
+ "_iRecordsDisplay": 0,
+
+ /**
+ * The classes to use for the table
+ */
+ "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.
+ * @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.
+ * @deprecated
+ */
+ "bSorted": false,
+
+ /**
+ * Indicate that if multiple rows are in the header and there is more than
+ * one unique cell per column. Replaced by titleRow
+ */
+ "bSortCellsTop": null,
+
+ /**
+ * Initialisation object that is used for the table
+ */
+ "oInit": null,
+
+ /**
+ * Destroy callback functions - for plug-ins to attach themselves to the
+ * destroy so they can clean up markup and events.
+ */
+ "aoDestroyCallback": [],
+
+
+ /**
+ * Get the number of records in the current record set, before filtering
+ */
+ "fnRecordsTotal": function ()
+ {
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsTotal * 1 :
+ this.aiDisplayMaster.length;
+ },
+
+ /**
+ * Get the number of records in the current record set, after filtering
+ */
+ "fnRecordsDisplay": function ()
+ {
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsDisplay * 1 :
+ this.aiDisplay.length;
+ },
+
+ /**
+ * Get the display end point - aiDisplay index
+ */
+ "fnDisplayEnd": function ()
+ {
var
- numbers = [],
- buttons = extPagination.numbers_length,
- half = Math.floor( buttons / 2 ),
- i = 1;
-
- if ( pages <= buttons ) {
- numbers = _range( 0, pages );
+ 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
+ */
+ "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.
+ */
+ "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
+ */
+ "aLastSort": [],
+
+ /**
+ * Stored plug-in instances
+ */
+ "oPlugins": {},
+
+ /**
+ * Function used to get a row's id from the row's data
+ */
+ "rowIdFn": null,
+
+ /**
+ * Data location where to store a row's id
+ */
+ "rowId": null,
+
+ caption: '',
+
+ captionNode: null,
+
+ colgroup: null,
+
+ /** Delay loading of data */
+ deferLoading: null,
+
+ /** Allow auto type detection */
+ typeDetect: true,
+
+ /** ResizeObserver for the container div */
+ resizeObserver: null,
+
+ /** Keep a record of the last size of the container, so we can skip duplicates */
+ containerWidth: -1,
+
+ /** Reverse the initial order of the data set on desc ordering */
+ orderDescReverse: null,
+
+ /** Show / hide ordering indicators in headers */
+ orderIndicators: true,
+
+ /** Default ordering listener */
+ orderHandler: true,
+
+ /** Title row indicator */
+ titleRow: null,
+
+ /** Title wrapper element type */
+ columnTitleTag: 'span'
+ };
+
+ /**
+ * 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
+ */
+
+
+ var extPagination = DataTable.ext.pager;
+
+ // Paging buttons configuration
+ $.extend( extPagination, {
+ simple: function () {
+ return [ 'previous', 'next' ];
+ },
+
+ full: function () {
+ return [ 'first', 'previous', 'next', 'last' ];
+ },
+
+ numbers: function () {
+ return [ 'numbers' ];
+ },
+
+ simple_numbers: function () {
+ return [ 'previous', 'numbers', 'next' ];
+ },
+
+ full_numbers: function () {
+ return [ 'first', 'previous', 'numbers', 'next', 'last' ];
+ },
+
+ first_last: function () {
+ return ['first', 'last'];
+ },
+
+ first_last_numbers: function () {
+ return ['first', 'numbers', 'last'];
+ },
+
+ // For testing and plug-ins to use
+ _numbers: _pagingNumbers,
+
+ // Number of number buttons - legacy, use `numbers` option for paging feature
+ numbers_length: 7
+ } );
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ pagingButton: {
+ _: function (settings, buttonType, content, active, disabled) {
+ var classes = settings.oClasses.paging;
+ var btnClasses = [classes.button];
+ var btn;
+
+ if (active) {
+ btnClasses.push(classes.active);
+ }
+
+ if (disabled) {
+ btnClasses.push(classes.disabled)
+ }
+
+ if (buttonType === 'ellipsis') {
+ btn = $('<span class="ellipsis"></span>').html(content)[0];
+ }
+ else {
+ btn = $('<button>', {
+ class: btnClasses.join(' '),
+ role: 'link',
+ type: 'button'
+ }).html(content);
+ }
+
+ return {
+ display: btn,
+ clicker: btn
+ }
}
- else if ( page <= half ) {
- numbers = _range( 0, buttons-2 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
+ },
+
+ pagingContainer: {
+ _: function (settings, buttons) {
+ // No wrapping element - just append directly to the host
+ return buttons;
}
- 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 );
+ }
+ } );
+
+ // Common function to remove new lines, strip HTML and diacritic control
+ var _filterString = function (stripHtml, normalize) {
+ return function (str) {
+ if (_empty(str) || typeof str !== 'string') {
+ return str;
}
- 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 );
+
+ str = str.replace( _re_new_lines, " " );
+
+ if (stripHtml) {
+ str = _stripHtml(str);
}
-
- numbers.DT_el = 'span';
- return numbers;
+
+ if (normalize) {
+ str = _normalize(str, false);
+ }
+
+ return str;
+ };
+ }
+
+ /*
+ * 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.
+ */
+
+ /**
+ * Common logic for moment, luxon or a date action.
+ *
+ * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again
+ */
+ function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) {
+ if (__moment) {
+ return dtLib[momentFn]( arg1 );
+ }
+ else if (__luxon) {
+ return dtLib[luxonFn]( arg1 );
}
+ return dateFn ? dtLib[dateFn]( arg1 ) : dtLib;
+ }
+
+
+ var __mlWarning = false;
+ var __luxon; // Can be assigned in DateTable.use()
+ var __moment; // Can be assigned in DateTable.use()
+ var __reIsoTimezone = /[T\s]\d{2}.*?(Z|[+-]\d{2}(?::?\d{2})?)$/;
+
+ /**
+ *
+ */
+ function resolveWindowLibs() {
+ if (window.luxon && ! __luxon) {
+ __luxon = window.luxon;
+ }
- $.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 );
- }
- else {
- btnDisplay = null;
- btnClass = button;
- tabIndex = settings.iTabIndex;
-
- switch ( button ) {
- case 'ellipsis':
- container.append('<span class="ellipsis">&#x2026;</span>');
- break;
-
- case 'first':
- btnDisplay = lang.sFirst;
-
- if ( page === 0 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'previous':
- btnDisplay = lang.sPrevious;
-
- if ( page === 0 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'next':
- btnDisplay = lang.sNext;
-
- if ( pages === 0 || page === pages-1 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- case 'last':
- btnDisplay = lang.sLast;
-
- if ( pages === 0 || page === pages-1 ) {
- tabIndex = -1;
- btnClass += ' ' + disabledClass;
- }
- break;
-
- default:
- btnDisplay = settings.fnFormatNumber( button + 1 );
- btnClass = page === button ?
- classes.sPageButtonActive : '';
- break;
- }
-
- if ( btnDisplay !== null ) {
- node = $('<a>', {
- 'class': classes.sPageButton+' '+btnClass,
- 'aria-controls': settings.sTableId,
- 'aria-label': aria[ button ],
- 'data-dt-idx': counter,
- 'tabindex': tabIndex,
- 'id': idx === 0 && typeof button === 'string' ?
- settings.sTableId +'_'+ button :
- null
- } )
- .html( btnDisplay )
- .appendTo( container );
-
- _fnBindAction(
- node, {action: button}, clickHandler
- );
-
- counter++;
- }
- }
+ if (window.moment && ! __moment) {
+ __moment = window.moment;
+ }
+ }
+
+ function __mldObj (d, format, locale) {
+ var dt;
+
+ resolveWindowLibs();
+
+ if (__moment) {
+ dt = __moment( d, format, locale, true );
+
+ if (! dt.isValid()) {
+ return null;
+ }
+ }
+ else if (__luxon) {
+ dt = format && typeof d === 'string'
+ ? __luxon.DateTime.fromFormat( d, format )
+ : __luxon.DateTime.fromISO( d );
+
+ if (! dt.isValid) {
+ return null;
+ }
+
+ dt = 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');
+ }
+
+ __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 ? '-' + 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 + '-pre']) {
+ DataTable.type(typeName, {
+ detect: function (d) {
+ // The renderer will give the value to type detect as the type!
+ return d === typeName ? typeName : false;
+ },
+ order: {
+ pre: function (d) {
+ // The renderer gives us Moment, Luxon or Date objects for the sorting, all of which have a
+ // `valueOf` which gives milliseconds epoch
+ return d.valueOf();
}
- };
-
- // 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;
+ },
+ className: 'dt-right'
+ });
+ }
- 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');
+ 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()
+ ) );
}
- catch (e) {}
-
- attach( $(host).empty(), buttons );
+ 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;
+ }
+
+ // Determine if there is a timezone. If there is, we want to reuse
+ // it for the output, so the timezone doesn't change between the
+ // input and output.
+ let options = {};
+ let tzMatch = typeof d === 'string' ? d.match(__reIsoTimezone) : null;
+
+ if (tzMatch) {
+ options.timeZone = tzMatch[1] === 'Z' ? 'UTC' : tzMatch[1];
+ }
+
+ 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](
+ navigator.language,
+ options
+ )
+ : __mld(dt, 'format', 'toFormat', 'toISOString', to);
+
+ // XSS protection
+ return type === 'display' ?
+ _escapeHtml( formatted ) :
+ formatted;
+ };
+ }
+ }
+
+ // Based on locale, determine standard number formatting
+ // Fallback for legacy browsers is US English
+ var __thousands = ',';
+ var __decimal = '.';
+
+ if (window.Intl !== undefined) {
+ try {
+ var num = new Intl.NumberFormat().formatToParts(100000.1);
- if ( activeEl !== undefined ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
+ 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;
+ }
+ }
+ }
+ 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-' + format;
+
+ if (! locale) {
+ locale = 'en';
+ }
+
+ if (! DataTable.ext.type.order[typeName]) {
+ DataTable.type(typeName, {
+ detect: function (d) {
+ var dt = __mldObj(d, format, locale);
+ return d === '' || dt ? typeName : false;
+ },
+ order: {
+ pre: function (d) {
+ return __mldObj(d, format, locale) || 0;
}
+ },
+ className: 'dt-right'
+ });
+ }
+ }
+
+ /**
+ * 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:
+ *
+ * * `moment` - Uses the MomentJS library to convert from a given format into another.
+ * This renderer has three overloads:
+ * * 1 parameter:
+ * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`)
+ * * 2 parameters:
+ * * `string` - Format to convert from
+ * * `string` - Format to convert to. Assumes `en` locale
+ * * 3 parameters:
+ * * `string` - Format to convert from
+ * * `string` - Format to convert to
+ * * `string` - Locale
+ * * `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;
+ }
+
+ 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 );
+ var abs = Math.abs(flo);
+
+ // Scientific notation for large and small numbers
+ if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) {
+ var exp = flo.toExponential(precision).split(/e\+?/);
+ return exp[0] + ' x 10<sup>' + exp[1] + '</sup>';
+ }
+
+ // 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 _escapeHtml( 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: _escapeHtml,
+ filter: _escapeHtml
+ };
+ }
+ };
+
+
+ var _extTypes = DataTable.ext.type;
+
+ // Get / set type
+ DataTable.type = function (name, prop, val) {
+ if (! prop) {
+ return {
+ className: _extTypes.className[name],
+ detect: _extTypes.detect.find(function (fn) {
+ return fn._name === name;
+ }),
+ order: {
+ pre: _extTypes.order[name + '-pre'],
+ asc: _extTypes.order[name + '-asc'],
+ desc: _extTypes.order[name + '-desc']
+ },
+ render: _extTypes.render[name],
+ search: _extTypes.search[name]
+ };
+ }
+
+ var setProp = function(prop, propVal) {
+ _extTypes[prop][name] = propVal;
+ };
+ var setDetect = function (detect) {
+ // `detect` can be a function or an object - we set a name
+ // property for either - that is used for the detection
+ Object.defineProperty(detect, "_name", {value: name});
+
+ var idx = _extTypes.detect.findIndex(function (item) {
+ return item._name === name;
+ });
+
+ if (idx === -1) {
+ _extTypes.detect.unshift(detect);
}
- } );
-
-
-
- // 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;
+ else {
+ _extTypes.detect.splice(idx, 1, detect);
+ }
+ };
+ var setOrder = function (obj) {
+ _extTypes.order[name + '-pre'] = obj.pre; // can be undefined
+ _extTypes.order[name + '-asc'] = obj.asc; // can be undefined
+ _extTypes.order[name + '-desc'] = obj.desc; // can be undefined
+ };
+
+ // prop is optional
+ if (val === undefined) {
+ val = prop;
+ prop = null;
+ }
+
+ if (prop === 'className') {
+ setProp('className', val);
+ }
+ else if (prop === 'detect') {
+ setDetect(val);
+ }
+ else if (prop === 'order') {
+ setOrder(val);
+ }
+ else if (prop === 'render') {
+ setProp('render', val);
+ }
+ else if (prop === 'search') {
+ setProp('search', val);
+ }
+ else if (! prop) {
+ if (val.className) {
+ setProp('className', val.className);
+ }
+
+ if (val.detect !== undefined) {
+ setDetect(val.detect);
+ }
+
+ if (val.order) {
+ setOrder(val.order);
+ }
+
+ if (val.render !== undefined) {
+ setProp('render', val.render);
+ }
+
+ if (val.search !== undefined) {
+ setProp('search', val.search);
+ }
+ }
+ }
+
+ // Get a list of types
+ DataTable.types = function () {
+ return _extTypes.detect.map(function (fn) {
+ return fn._name;
+ });
+ };
+
+ var __diacriticSort = function (a, b) {
+ a = a !== null && a !== undefined ? a.toString().toLowerCase() : '';
+ b = b !== null && b !== undefined ? b.toString().toLowerCase() : '';
+
+ // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old
+ // Safari and thus can disable this check
+ // eslint-disable-next-line compat/compat
+ return a.localeCompare(b, navigator.languages[0] || navigator.language, {
+ numeric: true,
+ ignorePunctuation: true,
+ });
+ }
+
+ var __diacriticHtmlSort = function (a, b) {
+ a = _stripHtml(a);
+ b = _stripHtml(b);
+
+ return __diacriticSort(a, b);
+ }
+
+ //
+ // Built in data types
+ //
+
+ DataTable.type('string', {
+ detect: function () {
+ return 'string';
+ },
+ order: {
+ 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 !== 'boolean' ?
+ '' :
+ typeof a === 'string' ?
+ a.toLowerCase() :
+ ! a.toString ?
+ '' :
+ a.toString();
+ }
+ },
+ search: _filterString(false, true)
+ });
+
+ DataTable.type('string-utf8', {
+ detect: {
+ allOf: function ( d ) {
+ return true;
},
-
- // Dates (only those recognised by the browser's Date.parse)
- function ( d, settings )
- {
+ oneOf: function ( d ) {
+ // At least one data point must contain a non-ASCII character
+ // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-)
+ // this data type won't be supported.
+ // eslint-disable-next-line compat/compat
+ return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/);
+ }
+ },
+ order: {
+ asc: __diacriticSort,
+ desc: function (a, b) {
+ return __diacriticSort(a, b) * -1;
+ }
+ },
+ search: _filterString(false, true)
+ });
+
+
+ DataTable.type('html', {
+ detect: {
+ allOf: function ( d ) {
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
+ },
+ oneOf: function ( d ) {
+ // At least one data point must contain a `<`
+ return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1;
+ }
+ },
+ order: {
+ pre: function ( a ) {
+ return _empty(a) ?
+ '' :
+ a.replace ?
+ _stripHtml(a).trim().toLowerCase() :
+ a+'';
+ }
+ },
+ search: _filterString(true, true)
+ });
+
+
+ DataTable.type('html-utf8', {
+ detect: {
+ allOf: function ( d ) {
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
+ },
+ oneOf: function ( d ) {
+ // At least one data point must contain a `<` and a non-ASCII character
+ // eslint-disable-next-line compat/compat
+ return navigator.languages &&
+ ! _empty( d ) &&
+ typeof d === 'string' &&
+ d.indexOf('<') !== -1 &&
+ typeof d === 'string' && d.match(/[^\x00-\x7F]/);
+ }
+ },
+ order: {
+ asc: __diacriticHtmlSort,
+ desc: function (a, b) {
+ return __diacriticHtmlSort(a, b) * -1;
+ }
+ },
+ search: _filterString(true, true)
+ });
+
+
+ DataTable.type('date', {
+ className: 'dt-type-date',
+ detect: {
+ allOf: function ( d ) {
// 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
@@ -14851,505 +13150,1055 @@
return null;
}
var parsed = Date.parse(d);
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
+ return (parsed !== null && !isNaN(parsed)) || _empty(d);
},
-
- // Formatted numbers
- function ( d, settings )
- {
+ oneOf: function ( d ) {
+ // At least one entry must be a date or a string with a date
+ return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d));
+ }
+ },
+ order: {
+ pre: function ( d ) {
+ var ts = Date.parse( d );
+ return isNaN(ts) ? -Infinity : ts;
+ }
+ }
+ });
+
+
+ DataTable.type('html-num-fmt', {
+ className: 'dt-type-numeric',
+ detect: {
+ allOf: function ( d, settings ) {
var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
+ return _htmlNumeric( d, decimal, true, false );
},
-
- // HTML numeric
- function ( d, settings )
- {
+ oneOf: function (d, settings) {
+ // At least one data point must contain a numeric value
var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
+ return _htmlNumeric( d, decimal, true, false );
+ }
+ },
+ order: {
+ pre: function ( d, s ) {
+ var dp = s.oLanguage.sDecimal;
+ return __numericReplace( d, dp, _re_html, _re_formatted_numeric );
+ }
+ },
+ search: _filterString(true, true)
+ });
+
+
+ DataTable.type('html-num', {
+ className: 'dt-type-numeric',
+ detect: {
+ allOf: function ( d, settings ) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal, false, true );
},
-
- // HTML numeric, formatted
- function ( d, settings )
- {
+ oneOf: function (d, settings) {
+ // At least one data point must contain a numeric value
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal, false, false );
+ }
+ },
+ order: {
+ pre: function ( d, s ) {
+ var dp = s.oLanguage.sDecimal;
+ return __numericReplace( d, dp, _re_html );
+ }
+ },
+ search: _filterString(true, true)
+ });
+
+
+ DataTable.type('num-fmt', {
+ className: 'dt-type-numeric',
+ detect: {
+ allOf: function ( d, settings ) {
var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
+ return _isNumber( d, decimal, true, true );
},
-
- // HTML (this is strict checking - there must be html)
- function ( d, settings )
- {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
+ oneOf: function (d, settings) {
+ // At least one data point must contain a numeric value
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, true, false );
}
- ] );
-
-
-
- // 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, "" ) :
- '';
+ },
+ order: {
+ pre: function ( d, s ) {
+ var dp = s.oLanguage.sDecimal;
+ return __numericReplace( d, dp, _re_formatted_numeric );
+ }
+ }
+ });
+
+
+ DataTable.type('num', {
+ className: 'dt-type-numeric',
+ detect: {
+ allOf: function ( d, settings ) {
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, false, true );
},
-
- string: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data.replace( _re_new_lines, " " ) :
- data;
+ oneOf: function (d, settings) {
+ // At least one data point must contain a numeric value
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, false, false );
}
- } );
-
-
-
- var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( d !== 0 && (!d || d === '-') ) {
- return -Infinity;
+ },
+ order: {
+ pre: function (d, s) {
+ var dp = s.oLanguage.sDecimal;
+ return __numericReplace( d, dp );
}
+ }
+ });
+
+
+
+
+ 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 );
+ var type = typeof d;
+
+ if (type === 'number' || type === 'bigint') {
+ return d;
+ }
+
+ // 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 ( d.replace ) {
- if ( re1 ) {
- d = d.replace( re1, '' );
+
+ if ( re2 ) {
+ d = d.replace( re2, '' );
+ }
+ }
+
+ return d * 1;
+ };
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ footer: {
+ _: function ( settings, cell, classes ) {
+ cell.addClass(classes.tfoot.cell);
+ }
+ },
+
+ header: {
+ _: function ( settings, cell, classes ) {
+ cell.addClass(classes.thead.cell);
+
+ if (! settings.oFeatures.bSort) {
+ cell.addClass(classes.order.none);
}
-
- if ( re2 ) {
- d = d.replace( re2, '' );
+
+ var titleRow = settings.titleRow;
+ var headerRows = cell.closest('thead').find('tr');
+ var rowIdx = cell.parent().index();
+
+ // Conditions to not apply the ordering icons
+ if (
+ // Cells and rows which have the attribute to disable the icons
+ cell.attr('data-dt-order') === 'disable' ||
+ cell.parent().attr('data-dt-order') === 'disable' ||
+
+ // titleRow support, for defining a specific row in the header
+ (titleRow === true && rowIdx !== 0) ||
+ (titleRow === false && rowIdx !== headerRows.length - 1) ||
+ (typeof titleRow === 'number' && rowIdx !== titleRow)
+ ) {
+ return;
}
+
+ // 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 column-visibility.dt.DT', function ( e, ctx, column ) {
+ if ( settings !== ctx ) { // need to check if this is the host
+ return; // table, not a nested one
+ }
+
+ var sorting = ctx.sortDetails;
+
+ if (! sorting) {
+ return;
+ }
+
+ var orderedColumns = _pluck(sorting, 'col');
+
+ // This handler is only needed on column visibility if the column is part of the
+ // ordering. If it isn't, then we can bail out to save performance. It could be a
+ // separate event handler, but this is a balance between code reuse / size and performance
+ // console.log(e, e.name, column, orderedColumns, orderedColumns.includes(column))
+ if (e.type === 'column-visibility' && ! orderedColumns.includes(column)) {
+ return;
+ }
+
+ var i;
+ var orderClasses = classes.order;
+ var columns = ctx.api.columns( cell );
+ var col = settings.aoColumns[columns.flatten()[0]];
+ var orderable = columns.orderable().includes(true);
+ var ariaType = '';
+ var indexes = columns.indexes();
+ var sortDirs = columns.orderable(true).flatten();
+ var tabIndex = settings.iTabIndex;
+ var canOrder = ctx.orderHandler && orderable;
+
+ cell
+ .removeClass(
+ orderClasses.isAsc +' '+
+ orderClasses.isDesc
+ )
+ .toggleClass( orderClasses.none, ! orderable )
+ .toggleClass( orderClasses.canAsc, canOrder && sortDirs.includes('asc') )
+ .toggleClass( orderClasses.canDesc, canOrder && sortDirs.includes('desc') );
+
+ // Determine if all of the columns that this cell covers are included in the
+ // current ordering
+ var isOrdering = true;
+
+ for (i=0; i<indexes.length; i++) {
+ if (! orderedColumns.includes(indexes[i])) {
+ isOrdering = false;
+ }
+ }
+
+ if ( isOrdering ) {
+ // Get the ordering direction for the columns under this cell
+ // Note that it is possible for a cell to be asc and desc sorting
+ // (column spanning cells)
+ var orderDirs = columns.order();
+
+ cell.addClass(
+ orderDirs.includes('asc') ? orderClasses.isAsc : '' +
+ orderDirs.includes('desc') ? orderClasses.isDesc : ''
+ );
+ }
+
+ // Find the first visible column that has ordering applied to it - it get's
+ // the aria information, as the ARIA spec says that only one column should
+ // be marked with aria-sort
+ var firstVis = -1; // column index
+
+ for (i=0; i<orderedColumns.length; i++) {
+ if (settings.aoColumns[orderedColumns[i]].bVisible) {
+ firstVis = orderedColumns[i];
+ break;
+ }
+ }
+
+ if (indexes[0] == firstVis) {
+ var firstSort = sorting[0];
+ var sortOrder = col.asSorting;
+
+ cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending');
+
+ // Determine if the next click will remove sorting or change the sort
+ ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse';
+ }
+ else {
+ cell.removeAttr('aria-sort');
+ }
+
+ // Make the headers tab-able for keyboard navigation
+ if (orderable) {
+ var orderSpan = cell.find('.dt-column-order');
+
+ orderSpan
+ .attr('role', 'button')
+ .attr('aria-label', orderable
+ ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
+ : col.ariaTitle
+ );
+
+ if (tabIndex !== -1) {
+ orderSpan.attr('tabindex', tabIndex);
+ }
+ }
+ } );
}
-
- 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 );
+ },
+
+ layout: {
+ _: function ( settings, container, items ) {
+ var classes = settings.oClasses.layout;
+ var row = $('<div/>')
+ .attr('id', items.id || null)
+ .addClass(items.className || classes.row)
+ .appendTo( container );
+
+ DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) {
+ if (key === 'id' || key === 'className') {
+ return;
}
- },
- 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;
+
+ var klass = '';
+
+ if (val.table) {
+ row.addClass(classes.tableRow);
+ klass += classes.tableCell + ' ';
}
- }
+
+ if (key === 'start') {
+ klass += classes.start;
+ }
+ else if (key === 'end') {
+ klass += classes.end;
+ }
+ else {
+ klass += classes.full;
+ }
+
+ $('<div/>')
+ .attr({
+ id: val.id || null,
+ "class": val.className
+ ? val.className
+ : classes.cell + ' ' + klass
+ })
+ .append( val.contents )
+ .appendTo( row );
+ });
+ },
+
+ // Shared for use by the styling frameworks
+ _forLayoutRow: function (items, fn) {
+ // As we are inserting dom elements, we need start / end in a
+ // specific order, this function is used for sorting the layout
+ // keys.
+ var layoutEnum = function (x) {
+ switch (x) {
+ case '': return 0;
+ case 'start': return 1;
+ case 'end': return 2;
+ default: return 3;
+ }
+ };
+
+ Object
+ .keys(items)
+ .sort(function (a, b) {
+ return layoutEnum(a) - layoutEnum(b);
+ })
+ .forEach(function (key) {
+ fn(key, items[key]);
+ });
+ }
+ }
+ } );
+
+
+ DataTable.feature = {};
+
+ // Third parameter is internal only!
+ DataTable.feature.register = function ( name, cb, legacy ) {
+ DataTable.ext.features[ name ] = cb;
+
+ if (legacy) {
+ _ext.feature.push({
+ cFeature: legacy,
+ fnInit: cb
+ });
+ }
+ };
+
+ function _divProp(el, prop, val) {
+ if (val) {
+ el[prop] = val;
+ }
+ }
+
+ DataTable.feature.register( 'div', function ( settings, opts ) {
+ var n = $('<div>')[0];
+
+ if (opts) {
+ _divProp(n, 'className', opts.className);
+ _divProp(n, 'id', opts.id);
+ _divProp(n, 'innerHTML', opts.html);
+ _divProp(n, 'textContent', opts.text);
+ }
+
+ return n;
+ } );
+
+ DataTable.feature.register( 'info', function ( settings, opts ) {
+ // For compatibility with the legacy `info` top level option
+ if (! settings.oFeatures.bInfo) {
+ return null;
+ }
+
+ var
+ lang = settings.oLanguage,
+ tid = settings.sTableId,
+ n = $('<div/>', {
+ 'class': settings.oClasses.info.container,
+ } );
+
+ opts = $.extend({
+ callback: lang.fnInfoCallback,
+ empty: lang.sInfoEmpty,
+ postfix: lang.sInfoPostFix,
+ search: lang.sInfoFiltered,
+ text: lang.sInfo,
+ }, opts);
+
+
+ // Update display on each draw
+ settings.aoDrawCallback.push(function (s) {
+ _fnUpdateInfo(s, opts, n);
+ });
+
+ // For the first info display in the table, we add a callback and aria information.
+ if (! settings._infoEl) {
+ n.attr({
+ 'aria-live': 'polite',
+ id: tid+'_info',
+ role: 'status'
+ });
+
+ // Table is described by our info div
+ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
+
+ settings._infoEl = n;
+ }
+
+ return n;
+ }, 'i' );
+
+ /**
+ * Update the information elements in the display
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnUpdateInfo ( settings, opts, node )
+ {
+ var
+ start = settings._iDisplayStart+1,
+ end = settings.fnDisplayEnd(),
+ max = settings.fnRecordsTotal(),
+ total = settings.fnRecordsDisplay(),
+ out = total
+ ? opts.text
+ : opts.empty;
+
+ if ( total !== max ) {
+ // Record set after filtering
+ out += ' ' + opts.search;
+ }
+
+ // Convert the macros
+ out += opts.postfix;
+ out = _fnMacros( settings, out );
+
+ if ( opts.callback ) {
+ out = opts.callback.call( settings.oInstance,
+ settings, start, end, max, total, out
);
}
-
-
- // 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));
+
+ node.html( out );
+
+ _fnCallbackFire(settings, null, 'info', [settings, node[0], out]);
+ }
+
+ var __searchCounter = 0;
+
+ // opts
+ // - text
+ // - placeholder
+ DataTable.feature.register( 'search', function ( settings, opts ) {
+ // Don't show the input if filtering isn't available on the table
+ if (! settings.oFeatures.bFilter) {
+ return null;
+ }
+
+ var classes = settings.oClasses.search;
+ var tableId = settings.sTableId;
+ var language = settings.oLanguage;
+ var previousSearch = settings.oPreviousSearch;
+ var input = '<input type="search" class="'+classes.input+'"/>';
+
+ opts = $.extend({
+ placeholder: language.sSearchPlaceholder,
+ processing: false,
+ text: language.sSearch
+ }, opts);
+
+ // The _INPUT_ is optional - is appended if not present
+ if (opts.text.indexOf('_INPUT_') === -1) {
+ opts.text += '_INPUT_';
+ }
+
+ opts.text = _fnMacros(settings, opts.text);
+
+ // We can put the <input> outside of the label if it is at the start or end
+ // which helps improve accessability (not all screen readers like implicit
+ // for elements).
+ var end = opts.text.match(/_INPUT_$/);
+ var start = opts.text.match(/^_INPUT_/);
+ var removed = opts.text.replace(/_INPUT_/, '');
+ var str = '<label>' + opts.text + '</label>';
+
+ if (start) {
+ str = '_INPUT_<label>' + removed + '</label>';
+ }
+ else if (end) {
+ str = '<label>' + removed + '</label>_INPUT_';
+ }
+
+ var filter = $('<div>')
+ .addClass(classes.container)
+ .append(str.replace(/_INPUT_/, input));
+
+ // add for and id to label and input
+ filter.find('label').attr('for', 'dt-search-' + __searchCounter);
+ filter.find('input').attr('id', 'dt-search-' + __searchCounter);
+ __searchCounter++;
+
+ var searchFn = function(event) {
+ var val = this.value;
+
+ if(previousSearch.return && event.key !== "Enter") {
+ return;
}
- } );
-
-
- // 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
- );
- } );
+
+ /* Now do the filter */
+ if ( val != previousSearch.search ) {
+ _fnProcessingRun(settings, opts.processing, function () {
+ previousSearch.search = val;
+
+ _fnFilterComplete( settings, previousSearch );
+
+ // Need to redraw, without resorting
+ settings._iDisplayStart = 0;
+ _fnDraw( settings );
+ });
+ }
+ };
+
+ var searchDelay = settings.searchDelay !== null ?
+ settings.searchDelay :
+ 0;
+
+ var jqFilter = $('input', filter)
+ .val( previousSearch.search )
+ .attr( 'placeholder', opts.placeholder )
+ .on(
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
+ searchDelay ?
+ DataTable.util.debounce( searchFn, searchDelay ) :
+ searchFn
+ )
+ .on( 'mouseup.DT', function(e) {
+ // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
+ // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
+ // checks the value to see if it has changed. In other browsers it won't have.
+ setTimeout( function () {
+ searchFn.call(jqFilter[0], e);
+ }, 10);
+ } )
+ .on( 'keypress.DT', function(e) {
+ /* Prevent form submission */
+ if ( e.keyCode == 13 ) {
+ return false;
}
+ } )
+ .attr('aria-controls', tableId);
+
+ // Update the input elements whenever the table is filtered
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
+ if ( settings === s && jqFilter[0] !== document.activeElement ) {
+ jqFilter.val( typeof previousSearch.search !== 'function'
+ ? previousSearch.search
+ : ''
+ );
}
} );
-
- /*
- * 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 filter;
+ }, 'f' );
+
+ // opts
+ // - type - button configuration
+ // - buttons - number of buttons to show - must be odd
+ DataTable.feature.register( 'paging', function ( settings, opts ) {
+ // Don't show the paging input if the table doesn't have paging enabled
+ if (! settings.oFeatures.bPaginate) {
+ return null;
+ }
+
+ opts = $.extend({
+ buttons: DataTable.ext.pager.numbers_length,
+ type: settings.sPaginationType,
+ boundaryNumbers: true,
+ firstLast: true,
+ previousNext: true,
+ numbers: true
+ }, opts);
+
+ var host = $('<div/>')
+ .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : ''))
+ .append(
+ $('<nav>')
+ .attr('aria-label', 'pagination')
+ .addClass(settings.oClasses.paging.nav)
+ );
+ var draw = function () {
+ _pagingDraw(settings, host.children(), opts);
+ };
+
+ settings.aoDrawCallback.push(draw);
+
+ // Responsive redraw of paging control
+ $(settings.nTable).on('column-sizing.dt.DT', draw);
+
+ return host;
+ }, 'p' );
+
+ /**
+ * Dynamically create the button type array based on the configuration options.
+ * This will only happen if the paging type is not defined.
+ */
+ function _pagingDynamic(opts) {
+ var out = [];
+
+ if (opts.numbers) {
+ out.push('numbers');
+ }
+
+ if (opts.previousNext) {
+ out.unshift('previous');
+ out.push('next');
+ }
+
+ if (opts.firstLast) {
+ out.unshift('first');
+ out.push('last');
+ }
+
+ return out;
+ }
+
+ function _pagingDraw(settings, host, opts) {
+ if (! settings._bInitComplete) {
+ return;
+ }
+
+ var
+ plugin = opts.type
+ ? DataTable.ext.pager[ opts.type ]
+ : _pagingDynamic,
+ aria = settings.oLanguage.oAria.paginate || {},
+ start = settings._iDisplayStart,
+ len = settings._iDisplayLength,
+ visRecords = settings.fnRecordsDisplay(),
+ all = len === -1,
+ page = all ? 0 : Math.ceil( start / len ),
+ pages = all ? 1 : Math.ceil( visRecords / len ),
+ buttons = [],
+ buttonEls = [],
+ buttonsNested = plugin(opts)
+ .map(function (val) {
+ return val === 'numbers'
+ ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
+ : val;
+ });
+
+ // .flat() would be better, but not supported in old Safari
+ buttons = buttons.concat.apply(buttons, buttonsNested);
+
+ for (var i=0 ; i<buttons.length ; i++) {
+ var button = buttons[i];
+
+ var btnInfo = _pagingButtonInfo(settings, button, page, pages);
+ var btn = _fnRenderer( settings, 'pagingButton' )(
+ settings,
+ button,
+ btnInfo.display,
+ btnInfo.active,
+ btnInfo.disabled
+ );
+
+ var ariaLabel = typeof button === 'string'
+ ? aria[ button ]
+ : aria.number
+ ? aria.number + (button+1)
+ : null;
+
+ // Common attributes
+ $(btn.clicker).attr({
+ 'aria-controls': settings.sTableId,
+ 'aria-disabled': btnInfo.disabled ? 'true' : null,
+ 'aria-current': btnInfo.active ? 'page' : null,
+ 'aria-label': ariaLabel,
+ 'data-dt-idx': button,
+ 'tabIndex': btnInfo.disabled
+ ? -1
+ : settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span'
+ ? settings.iTabIndex
+ : null, // `0` doesn't need a tabIndex since it is the default
+ });
+
+ if (typeof button !== 'number') {
+ $(btn.clicker).addClass(button);
}
+
+ _fnBindAction(
+ btn.clicker, {action: button}, function(e) {
+ e.preventDefault();
+
+ _fnPageChange( settings, e.data.action, true );
+ }
+ );
+
+ buttonEls.push(btn.display);
+ }
+
+ var wrapped = _fnRenderer(settings, 'pagingContainer')(
+ settings, buttonEls
+ );
+
+ var activeEl = host.find(document.activeElement).data('dt-idx');
+
+ host.empty().append(wrapped);
+
+ if ( activeEl !== undefined ) {
+ host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
+ }
+
+ // Responsive - check if the buttons are over two lines based on the
+ // height of the buttons and the container.
+ if (buttonEls.length) {
+ var outerHeight = $(buttonEls[0]).outerHeight();
- return typeof d === 'string' ?
- d
- .replace(/&/g, '&amp;')
- .replace(/</g, '&lt;')
- .replace(/>/g, '&gt;')
- .replace(/"/g, '&quot;') :
- d;
+ if (
+ opts.buttons > 1 && // prevent infinite
+ outerHeight > 0 && // will be 0 if hidden
+ $(host).height() >= (outerHeight * 2) - 10
+ ) {
+ _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
+ }
+ }
+ }
+
+ /**
+ * Get properties for a button based on the current paging state of the table
+ *
+ * @param {*} settings DT settings object
+ * @param {*} button The button type in question
+ * @param {*} page Table's current page
+ * @param {*} pages Number of pages
+ * @returns Info object
+ */
+ function _pagingButtonInfo(settings, button, page, pages) {
+ var lang = settings.oLanguage.oPaginate;
+ var o = {
+ display: '',
+ active: false,
+ disabled: false
};
-
- /**
- * 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||'');
+
+ switch ( button ) {
+ case 'ellipsis':
+ o.display = '&#x2026;';
+ break;
+
+ case 'first':
+ o.display = lang.sFirst;
+
+ if (page === 0) {
+ o.disabled = true;
+ }
+ break;
+
+ case 'previous':
+ o.display = lang.sPrevious;
+
+ if ( page === 0 ) {
+ o.disabled = true;
+ }
+ break;
+
+ case 'next':
+ o.display = lang.sNext;
+
+ if ( pages === 0 || page === pages-1 ) {
+ o.disabled = true;
+ }
+ break;
+
+ case 'last':
+ o.display = lang.sLast;
+
+ if ( pages === 0 || page === pages-1 ) {
+ o.disabled = true;
+ }
+ break;
+
+ default:
+ if ( typeof button === 'number' ) {
+ o.display = settings.fnFormatNumber( button + 1 );
+
+ if (page === button) {
+ o.active = true;
}
- };
- },
-
- text: function () {
- return {
- display: __htmlEscapeEntities,
- filter: __htmlEscapeEntities
- };
+ }
+ break;
+ }
+
+ return o;
+ }
+
+ /**
+ * Compute what number buttons to show in the paging control
+ *
+ * @param {*} page Current page
+ * @param {*} pages Total number of pages
+ * @param {*} buttons Target number of number buttons
+ * @param {boolean} addFirstLast Indicate if page 1 and end should be included
+ * @returns Buttons to show
+ */
+ function _pagingNumbers ( page, pages, buttons, addFirstLast ) {
+ var
+ numbers = [],
+ half = Math.floor(buttons / 2),
+ before = addFirstLast ? 2 : 1,
+ after = addFirstLast ? 1 : 0;
+
+ if ( pages <= buttons ) {
+ numbers = _range(0, pages);
+ }
+ else if (buttons === 1) {
+ // Single button - current page only
+ numbers = [page];
+ }
+ else if (buttons === 3) {
+ // Special logic for just three buttons
+ if (page <= 1) {
+ numbers = [0, 1, 'ellipsis'];
+ }
+ else if (page >= pages - 2) {
+ numbers = _range(pages-2, pages);
+ numbers.unshift('ellipsis');
+ }
+ else {
+ numbers = ['ellipsis', page, 'ellipsis'];
}
- };
-
-
- /*
- * 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 );
- };
}
-
-
- /**
- * 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
+ else if ( page <= half ) {
+ numbers = _range(0, buttons-before);
+ numbers.push('ellipsis');
+
+ if (addFirstLast) {
+ numbers.push(pages-1);
+ }
+ }
+ else if ( page >= pages - 1 - half ) {
+ numbers = _range(pages-(buttons-before), pages);
+ numbers.unshift('ellipsis');
+
+ if (addFirstLast) {
+ numbers.unshift(0);
+ }
+ }
+ else {
+ numbers = _range(page-half+before, page+half-after);
+ numbers.push('ellipsis');
+ numbers.unshift('ellipsis');
+
+ if (addFirstLast) {
+ numbers.push(pages-1);
+ numbers.unshift(0);
+ }
+ }
+
+ return numbers;
+ }
+
+ var __lengthCounter = 0;
+
+ // opts
+ // - menu
+ // - text
+ DataTable.feature.register( 'pageLength', function ( settings, opts ) {
+ var features = settings.oFeatures;
+
+ // For compatibility with the legacy `pageLength` top level option
+ if (! features.bPaginate || ! features.bLengthChange) {
+ return null;
+ }
+
+ opts = $.extend({
+ menu: settings.aLengthMenu,
+ text: settings.oLanguage.sLengthMenu
+ }, opts);
+
+ var
+ classes = settings.oClasses.length,
+ tableId = settings.sTableId,
+ menu = opts.menu,
+ lengths = [],
+ language = [],
+ i;
+
+ // Options can be given in a number of ways
+ if (Array.isArray( menu[0] )) {
+ // Old 1.x style - 2D array
+ lengths = menu[0];
+ language = menu[1];
+ }
+ else {
+ for ( i=0 ; i<menu.length ; i++ ) {
+ // An object with different label and value
+ if ($.isPlainObject(menu[i])) {
+ lengths.push(menu[i].value);
+ language.push(menu[i].label);
+ }
+ else {
+ // Or just a number to display and use
+ lengths.push(menu[i]);
+ language.push(menu[i]);
+ }
+ }
+ }
+
+ // We can put the <select> outside of the label if it is at the start or
+ // end which helps improve accessability (not all screen readers like
+ // implicit for elements).
+ var end = opts.text.match(/_MENU_$/);
+ var start = opts.text.match(/^_MENU_/);
+ var removed = opts.text.replace(/_MENU_/, '');
+ var str = '<label>' + opts.text + '</label>';
+
+ if (start) {
+ str = '_MENU_<label>' + removed + '</label>';
+ }
+ else if (end) {
+ str = '<label>' + removed + '</label>_MENU_';
+ }
+
+ // Wrapper element - use a span as a holder for where the select will go
+ var tmpId = 'tmp-' + (+new Date())
+ var div = $('<div/>')
+ .addClass( classes.container )
+ .append(
+ str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' )
+ );
+
+ // Save text node content for macro updating
+ var textNodes = [];
+ Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) {
+ if (el.nodeType === Node.TEXT_NODE) {
+ textNodes.push({
+ el: el,
+ text: el.textContent
+ });
+ }
+ });
+
+ // Update the label text in case it has an entries value
+ var updateEntries = function (len) {
+ textNodes.forEach(function (node) {
+ node.el.textContent = _fnMacros(settings, node.text, len);
+ });
+ }
+
+ // Next, the select itself, along with the options
+ var select = $('<select/>', {
+ 'aria-controls': tableId,
+ 'class': classes.select
} );
-
-
- // 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;
+
+ for ( i=0 ; i<lengths.length ; i++ ) {
+ // Attempt to look up the length from the i18n options
+ var label = settings.api.i18n('lengthLabels.' + lengths[i], null);
+
+ if (label === null) {
+ // If not present, fallback to old style
+ label = typeof language[i] === 'number' ?
+ settings.fnFormatNumber( language[i] ) :
+ language[i];
+ }
+
+ select[0][ i ] = new Option(label, lengths[i]);
+ }
+
+ // add for and id to label and input
+ div.find('label').attr('for', 'dt-length-' + __lengthCounter);
+ select.attr('id', 'dt-length-' + __lengthCounter);
+ __lengthCounter++;
+
+ // Swap in the select list
+ div.find('#' + tmpId).replaceWith(select);
+
+ // Can't use `select` variable as user might provide their own and the
+ // reference is broken by the use of outerHTML
+ $('select', div)
+ .val( settings._iDisplayLength )
+ .on( 'change.DT', function() {
+ _fnLengthChange( settings, $(this).val() );
+ _fnDraw( settings );
+ } );
+
+ // Update node value whenever anything changes the table's length
+ $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
+ if ( settings === s ) {
+ $('select', div).val( len );
+
+ // Resolve plurals in the text for the new length
+ updateEntries(len);
+ }
} );
- return DataTable;
+ updateEntries(settings._iDisplayLength);
+
+ return div;
+ }, 'l' );
+
+ // 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;
}));
/*! DataTables styling integration
- * ©2018 SpryMedia Ltd - datatables.net/license
+ * © SpryMedia Ltd - datatables.net/license
*/
(function( factory ){
@@ -15361,29 +14210,47 @@
}
else if ( typeof exports === 'object' ) {
// CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
+ var jq = require('jquery');
+ var cjsRequires = function (root, $) {
+ if ( ! $.fn.dataTable ) {
+ require('datatables.net')(root, $);
}
+ };
- if ( ! $ || ! $.fn.dataTable ) {
- // Require DataTables, which attaches to jQuery, including
- // jQuery if needed and have a $ property so we can access the
- // jQuery object that is used
- $ = require('datatables.net')(root, $).$;
- }
+ if (typeof window === 'undefined') {
+ module.exports = function (root, $) {
+ if ( ! root ) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
- return factory( $, root, root.document );
- };
+ if ( ! $ ) {
+ $ = jq( root );
+ }
+
+ cjsRequires( root, $ );
+ return factory( $, root, root.document );
+ };
+ }
+ else {
+ cjsRequires( window, jq );
+ module.exports = factory( jq, window, window.document );
+ }
}
else {
// Browser
factory( jQuery, window, document );
}
-}(function( $, window, document, undefined ) {
+}(function( $, window, document ) {
+'use strict';
+var DataTable = $.fn.dataTable;
+
+
+
-return $.fn.dataTable;
+return DataTable;
}));