/************************************************************************ * 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 (jQuery object) _$tableBody: null, //Reference to in the table (jQuery object) _$tableRows: null, //Array of all 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 = $('
') .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 = $('
') .addClass('jtable-title') .appendTo(self._$mainContainer); self._jqueryuiThemeAddClass($titleDiv, 'ui-widget-header'); $('
') .addClass('jtable-title-text') .appendTo($titleDiv) .append(self.options.title); if (self.options.showCloseButton) { var $textSpan = $('') .html(self.options.messages.close); $('') .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 = $('
') .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 = $('') .appendTo(this._$table); this._addRowToTableHead($thead); }, /* Adds tr element to given thead element *************************************************************************/ _addRowToTableHead: function ($thead) { var $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 = $('') .addClass('jtable-column-header-text') .html(field.title); var $headerContainerDiv = $('
') .addClass('jtable-column-header-container') .append($headerTextSpan); var $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 = $('') .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 = $('').appendTo(this._$table); }, /* Creates a div to block UI while jTable is busy. *************************************************************************/ _createBusyPanel: function () { this._$busyMessageDiv = $('
').addClass('jtable-busy-message').prependTo(this._$mainContainer); this._$busyDiv = $('
').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 = $('
').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 = $('') .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 $('') .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 = $('') .addClass('jtable-no-data-row') .appendTo(this._$tableBody); var totalColumnCount = this._$table.find('thead th').length; $('') .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 = $('
') .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 = $('') .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 = $('').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) { $('') .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));