800 lines
32 KiB
JavaScript
800 lines
32 KiB
JavaScript
|
var $;
|
||
|
var dataTable;
|
||
|
export function setJQuery(jq) {
|
||
|
$ = jq;
|
||
|
dataTable = jq.fn.dataTable;
|
||
|
}
|
||
|
var StateRestore = /** @class */ (function () {
|
||
|
function StateRestore(settings, opts, identifier, state, isPreDefined, successCallback) {
|
||
|
if (state === void 0) { state = undefined; }
|
||
|
if (isPreDefined === void 0) { isPreDefined = false; }
|
||
|
if (successCallback === void 0) { successCallback = function () { return null; }; }
|
||
|
// Check that the required version of DataTables is included
|
||
|
if (!dataTable || !dataTable.versionCheck || !dataTable.versionCheck('1.10.0')) {
|
||
|
throw new Error('StateRestore requires DataTables 1.10 or newer');
|
||
|
}
|
||
|
// Check that Select is included
|
||
|
// eslint-disable-next-line no-extra-parens
|
||
|
if (!dataTable.Buttons) {
|
||
|
throw new Error('StateRestore requires Buttons');
|
||
|
}
|
||
|
var table = new dataTable.Api(settings);
|
||
|
this.classes = $.extend(true, {}, StateRestore.classes);
|
||
|
// Get options from user
|
||
|
this.c = $.extend(true, {}, StateRestore.defaults, opts);
|
||
|
this.s = {
|
||
|
dt: table,
|
||
|
identifier: identifier,
|
||
|
isPreDefined: isPreDefined,
|
||
|
savedState: null,
|
||
|
tableId: state && state.stateRestore ? state.stateRestore.tableId : undefined
|
||
|
};
|
||
|
this.dom = {
|
||
|
background: $('<div class="' + this.classes.background + '"/>'),
|
||
|
closeButton: $('<div class="' + this.classes.closeButton + '">×</div>'),
|
||
|
confirmation: $('<div class="' + this.classes.confirmation + '"/>'),
|
||
|
confirmationButton: $('<button class="' + this.classes.confirmationButton + ' ' + this.classes.dtButton + '">'),
|
||
|
confirmationTitleRow: $('<div class="' + this.classes.confirmationTitleRow + '"></div>'),
|
||
|
dtContainer: $(this.s.dt.table().container()),
|
||
|
duplicateError: $('<span class="' + this.classes.modalError + '">' +
|
||
|
this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError) +
|
||
|
'</span>'),
|
||
|
emptyError: $('<span class="' + this.classes.modalError + '">' +
|
||
|
this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError) +
|
||
|
'</span>'),
|
||
|
removeContents: $('<div class="' + this.classes.confirmationText + '"><span>' +
|
||
|
this.s.dt
|
||
|
.i18n('stateRestore.removeConfirm', this.c.i18n.removeConfirm)
|
||
|
.replace(/%s/g, StateRestore.entityEncode(this.s.identifier)) +
|
||
|
'</span></div>'),
|
||
|
removeError: $('<span class="' + this.classes.modalError + '">' +
|
||
|
this.s.dt.i18n('stateRestore.removeError', this.c.i18n.removeError) +
|
||
|
'</span>'),
|
||
|
removeTitle: $('<h2 class="' + this.classes.confirmationTitle + '">' +
|
||
|
this.s.dt.i18n('stateRestore.removeTitle', this.c.i18n.removeTitle) +
|
||
|
'</h2>'),
|
||
|
renameContents: $('<div class="' + this.classes.confirmationText + ' ' + this.classes.renameModal + '">' +
|
||
|
'<label class="' + this.classes.confirmationMessage + '">' +
|
||
|
this.s.dt
|
||
|
.i18n('stateRestore.renameLabel', this.c.i18n.renameLabel)
|
||
|
.replace(/%s/g, StateRestore.entityEncode(this.s.identifier)) +
|
||
|
'</label>' +
|
||
|
'</div>'),
|
||
|
renameInput: $('<input class="' + this.classes.input + '" type="text"></input>'),
|
||
|
renameTitle: $('<h2 class="' + this.classes.confirmationTitle + '">' +
|
||
|
this.s.dt.i18n('stateRestore.renameTitle', this.c.i18n.renameTitle) +
|
||
|
'</h2>')
|
||
|
};
|
||
|
// When a StateRestore instance is created the current state of the table should also be saved.
|
||
|
this.save(state, successCallback);
|
||
|
}
|
||
|
/**
|
||
|
* Removes a state from storage and then triggers the dtsr-remove event
|
||
|
* so that the StateRestoreCollection class can remove it's references as well.
|
||
|
*
|
||
|
* @param skipModal Flag to indicate if the modal should be skipped or not
|
||
|
*/
|
||
|
StateRestore.prototype.remove = function (skipModal) {
|
||
|
var _a;
|
||
|
var _this = this;
|
||
|
if (skipModal === void 0) { skipModal = false; }
|
||
|
// Check if removal of states is allowed
|
||
|
if (!this.c.remove) {
|
||
|
return false;
|
||
|
}
|
||
|
var removeFunction;
|
||
|
var ajaxData = {
|
||
|
action: 'remove',
|
||
|
stateRestore: (_a = {},
|
||
|
_a[this.s.identifier] = this.s.savedState,
|
||
|
_a)
|
||
|
};
|
||
|
var successCallback = function () {
|
||
|
_this.dom.confirmation.trigger('dtsr-remove');
|
||
|
$(_this.s.dt.table().node()).trigger('stateRestore-change');
|
||
|
_this.dom.background.click();
|
||
|
_this.dom.confirmation.remove();
|
||
|
$(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
|
||
|
_this.dom.confirmationButton.off('click');
|
||
|
};
|
||
|
// If the remove is not happening over ajax remove it from local storage and then trigger the event
|
||
|
if (!this.c.ajax) {
|
||
|
removeFunction = function () {
|
||
|
try {
|
||
|
localStorage.removeItem('DataTables_stateRestore_' + _this.s.identifier + '_' + location.pathname +
|
||
|
(_this.s.tableId ? '_' + _this.s.tableId : ''));
|
||
|
successCallback();
|
||
|
}
|
||
|
catch (e) {
|
||
|
_this.dom.confirmation.children('.' + _this.classes.modalError).remove();
|
||
|
_this.dom.confirmation.append(_this.dom.removeError);
|
||
|
return 'remove';
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
}
|
||
|
// Ajax property has to be a string, not just true
|
||
|
// Also only want to save if the table has been initialised and the states have been loaded in
|
||
|
else if (typeof this.c.ajax === 'string' && this.s.dt.settings()[0]._bInitComplete) {
|
||
|
removeFunction = function () {
|
||
|
$.ajax({
|
||
|
data: ajaxData,
|
||
|
success: successCallback,
|
||
|
type: 'POST',
|
||
|
url: _this.c.ajax
|
||
|
});
|
||
|
return true;
|
||
|
};
|
||
|
}
|
||
|
else if (typeof this.c.ajax === 'function') {
|
||
|
removeFunction = function () {
|
||
|
if (typeof _this.c.ajax === 'function') {
|
||
|
_this.c.ajax.call(_this.s.dt, ajaxData, successCallback);
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
}
|
||
|
// If the modal is to be skipped then remove straight away
|
||
|
if (skipModal) {
|
||
|
this.dom.confirmation.appendTo(this.dom.dtContainer);
|
||
|
$(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
|
||
|
removeFunction();
|
||
|
this.dom.confirmation.remove();
|
||
|
}
|
||
|
// Otherwise display the modal
|
||
|
else {
|
||
|
this._newModal(this.dom.removeTitle, this.s.dt.i18n('stateRestore.removeSubmit', this.c.i18n.removeSubmit), removeFunction, this.dom.removeContents);
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
/**
|
||
|
* Compares the state held within this instance with a state that is passed in
|
||
|
*
|
||
|
* @param state The state that is to be compared against
|
||
|
* @returns boolean indicating if the states match
|
||
|
*/
|
||
|
StateRestore.prototype.compare = function (state) {
|
||
|
// Order
|
||
|
if (!this.c.saveState.order) {
|
||
|
state.order = undefined;
|
||
|
}
|
||
|
// Search
|
||
|
if (!this.c.saveState.search) {
|
||
|
state.search = undefined;
|
||
|
}
|
||
|
// Columns
|
||
|
if (this.c.saveState.columns && state.columns) {
|
||
|
for (var i = 0, ien = state.columns.length; i < ien; i++) {
|
||
|
// Visibility
|
||
|
if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.visible) {
|
||
|
state.columns[i].visible = undefined;
|
||
|
}
|
||
|
// Search
|
||
|
if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.search) {
|
||
|
state.columns[i].search = undefined;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (!this.c.saveState.columns) {
|
||
|
state.columns = undefined;
|
||
|
}
|
||
|
// Paging
|
||
|
if (!this.c.saveState.paging) {
|
||
|
state.page = undefined;
|
||
|
}
|
||
|
// SearchBuilder
|
||
|
if (!this.c.saveState.searchBuilder) {
|
||
|
state.searchBuilder = undefined;
|
||
|
}
|
||
|
// SearchPanes
|
||
|
if (!this.c.saveState.searchPanes) {
|
||
|
state.searchPanes = undefined;
|
||
|
}
|
||
|
// Select
|
||
|
if (!this.c.saveState.select) {
|
||
|
state.select = undefined;
|
||
|
}
|
||
|
// ColReorder
|
||
|
if (!this.c.saveState.colReorder) {
|
||
|
state.ColReorder = undefined;
|
||
|
}
|
||
|
// Scroller
|
||
|
if (!this.c.saveState.scroller) {
|
||
|
state.scroller = undefined;
|
||
|
if (dataTable.Scroller !== undefined) {
|
||
|
state.start = 0;
|
||
|
}
|
||
|
}
|
||
|
// Paging
|
||
|
if (!this.c.saveState.paging) {
|
||
|
state.start = 0;
|
||
|
}
|
||
|
// Page Length
|
||
|
if (!this.c.saveState.length) {
|
||
|
state.length = undefined;
|
||
|
}
|
||
|
// Need to delete properties that we do not want to compare
|
||
|
delete state.time;
|
||
|
var copyState = this.s.savedState;
|
||
|
delete copyState.time;
|
||
|
delete copyState.c;
|
||
|
delete copyState.stateRestore;
|
||
|
// Perform a deep compare of the two state objects
|
||
|
return this._deepCompare(state, copyState);
|
||
|
};
|
||
|
/**
|
||
|
* Removes all of the dom elements from the document
|
||
|
*/
|
||
|
StateRestore.prototype.destroy = function () {
|
||
|
$.each(this.dom, function (name, el) {
|
||
|
el.off().remove();
|
||
|
});
|
||
|
};
|
||
|
/**
|
||
|
* Loads the state referenced by the identifier from storage
|
||
|
*
|
||
|
* @param state The identifier of the state that should be loaded
|
||
|
* @returns the state that has been loaded
|
||
|
*/
|
||
|
StateRestore.prototype.load = function () {
|
||
|
var _this = this;
|
||
|
var loadedState = this.s.savedState;
|
||
|
var settings = this.s.dt.settings()[0];
|
||
|
// Always want the states stored here to be loaded in - regardless of when they were created
|
||
|
loadedState.time = +new Date();
|
||
|
settings.oLoadedState = $.extend(true, {}, loadedState);
|
||
|
// Click on a background if there is one to shut the collection
|
||
|
$('div.dt-button-background').click();
|
||
|
var loaded = function () {
|
||
|
var correctPaging = function (e, preSettings) {
|
||
|
setTimeout(function () {
|
||
|
var currpage = preSettings._iDisplayStart / preSettings._iDisplayLength;
|
||
|
var intendedPage = loadedState.start / loadedState.length;
|
||
|
// If the paging is incorrect then we have to set it again so that it is correct
|
||
|
// This happens when a searchpanes filter is removed
|
||
|
// This has to happen in a timeout because searchpanes only deselects after a timeout
|
||
|
if (currpage >= 0 && intendedPage >= 0 && currpage !== intendedPage) {
|
||
|
_this.s.dt.page(intendedPage).draw(false);
|
||
|
}
|
||
|
}, 50);
|
||
|
};
|
||
|
_this.s.dt.one('preDraw', correctPaging);
|
||
|
_this.s.dt.draw(false);
|
||
|
};
|
||
|
// Call the internal datatables function to implement the state on the table
|
||
|
if (DataTable.versionCheck('2')) {
|
||
|
this.s.dt.state(loadedState);
|
||
|
loaded();
|
||
|
}
|
||
|
else {
|
||
|
// Legacy
|
||
|
DataTable.ext.oApi._fnImplementState(settings, loadedState, loaded);
|
||
|
}
|
||
|
return loadedState;
|
||
|
};
|
||
|
/**
|
||
|
* Shows a modal that allows a state to be renamed
|
||
|
*
|
||
|
* @param newIdentifier Optional. The new identifier for this state
|
||
|
*/
|
||
|
StateRestore.prototype.rename = function (newIdentifier, currentIdentifiers) {
|
||
|
var _this = this;
|
||
|
if (newIdentifier === void 0) { newIdentifier = null; }
|
||
|
// Check if renaming of states is allowed
|
||
|
if (!this.c.rename) {
|
||
|
return;
|
||
|
}
|
||
|
var renameFunction = function () {
|
||
|
var _a;
|
||
|
if (newIdentifier === null) {
|
||
|
var tempIdentifier = $('input.' + _this.classes.input.replace(/ /g, '.')).val();
|
||
|
if (tempIdentifier.length === 0) {
|
||
|
_this.dom.confirmation.children('.' + _this.classes.modalError).remove();
|
||
|
_this.dom.confirmation.append(_this.dom.emptyError);
|
||
|
return 'empty';
|
||
|
}
|
||
|
else if (currentIdentifiers.includes(tempIdentifier)) {
|
||
|
_this.dom.confirmation.children('.' + _this.classes.modalError).remove();
|
||
|
_this.dom.confirmation.append(_this.dom.duplicateError);
|
||
|
return 'duplicate';
|
||
|
}
|
||
|
else {
|
||
|
newIdentifier = tempIdentifier;
|
||
|
}
|
||
|
}
|
||
|
var ajaxData = {
|
||
|
action: 'rename',
|
||
|
stateRestore: (_a = {},
|
||
|
_a[_this.s.identifier] = newIdentifier,
|
||
|
_a)
|
||
|
};
|
||
|
var successCallback = function () {
|
||
|
_this.s.identifier = newIdentifier;
|
||
|
_this.save(_this.s.savedState, function () { return null; }, false);
|
||
|
_this.dom.removeContents = $('<div class="' + _this.classes.confirmationText + '"><span>' +
|
||
|
_this.s.dt
|
||
|
.i18n('stateRestore.removeConfirm', _this.c.i18n.removeConfirm)
|
||
|
.replace(/%s/g, _this.s.identifier) +
|
||
|
'</span></div>');
|
||
|
_this.dom.confirmation.trigger('dtsr-rename');
|
||
|
_this.dom.background.click();
|
||
|
_this.dom.confirmation.remove();
|
||
|
$(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
|
||
|
_this.dom.confirmationButton.off('click');
|
||
|
};
|
||
|
if (!_this.c.ajax) {
|
||
|
try {
|
||
|
localStorage.removeItem('DataTables_stateRestore_' + _this.s.identifier + '_' + location.pathname +
|
||
|
(_this.s.tableId ? '_' + _this.s.tableId : ''));
|
||
|
successCallback();
|
||
|
}
|
||
|
catch (e) {
|
||
|
_this.dom.confirmation.children('.' + _this.classes.modalError).remove();
|
||
|
_this.dom.confirmation.append(_this.dom.removeError);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else if (typeof _this.c.ajax === 'string' && _this.s.dt.settings()[0]._bInitComplete) {
|
||
|
$.ajax({
|
||
|
data: ajaxData,
|
||
|
success: successCallback,
|
||
|
type: 'POST',
|
||
|
url: _this.c.ajax
|
||
|
});
|
||
|
}
|
||
|
else if (typeof _this.c.ajax === 'function') {
|
||
|
_this.c.ajax.call(_this.s.dt, ajaxData, successCallback);
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
// Check if a new identifier has been provided, if so no need for a modal
|
||
|
if (newIdentifier !== null) {
|
||
|
if (currentIdentifiers.includes(newIdentifier)) {
|
||
|
throw new Error(this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError));
|
||
|
}
|
||
|
else if (newIdentifier.length === 0) {
|
||
|
throw new Error(this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError));
|
||
|
}
|
||
|
else {
|
||
|
this.dom.confirmation.appendTo(this.dom.dtContainer);
|
||
|
$(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
|
||
|
renameFunction();
|
||
|
this.dom.confirmation.remove();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
this.dom.renameInput.val(this.s.identifier);
|
||
|
this.dom.renameContents.append(this.dom.renameInput);
|
||
|
this._newModal(this.dom.renameTitle, this.s.dt.i18n('stateRestore.renameButton', this.c.i18n.renameButton), renameFunction, this.dom.renameContents);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Saves the tables current state using the identifier that is passed in.
|
||
|
*
|
||
|
* @param state Optional. If provided this is the state that will be saved rather than using the current state
|
||
|
*/
|
||
|
StateRestore.prototype.save = function (state, passedSuccessCallback, callAjax) {
|
||
|
var _a;
|
||
|
var _this = this;
|
||
|
if (callAjax === void 0) { callAjax = true; }
|
||
|
// Check if saving states is allowed
|
||
|
if (!this.c.save) {
|
||
|
if (passedSuccessCallback) {
|
||
|
passedSuccessCallback.call(this);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// this.s.dt.state.save();
|
||
|
var savedState;
|
||
|
// If no state has been provided then create a new one from the current state
|
||
|
this.s.dt.state.save();
|
||
|
if (state === undefined) {
|
||
|
savedState = this.s.dt.state();
|
||
|
}
|
||
|
else if (typeof state !== 'object') {
|
||
|
return;
|
||
|
}
|
||
|
else {
|
||
|
savedState = state;
|
||
|
}
|
||
|
if (savedState.stateRestore) {
|
||
|
savedState.stateRestore.isPreDefined = this.s.isPreDefined;
|
||
|
savedState.stateRestore.state = this.s.identifier;
|
||
|
savedState.stateRestore.tableId = this.s.tableId;
|
||
|
}
|
||
|
else {
|
||
|
savedState.stateRestore = {
|
||
|
isPreDefined: this.s.isPreDefined,
|
||
|
state: this.s.identifier,
|
||
|
tableId: this.s.tableId
|
||
|
};
|
||
|
}
|
||
|
this.s.savedState = savedState;
|
||
|
// Order
|
||
|
if (!this.c.saveState.order) {
|
||
|
this.s.savedState.order = undefined;
|
||
|
}
|
||
|
// Search
|
||
|
if (!this.c.saveState.search) {
|
||
|
this.s.savedState.search = undefined;
|
||
|
}
|
||
|
// Columns
|
||
|
if (this.c.saveState.columns && this.s.savedState.columns) {
|
||
|
for (var i = 0, ien = this.s.savedState.columns.length; i < ien; i++) {
|
||
|
// Visibility
|
||
|
if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.visible) {
|
||
|
this.s.savedState.columns[i].visible = undefined;
|
||
|
}
|
||
|
// Search
|
||
|
if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.search) {
|
||
|
this.s.savedState.columns[i].search = undefined;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (!this.c.saveState.columns) {
|
||
|
this.s.savedState.columns = undefined;
|
||
|
}
|
||
|
// SearchBuilder
|
||
|
if (!this.c.saveState.searchBuilder) {
|
||
|
this.s.savedState.searchBuilder = undefined;
|
||
|
}
|
||
|
// SearchPanes
|
||
|
if (!this.c.saveState.searchPanes) {
|
||
|
this.s.savedState.searchPanes = undefined;
|
||
|
}
|
||
|
// Select
|
||
|
if (!this.c.saveState.select) {
|
||
|
this.s.savedState.select = undefined;
|
||
|
}
|
||
|
// ColReorder
|
||
|
if (!this.c.saveState.colReorder) {
|
||
|
this.s.savedState.ColReorder = undefined;
|
||
|
}
|
||
|
// Scroller
|
||
|
if (!this.c.saveState.scroller) {
|
||
|
this.s.savedState.scroller = undefined;
|
||
|
if (dataTable.Scroller !== undefined) {
|
||
|
this.s.savedState.start = 0;
|
||
|
}
|
||
|
}
|
||
|
// Paging
|
||
|
if (!this.c.saveState.paging) {
|
||
|
this.s.savedState.start = 0;
|
||
|
}
|
||
|
// Page Length
|
||
|
if (!this.c.saveState.length) {
|
||
|
this.s.savedState.length = undefined;
|
||
|
}
|
||
|
this.s.savedState.c = this.c;
|
||
|
// Need to remove the parent reference before we save the state
|
||
|
// Its not needed to rebuild, but it does cause a circular reference when converting to JSON
|
||
|
if (this.s.savedState.c.splitSecondaries.length) {
|
||
|
for (var _i = 0, _b = this.s.savedState.c.splitSecondaries; _i < _b.length; _i++) {
|
||
|
var secondary = _b[_i];
|
||
|
if (secondary.parent) {
|
||
|
secondary.parent = undefined;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If the state is predefined there is no need to save it over ajax or to local storage
|
||
|
if (this.s.isPreDefined) {
|
||
|
if (passedSuccessCallback) {
|
||
|
passedSuccessCallback.call(this);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
var ajaxData = {
|
||
|
action: 'save',
|
||
|
stateRestore: (_a = {},
|
||
|
_a[this.s.identifier] = this.s.savedState,
|
||
|
_a)
|
||
|
};
|
||
|
var successCallback = function () {
|
||
|
if (passedSuccessCallback) {
|
||
|
passedSuccessCallback.call(_this);
|
||
|
}
|
||
|
_this.dom.confirmation.trigger('dtsr-save');
|
||
|
$(_this.s.dt.table().node()).trigger('stateRestore-change');
|
||
|
};
|
||
|
if (!this.c.ajax) {
|
||
|
localStorage.setItem('DataTables_stateRestore_' + this.s.identifier + '_' + location.pathname +
|
||
|
(this.s.tableId ? '_' + this.s.tableId : ''), JSON.stringify(this.s.savedState));
|
||
|
successCallback();
|
||
|
}
|
||
|
else if (typeof this.c.ajax === 'string' && callAjax) {
|
||
|
if (this.s.dt.settings()[0]._bInitComplete) {
|
||
|
$.ajax({
|
||
|
data: ajaxData,
|
||
|
success: successCallback,
|
||
|
type: 'POST',
|
||
|
url: this.c.ajax
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
this.s.dt.one('init', function () {
|
||
|
$.ajax({
|
||
|
data: ajaxData,
|
||
|
success: successCallback,
|
||
|
type: 'POST',
|
||
|
url: _this.c.ajax
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
else if (typeof this.c.ajax === 'function' && callAjax) {
|
||
|
this.c.ajax.call(this.s.dt, ajaxData, successCallback);
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Encode HTML entities
|
||
|
*
|
||
|
* @param d String to encode
|
||
|
* @returns Encoded string
|
||
|
* @todo When DT1 support is dropped, switch to using `DataTable.util.escapeHtml`
|
||
|
*/
|
||
|
StateRestore.entityEncode = function (d) {
|
||
|
return typeof d === 'string' ?
|
||
|
d
|
||
|
.replace(/&/g, '&')
|
||
|
.replace(/</g, '<')
|
||
|
.replace(/>/g, '>')
|
||
|
.replace(/"/g, '"') :
|
||
|
d;
|
||
|
};
|
||
|
/**
|
||
|
* Performs a deep compare of two state objects, returning true if they match
|
||
|
*
|
||
|
* @param state1 The first object to compare
|
||
|
* @param state2 The second object to compare
|
||
|
* @returns boolean indicating if the objects match
|
||
|
*/
|
||
|
StateRestore.prototype._deepCompare = function (state1, state2) {
|
||
|
// Put keys and states into arrays as this makes the later code easier to work
|
||
|
var states = [state1, state2];
|
||
|
var keys = [Object.keys(state1).sort(), Object.keys(state2).sort()];
|
||
|
var startIdx, i;
|
||
|
// If scroller is included then we need to remove the start value
|
||
|
// as it can be different but yield the same results
|
||
|
if (keys[0].includes('scroller')) {
|
||
|
startIdx = keys[0].indexOf('start');
|
||
|
if (startIdx) {
|
||
|
keys[0].splice(startIdx, 1);
|
||
|
}
|
||
|
}
|
||
|
if (keys[1].includes('scroller')) {
|
||
|
startIdx = keys[1].indexOf('start');
|
||
|
if (startIdx) {
|
||
|
keys[1].splice(startIdx, 1);
|
||
|
}
|
||
|
}
|
||
|
// We want to remove any private properties within the states
|
||
|
for (i = 0; i < keys[0].length; i++) {
|
||
|
if (keys[0][i].indexOf('_') === 0) {
|
||
|
keys[0].splice(i, 1);
|
||
|
i--;
|
||
|
continue;
|
||
|
}
|
||
|
// If scroller is included then we need to remove the following values
|
||
|
// as they can be different but yield the same results
|
||
|
if (keys[0][i] === 'baseRowTop' ||
|
||
|
keys[0][i] === 'baseScrollTop' ||
|
||
|
keys[0][i] === 'scrollTop' ||
|
||
|
(!this.c.saveState.paging && keys[0][i] === 'page')) {
|
||
|
keys[0].splice(i, 1);
|
||
|
i--;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < keys[1].length; i++) {
|
||
|
if (keys[1][i].indexOf('_') === 0) {
|
||
|
keys[1].splice(i, 1);
|
||
|
i--;
|
||
|
continue;
|
||
|
}
|
||
|
if (keys[1][i] === 'baseRowTop' ||
|
||
|
keys[1][i] === 'baseScrollTop' ||
|
||
|
keys[1][i] === 'scrollTop' ||
|
||
|
(!this.c.saveState.paging && keys[0][i] === 'page')) {
|
||
|
keys[1].splice(i, 1);
|
||
|
i--;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (keys[0].length === 0 && keys[1].length > 0 ||
|
||
|
keys[1].length === 0 && keys[0].length > 0) {
|
||
|
return false;
|
||
|
}
|
||
|
// We are only going to compare the keys that are common between both states
|
||
|
for (i = 0; i < keys[0].length; i++) {
|
||
|
if (!keys[1].includes(keys[0][i])) {
|
||
|
keys[0].splice(i, 1);
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < keys[1].length; i++) {
|
||
|
if (!keys[0].includes(keys[1][i])) {
|
||
|
keys[1].splice(i, 1);
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
// Then each key and value has to be checked against each other
|
||
|
for (i = 0; i < keys[0].length; i++) {
|
||
|
// If the keys dont equal, or their corresponding types are different we can return false
|
||
|
if (keys[0][i] !== keys[1][i] || typeof states[0][keys[0][i]] !== typeof states[1][keys[1][i]]) {
|
||
|
return false;
|
||
|
}
|
||
|
// If the type is an object then further deep comparisons are required
|
||
|
if (typeof states[0][keys[0][i]] === 'object') {
|
||
|
if (!this._deepCompare(states[0][keys[0][i]], states[1][keys[1][i]])) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else if (typeof states[0][keys[0][i]] === 'number' && typeof states[1][keys[1][i]] === 'number') {
|
||
|
if (Math.round(states[0][keys[0][i]]) !== Math.round(states[1][keys[1][i]])) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
// Otherwise we can just check the value
|
||
|
else if (states[0][keys[0][i]] !== states[1][keys[1][i]]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
// If we get all the way to here there are no differences so return true for this object
|
||
|
return true;
|
||
|
};
|
||
|
StateRestore.prototype._keyupFunction = function (e) {
|
||
|
// If enter same action as pressing the button
|
||
|
if (e.key === 'Enter') {
|
||
|
this.dom.confirmationButton.click();
|
||
|
}
|
||
|
// If escape close modal
|
||
|
else if (e.key === 'Escape') {
|
||
|
$('div.' + this.classes.background.replace(/ /g, '.')).click();
|
||
|
}
|
||
|
};
|
||
|
/**
|
||
|
* Creates a new confirmation modal for the user to approve an action
|
||
|
*
|
||
|
* @param title The title that is to be displayed at the top of the modal
|
||
|
* @param buttonText The text that is to be displayed in the confirmation button of the modal
|
||
|
* @param buttonAction The action that should be taken when the confirmation button is pressed
|
||
|
* @param modalContents The contents for the main body of the modal
|
||
|
*/
|
||
|
StateRestore.prototype._newModal = function (title, buttonText, buttonAction, modalContents) {
|
||
|
var _this = this;
|
||
|
this.dom.background.appendTo(this.dom.dtContainer);
|
||
|
this.dom.confirmationTitleRow.empty().append(title);
|
||
|
this.dom.confirmationButton.html(buttonText);
|
||
|
this.dom.confirmation
|
||
|
.empty()
|
||
|
.append(this.dom.confirmationTitleRow)
|
||
|
.append(modalContents)
|
||
|
.append($('<div class="' + this.classes.confirmationButtons + '"></div>')
|
||
|
.append(this.dom.confirmationButton))
|
||
|
.appendTo(this.dom.dtContainer);
|
||
|
$(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
|
||
|
var inputs = modalContents.children('input');
|
||
|
// If there is an input focus on that
|
||
|
if (inputs.length > 0) {
|
||
|
$(inputs[0]).focus();
|
||
|
}
|
||
|
// Otherwise focus on the confirmation button
|
||
|
else {
|
||
|
this.dom.confirmationButton.focus();
|
||
|
}
|
||
|
var background = $('div.' + this.classes.background.replace(/ /g, '.'));
|
||
|
if (this.c.modalCloseButton) {
|
||
|
this.dom.confirmation.append(this.dom.closeButton);
|
||
|
this.dom.closeButton.on('click', function () { return background.click(); });
|
||
|
}
|
||
|
// When the button is clicked, call the appropriate action,
|
||
|
// remove the background and modal from the screen and unbind the keyup event.
|
||
|
this.dom.confirmationButton.on('click', function () { return buttonAction(); });
|
||
|
this.dom.confirmation.on('click', function (e) {
|
||
|
e.stopPropagation();
|
||
|
});
|
||
|
// When the button is clicked, remove the background and modal from the screen and unbind the keyup event.
|
||
|
background.one('click', function () {
|
||
|
_this.dom.background.remove();
|
||
|
_this.dom.confirmation.remove();
|
||
|
$(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
|
||
|
});
|
||
|
$(document).on('keyup', function (e) { return _this._keyupFunction(e); });
|
||
|
};
|
||
|
StateRestore.version = '1.4.1';
|
||
|
StateRestore.classes = {
|
||
|
background: 'dtsr-background',
|
||
|
closeButton: 'dtsr-popover-close',
|
||
|
confirmation: 'dtsr-confirmation',
|
||
|
confirmationButton: 'dtsr-confirmation-button',
|
||
|
confirmationButtons: 'dtsr-confirmation-buttons',
|
||
|
confirmationMessage: 'dtsr-confirmation-message dtsr-name-label',
|
||
|
confirmationText: 'dtsr-confirmation-text',
|
||
|
confirmationTitle: 'dtsr-confirmation-title',
|
||
|
confirmationTitleRow: 'dtsr-confirmation-title-row',
|
||
|
dtButton: 'dt-button',
|
||
|
input: 'dtsr-input',
|
||
|
modalError: 'dtsr-modal-error',
|
||
|
renameModal: 'dtsr-rename-modal'
|
||
|
};
|
||
|
StateRestore.defaults = {
|
||
|
_createInSaved: false,
|
||
|
ajax: false,
|
||
|
create: true,
|
||
|
creationModal: false,
|
||
|
i18n: {
|
||
|
creationModal: {
|
||
|
button: 'Create',
|
||
|
colReorder: 'Column Order:',
|
||
|
columns: {
|
||
|
search: 'Column Search:',
|
||
|
visible: 'Column Visibility:'
|
||
|
},
|
||
|
length: 'Page Length:',
|
||
|
name: 'Name:',
|
||
|
order: 'Sorting:',
|
||
|
paging: 'Paging:',
|
||
|
scroller: 'Scroll Position:',
|
||
|
search: 'Search:',
|
||
|
searchBuilder: 'SearchBuilder:',
|
||
|
searchPanes: 'SearchPanes:',
|
||
|
select: 'Select:',
|
||
|
title: 'Create New State',
|
||
|
toggleLabel: 'Includes:'
|
||
|
},
|
||
|
duplicateError: 'A state with this name already exists.',
|
||
|
emptyError: 'Name cannot be empty.',
|
||
|
emptyStates: 'No saved states',
|
||
|
removeConfirm: 'Are you sure you want to remove %s?',
|
||
|
removeError: 'Failed to remove state.',
|
||
|
removeJoiner: ' and ',
|
||
|
removeSubmit: 'Remove',
|
||
|
removeTitle: 'Remove State',
|
||
|
renameButton: 'Rename',
|
||
|
renameLabel: 'New Name for %s:',
|
||
|
renameTitle: 'Rename State'
|
||
|
},
|
||
|
modalCloseButton: true,
|
||
|
remove: true,
|
||
|
rename: true,
|
||
|
save: true,
|
||
|
saveState: {
|
||
|
colReorder: true,
|
||
|
columns: {
|
||
|
search: true,
|
||
|
visible: true
|
||
|
},
|
||
|
length: true,
|
||
|
order: true,
|
||
|
paging: true,
|
||
|
scroller: true,
|
||
|
search: true,
|
||
|
searchBuilder: true,
|
||
|
searchPanes: true,
|
||
|
select: true
|
||
|
},
|
||
|
splitSecondaries: [
|
||
|
'updateState',
|
||
|
'renameState',
|
||
|
'removeState'
|
||
|
],
|
||
|
toggle: {
|
||
|
colReorder: false,
|
||
|
columns: {
|
||
|
search: false,
|
||
|
visible: false
|
||
|
},
|
||
|
length: false,
|
||
|
order: false,
|
||
|
paging: false,
|
||
|
scroller: false,
|
||
|
search: false,
|
||
|
searchBuilder: false,
|
||
|
searchPanes: false,
|
||
|
select: false
|
||
|
}
|
||
|
};
|
||
|
return StateRestore;
|
||
|
}());
|
||
|
export default StateRestore;
|