diff options
author | Halil İbrahim Kalkan <hikalkan@gmail.com> | 2013-02-10 13:05:16 +0200 |
---|---|---|
committer | Halil İbrahim Kalkan <hikalkan@gmail.com> | 2013-02-10 13:05:16 +0200 |
commit | 277c6e18d910accfe145f0b3cacb27aef437d1ac (patch) | |
tree | 1c6ebdcabb29eebacabac485b234fcabaeec14d8 /dev/jquery.jtable.core.js | |
parent | 82df9fff9f0a1ea9b4bc8a91d4481b74f391a36f (diff) | |
download | jtable-277c6e18d910accfe145f0b3cacb27aef437d1ac.zip jtable-277c6e18d910accfe145f0b3cacb27aef437d1ac.tar.gz jtable-277c6e18d910accfe145f0b3cacb27aef437d1ac.tar.bz2 |
jTable v.2.2.0v2.2.0
Feature: Toolbar. [#188]
Feature: 'Change page size' combobox. [#1, #128]
Feature: 'Go to page' input. [#63]
Feature: Multiple sorting of columns by holding CTRL key. [#48]
Added options: multiSorting, gotoPageArea, pageSizes,
pageSizeChangeArea, pageList and toolbar.
Hungarian and Italian localizations [#179]
Fixed some issues. [#209]
Diffstat (limited to 'dev/jquery.jtable.core.js')
-rw-r--r-- | dev/jquery.jtable.core.js | 2109 |
1 files changed, 1099 insertions, 1010 deletions
diff --git a/dev/jquery.jtable.core.js b/dev/jquery.jtable.core.js index a8d599e..3e26eee 100644 --- a/dev/jquery.jtable.core.js +++ b/dev/jquery.jtable.core.js @@ -1,1010 +1,1099 @@ -/************************************************************************
-* CORE jTable module *
-*************************************************************************/
-(function ($) {
-
- $.widget("hik.jtable", {
-
- /************************************************************************
- * DEFAULT OPTIONS / EVENTS *
- *************************************************************************/
- options: {
-
- //Options
- actions: {},
- fields: {},
- animationsEnabled: true,
- defaultDateFormat: 'yy-mm-dd',
- dialogShowEffect: 'fade',
- dialogHideEffect: 'fade',
- showCloseButton: false,
- loadingAnimationDelay: 500,
- ajaxSettings: {
- type: 'POST',
- dataType: 'json'
- },
-
- //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)
-
- _$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)
-
- _$bottomPanel: null, //Reference to the panel at the bottom of the table (jQuery object)
-
- _$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._createTable();
- this._createBottomPanel();
- this._createBusyPanel();
- this._createErrorDialogDiv();
- this._addNoDataRow();
- },
-
- /* 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 = '';
- }
-
- //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);
- },
-
- /* 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);
-
- $('<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();
- });
- }
- },
-
- /* Creates the table.
- *************************************************************************/
- _createTable: function () {
- this._$table = $('<table></table>')
- .addClass('jtable')
- .appendTo(this._$mainContainer);
-
- 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')
- .css('width', field.width)
- .data('fieldName', fieldName)
- .append($headerContainerDiv);
-
- return $th;
- },
-
- /* Creates an empty header cell that can be used as command column headers.
- *************************************************************************/
- _createEmptyCommandHeader: function () {
- return $('<th></th>')
- .addClass('jtable-command-column-header')
- .css('width', '1%');
- },
-
- /* Creates tbody tag and adds to the table.
- *************************************************************************/
- _createTableBody: function () {
- this._$tableBody = $('<tbody></tbody>').appendTo(this._$table);
- },
-
- /* Creates bottom panel and adds to the page.
- *************************************************************************/
- _createBottomPanel: function () {
- this._$bottomPanel = $('<div />')
- .addClass('jtable-bottom-panel')
- .appendTo(this._$mainContainer);
-
- $('<div />').addClass('jtable-left-area').appendTo(this._$bottomPanel);
- $('<div />').addClass('jtable-right-area').appendTo(this._$bottomPanel);
- },
-
- /* 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._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 *
- *************************************************************************/
-
- /* LOADING RECORDS *****************************************************/
-
- /* Performs an AJAX call to reload data of the table.
- *************************************************************************/
- _reloadTable: function (completeCallback) {
- var self = this;
-
- //Disable table since it's busy
- self._showBusy(self.options.messages.loadingMessage, self.options.loadingAnimationDelay);
-
- //Generate URL (with query string parameters) to load records
- var loadUrl = self._createRecordLoadUrl();
-
- //Load data from server
- self._onLoadingRecords();
- self._ajax({
- url: loadUrl,
- data: self._lastPostData,
- success: 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();
- }
- },
- error: function () {
- self._hideBusy();
- self._showError(self.options.messages.serverCommunicationError);
- }
- });
- },
-
- /* Creates URL to load records.
- *************************************************************************/
- _createRecordLoadUrl: function () {
- return this.options.actions.listAction;
- },
-
- /* 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) {
- $tableRow.addClass('jtable-row-created', 'slow', '', function () {
- $tableRow.removeClass('jtable-row-created', 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))
- );
- } else if (dateString.length == 10) { //Format: 2011-01-01
- return new Date(
- parseInt(dateString.substr(0, 4)),
- parseInt(dateString.substr(5, 2)) - 1,
- parseInt(dateString.substr(8, 2))
- );
- } else if (dateString.length == 19) { //Format: 2011-01-01 20:32:42
- return new Date(
- parseInt(dateString.substr(0, 4)),
- parseInt(dateString.substr(5, 2)) - 1,
- parseInt(dateString.substr(8, 2)),
- parseInt(dateString.substr(11, 2)),
- parseInt(dateString.substr(14, 2)),
- parseInt(dateString.substr(17, 2))
- );
- } else {
- this._logWarn('Given date is not properly formatted: ' + dateString);
- return 'format error!';
- }
- },
-
- /* 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, //TODO: Think for a better way!
- _showBusy: function (message, delay) {
- var self = this;
-
- var show = function () {
- if (!self._$busyMessageDiv.is(':visible')) {
- self._$busyDiv.width(self._$mainContainer.width());
- self._$busyDiv.height(self._$mainContainer.height());
- self._$busyDiv.show();
- self._$busyMessageDiv.show();
- }
-
- self._$busyMessageDiv.html(message);
- };
-
- //TODO: Put an overlay always (without color) to not allow to click the table
- //TODO: and change it visible when timeout occurs.
- if (delay) {
- self._setBusyTimer = setTimeout(show, delay);
- } else {
- show();
- }
- },
-
- /* Hides busy indicator and unblocks table UI.
- *************************************************************************/
- _hideBusy: function () {
- clearTimeout(this._setBusyTimer);
- this._$busyDiv.hide();
- this._$busyMessageDiv.html('').hide();
- },
-
- /* Returns true if jTable is busy.
- *************************************************************************/
- _isBusy: function () {
- return this._$busyMessageDiv.is(':visible');
- },
-
- /* 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
- });
- },
-
- /* This method is used to perform AJAX calls in jTable instead of direct
- * usage of jQuery.ajax method.
- *************************************************************************/
- _ajax: function (options) {
- var opts = $.extend({}, this.options.ajaxSettings, options);
-
- //Override success
- opts.success = function (data) {
- if (options.success) {
- options.success(data);
- }
- };
-
- //Override error
- opts.error = function () {
- if (options.error) {
- options.error();
- }
- };
-
- //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];
- },
-
- /************************************************************************
- * 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));
+/************************************************************************ +* CORE jTable module * +*************************************************************************/ +(function ($) { + + $.widget("hik.jtable", { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Options + actions: {}, + fields: {}, + animationsEnabled: true, + defaultDateFormat: 'yy-mm-dd', + dialogShowEffect: 'fade', + dialogHideEffect: 'fade', + showCloseButton: false, + loadingAnimationDelay: 500, + + 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(); + }, + + /* 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 = ''; + } + + //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); + }, + + /* 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); + + $('<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); + + 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') + .css('width', field.width) + .data('fieldName', fieldName) + .append($headerContainerDiv); + + return $th; + }, + + /* Creates an empty header cell that can be used as command column headers. + *************************************************************************/ + _createEmptyCommandHeader: function () { + return $('<th></th>') + .addClass('jtable-command-column-header') + .css('width', '1%'); + }, + + /* 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._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; + + //Disable table since it's busy + self._showBusy(self.options.messages.loadingMessage, self.options.loadingAnimationDelay); + + //Generate URL (with query string parameters) to load records + var loadUrl = self._createRecordLoadUrl(); + + //Load data from server + self._onLoadingRecords(); + self._ajax({ + url: loadUrl, + data: self._lastPostData, + success: 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(); + } + }, + error: function () { + self._hideBusy(); + self._showError(self.options.messages.serverCommunicationError); + } + }); + }, + + /* Creates URL to load records. + *************************************************************************/ + _createRecordLoadUrl: function () { + return this.options.actions.listAction; + }, + + /* 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) { + $tableRow.addClass('jtable-row-created', 'slow', '', function () { + $tableRow.removeClass('jtable-row-created', 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)) + ); + } else if (dateString.length == 10) { //Format: 2011-01-01 + return new Date( + parseInt(dateString.substr(0, 4)), + parseInt(dateString.substr(5, 2)) - 1, + parseInt(dateString.substr(8, 2)) + ); + } else if (dateString.length == 19) { //Format: 2011-01-01 20:32:42 + return new Date( + parseInt(dateString.substr(0, 4)), + parseInt(dateString.substr(5, 2)) - 1, + parseInt(dateString.substr(8, 2)), + parseInt(dateString.substr(11, 2)), + parseInt(dateString.substr(14, 2)), + parseInt(dateString.substr(17, 2)) + ); + } 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); + + //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, //TODO: Think for a better way! + _showBusy: function (message, delay) { + var self = this; + + var show = function () { + if (!self._$busyMessageDiv.is(':visible')) { + self._$busyDiv.width(self._$mainContainer.width()); + self._$busyDiv.height(self._$mainContainer.height()); + self._$busyDiv.show(); + self._$busyMessageDiv.show(); + } + + self._$busyMessageDiv.html(message); + }; + + //TODO: Put an overlay always (without color) to not allow to click the table + //TODO: and change it visible when timeout occurs. + if (delay) { + self._setBusyTimer = setTimeout(show, delay); + } else { + show(); + } + }, + + /* Hides busy indicator and unblocks table UI. + *************************************************************************/ + _hideBusy: function () { + clearTimeout(this._setBusyTimer); + this._$busyDiv.hide(); + this._$busyMessageDiv.html('').hide(); + }, + + /* Returns true if jTable is busy. + *************************************************************************/ + _isBusy: function () { + return this._$busyMessageDiv.is(':visible'); + }, + + /* 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 + }); + }, + + /* This method is used to perform AJAX calls in jTable instead of direct + * usage of jQuery.ajax method. + *************************************************************************/ + _ajax: function (options) { + var opts = $.extend({}, this.options.ajaxSettings, options); + + //Override success + opts.success = function (data) { + if (options.success) { + options.success(data); + } + }; + + //Override error + opts.error = function () { + if (options.error) { + options.error(); + } + }; + + //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]; + }, + + /************************************************************************ + * 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)); |