From f3ee594f974ea8279db2c81f82eb727e3497c83a Mon Sep 17 00:00:00 2001 From: Michael Kelleher Date: Fri, 12 Dec 2025 17:26:16 -0500 Subject: [PATCH 01/13] Add alternate statistical tests to MVK --- evv4esm/extensions/ks.py | 141 ++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 39 deletions(-) diff --git a/evv4esm/extensions/ks.py b/evv4esm/extensions/ks.py index 8dfb9c4..a335de4 100755 --- a/evv4esm/extensions/ks.py +++ b/evv4esm/extensions/ks.py @@ -165,6 +165,15 @@ def parse_args(args=None): type=str, ) + parser.add_argument( + "--use-test", + default="K-S", + help=( + "Test to use for determination of global PASS/FAIL, " + "all others provided as information" + ), + ) + args, _ = parser.parse_known_args(args) # use config file arguments, but override with command line arguments @@ -225,23 +234,24 @@ def run(name, config): args.img_dir = os.path.join(livvkit.output_dir, "validation", "imgs", name) fn.mkdir_p(args.img_dir) - - details, img_gal = main(args) + tests = ["K-S", "M-W", "C-VM", "T"] + details, img_gal = main(args, tests) table_data = pd.DataFrame(details).T uc_rejections = (table_data["K-S test p-val"] < args.alpha).sum() - _hdrs = [ - "h0", - "K-S test stat", - "K-S test p-val", - "K-S test p-val cor", - "T test stat", - "T test p-val", - "mean test case", - "mean ref. case", - "std test case", - "std ref. case", - ] + _hdrs = ["h0"] + for _test in tests: + _hdrs.extend( + [f"{_test} test stat", f"{_test} test p-val", f"{_test} test p-val cor"] + ) + _hdrs.extend( + [ + "mean test case", + "mean ref. case", + "std test case", + "std ref. case", + ] + ) table_data = table_data[_hdrs] for _hdr in _hdrs[1:]: table_data[_hdr] = table_data[_hdr].apply(col_fmt) @@ -367,7 +377,56 @@ def populate_metadata(): return metadata -def compute_details(annual_avgs, common_vars, args): +def test_compare(annuals_1, annuals_2, test_name): + """ + Generate statistic and p-value for a statistical test with pre-specified parameters + + Parameters + ---------- + annuals_1 : array_like + Annual global mean for ensemble 1 (ref) (n ensembles) + annuals_2 : array_like + Annual global mean for ensemble 2 (test) (n ensembles) + test_name : str + Name of test (one of "T", "K-S", "M-W", or "C-VM" for Student's t-test, + Kolmogorov-Smirnov test, Mann-Whitney U test, or Cramer-von Mises test + respectively) + + Raises + ------ + NotImplementedError : If `test_name` is not implemented + + Returns + ------- + _stat : float + Test statistic + _pval : float + P-value + + """ + if test_name == "T": + _stat, _pval = stats.ttest_ind( + annuals_1, annuals_2, equal_var=False, nan_policy=str("omit") + ) + elif test_name == "K-S": + _stat, _pval = stats.ks_2samp(annuals_1, annuals_2) + elif test_name == "M-W": + _res = stats.mannwhitneyu(annuals_1, annuals_2) + _stat = _res.statistic + _pval = _res.pvalue + elif test_name == "C-VM": + _res = stats.cramervonmises_2samp(annuals_1, annuals_2) + _stat, _pval = _res.statistic, _res.pvalue + else: + _stat = np.nan + _pval = np.nan + raise NotImplementedError( + f"THE TEST {test_name} IS NOT (YET) IMPLEMENTED IN EVV" + ) + return _stat, _pval + + +def compute_details(annual_avgs, common_vars, args, tests): """Compute the detail table, perform a T Test and K-S test for each variable.""" details = LIVVDict() for var in sorted(common_vars): @@ -378,20 +437,14 @@ def compute_details(annual_avgs, common_vars, args): "case == @args.ref_case & variable == @var" ).monthly_mean.values - ttest_t, ttest_p = stats.ttest_ind( - annuals_1, annuals_2, equal_var=False, nan_policy=str("omit") - ) - ks_d, ks_p = stats.ks_2samp(annuals_1, annuals_2) - - if np.isnan([ttest_t, ttest_p]).any() or np.isinf([ttest_t, ttest_p]).any(): - ttest_t = None - ttest_p = None - - details[var]["T test stat"] = ttest_t - details[var]["T test p-val"] = ttest_p - - details[var]["K-S test stat"] = ks_d - details[var]["K-S test p-val"] = ks_p + # Compute all test statistics and p-values + for _test in tests: + _stat, _pval = test_compare(annuals_1, annuals_2, _test) + if np.isnan([_stat, _pval]).any() or np.isinf([_stat, _pval]).any(): + _stat = None + _pval = None + details[var][f"{_test} test stat"] = _stat + details[var][f"{_test} test p-val"] = _pval details[var]["mean test case"] = annuals_1.mean() details[var]["mean ref. case"] = annuals_2.mean() @@ -409,18 +462,28 @@ def compute_details(annual_avgs, common_vars, args): # Convert to a Dataframe, transposed so that the index is the variable name detail_df = pd.DataFrame(details).T # Create a null hypothesis rejection column for un-corrected p-values - detail_df["h0_uc"] = detail_df["K-S test p-val"] < args.alpha + for _test in tests: + detail_df[f"h0_uc_{_test}"] = detail_df[f"{_test} test p-val"] < args.alpha + + (detail_df[f"h0_c_{_test}"], detail_df[f"{_test} test p-val cor"]) = ( + smm.fdrcorrection( + detail_df[f"{_test} test p-val"], + alpha=args.alpha, + method="p", + is_sorted=False, + ) + ) - (detail_df["h0_c"], detail_df["K-S test p-val cor"]) = smm.fdrcorrection( - detail_df["K-S test p-val"], alpha=args.alpha, method="p", is_sorted=False - ) if args.uncorrected: - _testkey = "h0_uc" + _testkey = f"h0_uc_{args.use_test}" else: - _testkey = "h0_c" + _testkey = f"h0_c_{args.use_test}" for var in common_vars: - details[var]["K-S test p-val cor"] = detail_df.loc[var, "K-S test p-val cor"] + for _test in tests: + details[var][f"{_test} test p-val cor"] = detail_df.loc[ + var, f"{_test} test p-val cor" + ] if details[var]["T test stat"] is None: details[var]["h0"] = "-" @@ -432,7 +495,7 @@ def compute_details(annual_avgs, common_vars, args): return details -def main(args): +def main(args, tests): ens_files, key1, key2 = case_files(args) if args.test_case == args.ref_case: args.test_case = key1 @@ -459,7 +522,7 @@ def main(args): ) images = {"accept": [], "reject": [], "-": []} - details = compute_details(annual_avgs, common_vars, args) + details = compute_details(annual_avgs, common_vars, args, tests) for var in sorted(common_vars): annuals_1 = annual_avgs.query( @@ -521,4 +584,4 @@ def main(args): if __name__ == "__main__": - print_details(main(parse_args())) + print_details(main(parse_args(), ["K-S", "M-W", "C-VM", "T"])) From 37370a7cb15d6e48ff0e50631da9f5828a14ac3c Mon Sep 17 00:00:00 2001 From: Michael Kelleher Date: Fri, 12 Dec 2025 17:30:17 -0500 Subject: [PATCH 02/13] Add DataTable functionality for sorting, scolling result tables --- evv4esm/extensions/ks.py | 6 +- evv4esm/extensions/kso.py | 50 +- evv4esm/extensions/tsc.py | 2 +- evv4esm/resources/css/datatables.css | 868 ++ evv4esm/resources/css/datatables.min.css | 15 + evv4esm/resources/css/livv.css | 78 +- evv4esm/resources/index.html | 1 + evv4esm/resources/js/common.js | 8 + evv4esm/resources/js/datatables.js | 14198 +++++++++++++++++++++ evv4esm/resources/js/datatables.min.js | 22 + evv4esm/resources/validation.html | 2 + 11 files changed, 15187 insertions(+), 63 deletions(-) create mode 100644 evv4esm/resources/css/datatables.css create mode 100644 evv4esm/resources/css/datatables.min.css create mode 100644 evv4esm/resources/js/datatables.js create mode 100644 evv4esm/resources/js/datatables.min.js diff --git a/evv4esm/extensions/ks.py b/evv4esm/extensions/ks.py index a335de4..72460d8 100755 --- a/evv4esm/extensions/ks.py +++ b/evv4esm/extensions/ks.py @@ -257,9 +257,9 @@ def run(name, config): table_data[_hdr] = table_data[_hdr].apply(col_fmt) tables = [ - el.Table("Rejected", data=table_data[table_data["h0"] == "reject"]), - el.Table("Accepted", data=table_data[table_data["h0"] == "accept"]), - el.Table("Null", data=table_data[~table_data["h0"].isin(["accept", "reject"])]), + el.Table("Rejected", data=table_data[table_data["h0"] == "reject"], data_table=True), + el.Table("Accepted", data=table_data[table_data["h0"] == "accept"], data_table=True), + el.Table("Null", data=table_data[~table_data["h0"].isin(["accept", "reject"])], data_table=True), ] bib_html = bib2html(os.path.join(os.path.dirname(__file__), "ks.bib")) diff --git a/evv4esm/extensions/kso.py b/evv4esm/extensions/kso.py index 65722ac..29ef426 100644 --- a/evv4esm/extensions/kso.py +++ b/evv4esm/extensions/kso.py @@ -223,10 +223,14 @@ def run(name, config): table_data = pd.DataFrame(details).T _hdrs = [ "h0", - f"Pre-Correction (N, %) < {args.alpha}", - f"Post-Correction (N, %) < {args.alpha}", - "mean (test case, ref. case)", - "std (test case, ref. case)", + f"Pre-Correction N < {args.alpha}", + f"Pre-Correction % < {args.alpha}", + f"Post-Correction N < {args.alpha}", + f"Post-Correction % < {args.alpha}", + "mean test case", + "mean ref. case", + "std test case", + "std ref. case", ] table_data = table_data[_hdrs] for _hdr in _hdrs[1:]: @@ -236,9 +240,9 @@ def run(name, config): table_data[_hdr] = table_data[_hdr].apply(col_fmt_ff) tables = [ - el.Table("Rejected", data=table_data[table_data["h0"] == "reject"]), - el.Table("Accepted", data=table_data[table_data["h0"] == "accept"]), - el.Table("Null", data=table_data[~table_data["h0"].isin(["accept", "reject"])]), + el.Table("Rejected", data=table_data[table_data["h0"] == "reject"], data_table=True), + el.Table("Accepted", data=table_data[table_data["h0"] == "accept"], data_table=True), + el.Table("Null", data=table_data[~table_data["h0"].isin(["accept", "reject"])], data_table=True), ] bib_html = bib2html(os.path.join(os.path.dirname(__file__), "ks.bib")) @@ -411,14 +415,19 @@ def main(args): if null_reject_post_correct <= args.critical: test_result = "accept" - details[var][f"Pre-Correction (N, %) < {args.alpha}"] = ( - null_reject_pre_correct, - 100 * null_reject_pre_correct / np.prod(p_val.shape), + details[var][f"Pre-Correction N < {args.alpha}"] = ( + null_reject_pre_correct + ) + details[var][f"Pre-Correction % < {args.alpha}"] = ( + 100 * null_reject_pre_correct / np.prod(p_val.shape) + ) + + details[var][f"Post-Correction N < {args.alpha}"] = ( + null_reject_post_correct ) - details[var][f"Post-Correction (N, %) < {args.alpha}"] = ( - null_reject_post_correct, - 100 * null_reject_post_correct / np.prod(p_val.shape), + details[var][f"Post-Correction % < {args.alpha}"] = ( + 100 * null_reject_post_correct / np.prod(p_val.shape) ) # For output, mask out missing data, can't do this before the K-S test because @@ -427,14 +436,11 @@ def main(args): mask_value = -0.9999e33 annuals_1 = np.ma.masked_less(annuals_1, mask_value) annuals_2 = np.ma.masked_less(annuals_2, mask_value) - details[var]["mean (test case, ref. case)"] = ( - annuals_1.mean(), - annuals_2.mean(), - ) + agg_fcns = [(np.nanmax, "max"), (np.nanmin, "min"), (np.nanmean, "mean"), (np.nanstd, "std")] + for _fcn, fname in agg_fcns: + for _case, _data in [("test", annuals_1), ("ref.", annuals_2)]: + details[var][f"{fname} {_case} case"] = _fcn(_data) - details[var]["max (test case, ref. case)"] = (annuals_1.max(), annuals_2.max()) - details[var]["min (test case, ref. case)"] = (annuals_1.min(), annuals_2.min()) - details[var]["std (test case, ref. case)"] = (annuals_1.std(), annuals_2.std()) details[var]["h0"] = test_result img_file = os.path.relpath( @@ -457,9 +463,9 @@ def main(args): img_desc = ( f"Mean annual global average of {var}{var_1['desc']} for " f"{args.test_case} is " - f"{details[var]['mean (test case, ref. case)'][0]:.4e} and for " + f"{details[var]['mean test case']:.4e} and for " f"{args.ref_case} is " - f"{details[var]['mean (test case, ref. case)'][1]:.4e}. " + f"{details[var]['mean ref. case']:.4e}. " f"Pass (fail) is indicated by {human_color_names['fail'][0]} " f"({human_color_names['pass'][0]}) coloring of the plot markers and bars." ) diff --git a/evv4esm/extensions/tsc.py b/evv4esm/extensions/tsc.py index aa99670..d0a414c 100644 --- a/evv4esm/extensions/tsc.py +++ b/evv4esm/extensions/tsc.py @@ -131,7 +131,7 @@ def run(name, config, print_details=False): tbl_data[_hdr].append(val) detail_tables[level].append( - el.Table(title=f"{level.capitalize()}, {_time}", data=tbl_data) + el.Table(title=f"{level.capitalize()}, {_time}", data=tbl_data, data_table=True) ) tabs = el.Tabs( diff --git a/evv4esm/resources/css/datatables.css b/evv4esm/resources/css/datatables.css new file mode 100644 index 0000000..f1fc35c --- /dev/null +++ b/evv4esm/resources/css/datatables.css @@ -0,0 +1,868 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-2.3.5 + * + * Included libraries: + * DataTables 2.3.5 + */ + +: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 span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before { + position: absolute; + display: block; + bottom: 50%; + content: "\25B2"; + content: "\25B2"/""; +} +table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + position: absolute; + display: block; + top: 50%; + content: "\25BC"; + content: "\25BC"/""; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order { + position: relative; + width: 12px; + height: 20px; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.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 span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + opacity: 0.6; +} +table.dataTable thead > tr > th.dt-orderable-none:not(.dt-ordering-asc, .dt-ordering-desc) span.dt-column-order:empty, table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-none:not(.dt-ordering-asc, .dt-ordering-desc) span.dt-column-order:empty, +table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after, +table.dataTable thead > tr > td.sorting_asc_disabled span.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 span.dt-column-title, +table.dataTable thead > tr > th div.dt-column-footer span.dt-column-title, +table.dataTable thead > tr > td div.dt-column-header span.dt-column-title, +table.dataTable thead > tr > td div.dt-column-footer span.dt-column-title, +table.dataTable tfoot > tr > th div.dt-column-header span.dt-column-title, +table.dataTable tfoot > tr > th div.dt-column-footer span.dt-column-title, +table.dataTable tfoot > tr > td div.dt-column-header span.dt-column-title, +table.dataTable tfoot > tr > td div.dt-column-footer span.dt-column-title { + flex-grow: 1; +} +table.dataTable thead > tr > th div.dt-column-header span.dt-column-title:empty, +table.dataTable thead > tr > th div.dt-column-footer span.dt-column-title:empty, +table.dataTable thead > tr > td div.dt-column-header span.dt-column-title:empty, +table.dataTable thead > tr > td div.dt-column-footer span.dt-column-title:empty, +table.dataTable tfoot > tr > th div.dt-column-header span.dt-column-title:empty, +table.dataTable tfoot > tr > th div.dt-column-footer span.dt-column-title:empty, +table.dataTable tfoot > tr > td div.dt-column-header span.dt-column-title:empty, +table.dataTable tfoot > tr > td div.dt-column-footer span.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 { + 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, +table.dataTable tfoot td.dt-head-center { + text-align: center; +} +table.dataTable thead th.dt-head-right, +table.dataTable thead td.dt-head-right, +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, +table.dataTable tfoot td.dt-head-nowrap { + white-space: nowrap; +} +table.dataTable tbody th.dt-body-left, +table.dataTable tbody td.dt-body-left { + text-align: left; +} +table.dataTable tbody th.dt-body-center, +table.dataTable tbody td.dt-body-center { + text-align: center; +} +table.dataTable tbody th.dt-body-right, +table.dataTable tbody td.dt-body-right { + text-align: right; +} +table.dataTable tbody th.dt-body-justify, +table.dataTable tbody td.dt-body-justify { + text-align: justify; +} +table.dataTable tbody th.dt-body-nowrap, +table.dataTable tbody td.dt-body-nowrap { + white-space: nowrap; +} + +: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; +} + +/* + * Table styles + */ +table.dataTable { + width: 100%; + margin: 0 auto; + border-spacing: 0; + /* + * Header and footer styles + */ +} +table.dataTable thead th, +table.dataTable tfoot th { + font-weight: bold; +} +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 > tr > th:active, +table.dataTable > thead > tr > td:active { + outline: none; +} +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 { + /* + * Body styles + */ +} +table.dataTable > tbody > tr { + background-color: transparent; +} +table.dataTable > tbody > tr:first-child > * { + border-top: none; +} +table.dataTable > tbody > tr:last-child > * { + border-bottom: none; +} +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 a { + color: rgb(228, 228, 228); + color: rgb(var(--dt-row-selected-link)); +} +table.dataTable > tbody > tr > th, +table.dataTable > tbody > tr > td { + padding: 8px 10px; +} +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 > *, table.dataTable.display > tbody > tr:first-child > * { + border-top: none; +} +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 > * { + 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 { + border-left: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.cell-border > tbody > tr:first-child > * { + border-top: 1px solid rgba(0, 0, 0, 0.3); +} +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.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 > *, 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.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 > .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.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: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: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: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: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: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: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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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, +table.dataTable.compact tfoot th, +table.dataTable.compact tfoot td, +table.dataTable.compact tbody th, +table.dataTable.compact tbody td { + padding: 4px; +} + +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 + */ +div.dt-container { + position: relative; + clear: both; +} +div.dt-container .dt-search input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + color: inherit; + margin-left: 3px; +} +div.dt-container .dt-input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + color: inherit; +} +div.dt-container select.dt-input { + padding: 4px; +} +div.dt-container .dt-paging .dt-paging-button { + box-sizing: border-box; + display: inline-block; + min-width: 1.5em; + padding: 0.5em 1em; + margin-left: 2px; + text-align: center; + text-decoration: none !important; + cursor: pointer; + color: inherit !important; + border: 1px solid transparent; + border-radius: 2px; + background: transparent; +} +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: rgba(0, 0, 0, 0.5) !important; + border: 1px solid transparent; + background: transparent; + box-shadow: none; +} +div.dt-container .dt-paging .dt-paging-button:hover { + color: white !important; + border: 1px solid #111; + 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: 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; +} +div.dt-container .dt-paging .ellipsis { + padding: 0 1em; +} +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; +} +div.dt-container .dataTables_scroll div.dt-scroll-body { + -webkit-overflow-scrolling: touch; +} +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; +} +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; +} +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); +} +div.dt-container.dt-empty-footer .dt-scroll-body tbody > tr:last-child > * { + border-bottom: none; +} + +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); +} + +/* + * 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; +} +*[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/evv4esm/resources/css/datatables.min.css b/evv4esm/resources/css/datatables.min.css new file mode 100644 index 0000000..b7aa6e4 --- /dev/null +++ b/evv4esm/resources/css/datatables.min.css @@ -0,0 +1,15 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-2.3.5 + * + * Included libraries: + * DataTables 2.3.5 + */ + +: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 span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before{position:absolute;display:block;bottom:50%;content:"▲";content:"▲"/""}table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{position:absolute;display:block;top:50%;content:"▼";content:"▼"/""}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order{position:relative;width:12px;height:20px}table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{left:0;opacity:.125;line-height:9px;font-size:.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 span.dt-column-order:before,table.dataTable thead>tr>th.dt-ordering-desc span.dt-column-order:after,table.dataTable thead>tr>td.dt-ordering-asc span.dt-column-order:before,table.dataTable thead>tr>td.dt-ordering-desc span.dt-column-order:after{opacity:.6}table.dataTable thead>tr>th.dt-orderable-none:not(.dt-ordering-asc,.dt-ordering-desc) span.dt-column-order:empty,table.dataTable thead>tr>th.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>th.sorting_asc_disabled span.dt-column-order:before,table.dataTable thead>tr>td.dt-orderable-none:not(.dt-ordering-asc,.dt-ordering-desc) span.dt-column-order:empty,table.dataTable thead>tr>td.sorting_desc_disabled span.dt-column-order:after,table.dataTable thead>tr>td.sorting_asc_disabled span.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 span.dt-column-title,table.dataTable thead>tr>th div.dt-column-footer span.dt-column-title,table.dataTable thead>tr>td div.dt-column-header span.dt-column-title,table.dataTable thead>tr>td div.dt-column-footer span.dt-column-title,table.dataTable tfoot>tr>th div.dt-column-header span.dt-column-title,table.dataTable tfoot>tr>th div.dt-column-footer span.dt-column-title,table.dataTable tfoot>tr>td div.dt-column-header span.dt-column-title,table.dataTable tfoot>tr>td div.dt-column-footer span.dt-column-title{flex-grow:1}table.dataTable thead>tr>th div.dt-column-header span.dt-column-title:empty,table.dataTable thead>tr>th div.dt-column-footer span.dt-column-title:empty,table.dataTable thead>tr>td div.dt-column-header span.dt-column-title:empty,table.dataTable thead>tr>td div.dt-column-footer span.dt-column-title:empty,table.dataTable tfoot>tr>th div.dt-column-header span.dt-column-title:empty,table.dataTable tfoot>tr>th div.dt-column-footer span.dt-column-title:empty,table.dataTable tfoot>tr>td div.dt-column-header span.dt-column-title:empty,table.dataTable tfoot>tr>td div.dt-column-footer span.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 .6s infinite}div.dt-processing>div:last-child>div:nth-child(2){left:8px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(3){left:32px;animation:datatables-loader-2 .6s infinite}div.dt-processing>div:last-child>div:nth-child(4){left:56px;animation:datatables-loader-3 .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{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,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,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,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}: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}table.dataTable{width:100%;margin:0 auto;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}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>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}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>tbody>tr{background-color:transparent}table.dataTable>tbody>tr:first-child>*{border-top:none}table.dataTable>tbody>tr:last-child>*{border-bottom:none}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 a{color:rgb(228, 228, 228);color:rgb(var(--dt-row-selected-link))}table.dataTable>tbody>tr>th,table.dataTable>tbody>tr>td{padding:8px 10px}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>*,table.dataTable.display>tbody>tr:first-child>*{border-top:none}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>*{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{border-left:1px solid rgba(0, 0, 0, 0.15)}table.dataTable.cell-border>tbody>tr:first-child>*{border-top:1px solid rgba(0, 0, 0, 0.3)}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.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>*,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.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>.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.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: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: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: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: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: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: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{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{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{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{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{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{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,table.dataTable.compact tfoot th,table.dataTable.compact tfoot td,table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}div.dt-container div.dt-layout-row{display:flex;justify-content:space-between;align-items:center;width:100%;margin:.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:.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}}div.dt-container{position:relative;clear:both}div.dt-container .dt-search input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit;margin-left:3px}div.dt-container .dt-input{border:1px solid #aaa;border-radius:3px;padding:5px;background-color:transparent;color:inherit}div.dt-container select.dt-input{padding:4px}div.dt-container .dt-paging .dt-paging-button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;color:inherit !important;border:1px solid transparent;border-radius:2px;background:transparent}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)));background:-webkit-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-moz-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-ms-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:-o-linear-gradient(top, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%);background:linear-gradient(to bottom, rgba(229.5, 229.5, 229.5, 0.05) 0%, rgba(0, 0, 0, 0.05) 100%)}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:rgba(0, 0, 0, 0.5) !important;border:1px solid transparent;background:transparent;box-shadow:none}div.dt-container .dt-paging .dt-paging-button:hover{color:white !important;border:1px solid #111;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));background:-webkit-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%);background:-moz-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%);background:-ms-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%);background:-o-linear-gradient(top, rgb(88.4, 88.4, 88.4) 0%, #111 100%);background:linear-gradient(to bottom, rgb(88.4, 88.4, 88.4) 0%, #111 100%)}div.dt-container .dt-paging .dt-paging-button:active{outline:none;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)));background:-webkit-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%);background:-moz-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%);background:-ms-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%);background:-o-linear-gradient(top, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%);background:linear-gradient(to bottom, rgb(42.5, 42.5, 42.5) 0%, rgb(11.9, 11.9, 11.9) 100%);box-shadow:inset 0 0 3px #111}div.dt-container .dt-paging .ellipsis{padding:0 1em}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}div.dt-container .dataTables_scroll div.dt-scroll-body{-webkit-overflow-scrolling:touch}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}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}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)}div.dt-container.dt-empty-footer .dt-scroll-body tbody>tr:last-child>*{border-bottom:none}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)}*[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}*[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/evv4esm/resources/css/livv.css b/evv4esm/resources/css/livv.css index 92b38c9..bf6561f 100644 --- a/evv4esm/resources/css/livv.css +++ b/evv4esm/resources/css/livv.css @@ -2,16 +2,16 @@ Basic Styling Overrides ******************************************************************************/ html { - font-family: 'Lato', helvetica, sans-serif; - height: 100%; + font-family: 'Lato', helvetica, sans-serif; + height: 100%; } body { - height: 100%; - margin-top: 0px; - margin-right: 0px; - margin-bottom: 0px; - margin-left: 0px; + height: 100%; + margin-top: 0px; + margin-right: 0px; + margin-bottom: 0px; + margin-left: 0px; min-width: 1125px; background: url("../imgs/background.jpg") no-repeat center center fixed; -webkit-background-size: cover; @@ -20,8 +20,11 @@ body { background-size: cover; } -h1, h2, h3, h4 { - font-family: 'Lato', helvetica, sans-serif; +h1, +h2, +h3, +h4 { + font-family: 'Lato', helvetica, sans-serif; } hr { @@ -38,22 +41,21 @@ a { ******************************************************************************/ #header { min-width: 800px; - background-color: #222222; + background-color: #222222; border-bottom: 1px solid #222222; font-size: 1.5em; font-weight: 500; } #header:after { - content: ""; - display: table; - clear: both; + content: ""; + display: table; + clear: both; } -#header-spacer { -} +#header-spacer {} -#header a{ +#header a { text-decoration: none; } @@ -65,31 +67,31 @@ a { } #header-home { - float: left; + float: left; color: #F26C3A; height: 1.25em; - line-height: 1.25em; - margin-top: 15px; + line-height: 1.25em; + margin-top: 15px; text-align: left; width: 15%; } #header-title { - float: left; + float: left; color: #F26C3A; - height: 1.25em; - line-height: 1.25em; - margin-top: 15px; + height: 1.25em; + line-height: 1.25em; + margin-top: 15px; text-align: center; width: 50%; } #header-docs { - float: left; + float: left; color: #F26C3A; - height: 1.25em; - line-height: 1.25em; - margin-top: 15px; + height: 1.25em; + line-height: 1.25em; + margin-top: 15px; padding-right: 15px; text-align: right; width: 25%; @@ -108,13 +110,10 @@ a { padding: 20px; } -#content { +#content{ padding: 10px; width: 71%; float: left; -} - -#content div { background-color: white; } @@ -127,11 +126,11 @@ table { border-spacing: 0; } -th { - background-color: #512E5F; - color: #ffffff; - padding: 10px; - border-bottom: 2px solid #F26C3A; +thead, th { + background-color: #512E5F; + color: #ffffff; + padding: 10px; + border-bottom: 2px solid #F26C3A; } td { @@ -148,6 +147,11 @@ td { text-align: right; } +div.dt-container { + width: 100%; + margin: auto auto; +} + /****************************************************************************** Element Styling ******************************************************************************/ @@ -179,7 +183,7 @@ td { .diff p { line-height: 1.05em; margin: 0px; - padding-left:10%; + padding-left: 10%; font-family: monospace; } diff --git a/evv4esm/resources/index.html b/evv4esm/resources/index.html index 1a84c95..c175699 100644 --- a/evv4esm/resources/index.html +++ b/evv4esm/resources/index.html @@ -15,6 +15,7 @@ + + diff --git a/evv4esm/resources/js/common.js b/evv4esm/resources/js/common.js index 41232dd..54710db 100644 --- a/evv4esm/resources/js/common.js +++ b/evv4esm/resources/js/common.js @@ -77,7 +77,10 @@ function drawNav() { // Go through each category: numerics, verification, performance, and validation for (var el_idx in data["elements"]) { if (data["elements"][el_idx] != null && Object.keys(data["elements"][el_idx]["Table"]["data"]).length > 0) { - html += "

