mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-03 09:20:10 +00:00
1358 lines
37 KiB
JavaScript
1358 lines
37 KiB
JavaScript
/*!
|
|
* Bootstrap Data Table Plugin v1.5.5
|
|
*
|
|
* Author: Jeff Dupont
|
|
* ==========================================================
|
|
* Copyright 2012
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
* ==========================================================
|
|
*/
|
|
|
|
(function($) {
|
|
/* DATATABLE CLASS DEFINITION
|
|
* ========================== */
|
|
var DataTable = function(element, options) {
|
|
this.$element = $(element);
|
|
this.options = options;
|
|
this.pendingRequest = null;
|
|
this.enabled = true;
|
|
this.columns = [];
|
|
this.rows = [];
|
|
this.buttons = [];
|
|
|
|
// this needs to be handled better
|
|
this.localStorageId =
|
|
"datatable_" + (options.id || options.url.replace(/\W/gi, "_"));
|
|
|
|
// set the defaults for the column options array
|
|
for (var column in this.options.columns) {
|
|
// check sortable
|
|
if (typeof this.options.columns[column].sortable === undefined)
|
|
this.options.columns[column].sortable = true;
|
|
}
|
|
|
|
this.$default = this.$element.children().length
|
|
? this.$element.children()
|
|
: $("<div></div>")
|
|
.addClass("alert alert-danger")
|
|
.html(i18n.no_results_found);
|
|
|
|
this.$element.addClass("clearfix");
|
|
|
|
// clear out the localStorage for this entry
|
|
if (localStorage) {
|
|
localStorage[this.localStorageId] = "false";
|
|
}
|
|
|
|
if (
|
|
this.options.tablePreRender &&
|
|
typeof this.options.tablePreRender === "function"
|
|
)
|
|
this.options.tablePreRender.call(this);
|
|
|
|
// initialize the toolbar
|
|
_initToolbar.call(this);
|
|
|
|
if (this.options.autoLoad === true) this.render();
|
|
};
|
|
|
|
DataTable.prototype = {
|
|
constructor: DataTable,
|
|
|
|
render: function() {
|
|
var o = this.options,
|
|
$e = this.$element;
|
|
|
|
// show loading
|
|
this.loading(true);
|
|
|
|
// reset the columns and rows
|
|
this.columns = [];
|
|
this.rows = [];
|
|
this.buttons = [];
|
|
this.$wrapper = undefined;
|
|
this.$table = undefined;
|
|
this.$header = undefined;
|
|
this.$body = undefined;
|
|
this.$footer = undefined;
|
|
this.$pagination = undefined;
|
|
|
|
if (this.$toolbar) this.$toolbar.remove();
|
|
|
|
// add summary on top
|
|
this.$summary_table = $("<details id='dt-top-summary' open></details>")
|
|
.append("<summary></summary>").append("<div class='details mt-2'><ul></ul></div>");
|
|
|
|
// top
|
|
this.$top_details = $("<div></div>").attr("id", "dt-top-details");
|
|
// bottom
|
|
this.$bottom_details = $("<div class='d-flex flex-column'></div>").attr("id", "dt-bottom-details");
|
|
|
|
// localize the object
|
|
var that = this;
|
|
|
|
// pull in the data from the ajax call
|
|
if (o.debug)
|
|
console.log(
|
|
$.extend({}, o.post, {
|
|
currentPage: o.currentPage,
|
|
perPage: o.perPage,
|
|
sort: o.sort,
|
|
filter: o.filter
|
|
})
|
|
);
|
|
if (o.url !== "") {
|
|
var req_data = typeof o.post === "function" ? o.post() : o.post;
|
|
|
|
if (this.pendingRequest) this.pendingRequest.abort();
|
|
|
|
this.pendingRequest = $.ajax({
|
|
url: o.url,
|
|
type: "GET",
|
|
dataType: "json",
|
|
data: $.extend({}, req_data, {
|
|
currentPage: o.currentPage,
|
|
perPage: o.perPage,
|
|
sortColumn: o.sort.length > 0 ? o.sort[0][0] : null,
|
|
sortOrder: o.sort.length > 0 ? o.sort[0][1] : null,
|
|
filter: o.filter
|
|
}),
|
|
success: function(res, status, xhr) {
|
|
that.pendingRequest = null;
|
|
|
|
if (o.dataAdapter) res = o.dataAdapter(res, o);
|
|
|
|
that.resultset = res;
|
|
if (!res || res === undefined || !res.data || (res.data.length == 0 && !o.forceTable)) {
|
|
showError.call(that, res);
|
|
return;
|
|
}
|
|
|
|
// clear out the current elements in the container
|
|
$e.empty();
|
|
|
|
// set the sort and filter configuration
|
|
o.sort = res.sort;
|
|
o.filter = res.filter;
|
|
o.totalRows = res.totalRows;
|
|
|
|
// set the current page if we're forcing it from the server
|
|
if (res.currentPage) o.currentPage = parseInt(res.currentPage);
|
|
|
|
// retrieve the saved columns
|
|
_retrieveColumns.call(that, localStorage[that.localStorageId]);
|
|
|
|
// append the table
|
|
$e.append(that.table());
|
|
|
|
// append the detail boxes
|
|
$e.prepend(that.$top_details);
|
|
|
|
if (res.summary != undefined) $e.prepend(that.$summary_table);
|
|
$e.append(that.$bottom_details);
|
|
|
|
// render the rest of the table
|
|
if (o.showHeader) that.header();
|
|
if (o.showFooter) that.footer();
|
|
|
|
// fill in the table body
|
|
that.body();
|
|
|
|
// render table summary
|
|
if (res.summary != undefined) {
|
|
|
|
// do a validity check summing all percentages
|
|
if (res.summary.map(s => s.percentage).reduce((acc, curr) => acc + curr) <= 100) {
|
|
|
|
// clear the previous state
|
|
const $summary = that.$summary_table.find('summary');
|
|
$summary.empty();
|
|
|
|
const colors = ["bg-primary", "bg-secondary", "bg-success", "bg-danger", "bg-warning", "bg-info", "bg-dark"];
|
|
const $progresses = $("<div class='progress'></div>");
|
|
const $list = that.$summary_table.find('div.details > ul');
|
|
$list.empty();
|
|
|
|
// create progresses bars
|
|
res.summary.forEach((s) => {
|
|
|
|
const color = colors.pop();
|
|
|
|
const $progress = $(`<div class='progress-bar'></div>`);
|
|
$progress.attr('role', 'progressbar');
|
|
$progress.css('width', `${s.percentage}%`);
|
|
$progress.addClass(color);
|
|
$progresses.append($progress);
|
|
|
|
const $li = $(`<li></li>`);
|
|
$li.html(`<i class='fas fa-circle ${color.replace('bg', 'text')}'></i> ${s.label} <span>(${s.percentage}%)</span>`);
|
|
|
|
$list.append($li);
|
|
|
|
});
|
|
|
|
$summary.append($progresses);
|
|
}
|
|
|
|
}
|
|
|
|
// render the pagination
|
|
if (o.showTopPagination && that.pagination())
|
|
that.$top_details.append(that.pagination().clone(true));
|
|
if (o.showPagination && that.pagination())
|
|
that.$bottom_details.append(that.pagination().clone(true));
|
|
|
|
// update the details for the results
|
|
if (!o.hideDetails) that.details();
|
|
|
|
// initialize the toolbar
|
|
_initToolbar.call(that);
|
|
|
|
// nearly complete... let the user apply any final adjustments
|
|
if (o.tableCallback && typeof o.tableCallback === "function")
|
|
o.tableCallback.call(that);
|
|
|
|
that.loading(false);
|
|
},
|
|
error: function(xhr, status, e) {
|
|
that.pendingRequest = null;
|
|
if (status !== "abort")
|
|
console.error("Datatable: error while loading data: " + e);
|
|
showError.call(that, null, status, e);
|
|
|
|
that.loading(false);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
loading: function(show) {
|
|
var $e = this.$element;
|
|
var o = this.options;
|
|
|
|
if (o.customLoading) {
|
|
o.customLoading(this, show);
|
|
return;
|
|
}
|
|
|
|
if (!this.$loading) {
|
|
this.$loading = $("<div></div>")
|
|
.css({
|
|
position: "absolute",
|
|
top: parseInt($e.position().top) + 5 + (o.loadingYOffset || 0),
|
|
left:
|
|
parseInt($e.position().left) +
|
|
parseInt($e.css("marginLeft")) +
|
|
Math.floor($e.width() / 4),
|
|
width: Math.floor($e.width() / 2) + "px"
|
|
})
|
|
.append(
|
|
$("<div></div>")
|
|
.addClass("progress progress-striped")
|
|
.append(
|
|
$("<div></div>")
|
|
.addClass(
|
|
"progress-bar progress-bar-animated progress-bar-info"
|
|
)
|
|
.attr("style", "width: 50%")
|
|
)
|
|
)
|
|
.appendTo(document.body);
|
|
}
|
|
|
|
if (show) {
|
|
$e.css({ opacity: 0.2 });
|
|
} else {
|
|
$e.css({ opacity: 1 });
|
|
|
|
this.$loading.remove();
|
|
this.$loading = undefined;
|
|
}
|
|
},
|
|
|
|
toolbar: function() {
|
|
var o = this.options,
|
|
$e = this.$element,
|
|
that = this;
|
|
|
|
this.$toolbar = $("<div></div>").addClass(
|
|
"dt-toolbar btn-toolbar ms-auto"
|
|
);
|
|
|
|
this.$button_group = $("<div></div>")
|
|
.addClass("btn-group")
|
|
.appendTo(this.$toolbar);
|
|
|
|
// add all the custom buttons
|
|
for (var i = 0; i < o.buttons.length; i++) {
|
|
this.buttons.push(o.buttons[i]);
|
|
}
|
|
|
|
// attach all buttons to the toolbar
|
|
$.each(this.buttons, function() {
|
|
that.$button_group.append(this);
|
|
});
|
|
|
|
// attach the toolbar to the section header
|
|
if (o.sectionHeader) {
|
|
this.$section_header = $(o.sectionHeader);
|
|
this.$section_header.append(this.$toolbar);
|
|
} else if (o.title !== "" && !this.$section_header) {
|
|
this.$section_header = $("<h2></h2>")
|
|
.html(o.title)
|
|
.append(this.$toolbar);
|
|
$e.before(this.$section_header);
|
|
} else {
|
|
if (!this.$toolbar_container) {
|
|
this.$toolbar_container = $("<div></div>").addClass(
|
|
"dt-toolbar-container d-flex"
|
|
);
|
|
}
|
|
$e.prepend(this.$toolbar_container.append(this.$toolbar));
|
|
}
|
|
|
|
return this.$toolbar;
|
|
},
|
|
|
|
details: function() {
|
|
var o = this.options,
|
|
res = this.resultset,
|
|
start = 0,
|
|
end = 0,
|
|
that = this;
|
|
|
|
start = o.currentPage * o.perPage - o.perPage + 1;
|
|
if (start < 1) start = 1;
|
|
|
|
end = o.currentPage * o.perPage;
|
|
if (end > o.totalRows) end = o.totalRows;
|
|
|
|
$(
|
|
'<div class="text-left"><p>' +
|
|
i18n.showing_x_to_y_rows.sformat(start, end, o.totalRows) +
|
|
"</p></div>"
|
|
).prependTo(this.$bottom_details);
|
|
},
|
|
|
|
table: function() {
|
|
var $e = this.$element,
|
|
o = this.options;
|
|
|
|
if (!this.$table_wrapper) {
|
|
this.$wrapper = $("<div></div>").addClass("dt-table-wrapper");
|
|
}
|
|
|
|
if (!this.$table) {
|
|
this.$table = $("<table></table>").addClass(o.class);
|
|
}
|
|
|
|
this.$wrapper.append(this.$table);
|
|
return this.$wrapper;
|
|
},
|
|
|
|
header: function() {
|
|
var o = this.options,
|
|
res = this.resultset;
|
|
|
|
if (!this.$header) {
|
|
this.$header = $("<thead></thead>");
|
|
var row = $("<tr></tr>");
|
|
|
|
// loop through the columns
|
|
for (var column in o.columns) {
|
|
var $cell = this.column(column),
|
|
colprop = $cell.data("column_properties");
|
|
|
|
// attach the sort click event
|
|
if (colprop.sortable && !colprop.custom)
|
|
$cell.click(this, this.sort).css({ cursor: "pointer" });
|
|
|
|
for (var i = 0; i < o.sort.length; i++) {
|
|
if (o.sort[i][0] == colprop.field) {
|
|
if (o.sort[i][1] == "asc") {
|
|
$cell.append($(o.ascending));
|
|
colprop.sortOrder = "asc";
|
|
} else if (o.sort[i][1] == "desc") {
|
|
$cell.append($(o.descending));
|
|
colprop.sortOrder = "desc";
|
|
}
|
|
}
|
|
}
|
|
|
|
row.append($cell);
|
|
this.$header.append(row);
|
|
this.columns.push($cell);
|
|
}
|
|
|
|
// any final user adjustments to the header
|
|
if (o.headerCallback && typeof o.headerCallback === "function")
|
|
o.headerCallback.call(this);
|
|
|
|
this.$table.append(this.$header);
|
|
}
|
|
return this.$header;
|
|
},
|
|
|
|
footer: function() {
|
|
var o = this.options,
|
|
res = this.resultset;
|
|
|
|
if (!this.$footer) {
|
|
this.$footer = $("<tfoot></tfoot>");
|
|
|
|
// loop through the columns
|
|
for (column in o.columns) {
|
|
var $cell = $("<td></td>");
|
|
|
|
$cell
|
|
.data("cell_properties", o.columns[column])
|
|
.addClass(o.columns[column].classname);
|
|
|
|
this.$footer.append($cell);
|
|
}
|
|
|
|
// any final user adjustments to the footer
|
|
if (o.footerCallback && typeof o.footerCallback === "function")
|
|
o.footerCallback.call(this, this.resultset.footer);
|
|
|
|
this.$table.append(this.$footer);
|
|
}
|
|
return this.$footer;
|
|
},
|
|
|
|
body: function() {
|
|
var res = this.resultset,
|
|
o = this.options;
|
|
|
|
if (!this.$body) {
|
|
this.$body = $("<tbody></tbody>");
|
|
|
|
// loop through the results
|
|
for (var i = 0; i < res.data.length; i++) {
|
|
var row = this.row(res.data[i]);
|
|
this.$body.append(row);
|
|
this.rows.push(row);
|
|
}
|
|
|
|
if (o.showFilterRow) this.$body.prepend(this.filter());
|
|
|
|
this.$table.append(this.$body);
|
|
}
|
|
return this.$body;
|
|
},
|
|
|
|
filter: function() {
|
|
var $row = $("<tr></tr>"),
|
|
o = this.options,
|
|
that = this;
|
|
|
|
$row.addClass("dt-filter-row");
|
|
|
|
// loop through the columns
|
|
for (var column in o.columns) {
|
|
var $cell = $("<td></td>").addClass(o.columns[column].classname);
|
|
|
|
if (o.columns[column].hidden) $cell.hide();
|
|
|
|
if (o.columns[column].filter && o.columns[column].field) {
|
|
$cell.append(
|
|
$("<input/>")
|
|
.attr("name", "filter_" + o.columns[column].field)
|
|
.data("filter", o.columns[column].field)
|
|
.val(o.filter[o.columns[column].field] || "")
|
|
// .change(this, this.runFilter)
|
|
.change(function(e) {
|
|
runFilter.call(this, that);
|
|
})
|
|
);
|
|
}
|
|
|
|
$row.append($cell);
|
|
}
|
|
return $row;
|
|
},
|
|
|
|
row: function(rowdata) {
|
|
var $row = $("<tr></tr>"),
|
|
o = this.options;
|
|
|
|
// loop through the columns
|
|
for (var column in o.columns) {
|
|
var cell = this.cell(rowdata, column);
|
|
$row.append(cell);
|
|
}
|
|
|
|
// callback for postprocessing on the row
|
|
if (o.rowCallback && typeof o.rowCallback === "function")
|
|
$row = o.rowCallback($row, rowdata);
|
|
|
|
return $row;
|
|
},
|
|
|
|
cell: function(data, column) {
|
|
var celldata =
|
|
data[this.options.columns[column].field] ||
|
|
this.options.columns[column].custom,
|
|
$cell = $("<td></td>"),
|
|
o = this.options;
|
|
|
|
// preprocess on the cell data for a column
|
|
if (
|
|
o.columns[column].callback &&
|
|
typeof o.columns[column].callback === "function"
|
|
)
|
|
celldata = o.columns[column].callback.call(
|
|
$cell,
|
|
data,
|
|
o.columns[column]
|
|
);
|
|
|
|
$cell
|
|
.data("cell_properties", o.columns[column])
|
|
.addClass(o.columns[column].classname)
|
|
.append(celldata || " ");
|
|
|
|
if (o.columns[column].css) $cell.css(o.columns[column].css);
|
|
|
|
if (o.columns[column].hidden) $cell.hide();
|
|
|
|
return $cell;
|
|
},
|
|
|
|
column: function(column) {
|
|
var $cell = $("<th></th>"),
|
|
o = this.options,
|
|
classname =
|
|
"dt-column_" + column + Math.floor(Math.random() * 1000 + 1);
|
|
|
|
o.columns[column].classname = classname;
|
|
|
|
$cell
|
|
.data("column_properties", o.columns[column])
|
|
.addClass(classname)
|
|
.text(o.columns[column].title);
|
|
|
|
if (o.columns[column].css) $cell.css(o.columns[column].css);
|
|
|
|
if (o.columns[column].hidden) $cell.hide();
|
|
|
|
return $cell;
|
|
},
|
|
|
|
sort: function(e) {
|
|
var colprop = $(this).data("column_properties"),
|
|
that = e.data,
|
|
o = e.data.options,
|
|
found = false;
|
|
|
|
colprop.sortOrder = colprop.sortOrder
|
|
? colprop.sortOrder == "asc"
|
|
? "desc"
|
|
: ""
|
|
: "asc";
|
|
|
|
if (o.allowMultipleSort) {
|
|
// does the sort already exist?
|
|
for (var i = 0; i < o.sort.length; i++) {
|
|
if (o.sort[i][0] == colprop.field) {
|
|
o.sort[i][1] = colprop.sortOrder;
|
|
if (colprop.sortOrder === "") o.sort.splice(i, 1);
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) o.sort.push([colprop.field, colprop.sortOrder]);
|
|
} else {
|
|
// clear out any current sorts
|
|
o.sort = [];
|
|
o.sort.push([colprop.field, colprop.sortOrder]);
|
|
}
|
|
if (o.debug) console.log(o.sort);
|
|
|
|
that.render();
|
|
},
|
|
|
|
pagination: function() {
|
|
var $e = this.$element,
|
|
that = this,
|
|
o = this.options,
|
|
res = this.resultset;
|
|
|
|
// no paging needed
|
|
if (o.perPage >= res.totalRows) return;
|
|
|
|
if (!this.$pagination) {
|
|
this.$pagination = $("<div></div>").addClass("ms-auto");
|
|
|
|
// how many pages?
|
|
o.pageCount = Math.ceil(res.totalRows / o.perPage);
|
|
|
|
// setup the pager container and the quick page buttons
|
|
var $pager = $("<ul></ul>").addClass("pagination"),
|
|
$first = $("<li></li>")
|
|
.addClass("page-item")
|
|
.append(
|
|
$("<a></a>")
|
|
.addClass("page-link")
|
|
.attr("href", "#")
|
|
.data("page", 1)
|
|
.html("«")
|
|
.click(function() {
|
|
o.currentPage = 1;
|
|
that.render();
|
|
return false;
|
|
})
|
|
),
|
|
$previous = $("<li></li>")
|
|
.addClass("page-item")
|
|
.append(
|
|
$("<a></a>")
|
|
.addClass("page-link")
|
|
.attr("href", "#")
|
|
.data("page", o.currentPage - 1)
|
|
.html("<")
|
|
.click(function() {
|
|
o.currentPage -= 1;
|
|
o.currentPage = o.currentPage >= 1 ? o.currentPage : 1;
|
|
that.render();
|
|
return false;
|
|
})
|
|
),
|
|
$next = $("<li></li>")
|
|
.addClass("page-item")
|
|
.append(
|
|
$("<a></a>")
|
|
.addClass("page-link")
|
|
.attr("href", "#")
|
|
.data("page", o.currentPage + 1)
|
|
.html(">")
|
|
.click(function() {
|
|
o.currentPage += 1;
|
|
o.currentPage =
|
|
o.currentPage <= o.pageCount ? o.currentPage : o.pageCount;
|
|
that.render();
|
|
return false;
|
|
})
|
|
),
|
|
$last = $("<li></li>")
|
|
.addClass("page-item")
|
|
.append(
|
|
$("<a></a>")
|
|
.addClass("page-link")
|
|
.attr("href", "#")
|
|
.data("page", o.pageCount)
|
|
.html("»")
|
|
.click(function() {
|
|
o.currentPage = o.pageCount;
|
|
that.render();
|
|
return false;
|
|
})
|
|
);
|
|
|
|
var totalPages = o.pagePadding * 2,
|
|
start,
|
|
end;
|
|
|
|
if (totalPages >= o.pageCount) {
|
|
start = 1;
|
|
end = o.pageCount;
|
|
} else {
|
|
start = o.currentPage - o.pagePadding;
|
|
if (start <= 0) start = 1;
|
|
|
|
end = start + totalPages;
|
|
if (end > o.pageCount) {
|
|
end = o.pageCount;
|
|
start = end - totalPages;
|
|
}
|
|
}
|
|
|
|
// append the pagination links
|
|
for (var i = start; i <= end; i++) {
|
|
var $link = $("<li></li>")
|
|
.addClass("page-item")
|
|
.append(
|
|
$("<a></a>")
|
|
.addClass("page-link")
|
|
.attr("href", "#")
|
|
.data("page", i)
|
|
.text(i)
|
|
.click(function() {
|
|
o.currentPage = $(this).data("page");
|
|
that.render();
|
|
return false;
|
|
})
|
|
);
|
|
|
|
if (i == o.currentPage) $link.addClass("active");
|
|
|
|
$pager.append($link);
|
|
}
|
|
|
|
// append quick jump buttons
|
|
if (o.currentPage == 1) {
|
|
$first.addClass("disabled");
|
|
$previous.addClass("disabled");
|
|
}
|
|
if (o.currentPage == o.pageCount) {
|
|
$next.addClass("disabled");
|
|
$last.addClass("disabled");
|
|
}
|
|
$pager.prepend($first, $previous);
|
|
$pager.append($next, $last);
|
|
|
|
this.$pagination.append($pager);
|
|
}
|
|
return this.$pagination;
|
|
},
|
|
|
|
remove: function() {
|
|
var $e = this.$element;
|
|
|
|
if (this.$section_header) this.$section_header.remove();
|
|
|
|
$e.data("datatable", null);
|
|
$e.empty();
|
|
}
|
|
};
|
|
|
|
/* DATATABLE PRIVATE METHODS
|
|
* ========================= */
|
|
|
|
function _initToolbar() {
|
|
var o = this.options;
|
|
|
|
// create the perpage dropdown
|
|
if (!o.hidePerPage) _initPerPage.call(this);
|
|
|
|
// handle filter options
|
|
if (o.filterModal) _initFilterModal.call(this);
|
|
|
|
// handle the column management
|
|
// ntop - disabled
|
|
//if(o.toggleColumns) _initColumnModal.call(this);
|
|
|
|
// handle the overflow option
|
|
// ntop - disabled
|
|
// if(o.allowOverflow) _initOverflowToggle.call(this);
|
|
|
|
// initialize the table info
|
|
if (o.allowTableinfo) _initTableInfo.call(this);
|
|
|
|
// create the buttons and section functions
|
|
this.toolbar();
|
|
}
|
|
|
|
function _initColumnModal() {
|
|
var o = this.options,
|
|
$e = this.$element,
|
|
$top_details = this.$top_details,
|
|
$toggle = $("<a></a>");
|
|
|
|
// localize the object
|
|
var that = this;
|
|
|
|
if (!this.$column_modal) {
|
|
var randId = Math.floor(Math.random() * 100 + 1);
|
|
this.$column_modal = $("<div></div>")
|
|
.attr("id", "dt-column-modal_" + randId)
|
|
.attr("tabindex", "-1")
|
|
.attr("role", "dialog")
|
|
.attr("aria-labelledby", "dt-column-modal-label_" + randId)
|
|
.attr("aria-hidden", "true")
|
|
.addClass("modal fade")
|
|
.hide();
|
|
|
|
// render the modal header
|
|
this.$column_modalheader = $("<div></div>")
|
|
.addClass("modal-header")
|
|
.append(
|
|
$("<button></button>")
|
|
.addClass("close")
|
|
.data("dismiss", "modal")
|
|
.attr("aria-hidden", "true")
|
|
.html("×")
|
|
.click(function() {
|
|
that.$column_modal.modal("hide");
|
|
})
|
|
)
|
|
.append(
|
|
$("<h3></h3>")
|
|
.addClass("modal-title")
|
|
.attr("id", "dt-column-modal-label_" + randId)
|
|
.text("Toggle Columns")
|
|
);
|
|
|
|
// render the modal footer
|
|
this.$column_modalfooter = $("<div></div>")
|
|
.addClass("modal-footer")
|
|
.append(
|
|
// show the check 'all / none' columns
|
|
$('<div class="float-left"></div>').append(
|
|
$('<div class="btn-group"></div>').append(
|
|
$("<button></button>")
|
|
.addClass("btn btn-info")
|
|
.append(
|
|
$("<span></span>")
|
|
.addClass("fas fa-check")
|
|
.text("All")
|
|
)
|
|
.click(function() {
|
|
$(this)
|
|
.closest(".modal")
|
|
.find("button.on-off")
|
|
.each(function() {
|
|
if ($(this).data("column-hidden")) {
|
|
$(this).click();
|
|
}
|
|
});
|
|
return false;
|
|
}),
|
|
$("<button></button>")
|
|
.addClass("btn btn-warning")
|
|
.append(
|
|
$("<span></span>")
|
|
.addClass("glyphicon glyphicon-unchecked")
|
|
.text("None")
|
|
)
|
|
.click(function() {
|
|
$(this)
|
|
.closest(".modal")
|
|
.find("button.on-off")
|
|
.each(function() {
|
|
if (!$(this).data("column-hidden")) {
|
|
$(this).click();
|
|
}
|
|
});
|
|
return false;
|
|
})
|
|
)
|
|
),
|
|
|
|
o.allowSaveColumns
|
|
? $("<button></button>")
|
|
.addClass("btn btn-primary")
|
|
.text("Save")
|
|
.click(function() {
|
|
_saveColumns.call(that);
|
|
return false;
|
|
})
|
|
: "",
|
|
|
|
$("<button></button>")
|
|
.addClass("btn btn-secondary")
|
|
.data("dismiss", "modal")
|
|
.append($("<span></span>"))
|
|
.text("Close")
|
|
.click(function() {
|
|
that.$column_modal.modal("hide");
|
|
return false;
|
|
})
|
|
);
|
|
|
|
// render the modal body
|
|
this.$column_modalbody = $("<div></div>").addClass("modal-body");
|
|
|
|
this.$column_modaldialog = $("<div></div>")
|
|
.addClass("modal-dialog")
|
|
.append(
|
|
$("<div></div>")
|
|
.addClass("modal-content")
|
|
.append(
|
|
this.$column_modalheader,
|
|
this.$column_modalbody,
|
|
this.$column_modalfooter
|
|
)
|
|
);
|
|
|
|
// render and add the modal to the container
|
|
this.$column_modal
|
|
.append(this.$column_modaldialog)
|
|
.appendTo(document.body);
|
|
}
|
|
// render the display modal button
|
|
$toggle
|
|
.addClass("btn")
|
|
.data("toggle", "modal")
|
|
.data("content", "Choose which columns you would like to display.")
|
|
.data("target", "#" + this.$column_modal.attr("id"))
|
|
.attr("href", "#" + this.$column_modal.attr("id"))
|
|
.append($("<span></span>").addClass("fas fa-cog"))
|
|
.click(function(e) {
|
|
that.$column_modal
|
|
.on("show.bs.modal", function() {
|
|
if (o.debug) console.log(that);
|
|
_updateColumnModalBody.call(that, that.$column_modalbody);
|
|
})
|
|
.modal();
|
|
return false;
|
|
})
|
|
this.buttons.unshift($toggle);
|
|
|
|
if (o.debug) console.log($toggle);
|
|
|
|
return this.$column_modal;
|
|
}
|
|
|
|
function _initFilterModal() {
|
|
var o = this.options,
|
|
$e = this.$element,
|
|
$toggle = $("<a></a>");
|
|
|
|
// render the display modal button
|
|
$toggle
|
|
.addClass("btn")
|
|
.data("toggle", "modal")
|
|
.attr("href", "#")
|
|
.data("content", "Open the filter dialog.")
|
|
.extend($("<span></span>").addClass("fas fa-filter"))
|
|
.click(function() {
|
|
if ($(o.filterModal).hasClass("modal")) $(o.filterModal).modal();
|
|
else if ($(o.filterModal).is(":visible")) $(o.filterModal).hide();
|
|
else $(o.filterModal).show();
|
|
|
|
return false;
|
|
})
|
|
this.buttons.unshift($toggle);
|
|
}
|
|
|
|
function _initPerPage() {
|
|
var o = this.options,
|
|
$e = this.$element,
|
|
that = this;
|
|
|
|
// per page options and current filter/sorting
|
|
var $perpage_select = $("<div></div>")
|
|
.addClass("btn-group")
|
|
.append($("<button></button>")
|
|
.addClass("btn btn-link dropdown-toggle")
|
|
.data("content", i18n.change_number_of_rows + ".")
|
|
.attr("data-bs-toggle", "dropdown")
|
|
.html(o.perPage + " ")
|
|
.append($("<span></span>")
|
|
.addClass("caret")));
|
|
|
|
var $perpage_values = $("<ul></ul>")
|
|
.addClass("dropdown-menu scrollable-dropdown")
|
|
.attr("role", "menu")
|
|
.append(
|
|
$(
|
|
'<li data-value="10"><a class="dropdown-item" href="#">10</a></li>'
|
|
).click(function() {
|
|
_updatePerPage.call(this, that);
|
|
return false;
|
|
}),
|
|
$(
|
|
'<li data-value="20"><a class="dropdown-item" href="#">20</a></li>'
|
|
).click(function() {
|
|
_updatePerPage.call(this, that);
|
|
return false;
|
|
}),
|
|
$(
|
|
'<li data-value="50"><a class="dropdown-item" href="#">50</a></li>'
|
|
).click(function() {
|
|
_updatePerPage.call(this, that);
|
|
return false;
|
|
}),
|
|
$(
|
|
'<li data-value="100"><a class="dropdown-item" href="#">100</a></li>'
|
|
).click(function() {
|
|
_updatePerPage.call(this, that);
|
|
return false;
|
|
}),
|
|
$(
|
|
'<li data-value="200"><a class="dropdown-item" href="#">200</a></li>'
|
|
).click(function() {
|
|
_updatePerPage.call(this, that);
|
|
return false;
|
|
})
|
|
);
|
|
|
|
$perpage_select.append($perpage_values);
|
|
|
|
this.buttons.push($perpage_select);
|
|
}
|
|
|
|
function _initTableInfo() {
|
|
var o = this.options,
|
|
$e = this.$element,
|
|
$info = $("<a></a>");
|
|
|
|
// render the display modal button
|
|
$info
|
|
.addClass("btn")
|
|
.attr("href", "#")
|
|
.append($("<span></span>").addClass("fas fa-info-circle"))
|
|
.click(function() {
|
|
return false;
|
|
});
|
|
|
|
var $page_sort = [],
|
|
$page_filter = [];
|
|
|
|
// sort
|
|
$.each(o.sort, function(i, v) {
|
|
if (!v.length) return;
|
|
var heading;
|
|
for (var column in o.columns) {
|
|
if (o.columns[column].field == v[0]) heading = o.columns[column].title;
|
|
}
|
|
$page_sort.push(heading + " " + v[1].toUpperCase());
|
|
});
|
|
|
|
// filter
|
|
$.each(o.filter, function(k, v) {
|
|
var heading;
|
|
for (var column in o.columns) {
|
|
if (o.columns[column].field == k) heading = o.columns[column].title;
|
|
}
|
|
$page_filter.push((heading || k) + " = '" + v + "'");
|
|
});
|
|
$($info)
|
|
.data(
|
|
"content",
|
|
$("<dl></dl>").append(
|
|
$page_sort.length > 0
|
|
? '<dt><i class="icon-th-list"></i> Sort:</dt><dd>' +
|
|
$page_sort.join(", ") +
|
|
"</dd>"
|
|
: "",
|
|
$page_filter.length > 0
|
|
? '<dt><i class="icon-filter"></i> Filter:</dt><dd>' +
|
|
$page_filter.join(", ") +
|
|
"</dd>"
|
|
: ""
|
|
)
|
|
)
|
|
|
|
this.buttons.unshift($info);
|
|
}
|
|
|
|
function _initOverflowToggle() {
|
|
var o = this.options,
|
|
$wrapper = this.$wrapper,
|
|
$overflow = $("<a></a>");
|
|
|
|
// create the button
|
|
$overflow
|
|
.addClass("btn")
|
|
.attr("href", "#")
|
|
.attr(
|
|
"title",
|
|
"Toggle the size of the table to fit the data or to fit the screen."
|
|
)
|
|
.append($("<span></span>").addClass("glyphicon glyphicon-resize-full"))
|
|
.click(function() {
|
|
if ($wrapper) _toggleOverflow.call(this, $wrapper);
|
|
return false;
|
|
});
|
|
|
|
if (
|
|
!this.resultset ||
|
|
!this.resultset.data ||
|
|
this.resultset.data.length == 0
|
|
)
|
|
$overflow.addClass("disabled");
|
|
|
|
this.buttons.push($overflow);
|
|
}
|
|
|
|
function _toggleOverflow(el) {
|
|
if (el.css("overflow") == "scroll") {
|
|
$(this)
|
|
.children("span.glyphicon")
|
|
.attr("class", "glyphicon glyphicon-resize-full");
|
|
|
|
el.css({
|
|
overflow: "visible",
|
|
width: "auto"
|
|
});
|
|
} else {
|
|
$(this)
|
|
.children("span.glyphicon")
|
|
.attr("class", "glyphicon glyphicon-resize-small");
|
|
|
|
el.css({
|
|
overflow: "scroll",
|
|
width: el.width()
|
|
});
|
|
}
|
|
}
|
|
|
|
function _updatePerPage(that) {
|
|
var o = that.options;
|
|
|
|
// update the perpage value
|
|
o.perPage = $(this).data("value");
|
|
|
|
// the offset
|
|
var offset = o.currentPage * o.perPage;
|
|
while (offset > o.totalRows) {
|
|
o.currentPage--;
|
|
offset = o.currentPage * o.perPage;
|
|
}
|
|
if (o.currentPage < 1) o.currentPage = 1;
|
|
|
|
// update the table
|
|
that.render();
|
|
|
|
return false;
|
|
}
|
|
|
|
function showError(data, status, err_msg) {
|
|
var o = this.options,
|
|
$e = this.$element;
|
|
|
|
$e.empty();
|
|
|
|
// initialize the toolbar
|
|
_initToolbar.call(this);
|
|
|
|
// nearly complete... let the user apply any final adjustments
|
|
if (o.tableCallback && typeof o.tableCallback === "function")
|
|
o.tableCallback.call(this);
|
|
|
|
this.loading(false);
|
|
|
|
var custom_no_res = null;
|
|
|
|
if (o.noResultsCallback) custom_no_res = o.noResultsCallback(this, data);
|
|
|
|
if (custom_no_res) {
|
|
$e.append(custom_no_res);
|
|
} else if (err_msg && status != "abort") {
|
|
$e.append(
|
|
$("<div></div>")
|
|
.addClass("alert alert-danger")
|
|
.html(err_msg)
|
|
);
|
|
} else if (data && typeof data.error === "string") {
|
|
$e.append(
|
|
$("<div></div>")
|
|
.addClass("alert alert-danger")
|
|
.html(data.error)
|
|
);
|
|
} else if (o.noResultsMessage) {
|
|
var msg = o.noResultsMessage(this, data);
|
|
|
|
$e.append(
|
|
$("<div></div>")
|
|
.addClass("alert alert-danger")
|
|
.html(msg)
|
|
);
|
|
} else if (this.$default) $e.append(this.$default);
|
|
}
|
|
|
|
function runFilter(that) {
|
|
var o = that.options;
|
|
|
|
o.filter[$(this).data("filter")] = $(this).val();
|
|
if (o.debug) console.log(o.filter);
|
|
|
|
that.render();
|
|
}
|
|
|
|
function _updateColumnModalBody(body) {
|
|
var o = this.options,
|
|
$container = $("<form></form>").addClass("form-inline"),
|
|
that = this;
|
|
|
|
// loop through the columns
|
|
for (var column in o.columns) {
|
|
if (o.columns[column].title === "") continue;
|
|
var $item = $("<div></div></br>")
|
|
.addClass("form-group")
|
|
.append(
|
|
$("<label></label>")
|
|
.addClass("control-label")
|
|
.append(
|
|
o.columns[column].title,
|
|
|
|
$("<button></button>")
|
|
.addClass(
|
|
"on-off btn " +
|
|
(o.columns[column].hidden ? "btn-info" : "btn-warning")
|
|
)
|
|
.data("column", column)
|
|
.data("column-hidden", o.columns[column].hidden)
|
|
.text(o.columns[column].hidden ? "ON" : "OFF")
|
|
.click(function() {
|
|
_toggleColumn.call(this, that);
|
|
return false;
|
|
})
|
|
)
|
|
);
|
|
$container.append($item);
|
|
}
|
|
|
|
body.empty();
|
|
body.append($container);
|
|
}
|
|
|
|
function _toggleColumn(that) {
|
|
var o = that.options,
|
|
column = $(this).data("column"),
|
|
$column = $("." + o.columns[column].classname);
|
|
|
|
if ($column.is(":visible")) {
|
|
$column.hide();
|
|
o.columns[column].hidden = true;
|
|
$(this)
|
|
.removeClass("btn-warning")
|
|
.addClass("btn-info")
|
|
.text("ON")
|
|
.data("column-hidden", true);
|
|
} else {
|
|
$column.show();
|
|
o.columns[column].hidden = false;
|
|
$(this)
|
|
.removeClass("btn-info")
|
|
.addClass("btn-warning")
|
|
.text("OFF")
|
|
.data("column-hidden", false);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function _saveColumns() {
|
|
var o = this.options,
|
|
columns = [];
|
|
|
|
// save the columns to the localstorage
|
|
if (localStorage) {
|
|
localStorage[this.localStorageId] = JSON.stringify(o.columns);
|
|
}
|
|
|
|
$.ajax({
|
|
url: o.url,
|
|
type: "POST",
|
|
dataType: "json",
|
|
data: $.extend({}, o.post, {
|
|
action: "save-columns",
|
|
columns: JSON.stringify(o.columns),
|
|
sort: JSON.stringify(o.sort),
|
|
filter: JSON.stringify(o.filter)
|
|
}),
|
|
success: function(res) {
|
|
if (o.debug) console.log("columns saved");
|
|
}
|
|
});
|
|
|
|
this.$column_modal.modal("hide");
|
|
}
|
|
|
|
function _retrieveColumns(data) {
|
|
var o = this.options,
|
|
columns = data ? JSON.parse(data) : [],
|
|
res = this.resultset;
|
|
|
|
// if the server doesn't pass the column property back
|
|
if (!res.columns) res.columns = [];
|
|
|
|
for (column in o.columns) {
|
|
o.columns[column] = $.extend(
|
|
{},
|
|
o.columns[column],
|
|
res.columns[column],
|
|
columns[column]
|
|
);
|
|
}
|
|
}
|
|
|
|
/* DATATABLE PLUGIN DEFINITION
|
|
* =========================== */
|
|
|
|
$.fn.datatable = function(options) {
|
|
$.fn.datatable.init.call(this, options, DataTable, "datatable");
|
|
return this;
|
|
};
|
|
|
|
$.fn.datatable.init = function(options, Constructor, name) {
|
|
var datatable;
|
|
|
|
if (options === true) {
|
|
return this.data(name);
|
|
} else if (typeof options == "string") {
|
|
datatable = this.data(name);
|
|
if (datatable) {
|
|
datatable[options]();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
options = $.extend({}, $.fn[name].defaults, options);
|
|
|
|
function get(el) {
|
|
var datatable = $.data(el, name);
|
|
|
|
if (!datatable) {
|
|
datatable = new Constructor(
|
|
el,
|
|
$.fn.datatable.elementOptions(el, options)
|
|
);
|
|
$.data(el, name, datatable);
|
|
}
|
|
|
|
return datatable;
|
|
}
|
|
|
|
this.each(function() {
|
|
get(this);
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
$.fn.datatable.DataTable = DataTable;
|
|
|
|
$.fn.datatable.elementOptions = function(el, options) {
|
|
return $.metadata ? $.extend({}, options, $(el).metadata()) : options;
|
|
};
|
|
|
|
$.fn.datatable.defaults = {
|
|
debug: false,
|
|
id: undefined,
|
|
title: "Data Table Results",
|
|
class: "table table-striped table-bordered",
|
|
perPage: 10,
|
|
pagePadding: 2,
|
|
sort: [],
|
|
filter: {},
|
|
post: {},
|
|
buttons: [],
|
|
sectionHeader: undefined,
|
|
totalRows: 0,
|
|
currentPage: 1,
|
|
showPagination: true,
|
|
showTopPagination: false,
|
|
showHeader: true,
|
|
showFooter: false,
|
|
showFilterRow: false,
|
|
filterModal: undefined,
|
|
allowExport: false,
|
|
allowOverflow: true,
|
|
allowMultipleSort: false,
|
|
allowSaveColumns: true,
|
|
toggleColumns: true,
|
|
url: "",
|
|
columns: [],
|
|
ascending: $("<span></span>").addClass("fas fa-chevron-up"),
|
|
descending: $("<span></span>").addClass("fas fa-chevron-down"),
|
|
rowCallback: undefined,
|
|
tableCallback: undefined,
|
|
headerCallback: undefined,
|
|
footerCallback: undefined,
|
|
tablePreRender: undefined,
|
|
autoLoad: true
|
|
};
|
|
})(window.jQuery);
|