mirror of
https://github.com/tuxis-ie/nsedit.git
synced 2025-04-20 20:13:40 +03:00
1290 lines
49 KiB
JavaScript
1290 lines
49 KiB
JavaScript
/************************************************************************
|
|
* CORE jTable module *
|
|
*************************************************************************/
|
|
(function ($) {
|
|
|
|
var unloadingPage;
|
|
|
|
$(window).on('beforeunload', function () {
|
|
unloadingPage = true;
|
|
});
|
|
$(window).on('unload', function () {
|
|
unloadingPage = false;
|
|
});
|
|
|
|
$.widget("hik.jtable", {
|
|
|
|
/************************************************************************
|
|
* DEFAULT OPTIONS / EVENTS *
|
|
*************************************************************************/
|
|
options: {
|
|
|
|
//Options
|
|
actions: {},
|
|
fields: {},
|
|
animationsEnabled: true,
|
|
defaultDateFormat: 'yy-mm-dd',
|
|
dialogShowEffect: 'fade',
|
|
dialogHideEffect: 'fade',
|
|
showCloseButton: false,
|
|
loadingAnimationDelay: 500,
|
|
saveUserPreferences: true,
|
|
jqueryuiTheme: false,
|
|
unAuthorizedRequestRedirectUrl: null,
|
|
|
|
ajaxSettings: {
|
|
type: 'POST',
|
|
dataType: 'json'
|
|
},
|
|
|
|
toolbar: {
|
|
hoverAnimation: true,
|
|
hoverAnimationDuration: 60,
|
|
hoverAnimationEasing: undefined,
|
|
items: []
|
|
},
|
|
|
|
//Events
|
|
closeRequested: function (event, data) { },
|
|
formCreated: function (event, data) { },
|
|
formSubmitting: function (event, data) { },
|
|
formClosed: function (event, data) { },
|
|
loadingRecords: function (event, data) { },
|
|
recordsLoaded: function (event, data) { },
|
|
rowInserted: function (event, data) { },
|
|
rowsRemoved: function (event, data) { },
|
|
|
|
//Localization
|
|
messages: {
|
|
serverCommunicationError: 'An error occured while communicating to the server.',
|
|
loadingMessage: 'Loading records...',
|
|
noDataAvailable: 'No data available!',
|
|
areYouSure: 'Are you sure?',
|
|
save: 'Save',
|
|
saving: 'Saving',
|
|
cancel: 'Cancel',
|
|
error: 'Error',
|
|
close: 'Close',
|
|
cannotLoadOptionsFor: 'Can not load options for field {0}'
|
|
}
|
|
},
|
|
|
|
/************************************************************************
|
|
* PRIVATE FIELDS *
|
|
*************************************************************************/
|
|
|
|
_$mainContainer: null, //Reference to the main container of all elements that are created by this plug-in (jQuery object)
|
|
|
|
_$titleDiv: null, //Reference to the title div (jQuery object)
|
|
_$toolbarDiv: null, //Reference to the toolbar div (jQuery object)
|
|
|
|
_$table: null, //Reference to the main <table> (jQuery object)
|
|
_$tableBody: null, //Reference to <body> in the table (jQuery object)
|
|
_$tableRows: null, //Array of all <tr> in the table (except "no data" row) (jQuery object array)
|
|
|
|
_$busyDiv: null, //Reference to the div that is used to block UI while busy (jQuery object)
|
|
_$busyMessageDiv: null, //Reference to the div that is used to show some message when UI is blocked (jQuery object)
|
|
_$errorDialogDiv: null, //Reference to the error dialog div (jQuery object)
|
|
|
|
_columnList: null, //Name of all data columns in the table (select column and command columns are not included) (string array)
|
|
_fieldList: null, //Name of all fields of a record (defined in fields option) (string array)
|
|
_keyField: null, //Name of the key field of a record (that is defined as 'key: true' in the fields option) (string)
|
|
|
|
_firstDataColumnOffset: 0, //Start index of first record field in table columns (some columns can be placed before first data column, such as select checkbox column) (integer)
|
|
_lastPostData: null, //Last posted data on load method (object)
|
|
|
|
_cache: null, //General purpose cache dictionary (object)
|
|
|
|
/************************************************************************
|
|
* CONSTRUCTOR AND INITIALIZATION METHODS *
|
|
*************************************************************************/
|
|
|
|
/* Contructor.
|
|
*************************************************************************/
|
|
_create: function () {
|
|
|
|
//Initialization
|
|
this._normalizeFieldsOptions();
|
|
this._initializeFields();
|
|
this._createFieldAndColumnList();
|
|
|
|
//Creating DOM elements
|
|
this._createMainContainer();
|
|
this._createTableTitle();
|
|
this._createToolBar();
|
|
this._createTable();
|
|
this._createBusyPanel();
|
|
this._createErrorDialogDiv();
|
|
this._addNoDataRow();
|
|
|
|
this._cookieKeyPrefix = this._generateCookieKeyPrefix();
|
|
},
|
|
|
|
/* Normalizes some options for all fields (sets default values).
|
|
*************************************************************************/
|
|
_normalizeFieldsOptions: function () {
|
|
var self = this;
|
|
$.each(self.options.fields, function (fieldName, props) {
|
|
self._normalizeFieldOptions(fieldName, props);
|
|
});
|
|
},
|
|
|
|
/* Normalizes some options for a field (sets default values).
|
|
*************************************************************************/
|
|
_normalizeFieldOptions: function (fieldName, props) {
|
|
if (props.listClass == undefined) {
|
|
props.listClass = '';
|
|
}
|
|
if (props.inputClass == undefined) {
|
|
props.inputClass = '';
|
|
}
|
|
if (props.placeholder == undefined) {
|
|
props.placeholder = '';
|
|
}
|
|
|
|
//Convert dependsOn to array if it's a comma seperated lists
|
|
if (props.dependsOn && $.type(props.dependsOn) === 'string') {
|
|
var dependsOnArray = props.dependsOn.split(',');
|
|
props.dependsOn = [];
|
|
for (var i = 0; i < dependsOnArray.length; i++) {
|
|
props.dependsOn.push($.trim(dependsOnArray[i]));
|
|
}
|
|
}
|
|
},
|
|
|
|
/* Intializes some private variables.
|
|
*************************************************************************/
|
|
_initializeFields: function () {
|
|
this._lastPostData = {};
|
|
this._$tableRows = [];
|
|
this._columnList = [];
|
|
this._fieldList = [];
|
|
this._cache = [];
|
|
},
|
|
|
|
/* Fills _fieldList, _columnList arrays and sets _keyField variable.
|
|
*************************************************************************/
|
|
_createFieldAndColumnList: function () {
|
|
var self = this;
|
|
|
|
$.each(self.options.fields, function (name, props) {
|
|
|
|
//Add field to the field list
|
|
self._fieldList.push(name);
|
|
|
|
//Check if this field is the key field
|
|
if (props.key == true) {
|
|
self._keyField = name;
|
|
}
|
|
|
|
//Add field to column list if it is shown in the table
|
|
if (props.list != false && props.type != 'hidden') {
|
|
self._columnList.push(name);
|
|
}
|
|
});
|
|
},
|
|
|
|
/* Creates the main container div.
|
|
*************************************************************************/
|
|
_createMainContainer: function () {
|
|
this._$mainContainer = $('<div />')
|
|
.addClass('jtable-main-container')
|
|
.appendTo(this.element);
|
|
|
|
this._jqueryuiThemeAddClass(this._$mainContainer, 'ui-widget');
|
|
},
|
|
|
|
/* Creates title of the table if a title supplied in options.
|
|
*************************************************************************/
|
|
_createTableTitle: function () {
|
|
var self = this;
|
|
|
|
if (!self.options.title) {
|
|
return;
|
|
}
|
|
|
|
var $titleDiv = $('<div />')
|
|
.addClass('jtable-title')
|
|
.appendTo(self._$mainContainer);
|
|
|
|
self._jqueryuiThemeAddClass($titleDiv, 'ui-widget-header');
|
|
|
|
$('<div />')
|
|
.addClass('jtable-title-text')
|
|
.appendTo($titleDiv)
|
|
.append(self.options.title);
|
|
|
|
if (self.options.showCloseButton) {
|
|
|
|
var $textSpan = $('<span />')
|
|
.html(self.options.messages.close);
|
|
|
|
$('<button></button>')
|
|
.addClass('jtable-command-button jtable-close-button')
|
|
.attr('title', self.options.messages.close)
|
|
.append($textSpan)
|
|
.appendTo($titleDiv)
|
|
.click(function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
self._onCloseRequested();
|
|
});
|
|
}
|
|
|
|
self._$titleDiv = $titleDiv;
|
|
},
|
|
|
|
/* Creates the table.
|
|
*************************************************************************/
|
|
_createTable: function () {
|
|
this._$table = $('<table></table>')
|
|
.addClass('jtable')
|
|
.appendTo(this._$mainContainer);
|
|
|
|
if (this.options.tableId) {
|
|
this._$table.attr('id', this.options.tableId);
|
|
}
|
|
|
|
this._jqueryuiThemeAddClass(this._$table, 'ui-widget-content');
|
|
|
|
this._createTableHead();
|
|
this._createTableBody();
|
|
},
|
|
|
|
/* Creates header (all column headers) of the table.
|
|
*************************************************************************/
|
|
_createTableHead: function () {
|
|
var $thead = $('<thead></thead>')
|
|
.appendTo(this._$table);
|
|
|
|
this._addRowToTableHead($thead);
|
|
},
|
|
|
|
/* Adds tr element to given thead element
|
|
*************************************************************************/
|
|
_addRowToTableHead: function ($thead) {
|
|
var $tr = $('<tr></tr>')
|
|
.appendTo($thead);
|
|
|
|
this._addColumnsToHeaderRow($tr);
|
|
},
|
|
|
|
/* Adds column header cells to given tr element.
|
|
*************************************************************************/
|
|
_addColumnsToHeaderRow: function ($tr) {
|
|
for (var i = 0; i < this._columnList.length; i++) {
|
|
var fieldName = this._columnList[i];
|
|
var $headerCell = this._createHeaderCellForField(fieldName, this.options.fields[fieldName]);
|
|
$headerCell.appendTo($tr);
|
|
}
|
|
},
|
|
|
|
/* Creates a header cell for given field.
|
|
* Returns th jQuery object.
|
|
*************************************************************************/
|
|
_createHeaderCellForField: function (fieldName, field) {
|
|
field.width = field.width || '10%'; //default column width: 10%.
|
|
|
|
var $headerTextSpan = $('<span />')
|
|
.addClass('jtable-column-header-text')
|
|
.html(field.title);
|
|
|
|
var $headerContainerDiv = $('<div />')
|
|
.addClass('jtable-column-header-container')
|
|
.append($headerTextSpan);
|
|
|
|
var $th = $('<th></th>')
|
|
.addClass('jtable-column-header')
|
|
.addClass(field.listClass)
|
|
.css('width', field.width)
|
|
.data('fieldName', fieldName)
|
|
.append($headerContainerDiv);
|
|
|
|
this._jqueryuiThemeAddClass($th, 'ui-state-default');
|
|
|
|
return $th;
|
|
},
|
|
|
|
/* Creates an empty header cell that can be used as command column headers.
|
|
*************************************************************************/
|
|
_createEmptyCommandHeader: function () {
|
|
var $th = $('<th></th>')
|
|
.addClass('jtable-command-column-header')
|
|
.css('width', '1%');
|
|
|
|
this._jqueryuiThemeAddClass($th, 'ui-state-default');
|
|
|
|
return $th;
|
|
},
|
|
|
|
/* Creates tbody tag and adds to the table.
|
|
*************************************************************************/
|
|
_createTableBody: function () {
|
|
this._$tableBody = $('<tbody></tbody>').appendTo(this._$table);
|
|
},
|
|
|
|
/* Creates a div to block UI while jTable is busy.
|
|
*************************************************************************/
|
|
_createBusyPanel: function () {
|
|
this._$busyMessageDiv = $('<div />').addClass('jtable-busy-message').prependTo(this._$mainContainer);
|
|
this._$busyDiv = $('<div />').addClass('jtable-busy-panel-background').prependTo(this._$mainContainer);
|
|
this._jqueryuiThemeAddClass(this._$busyMessageDiv, 'ui-widget-header');
|
|
this._hideBusy();
|
|
},
|
|
|
|
/* Creates and prepares error dialog div.
|
|
*************************************************************************/
|
|
_createErrorDialogDiv: function () {
|
|
var self = this;
|
|
|
|
self._$errorDialogDiv = $('<div></div>').appendTo(self._$mainContainer);
|
|
self._$errorDialogDiv.dialog({
|
|
autoOpen: false,
|
|
show: self.options.dialogShowEffect,
|
|
hide: self.options.dialogHideEffect,
|
|
modal: true,
|
|
title: self.options.messages.error,
|
|
buttons: [{
|
|
text: self.options.messages.close,
|
|
click: function () {
|
|
self._$errorDialogDiv.dialog('close');
|
|
}
|
|
}]
|
|
});
|
|
},
|
|
|
|
/************************************************************************
|
|
* PUBLIC METHODS *
|
|
*************************************************************************/
|
|
|
|
/* Loads data using AJAX call, clears table and fills with new data.
|
|
*************************************************************************/
|
|
load: function (postData, completeCallback) {
|
|
this._lastPostData = postData;
|
|
this._reloadTable(completeCallback);
|
|
},
|
|
|
|
/* Refreshes (re-loads) table data with last postData.
|
|
*************************************************************************/
|
|
reload: function (completeCallback) {
|
|
this._reloadTable(completeCallback);
|
|
},
|
|
|
|
/* Gets a jQuery row object according to given record key
|
|
*************************************************************************/
|
|
getRowByKey: function (key) {
|
|
for (var i = 0; i < this._$tableRows.length; i++) {
|
|
if (key == this._getKeyValueOfRecord(this._$tableRows[i].data('record'))) {
|
|
return this._$tableRows[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/* Completely removes the table from it's container.
|
|
*************************************************************************/
|
|
destroy: function () {
|
|
this.element.empty();
|
|
$.Widget.prototype.destroy.call(this);
|
|
},
|
|
|
|
/************************************************************************
|
|
* PRIVATE METHODS *
|
|
*************************************************************************/
|
|
|
|
/* Used to change options dynamically after initialization.
|
|
*************************************************************************/
|
|
_setOption: function (key, value) {
|
|
|
|
},
|
|
|
|
/* LOADING RECORDS *****************************************************/
|
|
|
|
/* Performs an AJAX call to reload data of the table.
|
|
*************************************************************************/
|
|
_reloadTable: function (completeCallback) {
|
|
var self = this;
|
|
|
|
var completeReload = function(data) {
|
|
self._hideBusy();
|
|
|
|
//Show the error message if server returns error
|
|
if (data.Result != 'OK') {
|
|
self._showError(data.Message);
|
|
return;
|
|
}
|
|
|
|
//Re-generate table rows
|
|
self._removeAllRows('reloading');
|
|
self._addRecordsToTable(data.Records);
|
|
|
|
self._onRecordsLoaded(data);
|
|
|
|
//Call complete callback
|
|
if (completeCallback) {
|
|
completeCallback();
|
|
}
|
|
};
|
|
|
|
self._showBusy(self.options.messages.loadingMessage, self.options.loadingAnimationDelay); //Disable table since it's busy
|
|
self._onLoadingRecords();
|
|
|
|
//listAction may be a function, check if it is
|
|
if ($.isFunction(self.options.actions.listAction)) {
|
|
|
|
//Execute the function
|
|
var funcResult = self.options.actions.listAction(self._lastPostData, self._createJtParamsForLoading());
|
|
|
|
//Check if result is a jQuery Deferred object
|
|
if (self._isDeferredObject(funcResult)) {
|
|
funcResult.done(function(data) {
|
|
completeReload(data);
|
|
}).fail(function() {
|
|
self._showError(self.options.messages.serverCommunicationError);
|
|
}).always(function() {
|
|
self._hideBusy();
|
|
});
|
|
} else { //assume it's the data we're loading
|
|
completeReload(funcResult);
|
|
}
|
|
|
|
} else { //assume listAction as URL string.
|
|
|
|
//Generate URL (with query string parameters) to load records
|
|
var loadUrl = self._createRecordLoadUrl();
|
|
|
|
//Load data from server using AJAX
|
|
self._ajax({
|
|
url: loadUrl,
|
|
data: self._lastPostData,
|
|
success: function (data) {
|
|
completeReload(data);
|
|
},
|
|
error: function () {
|
|
self._hideBusy();
|
|
self._showError(self.options.messages.serverCommunicationError);
|
|
}
|
|
});
|
|
|
|
}
|
|
},
|
|
|
|
/* Creates URL to load records.
|
|
*************************************************************************/
|
|
_createRecordLoadUrl: function () {
|
|
return this.options.actions.listAction;
|
|
},
|
|
|
|
_createJtParamsForLoading: function() {
|
|
return {
|
|
//Empty as default, paging, sorting or other extensions can override this method to add additional params to load request
|
|
};
|
|
},
|
|
|
|
/* TABLE MANIPULATION METHODS *******************************************/
|
|
|
|
/* Creates a row from given record
|
|
*************************************************************************/
|
|
_createRowFromRecord: function (record) {
|
|
var $tr = $('<tr></tr>')
|
|
.addClass('jtable-data-row')
|
|
.attr('data-record-key', this._getKeyValueOfRecord(record))
|
|
.data('record', record);
|
|
|
|
this._addCellsToRowUsingRecord($tr);
|
|
return $tr;
|
|
},
|
|
|
|
/* Adds all cells to given row.
|
|
*************************************************************************/
|
|
_addCellsToRowUsingRecord: function ($row) {
|
|
var record = $row.data('record');
|
|
for (var i = 0; i < this._columnList.length; i++) {
|
|
this._createCellForRecordField(record, this._columnList[i])
|
|
.appendTo($row);
|
|
}
|
|
},
|
|
|
|
/* Create a cell for given field.
|
|
*************************************************************************/
|
|
_createCellForRecordField: function (record, fieldName) {
|
|
return $('<td></td>')
|
|
.addClass(this.options.fields[fieldName].listClass)
|
|
.append((this._getDisplayTextForRecordField(record, fieldName)));
|
|
},
|
|
|
|
/* Adds a list of records to the table.
|
|
*************************************************************************/
|
|
_addRecordsToTable: function (records) {
|
|
var self = this;
|
|
|
|
$.each(records, function (index, record) {
|
|
self._addRow(self._createRowFromRecord(record));
|
|
});
|
|
|
|
self._refreshRowStyles();
|
|
},
|
|
|
|
/* Adds a single row to the table.
|
|
* NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
|
|
* USE _addRow METHOD.
|
|
*************************************************************************/
|
|
_addRowToTable: function ($tableRow, index, isNewRow, animationsEnabled) {
|
|
var options = {
|
|
index: this._normalizeNumber(index, 0, this._$tableRows.length, this._$tableRows.length)
|
|
};
|
|
|
|
if (isNewRow == true) {
|
|
options.isNewRow = true;
|
|
}
|
|
|
|
if (animationsEnabled == false) {
|
|
options.animationsEnabled = false;
|
|
}
|
|
|
|
this._addRow($tableRow, options);
|
|
},
|
|
|
|
/* Adds a single row to the table.
|
|
*************************************************************************/
|
|
_addRow: function ($row, options) {
|
|
//Set defaults
|
|
options = $.extend({
|
|
index: this._$tableRows.length,
|
|
isNewRow: false,
|
|
animationsEnabled: true
|
|
}, options);
|
|
|
|
//Remove 'no data' row if this is first row
|
|
if (this._$tableRows.length <= 0) {
|
|
this._removeNoDataRow();
|
|
}
|
|
|
|
//Add new row to the table according to it's index
|
|
options.index = this._normalizeNumber(options.index, 0, this._$tableRows.length, this._$tableRows.length);
|
|
if (options.index == this._$tableRows.length) {
|
|
//add as last row
|
|
this._$tableBody.append($row);
|
|
this._$tableRows.push($row);
|
|
} else if (options.index == 0) {
|
|
//add as first row
|
|
this._$tableBody.prepend($row);
|
|
this._$tableRows.unshift($row);
|
|
} else {
|
|
//insert to specified index
|
|
this._$tableRows[options.index - 1].after($row);
|
|
this._$tableRows.splice(options.index, 0, $row);
|
|
}
|
|
|
|
this._onRowInserted($row, options.isNewRow);
|
|
|
|
//Show animation if needed
|
|
if (options.isNewRow) {
|
|
this._refreshRowStyles();
|
|
if (this.options.animationsEnabled && options.animationsEnabled) {
|
|
this._showNewRowAnimation($row);
|
|
}
|
|
}
|
|
},
|
|
|
|
/* Shows created animation for a table row
|
|
* TODO: Make this animation cofigurable and changable
|
|
*************************************************************************/
|
|
_showNewRowAnimation: function ($tableRow) {
|
|
var className = 'jtable-row-created';
|
|
if (this.options.jqueryuiTheme) {
|
|
className = className + ' ui-state-highlight';
|
|
}
|
|
|
|
$tableRow.addClass(className, 'slow', '', function () {
|
|
$tableRow.removeClass(className, 5000);
|
|
});
|
|
},
|
|
|
|
/* Removes a row or rows (jQuery selection) from table.
|
|
*************************************************************************/
|
|
_removeRowsFromTable: function ($rows, reason) {
|
|
var self = this;
|
|
|
|
//Check if any row specified
|
|
if ($rows.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
//remove from DOM
|
|
$rows.addClass('jtable-row-removed').remove();
|
|
|
|
//remove from _$tableRows array
|
|
$rows.each(function () {
|
|
var index = self._findRowIndex($(this));
|
|
if (index >= 0) {
|
|
self._$tableRows.splice(index, 1);
|
|
}
|
|
});
|
|
|
|
self._onRowsRemoved($rows, reason);
|
|
|
|
//Add 'no data' row if all rows removed from table
|
|
if (self._$tableRows.length == 0) {
|
|
self._addNoDataRow();
|
|
}
|
|
|
|
self._refreshRowStyles();
|
|
},
|
|
|
|
/* Finds index of a row in table.
|
|
*************************************************************************/
|
|
_findRowIndex: function ($row) {
|
|
return this._findIndexInArray($row, this._$tableRows, function ($row1, $row2) {
|
|
return $row1.data('record') == $row2.data('record');
|
|
});
|
|
},
|
|
|
|
/* Removes all rows in the table and adds 'no data' row.
|
|
*************************************************************************/
|
|
_removeAllRows: function (reason) {
|
|
//If no rows does exists, do nothing
|
|
if (this._$tableRows.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
//Select all rows (to pass it on raising _onRowsRemoved event)
|
|
var $rows = this._$tableBody.find('tr.jtable-data-row');
|
|
|
|
//Remove all rows from DOM and the _$tableRows array
|
|
this._$tableBody.empty();
|
|
this._$tableRows = [];
|
|
|
|
this._onRowsRemoved($rows, reason);
|
|
|
|
//Add 'no data' row since we removed all rows
|
|
this._addNoDataRow();
|
|
},
|
|
|
|
/* Adds "no data available" row to the table.
|
|
*************************************************************************/
|
|
_addNoDataRow: function () {
|
|
if (this._$tableBody.find('>tr.jtable-no-data-row').length > 0) {
|
|
return;
|
|
}
|
|
|
|
var $tr = $('<tr></tr>')
|
|
.addClass('jtable-no-data-row')
|
|
.appendTo(this._$tableBody);
|
|
|
|
var totalColumnCount = this._$table.find('thead th').length;
|
|
$('<td></td>')
|
|
.attr('colspan', totalColumnCount)
|
|
.html(this.options.messages.noDataAvailable)
|
|
.appendTo($tr);
|
|
},
|
|
|
|
/* Removes "no data available" row from the table.
|
|
*************************************************************************/
|
|
_removeNoDataRow: function () {
|
|
this._$tableBody.find('.jtable-no-data-row').remove();
|
|
},
|
|
|
|
/* Refreshes styles of all rows in the table
|
|
*************************************************************************/
|
|
_refreshRowStyles: function () {
|
|
for (var i = 0; i < this._$tableRows.length; i++) {
|
|
if (i % 2 == 0) {
|
|
this._$tableRows[i].addClass('jtable-row-even');
|
|
} else {
|
|
this._$tableRows[i].removeClass('jtable-row-even');
|
|
}
|
|
}
|
|
},
|
|
|
|
/* RENDERING FIELD VALUES ***********************************************/
|
|
|
|
/* Gets text for a field of a record according to it's type.
|
|
*************************************************************************/
|
|
_getDisplayTextForRecordField: function (record, fieldName) {
|
|
var field = this.options.fields[fieldName];
|
|
var fieldValue = record[fieldName];
|
|
|
|
//if this is a custom field, call display function
|
|
if (field.display) {
|
|
return field.display({ record: record });
|
|
}
|
|
|
|
if (field.type == 'date') {
|
|
return this._getDisplayTextForDateRecordField(field, fieldValue);
|
|
} else if (field.type == 'checkbox') {
|
|
return this._getCheckBoxTextForFieldByValue(fieldName, fieldValue);
|
|
} else if (field.options) { //combobox or radio button list since there are options.
|
|
var options = this._getOptionsForField(fieldName, {
|
|
record: record,
|
|
value: fieldValue,
|
|
source: 'list',
|
|
dependedValues: this._createDependedValuesUsingRecord(record, field.dependsOn)
|
|
});
|
|
return this._findOptionByValue(options, fieldValue).DisplayText;
|
|
} else { //other types
|
|
return fieldValue;
|
|
}
|
|
},
|
|
|
|
/* Creates and returns an object that's properties are depended values of a record.
|
|
*************************************************************************/
|
|
_createDependedValuesUsingRecord: function (record, dependsOn) {
|
|
if (!dependsOn) {
|
|
return {};
|
|
}
|
|
|
|
var dependedValues = {};
|
|
for (var i = 0; i < dependsOn.length; i++) {
|
|
dependedValues[dependsOn[i]] = record[dependsOn[i]];
|
|
}
|
|
|
|
return dependedValues;
|
|
},
|
|
|
|
/* Finds an option object by given value.
|
|
*************************************************************************/
|
|
_findOptionByValue: function (options, value) {
|
|
for (var i = 0; i < options.length; i++) {
|
|
if (options[i].Value == value) {
|
|
return options[i];
|
|
}
|
|
}
|
|
|
|
return {}; //no option found
|
|
},
|
|
|
|
/* Gets text for a date field.
|
|
*************************************************************************/
|
|
_getDisplayTextForDateRecordField: function (field, fieldValue) {
|
|
if (!fieldValue) {
|
|
return '';
|
|
}
|
|
|
|
var displayFormat = field.displayFormat || this.options.defaultDateFormat;
|
|
var date = this._parseDate(fieldValue);
|
|
return $.datepicker.formatDate(displayFormat, date);
|
|
},
|
|
|
|
/* Gets options for a field according to user preferences.
|
|
*************************************************************************/
|
|
_getOptionsForField: function (fieldName, funcParams) {
|
|
var field = this.options.fields[fieldName];
|
|
var optionsSource = field.options;
|
|
|
|
if ($.isFunction(optionsSource)) {
|
|
//prepare parameter to the function
|
|
funcParams = $.extend(true, {
|
|
_cacheCleared: false,
|
|
dependedValues: {},
|
|
clearCache: function () {
|
|
this._cacheCleared = true;
|
|
}
|
|
}, funcParams);
|
|
|
|
//call function and get actual options source
|
|
optionsSource = optionsSource(funcParams);
|
|
}
|
|
|
|
var options;
|
|
|
|
//Build options according to it's source type
|
|
if (typeof optionsSource == 'string') { //It is an Url to download options
|
|
var cacheKey = 'options_' + fieldName + '_' + optionsSource; //create a unique cache key
|
|
if (funcParams._cacheCleared || (!this._cache[cacheKey])) {
|
|
//if user calls clearCache() or options are not found in the cache, download options
|
|
this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
|
|
this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
|
|
} else {
|
|
//found on cache..
|
|
//if this method (_getOptionsForField) is called to get option for a specific value (on funcParams.source == 'list')
|
|
//and this value is not in cached options, we need to re-download options to get the unfound (probably new) option.
|
|
if (funcParams.value != undefined) {
|
|
var optionForValue = this._findOptionByValue(this._cache[cacheKey], funcParams.value);
|
|
if (optionForValue.DisplayText == undefined) { //this value is not in cached options...
|
|
this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
|
|
this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
|
|
}
|
|
}
|
|
}
|
|
|
|
options = this._cache[cacheKey];
|
|
} else if (jQuery.isArray(optionsSource)) { //It is an array of options
|
|
options = this._buildOptionsFromArray(optionsSource);
|
|
this._sortFieldOptions(options, field.optionsSorting);
|
|
} else { //It is an object that it's properties are options
|
|
options = this._buildOptionsArrayFromObject(optionsSource);
|
|
this._sortFieldOptions(options, field.optionsSorting);
|
|
}
|
|
|
|
return options;
|
|
},
|
|
|
|
/* Download options for a field from server.
|
|
*************************************************************************/
|
|
_downloadOptions: function (fieldName, url) {
|
|
var self = this;
|
|
var options = [];
|
|
|
|
self._ajax({
|
|
url: url,
|
|
async: false,
|
|
success: function (data) {
|
|
if (data.Result != 'OK') {
|
|
self._showError(data.Message);
|
|
return;
|
|
}
|
|
|
|
options = data.Options;
|
|
},
|
|
error: function () {
|
|
var errMessage = self._formatString(self.options.messages.cannotLoadOptionsFor, fieldName);
|
|
self._showError(errMessage);
|
|
}
|
|
});
|
|
|
|
return options;
|
|
},
|
|
|
|
/* Sorts given options according to sorting parameter.
|
|
* sorting can be: 'value', 'value-desc', 'text' or 'text-desc'.
|
|
*************************************************************************/
|
|
_sortFieldOptions: function (options, sorting) {
|
|
|
|
if ((!options) || (!options.length) || (!sorting)) {
|
|
return;
|
|
}
|
|
|
|
//Determine using value of text
|
|
var dataSelector;
|
|
if (sorting.indexOf('value') == 0) {
|
|
dataSelector = function (option) {
|
|
return option.Value;
|
|
};
|
|
} else { //assume as text
|
|
dataSelector = function (option) {
|
|
return option.DisplayText;
|
|
};
|
|
}
|
|
|
|
var compareFunc;
|
|
if ($.type(dataSelector(options[0])) == 'string') {
|
|
compareFunc = function (option1, option2) {
|
|
return dataSelector(option1).localeCompare(dataSelector(option2));
|
|
};
|
|
} else { //asuume as numeric
|
|
compareFunc = function (option1, option2) {
|
|
return dataSelector(option1) - dataSelector(option2);
|
|
};
|
|
}
|
|
|
|
if (sorting.indexOf('desc') > 0) {
|
|
options.sort(function (a, b) {
|
|
return compareFunc(b, a);
|
|
});
|
|
} else { //assume as asc
|
|
options.sort(function (a, b) {
|
|
return compareFunc(a, b);
|
|
});
|
|
}
|
|
},
|
|
|
|
/* Creates an array of options from given object.
|
|
*************************************************************************/
|
|
_buildOptionsArrayFromObject: function (options) {
|
|
var list = [];
|
|
|
|
$.each(options, function (propName, propValue) {
|
|
list.push({
|
|
Value: propName,
|
|
DisplayText: propValue
|
|
});
|
|
});
|
|
|
|
return list;
|
|
},
|
|
|
|
/* Creates array of options from giving options array.
|
|
*************************************************************************/
|
|
_buildOptionsFromArray: function (optionsArray) {
|
|
var list = [];
|
|
|
|
for (var i = 0; i < optionsArray.length; i++) {
|
|
if ($.isPlainObject(optionsArray[i])) {
|
|
list.push(optionsArray[i]);
|
|
} else { //assumed as primitive type (int, string...)
|
|
list.push({
|
|
Value: optionsArray[i],
|
|
DisplayText: optionsArray[i]
|
|
});
|
|
}
|
|
}
|
|
|
|
return list;
|
|
},
|
|
|
|
/* Parses given date string to a javascript Date object.
|
|
* Given string must be formatted one of the samples shown below:
|
|
* /Date(1320259705710)/
|
|
* 2011-01-01 20:32:42 (YYYY-MM-DD HH:MM:SS)
|
|
* 2011-01-01 (YYYY-MM-DD)
|
|
*************************************************************************/
|
|
_parseDate: function (dateString) {
|
|
if (dateString.indexOf('Date') >= 0) { //Format: /Date(1320259705710)/
|
|
return new Date(
|
|
parseInt(dateString.substr(6), 10)
|
|
);
|
|
} else if (dateString.length == 10) { //Format: 2011-01-01
|
|
return new Date(
|
|
parseInt(dateString.substr(0, 4), 10),
|
|
parseInt(dateString.substr(5, 2), 10) - 1,
|
|
parseInt(dateString.substr(8, 2), 10)
|
|
);
|
|
} else if (dateString.length == 19) { //Format: 2011-01-01 20:32:42
|
|
return new Date(
|
|
parseInt(dateString.substr(0, 4), 10),
|
|
parseInt(dateString.substr(5, 2), 10) - 1,
|
|
parseInt(dateString.substr(8, 2), 10),
|
|
parseInt(dateString.substr(11, 2), 10),
|
|
parseInt(dateString.substr(14, 2), 10),
|
|
parseInt(dateString.substr(17, 2), 10)
|
|
);
|
|
} else {
|
|
this._logWarn('Given date is not properly formatted: ' + dateString);
|
|
return 'format error!';
|
|
}
|
|
},
|
|
|
|
/* TOOL BAR *************************************************************/
|
|
|
|
/* Creates the toolbar.
|
|
*************************************************************************/
|
|
_createToolBar: function () {
|
|
this._$toolbarDiv = $('<div />')
|
|
.addClass('jtable-toolbar')
|
|
.appendTo(this._$titleDiv);
|
|
|
|
for (var i = 0; i < this.options.toolbar.items.length; i++) {
|
|
this._addToolBarItem(this.options.toolbar.items[i]);
|
|
}
|
|
},
|
|
|
|
/* Adds a new item to the toolbar.
|
|
*************************************************************************/
|
|
_addToolBarItem: function (item) {
|
|
|
|
//Check if item is valid
|
|
if ((item == undefined) || (item.text == undefined && item.icon == undefined)) {
|
|
this._logWarn('Can not add tool bar item since it is not valid!');
|
|
this._logWarn(item);
|
|
return null;
|
|
}
|
|
|
|
var $toolBarItem = $('<span></span>')
|
|
.addClass('jtable-toolbar-item')
|
|
.appendTo(this._$toolbarDiv);
|
|
|
|
this._jqueryuiThemeAddClass($toolBarItem, 'ui-widget ui-state-default ui-corner-all', 'ui-state-hover');
|
|
|
|
//cssClass property
|
|
if (item.cssClass) {
|
|
$toolBarItem
|
|
.addClass(item.cssClass);
|
|
}
|
|
|
|
//tooltip property
|
|
if (item.tooltip) {
|
|
$toolBarItem
|
|
.attr('title', item.tooltip);
|
|
}
|
|
|
|
//icon property
|
|
if (item.icon) {
|
|
var $icon = $('<span class="jtable-toolbar-item-icon"></span>').appendTo($toolBarItem);
|
|
if (item.icon === true) {
|
|
//do nothing
|
|
} else if ($.type(item.icon === 'string')) {
|
|
$icon.css('background', 'url("' + item.icon + '")');
|
|
}
|
|
}
|
|
|
|
//text property
|
|
if (item.text) {
|
|
$('<span class=""></span>')
|
|
.html(item.text)
|
|
.addClass('jtable-toolbar-item-text').appendTo($toolBarItem);
|
|
}
|
|
|
|
//click event
|
|
if (item.click) {
|
|
$toolBarItem.click(function () {
|
|
item.click();
|
|
});
|
|
}
|
|
|
|
//set hover animation parameters
|
|
var hoverAnimationDuration = undefined;
|
|
var hoverAnimationEasing = undefined;
|
|
if (this.options.toolbar.hoverAnimation) {
|
|
hoverAnimationDuration = this.options.toolbar.hoverAnimationDuration;
|
|
hoverAnimationEasing = this.options.toolbar.hoverAnimationEasing;
|
|
}
|
|
|
|
//change class on hover
|
|
$toolBarItem.hover(function () {
|
|
$toolBarItem.addClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
|
|
}, function () {
|
|
$toolBarItem.removeClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
|
|
});
|
|
|
|
return $toolBarItem;
|
|
},
|
|
|
|
/* ERROR DIALOG *********************************************************/
|
|
|
|
/* Shows error message dialog with given message.
|
|
*************************************************************************/
|
|
_showError: function (message) {
|
|
this._$errorDialogDiv.html(message).dialog('open');
|
|
},
|
|
|
|
/* BUSY PANEL ***********************************************************/
|
|
|
|
/* Shows busy indicator and blocks table UI.
|
|
* TODO: Make this cofigurable and changable
|
|
*************************************************************************/
|
|
_setBusyTimer: null,
|
|
_showBusy: function (message, delay) {
|
|
var self = this; //
|
|
|
|
//Show a transparent overlay to prevent clicking to the table
|
|
self._$busyDiv
|
|
.width(self._$mainContainer.width())
|
|
.height(self._$mainContainer.height())
|
|
.addClass('jtable-busy-panel-background-invisible')
|
|
.show();
|
|
|
|
var makeVisible = function () {
|
|
self._$busyDiv.removeClass('jtable-busy-panel-background-invisible');
|
|
self._$busyMessageDiv.html(message).show();
|
|
};
|
|
|
|
if (delay) {
|
|
if (self._setBusyTimer) {
|
|
return;
|
|
}
|
|
|
|
self._setBusyTimer = setTimeout(makeVisible, delay);
|
|
} else {
|
|
makeVisible();
|
|
}
|
|
},
|
|
|
|
/* Hides busy indicator and unblocks table UI.
|
|
*************************************************************************/
|
|
_hideBusy: function () {
|
|
clearTimeout(this._setBusyTimer);
|
|
this._setBusyTimer = null;
|
|
this._$busyDiv.hide();
|
|
this._$busyMessageDiv.html('').hide();
|
|
},
|
|
|
|
/* Returns true if jTable is busy.
|
|
*************************************************************************/
|
|
_isBusy: function () {
|
|
return this._$busyMessageDiv.is(':visible');
|
|
},
|
|
|
|
/* Adds jQueryUI class to an item.
|
|
*************************************************************************/
|
|
_jqueryuiThemeAddClass: function ($elm, className, hoverClassName) {
|
|
if (!this.options.jqueryuiTheme) {
|
|
return;
|
|
}
|
|
|
|
$elm.addClass(className);
|
|
|
|
if (hoverClassName) {
|
|
$elm.hover(function () {
|
|
$elm.addClass(hoverClassName);
|
|
}, function () {
|
|
$elm.removeClass(hoverClassName);
|
|
});
|
|
}
|
|
},
|
|
|
|
/* COMMON METHODS *******************************************************/
|
|
|
|
/* Performs an AJAX call to specified URL.
|
|
* THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
|
|
* USE _ajax METHOD.
|
|
*************************************************************************/
|
|
_performAjaxCall: function (url, postData, async, success, error) {
|
|
this._ajax({
|
|
url: url,
|
|
data: postData,
|
|
async: async,
|
|
success: success,
|
|
error: error
|
|
});
|
|
},
|
|
|
|
_unAuthorizedRequestHandler: function() {
|
|
if (this.options.unAuthorizedRequestRedirectUrl) {
|
|
location.href = this.options.unAuthorizedRequestRedirectUrl;
|
|
} else {
|
|
location.reload(true);
|
|
}
|
|
},
|
|
|
|
/* This method is used to perform AJAX calls in jTable instead of direct
|
|
* usage of jQuery.ajax method.
|
|
*************************************************************************/
|
|
_ajax: function (options) {
|
|
var self = this;
|
|
|
|
//Handlers for HTTP status codes
|
|
var opts = {
|
|
statusCode: {
|
|
401: function () { //Unauthorized
|
|
self._unAuthorizedRequestHandler();
|
|
}
|
|
}
|
|
};
|
|
|
|
opts = $.extend(opts, this.options.ajaxSettings, options);
|
|
|
|
//Override success
|
|
opts.success = function (data) {
|
|
//Checking for Authorization error
|
|
if (data && data.UnAuthorizedRequest == true) {
|
|
self._unAuthorizedRequestHandler();
|
|
}
|
|
|
|
if (options.success) {
|
|
options.success(data);
|
|
}
|
|
};
|
|
|
|
//Override error
|
|
opts.error = function (jqXHR, textStatus, errorThrown) {
|
|
if (unloadingPage) {
|
|
jqXHR.abort();
|
|
return;
|
|
}
|
|
|
|
if (options.error) {
|
|
options.error(arguments);
|
|
}
|
|
};
|
|
|
|
//Override complete
|
|
opts.complete = function () {
|
|
if (options.complete) {
|
|
options.complete();
|
|
}
|
|
};
|
|
|
|
$.ajax(opts);
|
|
},
|
|
|
|
/* Gets value of key field of a record.
|
|
*************************************************************************/
|
|
_getKeyValueOfRecord: function (record) {
|
|
return record[this._keyField];
|
|
},
|
|
|
|
/************************************************************************
|
|
* COOKIE *
|
|
*************************************************************************/
|
|
|
|
/* Sets a cookie with given key.
|
|
*************************************************************************/
|
|
_setCookie: function (key, value) {
|
|
key = this._cookieKeyPrefix + key;
|
|
|
|
var expireDate = new Date();
|
|
expireDate.setDate(expireDate.getDate() + 30);
|
|
document.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + "; expires=" + expireDate.toUTCString();
|
|
},
|
|
|
|
/* Gets a cookie with given key.
|
|
*************************************************************************/
|
|
_getCookie: function (key) {
|
|
key = this._cookieKeyPrefix + key;
|
|
|
|
var equalities = document.cookie.split('; ');
|
|
for (var i = 0; i < equalities.length; i++) {
|
|
if (!equalities[i]) {
|
|
continue;
|
|
}
|
|
|
|
var splitted = equalities[i].split('=');
|
|
if (splitted.length != 2) {
|
|
continue;
|
|
}
|
|
|
|
if (decodeURIComponent(splitted[0]) === key) {
|
|
return decodeURIComponent(splitted[1] || '');
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
/* Generates a hash key to be prefix for all cookies for this jtable instance.
|
|
*************************************************************************/
|
|
_generateCookieKeyPrefix: function () {
|
|
|
|
var simpleHash = function (value) {
|
|
var hash = 0;
|
|
if (value.length == 0) {
|
|
return hash;
|
|
}
|
|
|
|
for (var i = 0; i < value.length; i++) {
|
|
var ch = value.charCodeAt(i);
|
|
hash = ((hash << 5) - hash) + ch;
|
|
hash = hash & hash;
|
|
}
|
|
|
|
return hash;
|
|
};
|
|
|
|
var strToHash = '';
|
|
if (this.options.tableId) {
|
|
strToHash = strToHash + this.options.tableId + '#';
|
|
}
|
|
|
|
strToHash = strToHash + this._columnList.join('$') + '#c' + this._$table.find('thead th').length;
|
|
return 'jtable#' + simpleHash(strToHash);
|
|
},
|
|
|
|
/************************************************************************
|
|
* EVENT RAISING METHODS *
|
|
*************************************************************************/
|
|
|
|
_onLoadingRecords: function () {
|
|
this._trigger("loadingRecords", null, {});
|
|
},
|
|
|
|
_onRecordsLoaded: function (data) {
|
|
this._trigger("recordsLoaded", null, { records: data.Records, serverResponse: data });
|
|
},
|
|
|
|
_onRowInserted: function ($row, isNewRow) {
|
|
this._trigger("rowInserted", null, { row: $row, record: $row.data('record'), isNewRow: isNewRow });
|
|
},
|
|
|
|
_onRowsRemoved: function ($rows, reason) {
|
|
this._trigger("rowsRemoved", null, { rows: $rows, reason: reason });
|
|
},
|
|
|
|
_onCloseRequested: function () {
|
|
this._trigger("closeRequested", null, {});
|
|
}
|
|
|
|
});
|
|
|
|
}(jQuery));
|