" + data["elements"][el_idx]["Table"]["title"] + "

\n"; + header = "

" + data["elements"][el_idx]["Table"]["title"] + "

\n"; + if (!html.includes(header) == true) { + html += header; + } var testList = Array.from(new Set(data["elements"][el_idx]["Table"]["index"])).sort(); for (var idx in testList) { html += "').addClass( + this.c.dom.container.className + ) + }; + + this._constructor(); +}; + +$.extend(Buttons.prototype, { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public methods + */ + + /** + * Get the action of a button + * @param {int|string} Button index + * @return {function} + */ /** + * Set the action of a button + * @param {node} node Button element + * @param {function} action Function to set + * @return {Buttons} Self for chaining + */ + action: function (node, action) { + var button = this._nodeToButton(node); + + if (action === undefined) { + return button.conf.action; + } + + button.conf.action = action; + + return this; + }, + + /** + * Add an active class to the button to make to look active or get current + * active state. + * @param {node} node Button element + * @param {boolean} [flag] Enable / disable flag + * @return {Buttons} Self for chaining or boolean for getter + */ + active: function (node, flag) { + var button = this._nodeToButton(node); + var klass = this.c.dom.button.active; + var jqNode = $(button.node); + + if ( + button.inCollection && + this.c.dom.collection.button && + this.c.dom.collection.button.active !== undefined + ) { + klass = this.c.dom.collection.button.active; + } + + if (flag === undefined) { + return jqNode.hasClass(klass); + } + + jqNode.toggleClass(klass, flag === undefined ? true : flag); + + return this; + }, + + /** + * Add a new button + * @param {object} config Button configuration object, base string name or function + * @param {int|string} [idx] Button index for where to insert the button + * @param {boolean} [draw=true] Trigger a draw. Set a false when adding + * lots of buttons, until the last button. + * @return {Buttons} Self for chaining + */ + add: function (config, idx, draw) { + var buttons = this.s.buttons; + + if (typeof idx === 'string') { + var split = idx.split('-'); + var base = this.s; + + for (var i = 0, ien = split.length - 1; i < ien; i++) { + base = base.buttons[split[i] * 1]; + } + + buttons = base.buttons; + idx = split[split.length - 1] * 1; + } + + let node = this._expandButton( + buttons, + config, + config !== undefined ? config.split : undefined, + (config === undefined || + config.split === undefined || + config.split.length === 0) && + base !== undefined, + false, + idx + ); + + if (draw === undefined || draw === true) { + this._draw(); + } + + return node; + }, + + /** + * Clear buttons from a collection and then insert new buttons + */ + collectionRebuild: function (node, newButtons) { + var button = this._nodeToButton(node); + + if (newButtons !== undefined) { + var i; + // Need to reverse the array + for (i = button.buttons.length - 1; i >= 0; i--) { + this.remove(button.buttons[i].node); + } + + // If the collection has prefix and / or postfix buttons we need to add them in + if (button.conf.prefixButtons) { + newButtons.unshift.apply(newButtons, button.conf.prefixButtons); + } + + if (button.conf.postfixButtons) { + newButtons.push.apply(newButtons, button.conf.postfixButtons); + } + + for (i = 0; i < newButtons.length; i++) { + var newBtn = newButtons[i]; + + this._expandButton( + button.buttons, + newBtn, + newBtn !== undefined && + newBtn.config !== undefined && + newBtn.config.split !== undefined, + true, + newBtn.parentConf !== undefined && + newBtn.parentConf.split !== undefined, + null, + newBtn.parentConf + ); + } + } + + this._draw(button.collection, button.buttons); + }, + + /** + * Get the container node for the buttons + * @return {jQuery} Buttons node + */ + container: function () { + return this.dom.container; + }, + + /** + * Disable a button + * @param {node} node Button node + * @return {Buttons} Self for chaining + */ + disable: function (node) { + var button = this._nodeToButton(node); + + if (button.isSplit) { + $(button.node.childNodes[0]) + .addClass(this.c.dom.button.disabled) + .prop('disabled', true); + } + else { + $(button.node) + .addClass(this.c.dom.button.disabled) + .prop('disabled', true); + } + + button.disabled = true; + + this._checkSplitEnable(); + + return this; + }, + + /** + * Destroy the instance, cleaning up event handlers and removing DOM + * elements + * @return {Buttons} Self for chaining + */ + destroy: function () { + // Key event listener + $('body').off('keyup.' + this.s.namespace); + + // Individual button destroy (so they can remove their own events if + // needed). Take a copy as the array is modified by `remove` + var buttons = this.s.buttons.slice(); + var i, ien; + + for (i = 0, ien = buttons.length; i < ien; i++) { + this.remove(buttons[i].node); + } + + // Container + this.dom.container.remove(); + + // Remove from the settings object collection + var buttonInsts = this.s.dt.settings()[0]; + + for (i = 0, ien = buttonInsts.length; i < ien; i++) { + if (buttonInsts.inst === this) { + buttonInsts.splice(i, 1); + break; + } + } + + return this; + }, + + /** + * Enable / disable a button + * @param {node} node Button node + * @param {boolean} [flag=true] Enable / disable flag + * @return {Buttons} Self for chaining + */ + enable: function (node, flag) { + if (flag === false) { + return this.disable(node); + } + + var button = this._nodeToButton(node); + + if (button.isSplit) { + $(button.node.childNodes[0]) + .removeClass(this.c.dom.button.disabled) + .prop('disabled', false); + } + else { + $(button.node) + .removeClass(this.c.dom.button.disabled) + .prop('disabled', false); + } + + button.disabled = false; + + this._checkSplitEnable(); + + return this; + }, + + /** + * Get a button's index + * + * This is internally recursive + * @param {element} node Button to get the index of + * @return {string} Button index + */ + index: function (node, nested, buttons) { + if (!nested) { + nested = ''; + buttons = this.s.buttons; + } + + for (var i = 0, ien = buttons.length; i < ien; i++) { + var inner = buttons[i].buttons; + + if (buttons[i].node === node) { + return nested + i; + } + + if (inner && inner.length) { + var match = this.index(node, i + '-', inner); + + if (match !== null) { + return match; + } + } + } + + return null; + }, + + /** + * Get the instance name for the button set selector + * @return {string} Instance name + */ + name: function () { + return this.c.name; + }, + + /** + * Get a button's node of the buttons container if no button is given + * @param {node} [node] Button node + * @return {jQuery} Button element, or container + */ + node: function (node) { + if (!node) { + return this.dom.container; + } + + var button = this._nodeToButton(node); + return $(button.node); + }, + + /** + * Set / get a processing class on the selected button + * @param {element} node Triggering button node + * @param {boolean} flag true to add, false to remove, undefined to get + * @return {boolean|Buttons} Getter value or this if a setter. + */ + processing: function (node, flag) { + var dt = this.s.dt; + var button = this._nodeToButton(node); + + if (flag === undefined) { + return $(button.node).hasClass('processing'); + } + + $(button.node).toggleClass('processing', flag); + + $(dt.table().node()).triggerHandler('buttons-processing.dt', [ + flag, + dt.button(node), + dt, + $(node), + button.conf + ]); + + return this; + }, + + /** + * Remove a button. + * @param {node} node Button node + * @return {Buttons} Self for chaining + */ + remove: function (node) { + var button = this._nodeToButton(node); + var host = this._nodeToHost(node); + var dt = this.s.dt; + + // Remove any child buttons first + if (button.buttons.length) { + for (var i = button.buttons.length - 1; i >= 0; i--) { + this.remove(button.buttons[i].node); + } + } + + button.conf.destroying = true; + + // Allow the button to remove event handlers, etc + if (button.conf.destroy) { + button.conf.destroy.call(dt.button(node), dt, $(node), button.conf); + } + + this._removeKey(button.conf); + + $(button.node).remove(); + + if (button.inserter) { + $(button.inserter).remove(); + } + + var idx = $.inArray(button, host); + host.splice(idx, 1); + + return this; + }, + + /** + * Get the text for a button + * @param {int|string} node Button index + * @return {string} Button text + */ /** + * Set the text for a button + * @param {int|string|function} node Button index + * @param {string} label Text + * @return {Buttons} Self for chaining + */ + text: function (node, label) { + var button = this._nodeToButton(node); + var textNode = button.textNode; + var dt = this.s.dt; + var jqNode = $(button.node); + var text = function (opt) { + return typeof opt === 'function' + ? opt(dt, jqNode, button.conf) + : opt; + }; + + if (label === undefined) { + return text(button.conf.text); + } + + button.conf.text = label; + textNode.html(text(label)); + + return this; + }, + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Constructor + */ + + /** + * Buttons constructor + * @private + */ + _constructor: function () { + var that = this; + var dt = this.s.dt; + var dtSettings = dt.settings()[0]; + var buttons = this.c.buttons; + + if (!dtSettings._buttons) { + dtSettings._buttons = []; + } + + dtSettings._buttons.push({ + inst: this, + name: this.c.name + }); + + for (var i = 0, ien = buttons.length; i < ien; i++) { + this.add(buttons[i]); + } + + dt.on('destroy', function (e, settings) { + if (settings === dtSettings) { + that.destroy(); + } + }); + + // Global key event binding to listen for button keys + $('body').on('keyup.' + this.s.namespace, function (e) { + if ( + !document.activeElement || + document.activeElement === document.body + ) { + // SUse a string of characters for fast lookup of if we need to + // handle this + var character = String.fromCharCode(e.keyCode).toLowerCase(); + + if (that.s.listenKeys.toLowerCase().indexOf(character) !== -1) { + that._keypress(character, e); + } + } + }); + }, + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private methods + */ + + /** + * Add a new button to the key press listener + * @param {object} conf Resolved button configuration object + * @private + */ + _addKey: function (conf) { + if (conf.key) { + this.s.listenKeys += $.isPlainObject(conf.key) + ? conf.key.key + : conf.key; + } + }, + + /** + * Insert the buttons into the container. Call without parameters! + * @param {node} [container] Recursive only - Insert point + * @param {array} [buttons] Recursive only - Buttons array + * @private + */ + _draw: function (container, buttons) { + if (!container) { + container = this.dom.container; + buttons = this.s.buttons; + } + + container.children().detach(); + + for (var i = 0, ien = buttons.length; i < ien; i++) { + container.append(buttons[i].inserter); + container.append(' '); + + if (buttons[i].buttons && buttons[i].buttons.length) { + this._draw(buttons[i].collection, buttons[i].buttons); + } + } + }, + + /** + * Create buttons from an array of buttons + * @param {array} attachTo Buttons array to attach to + * @param {object} button Button definition + * @param {boolean} inCollection true if the button is in a collection + * @private + */ + _expandButton: function ( + attachTo, + button, + split, + inCollection, + inSplit, + attachPoint, + parentConf + ) { + var dt = this.s.dt; + var isSplit = false; + var domCollection = this.c.dom.collection; + var buttons = !Array.isArray(button) ? [button] : button; + var lastButton; + + if (button === undefined) { + buttons = !Array.isArray(split) ? [split] : split; + } + + for (var i = 0, ien = buttons.length; i < ien; i++) { + var conf = this._resolveExtends(buttons[i]); + + if (!conf) { + continue; + } + + isSplit = conf.config && conf.config.split ? true : false; + + // If the configuration is an array, then expand the buttons at this + // point + if (Array.isArray(conf)) { + this._expandButton( + attachTo, + conf, + built !== undefined && built.conf !== undefined + ? built.conf.split + : undefined, + inCollection, + parentConf !== undefined && parentConf.split !== undefined, + attachPoint, + parentConf + ); + continue; + } + + var built = this._buildButton( + conf, + inCollection, + conf.split !== undefined || + (conf.config !== undefined && + conf.config.split !== undefined), + inSplit + ); + if (!built) { + continue; + } + + if (attachPoint !== undefined && attachPoint !== null) { + attachTo.splice(attachPoint, 0, built); + attachPoint++; + } + else { + attachTo.push(built); + } + + // Any button type can have a drop icon set + if (built.conf.dropIcon && ! built.conf.split) { + $(built.node) + .addClass(this.c.dom.button.dropClass) + .append(this.c.dom.button.dropHtml); + } + + // Create the dropdown for a collection + if (built.conf.buttons) { + built.collection = $( + '<' + domCollection.container.content.tag + '/>' + ); + built.conf._collection = built.collection; + + this._expandButton( + built.buttons, + built.conf.buttons, + built.conf.split, + !isSplit, + isSplit, + attachPoint, + built.conf + ); + } + + // And the split collection + if (built.conf.split) { + built.collection = $('<' + domCollection.container.tag + '/>'); + built.conf._collection = built.collection; + + for (var j = 0; j < built.conf.split.length; j++) { + var item = built.conf.split[j]; + + if (typeof item === 'object') { + item.parent = parentConf; + + if (item.collectionLayout === undefined) { + item.collectionLayout = built.conf.collectionLayout; + } + + if (item.dropup === undefined) { + item.dropup = built.conf.dropup; + } + + if (item.fade === undefined) { + item.fade = built.conf.fade; + } + } + } + + this._expandButton( + built.buttons, + built.conf.buttons, + built.conf.split, + !isSplit, + isSplit, + attachPoint, + built.conf + ); + } + + built.conf.parent = parentConf; + + // init call is made here, rather than buildButton as it needs to + // be selectable, and for that it needs to be in the buttons array + if (conf.init) { + conf.init.call(dt.button(built.node), dt, $(built.node), conf); + } + + lastButton = built.node; + } + + return lastButton; + }, + + /** + * Create an individual button + * @param {object} config Resolved button configuration + * @param {boolean} inCollection `true` if a collection button + * @return {object} Completed button description object + * @private + */ + _buildButton: function (config, inCollection, isSplit, inSplit) { + var that = this; + var configDom = this.c.dom; + var textNode; + var dt = this.s.dt; + var setLinerTab = false; + var text = function (opt) { + return typeof opt === 'function' ? opt(dt, button, config) : opt; + }; + + // Create an object that describes the button which can be in `dom.button`, or + // `dom.collection.button` or `dom.split.button` or `dom.collection.split.button`! + // Each should extend from `dom.button`. + var dom = $.extend(true, {}, configDom.button); + + if (inCollection && isSplit && configDom.collection.split) { + $.extend(true, dom, configDom.collection.split.action); + } + else if (inSplit || inCollection) { + $.extend(true, dom, configDom.collection.button); + } + else if (isSplit) { + $.extend(true, dom, configDom.split.button); + } + + // Spacers don't do much other than insert an element into the DOM + if (config.spacer) { + var spacer = $('<' + dom.spacer.tag + '/>') + .addClass( + 'dt-button-spacer ' + + config.style + + ' ' + + dom.spacer.className + ) + .html(text(config.text)); + + return { + conf: config, + node: spacer, + nodeChild: null, + inserter: spacer, + buttons: [], + inCollection: inCollection, + isSplit: isSplit, + collection: null, + textNode: spacer + }; + } + + // Make sure that the button is available based on whatever requirements + // it has. For example, PDF button require pdfmake + if ( + config.available && + !config.available(dt, config) && + !config.html + ) { + return false; + } + + var button; + + if (!config.html) { + var run = function (e, dt, button, config, done) { + config.action.call(dt.button(button), e, dt, button, config, done); + + $(dt.table().node()).triggerHandler('buttons-action.dt', [ + dt.button(button), + dt, + button, + config + ]); + }; + + var action = function(e, dt, button, config) { + if (config.async) { + that.processing(button[0], true); + + setTimeout(function () { + run(e, dt, button, config, function () { + that.processing(button[0], false); + }); + }, config.async); + } + else { + run(e, dt, button, config, function () {}); + } + }; + + var tag = config.tag || dom.tag; + var clickBlurs = + config.clickBlurs === undefined ? true : config.clickBlurs; + + button = $('<' + tag + '/>') + .addClass(dom.className) + .attr('aria-controls', this.s.dt.table().node().id) + .on('click.dtb', function (e) { + e.preventDefault(); + + if (!button.hasClass(dom.disabled) && config.action) { + action(e, dt, button, config); + } + + if (clickBlurs) { + button.trigger('blur'); + } + }) + .on('keypress.dtb', function (e) { + if (e.keyCode === 13) { + e.preventDefault(); + + if (!button.hasClass(dom.disabled) && config.action) { + action(e, dt, button, config); + } + } + }); + + // Make `a` tags act like a link + if (tag.toLowerCase() === 'a') { + button.attr('href', '#'); + } + + // Button tags should have `type=button` so they don't have any default behaviour + if (tag.toLowerCase() === 'button') { + button.attr('type', 'button'); + } + + if (dom.liner.tag) { + var lc = dom.liner.tag.toLowerCase(); + var liner = $('<' + lc + '/>') + .html(text(config.text)) + .addClass(dom.liner.className); + + if (lc === 'a') { + liner.attr('href', '#'); + } + + if (lc === 'a' || lc === 'button') { + liner.attr('tabindex', this.s.dt.settings()[0].iTabIndex); + setLinerTab = true; + } + + button.append(liner); + textNode = liner; + } + else { + button.html(text(config.text)); + textNode = button; + } + + if (! setLinerTab) { + button.attr('tabindex', this.s.dt.settings()[0].iTabIndex) + } + + if (config.enabled === false) { + button.addClass(dom.disabled); + } + + if (config.className) { + button.addClass(config.className); + } + + if (config.titleAttr) { + button.attr('title', text(config.titleAttr)); + } + + if (config.attr) { + button.attr(config.attr); + } + + if (!config.namespace) { + config.namespace = '.dt-button-' + _buttonCounter++; + } + + if (config.config !== undefined && config.config.split) { + config.split = config.config.split; + } + } + else { + button = $(config.html); + } + + var buttonContainer = this.c.dom.buttonContainer; + var inserter; + if (buttonContainer && buttonContainer.tag) { + inserter = $('<' + buttonContainer.tag + '/>') + .addClass(buttonContainer.className) + .append(button); + } + else { + inserter = button; + } + + this._addKey(config); + + // Style integration callback for DOM manipulation + // Note that this is _not_ documented. It is currently + // for style integration only + if (this.c.buttonCreated) { + inserter = this.c.buttonCreated(config, inserter); + } + + var splitDiv; + + if (isSplit) { + var dropdownConf = inCollection + ? $.extend(true, this.c.dom.split, this.c.dom.collection.split) + : this.c.dom.split; + var wrapperConf = dropdownConf.wrapper; + + splitDiv = $('<' + wrapperConf.tag + '/>') + .addClass(wrapperConf.className) + .append(button); + + var dropButtonConfig = $.extend(config, { + autoClose: true, + align: dropdownConf.dropdown.align, + attr: { + 'aria-haspopup': 'dialog', + 'aria-expanded': false + }, + className: dropdownConf.dropdown.className, + closeButton: false, + splitAlignClass: dropdownConf.dropdown.splitAlignClass, + text: dropdownConf.dropdown.text + }); + + this._addKey(dropButtonConfig); + + var splitAction = function (e, dt, button, config) { + _dtButtons.split.action.call( + dt.button(splitDiv), + e, + dt, + button, + config + ); + + $(dt.table().node()).triggerHandler('buttons-action.dt', [ + dt.button(button), + dt, + button, + config + ]); + button.attr('aria-expanded', true); + }; + + var dropButton = $( + '' + ) + .html(this.c.dom.button.dropHtml) + .addClass(this.c.dom.button.dropClass) + .on('click.dtb', function (e) { + e.preventDefault(); + e.stopPropagation(); + + if (!dropButton.hasClass(dom.disabled)) { + splitAction(e, dt, dropButton, dropButtonConfig); + } + if (clickBlurs) { + dropButton.trigger('blur'); + } + }) + .on('keypress.dtb', function (e) { + if (e.keyCode === 13) { + e.preventDefault(); + + if (!dropButton.hasClass(dom.disabled)) { + splitAction(e, dt, dropButton, dropButtonConfig); + } + } + }); + + if (config.split.length === 0) { + dropButton.addClass('dtb-hide-drop'); + } + + splitDiv.append(dropButton).attr(dropButtonConfig.attr); + } + + var node = isSplit ? splitDiv.get(0) : button.get(0); + + return { + conf: config, + node: node, + nodeChild: node && node.children && node.children.length ? node.children[0] : null, + inserter: isSplit ? splitDiv : inserter, + buttons: [], + inCollection: inCollection, + isSplit: isSplit, + inSplit: inSplit, + collection: null, + textNode: textNode + }; + }, + + /** + * Spin over buttons checking if splits should be enabled or not. + * @param {*} buttons Array of buttons to check + */ + _checkSplitEnable: function (buttons) { + if (! buttons) { + buttons = this.s.buttons; + } + + for (var i=0 ; i 30) { + // Protect against misconfiguration killing the browser + throw 'Buttons: Too many iterations'; + } + } + + return Array.isArray(base) ? base : $.extend({}, base); + }; + + conf = toConfObject(conf); + + while (conf && conf.extend) { + // Use `toConfObject` in case the button definition being extended + // is itself a string or a function + if (!_dtButtons[conf.extend]) { + throw 'Cannot extend unknown button type: ' + conf.extend; + } + + var objArray = toConfObject(_dtButtons[conf.extend]); + if (Array.isArray(objArray)) { + return objArray; + } + else if (!objArray) { + // This is a little brutal as it might be possible to have a + // valid button without the extend, but if there is no extend + // then the host button would be acting in an undefined state + return false; + } + + // Stash the current class name + var originalClassName = objArray.className; + + if (conf.config !== undefined && objArray.config !== undefined) { + conf.config = $.extend({}, objArray.config, conf.config); + } + + conf = $.extend({}, objArray, conf); + + // The extend will have overwritten the original class name if the + // `conf` object also assigned a class, but we want to concatenate + // them so they are list that is combined from all extended buttons + if (originalClassName && conf.className !== originalClassName) { + conf.className = originalClassName + ' ' + conf.className; + } + + // Although we want the `conf` object to overwrite almost all of + // the properties of the object being extended, the `extend` + // property should come from the object being extended + conf.extend = objArray.extend; + } + + // Buttons to be added to a collection -gives the ability to define + // if buttons should be added to the start or end of a collection + var postfixButtons = conf.postfixButtons; + if (postfixButtons) { + if (!conf.buttons) { + conf.buttons = []; + } + + for (i = 0, ien = postfixButtons.length; i < ien; i++) { + conf.buttons.push(postfixButtons[i]); + } + } + + var prefixButtons = conf.prefixButtons; + if (prefixButtons) { + if (!conf.buttons) { + conf.buttons = []; + } + + for (i = 0, ien = prefixButtons.length; i < ien; i++) { + conf.buttons.splice(i, 0, prefixButtons[i]); + } + } + + return conf; + }, + + /** + * Display (and replace if there is an existing one) a popover attached to a button + * @param {string|node} content Content to show + * @param {DataTable.Api} hostButton DT API instance of the button + * @param {object} inOpts Options (see object below for all options) + */ + _popover: function (content, hostButton, inOpts) { + var dt = hostButton; + var c = this.c; + var closed = false; + var options = $.extend( + { + align: 'button-left', // button-right, dt-container, split-left, split-right + autoClose: false, + background: true, + backgroundClassName: 'dt-button-background', + closeButton: true, + containerClassName: c.dom.collection.container.className, + contentClassName: c.dom.collection.container.content.className, + collectionLayout: '', + collectionTitle: '', + dropup: false, + fade: 400, + popoverTitle: '', + rightAlignClassName: 'dt-button-right', + tag: c.dom.collection.container.tag + }, + inOpts + ); + + var containerSelector = + options.tag + '.' + options.containerClassName.replace(/ /g, '.'); + var hostButtonNode = hostButton.node(); + var hostNode = options.collectionLayout.includes('fixed') ? $('body') : hostButton.node(); + + var close = function () { + closed = true; + + _fadeOut($(containerSelector), options.fade, function () { + $(this).detach(); + }); + + $( + dt + .buttons('[aria-haspopup="dialog"][aria-expanded="true"]') + .nodes() + ).attr('aria-expanded', 'false'); + + $('div.dt-button-background').off('click.dtb-collection'); + Buttons.background( + false, + options.backgroundClassName, + options.fade, + hostNode + ); + + $(window).off('resize.resize.dtb-collection'); + $('body').off('.dtb-collection'); + dt.off('buttons-action.b-internal'); + dt.off('destroy'); + + $('body').trigger('buttons-popover-hide.dt'); + }; + + if (content === false) { + close(); + return; + } + + var existingExpanded = $( + dt.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes() + ); + if (existingExpanded.length) { + // Reuse the current position if the button that was triggered is inside an existing collection + if (hostNode.closest(containerSelector).length) { + hostNode = existingExpanded.eq(0); + } + + close(); + } + + // Sort buttons if defined + if (options.sort) { + var elements = $('button', content) + .map(function (idx, el) { + return { + text: $(el).text(), + el: el + }; + }) + .toArray(); + + elements.sort(function (a, b) { + return a.text.localeCompare(b.text); + }); + + $(content).append(elements.map(function (v) { + return v.el; + })); + } + + // Try to be smart about the layout + var cnt = $('.dt-button', content).length; + var mod = ''; + + if (cnt === 3) { + mod = 'dtb-b3'; + } + else if (cnt === 2) { + mod = 'dtb-b2'; + } + else if (cnt === 1) { + mod = 'dtb-b1'; + } + + var display = $('<' + options.tag + '/>') + .addClass(options.containerClassName) + .addClass(options.collectionLayout) + .addClass(options.splitAlignClass) + .addClass(mod) + .css('display', 'none') + .attr({ + 'aria-modal': true, + role: 'dialog' + }); + + content = $(content) + .addClass(options.contentClassName) + .attr('role', 'menu') + .appendTo(display); + + hostButtonNode.attr('aria-expanded', 'true'); + + if (hostNode.parents('body')[0] !== document.body) { + hostNode = $(document.body).children('div, section, p').last(); + } + + if (options.popoverTitle) { + display.prepend( + '
' + + options.popoverTitle + + '
' + ); + } + else if (options.collectionTitle) { + display.prepend( + '
' + + options.collectionTitle + + '
' + ); + } + + if (options.closeButton) { + display + .prepend('
×
') + .addClass('dtb-collection-closeable'); + } + + _fadeIn(display.insertAfter(hostNode), options.fade); + + var tableContainer = $(hostButton.table().container()); + var position = display.css('position'); + + if (options.span === 'container' || options.align === 'dt-container') { + hostNode = hostNode.parent(); + display.css('width', tableContainer.width()); + } + + // Align the popover relative to the DataTables container + // Useful for wide popovers such as SearchPanes + if (position === 'absolute') { + // Align relative to the host button + var offsetParent = $(hostNode[0].offsetParent); + var buttonPosition = hostNode.position(); + var buttonOffset = hostNode.offset(); + var tableSizes = offsetParent.offset(); + var containerPosition = offsetParent.position(); + var computed = window.getComputedStyle(offsetParent[0]); + + tableSizes.height = offsetParent.outerHeight(); + tableSizes.width = + offsetParent.width() + parseFloat(computed.paddingLeft); + tableSizes.right = tableSizes.left + tableSizes.width; + tableSizes.bottom = tableSizes.top + tableSizes.height; + + // Set the initial position so we can read height / width + var top = buttonPosition.top + hostNode.outerHeight(); + var left = buttonPosition.left; + + display.css({ + top: top, + left: left + }); + + // Get the popover position + computed = window.getComputedStyle(display[0]); + var popoverSizes = display.offset(); + + popoverSizes.height = display.outerHeight(); + popoverSizes.width = display.outerWidth(); + popoverSizes.right = popoverSizes.left + popoverSizes.width; + popoverSizes.bottom = popoverSizes.top + popoverSizes.height; + popoverSizes.marginTop = parseFloat(computed.marginTop); + popoverSizes.marginBottom = parseFloat(computed.marginBottom); + + // First position per the class requirements - pop up and right align + if (options.dropup) { + top = + buttonPosition.top - + popoverSizes.height - + popoverSizes.marginTop - + popoverSizes.marginBottom; + } + + if ( + options.align === 'button-right' || + display.hasClass(options.rightAlignClassName) + ) { + left = + buttonPosition.left - + popoverSizes.width + + hostNode.outerWidth(); + } + + // Container alignment - make sure it doesn't overflow the table container + if ( + options.align === 'dt-container' || + options.align === 'container' + ) { + if (left < buttonPosition.left) { + left = -buttonPosition.left; + } + } + + // Window adjustment + if ( + containerPosition.left + left + popoverSizes.width > + $(window).width() + ) { + // Overflowing the document to the right + left = + $(window).width() - + popoverSizes.width - + containerPosition.left; + } + + if (buttonOffset.left + left < 0) { + // Off to the left of the document + left = -buttonOffset.left; + } + + if ( + containerPosition.top + top + popoverSizes.height > + $(window).height() + $(window).scrollTop() + ) { + // Pop up if otherwise we'd need the user to scroll down + top = + buttonPosition.top - + popoverSizes.height - + popoverSizes.marginTop - + popoverSizes.marginBottom; + } + + if (offsetParent.offset().top + top < $(window).scrollTop()) { + // Correction for when the top is beyond the top of the page + top = buttonPosition.top + hostNode.outerHeight(); + } + + // Calculations all done - now set it + display.css({ + top: top, + left: left + }); + } + else { + // Fix position - centre on screen + var place = function () { + var half = $(window).height() / 2; + + var top = display.height() / 2; + if (top > half) { + top = half; + } + + display.css('marginTop', top * -1); + }; + + place(); + + $(window).on('resize.dtb-collection', function () { + place(); + }); + } + + if (options.background) { + Buttons.background( + true, + options.backgroundClassName, + options.fade, + options.backgroundHost || hostNode + ); + } + + // This is bonkers, but if we don't have a click listener on the + // background element, iOS Safari will ignore the body click + // listener below. An empty function here is all that is + // required to make it work... + $('div.dt-button-background').on( + 'click.dtb-collection', + function () {} + ); + + if (options.autoClose) { + setTimeout(function () { + dt.on('buttons-action.b-internal', function (e, btn, dt, node) { + if (node[0] === hostNode[0]) { + return; + } + close(); + }); + }, 0); + } + + $(display).trigger('buttons-popover.dt'); + + dt.on('destroy', close); + + setTimeout(function () { + closed = false; + $('body') + .on('click.dtb-collection', function (e) { + if (closed) { + return; + } + + // andSelf is deprecated in jQ1.8, but we want 1.7 compat + var back = $.fn.addBack ? 'addBack' : 'andSelf'; + var parent = $(e.target).parent()[0]; + + if ( + (!$(e.target).parents()[back]().filter(content) + .length && + !$(parent).hasClass('dt-buttons')) || + $(e.target).hasClass('dt-button-background') + ) { + close(); + } + }) + .on('keyup.dtb-collection', function (e) { + if (e.keyCode === 27) { + close(); + } + }) + .on('keydown.dtb-collection', function (e) { + // Focus trap for tab key + var elements = $('a, button', content); + var active = document.activeElement; + + if (e.keyCode !== 9) { + // tab + return; + } + + if (elements.index(active) === -1) { + // If current focus is not inside the popover + elements.first().focus(); + e.preventDefault(); + } + else if (e.shiftKey) { + // Reverse tabbing order when shift key is pressed + if (active === elements[0]) { + elements.last().focus(); + e.preventDefault(); + } + } + else { + if (active === elements.last()[0]) { + elements.first().focus(); + e.preventDefault(); + } + } + }); + }, 0); + } +}); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Statics + */ + +/** + * Show / hide a background layer behind a collection + * @param {boolean} Flag to indicate if the background should be shown or + * hidden + * @param {string} Class to assign to the background + * @static + */ +Buttons.background = function (show, className, fade, insertPoint) { + if (fade === undefined) { + fade = 400; + } + if (!insertPoint) { + insertPoint = document.body; + } + + if (show) { + _fadeIn( + $('
') + .addClass(className) + .css('display', 'none') + .insertAfter(insertPoint), + fade + ); + } + else { + _fadeOut($('div.' + className), fade, function () { + $(this).removeClass(className).remove(); + }); + } +}; + +/** + * Instance selector - select Buttons instances based on an instance selector + * value from the buttons assigned to a DataTable. This is only useful if + * multiple instances are attached to a DataTable. + * @param {string|int|array} Instance selector - see `instance-selector` + * documentation on the DataTables site + * @param {array} Button instance array that was attached to the DataTables + * settings object + * @return {array} Buttons instances + * @static + */ +Buttons.instanceSelector = function (group, buttons) { + if (group === undefined || group === null) { + return $.map(buttons, function (v) { + return v.inst; + }); + } + + var ret = []; + var names = $.map(buttons, function (v) { + return v.name; + }); + + // Flatten the group selector into an array of single options + var process = function (input) { + if (Array.isArray(input)) { + for (var i = 0, ien = input.length; i < ien; i++) { + process(input[i]); + } + return; + } + + if (typeof input === 'string') { + if (input.indexOf(',') !== -1) { + // String selector, list of names + process(input.split(',')); + } + else { + // String selector individual name + var idx = $.inArray(input.trim(), names); + + if (idx !== -1) { + ret.push(buttons[idx].inst); + } + } + } + else if (typeof input === 'number') { + // Index selector + ret.push(buttons[input].inst); + } + else if (typeof input === 'object' && input.nodeName) { + // Element selector + for (var j = 0; j < buttons.length; j++) { + if (buttons[j].inst.dom.container[0] === input) { + ret.push(buttons[j].inst); + } + } + } + else if (typeof input === 'object') { + // Actual instance selector + ret.push(input); + } + }; + + process(group); + + return ret; +}; + +/** + * Button selector - select one or more buttons from a selector input so some + * operation can be performed on them. + * @param {array} Button instances array that the selector should operate on + * @param {string|int|node|jQuery|array} Button selector - see + * `button-selector` documentation on the DataTables site + * @return {array} Array of objects containing `inst` and `idx` properties of + * the selected buttons so you know which instance each button belongs to. + * @static + */ +Buttons.buttonSelector = function (insts, selector) { + var ret = []; + var nodeBuilder = function (a, buttons, baseIdx) { + var button; + var idx; + + for (var i = 0, ien = buttons.length; i < ien; i++) { + button = buttons[i]; + + if (button) { + idx = baseIdx !== undefined ? baseIdx + i : i + ''; + + a.push({ + node: button.node, + name: button.conf.name, + idx: idx + }); + + if (button.buttons) { + nodeBuilder(a, button.buttons, idx + '-'); + } + } + } + }; + + var run = function (selector, inst) { + var i, ien; + var buttons = []; + nodeBuilder(buttons, inst.s.buttons); + + var nodes = $.map(buttons, function (v) { + return v.node; + }); + + if (Array.isArray(selector) || selector instanceof $) { + for (i = 0, ien = selector.length; i < ien; i++) { + run(selector[i], inst); + } + return; + } + + if (selector === null || selector === undefined || selector === '*') { + // Select all + for (i = 0, ien = buttons.length; i < ien; i++) { + ret.push({ + inst: inst, + node: buttons[i].node + }); + } + } + else if (typeof selector === 'number') { + // Main button index selector + if (inst.s.buttons[selector]) { + ret.push({ + inst: inst, + node: inst.s.buttons[selector].node + }); + } + } + else if (typeof selector === 'string') { + if (selector.indexOf(',') !== -1) { + // Split + var a = selector.split(','); + + for (i = 0, ien = a.length; i < ien; i++) { + run(a[i].trim(), inst); + } + } + else if (selector.match(/^\d+(\-\d+)*$/)) { + // Sub-button index selector + var indexes = $.map(buttons, function (v) { + return v.idx; + }); + + ret.push({ + inst: inst, + node: buttons[$.inArray(selector, indexes)].node + }); + } + else if (selector.indexOf(':name') !== -1) { + // Button name selector + var name = selector.replace(':name', ''); + + for (i = 0, ien = buttons.length; i < ien; i++) { + if (buttons[i].name === name) { + ret.push({ + inst: inst, + node: buttons[i].node + }); + } + } + } + else { + // jQuery selector on the nodes + $(nodes) + .filter(selector) + .each(function () { + ret.push({ + inst: inst, + node: this + }); + }); + } + } + else if (typeof selector === 'object' && selector.nodeName) { + // Node selector + var idx = $.inArray(selector, nodes); + + if (idx !== -1) { + ret.push({ + inst: inst, + node: nodes[idx] + }); + } + } + }; + + for (var i = 0, ien = insts.length; i < ien; i++) { + var inst = insts[i]; + + run(selector, inst); + } + + return ret; +}; + +/** + * Default function used for formatting output data. + * @param {*} str Data to strip + */ +Buttons.stripData = function (str, config) { + // If the input is an HTML element, we can use the HTML from it (HTML might be stripped below). + if (str !== null && typeof str === 'object' && str.nodeName && str.nodeType) { + str = str.innerHTML; + } + + if (typeof str !== 'string') { + return str; + } + + // Always remove script tags + str = Buttons.stripHtmlScript(str); + + // Always remove comments + str = Buttons.stripHtmlComments(str); + + if (!config || config.stripHtml) { + str = DataTable.util.stripHtml(str); + } + + if (!config || config.trim) { + str = str.trim(); + } + + if (!config || config.stripNewlines) { + str = str.replace(/\n/g, ' '); + } + + if (!config || config.decodeEntities) { + if (_entityDecoder) { + str = _entityDecoder(str); + } + else { + _exportTextarea.innerHTML = str; + str = _exportTextarea.value; + } + } + + // Prevent Excel from running a formula + if (!config || config.escapeExcelFormula) { + if (str.match(/^[=@\t\r]/)) { + str = "'" + str; + } + } + + return str; +}; + +/** + * Provide a custom entity decoding function - e.g. a regex one, which can be + * much faster than the built in DOM option, but also larger code size. + * @param {function} fn + */ +Buttons.entityDecoder = function (fn) { + _entityDecoder = fn; +}; + +/** + * Common function for stripping HTML comments + * + * @param {*} input + * @returns + */ +Buttons.stripHtmlComments = function (input) { + var previous; + + do { + previous = input; + input = input.replace(/(