From 165dab927bdc4e60c3fb16827e586661695abaee Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 08:24:09 +0330 Subject: [PATCH 01/58] Add option formDialogColumns formDialogColumns is an option to indicate number of form dialog columns --- dev/jquery.jtable.creation.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dev/jquery.jtable.creation.js b/dev/jquery.jtable.creation.js index 4212e967..68d8a987 100644 --- a/dev/jquery.jtable.creation.js +++ b/dev/jquery.jtable.creation.js @@ -225,7 +225,10 @@ //Create add new record form var $addRecordForm = $('
'); - + var ColumnCount=self.options.formDialogColumns ? self.options.formDialogColumns : 1; + var CurrentColumnCount=0; + var $CreateFormTable = $('').appendTo($addRecordForm); + var $RowContainer = $('').appendTo($CreateFormTable); //Create input elements for (var i = 0; i < self._fieldList.length; i++) { @@ -247,10 +250,16 @@ continue; } - //Create a container div for this input field and add to form - var $fieldContainer = $('
') + //Create a container table cell for this input field and add to form + var $fieldContainer = $('
').appendTo($CreateFormTable); + CurrentColumnCount=0; + } //Create a label for input $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); From 9696a51e5b63fb5760c3da9b37af770431f2a26a Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 08:39:59 +0330 Subject: [PATCH 02/58] add createFormColumns option createFormColumns is an option to set the number of columns in create form --- dev/jquery.jtable.creation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/jquery.jtable.creation.js b/dev/jquery.jtable.creation.js index 68d8a987..31b9a207 100644 --- a/dev/jquery.jtable.creation.js +++ b/dev/jquery.jtable.creation.js @@ -225,7 +225,7 @@ //Create add new record form var $addRecordForm = $('
'); - var ColumnCount=self.options.formDialogColumns ? self.options.formDialogColumns : 1; + var ColumnCount=self.options.createFormColumns ? self.options.createFormColumns : 1; var CurrentColumnCount=0; var $CreateFormTable = $('
') .addClass('jtable-input-field-container') - .appendTo($addRecordForm); + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('
').appendTo($addRecordForm); var $RowContainer = $('').appendTo($CreateFormTable); From b00323372dfd4442e0beb3c1442d633e6c66b0c3 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 09:13:34 +0330 Subject: [PATCH 03/58] add createFormColumns option editFormColumns is an option to set the number of columns in edit form --- dev/jquery.jtable.editing.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/dev/jquery.jtable.editing.js b/dev/jquery.jtable.editing.js index d0a5c587..929c05e8 100644 --- a/dev/jquery.jtable.editing.js +++ b/dev/jquery.jtable.editing.js @@ -258,7 +258,10 @@ //Create edit form var $editForm = $('
'); - + var ColumnCount=self.options.editFormColumns ? self.options.editFormColumns : 1; + var CurrentColumnCount=0; + var $EditFormTable = $('
').appendTo($editForm); + var $RowContainer = $('').appendTo($EditFormTable); //Create input fields for (var i = 0; i < self._fieldList.length; i++) { @@ -289,7 +292,15 @@ } //Create a container div for this input field and add to form - var $fieldContainer = $('
').appendTo($editForm); + var $fieldContainer = $('').appendTo($EditFormTable); + CurrentColumnCount=0; + } //Create a label for input $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); From 42b96dbf82b1d3aa9d7c275e982f330f0d814552 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:22:19 +0330 Subject: [PATCH 04/58] Add multi column form style --- lib/themes/basic/jtable_basic.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/themes/basic/jtable_basic.css b/lib/themes/basic/jtable_basic.css index 2bd8a056..a456673f 100644 --- a/lib/themes/basic/jtable_basic.css +++ b/lib/themes/basic/jtable_basic.css @@ -183,13 +183,22 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { + +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } + form.jtable-dialog-form div.jtable-input-label { padding: 2px 3px; font-size: 1.1em; From 1b592427ebc4b86b7486d59e3e11af863d80dc59 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:27:27 +0330 Subject: [PATCH 05/58] Add multi columns form style --- lib/themes/jqueryui/jtable_jqueryui.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/themes/jqueryui/jtable_jqueryui.css b/lib/themes/jqueryui/jtable_jqueryui.css index 3a2ef934..166a9844 100644 --- a/lib/themes/jqueryui/jtable_jqueryui.css +++ b/lib/themes/jqueryui/jtable_jqueryui.css @@ -177,13 +177,22 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { + +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } + form.jtable-dialog-form div.jtable-input-label { padding: 2px 3px; font-size: 1.1em; From ef86d656b3f16b5a03159dbd44a839eef5907fc7 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:36:36 +0330 Subject: [PATCH 06/58] add multi column form style --- lib/themes/lightcolor/blue/jtable.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/themes/lightcolor/blue/jtable.css b/lib/themes/lightcolor/blue/jtable.css index 39942338..431e332d 100644 --- a/lib/themes/lightcolor/blue/jtable.css +++ b/lib/themes/lightcolor/blue/jtable.css @@ -181,13 +181,22 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { + +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } + form.jtable-dialog-form div.jtable-input-label { padding: 2px 3px; font-size: 1.1em; From 4f9fe18ac29868229d7a726678ab1febb9f56fc2 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:38:27 +0330 Subject: [PATCH 07/58] add multi column form style --- lib/themes/lightcolor/gray/jtable.css | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/themes/lightcolor/gray/jtable.css b/lib/themes/lightcolor/gray/jtable.css index 4380c00f..5497e490 100644 --- a/lib/themes/lightcolor/gray/jtable.css +++ b/lib/themes/lightcolor/gray/jtable.css @@ -181,13 +181,22 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { + +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } + form.jtable-dialog-form div.jtable-input-label { padding: 2px 3px; font-size: 1.1em; From 6300d4f0f8a889f27a3efe0c4668bca23906de96 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:39:12 +0330 Subject: [PATCH 08/58] add multi columns form style --- lib/themes/lightcolor/green/jtable.css | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/themes/lightcolor/green/jtable.css b/lib/themes/lightcolor/green/jtable.css index 63c480c6..34d21add 100644 --- a/lib/themes/lightcolor/green/jtable.css +++ b/lib/themes/lightcolor/green/jtable.css @@ -181,11 +181,17 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } form.jtable-dialog-form div.jtable-input-label { From f227951347903fd0390c865aa1f94bedfe6fc0e0 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:40:31 +0330 Subject: [PATCH 09/58] add multi columns form style --- lib/themes/lightcolor/orange/jtable.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/themes/lightcolor/orange/jtable.css b/lib/themes/lightcolor/orange/jtable.css index e1bee460..07a46b5d 100644 --- a/lib/themes/lightcolor/orange/jtable.css +++ b/lib/themes/lightcolor/orange/jtable.css @@ -181,13 +181,20 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; + +} +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { + +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } + form.jtable-dialog-form div.jtable-input-label { padding: 2px 3px; font-size: 1.1em; From 340af5232e1c34612a7b1aa28368d09d33dce19d Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 15 Feb 2020 10:48:22 +0330 Subject: [PATCH 10/58] add multi column form style --- lib/themes/lightcolor/red/jtable.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/themes/lightcolor/red/jtable.css b/lib/themes/lightcolor/red/jtable.css index 7985356d..0045a4ab 100644 --- a/lib/themes/lightcolor/red/jtable.css +++ b/lib/themes/lightcolor/red/jtable.css @@ -181,11 +181,15 @@ div.jtable-main-container div.jtable-column-selection-container ul.jtable-column div.jtable-main-container div.jtable-column-selection-container ul.jtable-column-select-list li input[type="checkbox"] { cursor: pointer; } -form.jtable-dialog-form div.jtable-input-field-container { +form.jtable-dialog-form table.jtable-input-field-container-grid { + border-spacing: 10px; + border-collapse: separate; +} +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row td.jtable-input-field-container { padding: 2px 0px 3px 0px; border-bottom: 1px solid #ddd; } -form.jtable-dialog-form div.jtable-input-field-container:last-child { +form.jtable-dialog-form table.jtable-input-field-container-grid tr.jtable-input-field-container-row:last-child td.jtable-input-field-container { border: none; } form.jtable-dialog-form div.jtable-input-label { From 2065607ef2f329d66fb4a44f0ecd73718d197615 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 28 Mar 2021 17:41:59 +0430 Subject: [PATCH 11/58] Add additional fields to row delete request POST data Add additional fields to row delete POST request data by setting 'delete' option of the field to true --- dev/jquery.jtable.deletion.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/dev/jquery.jtable.deletion.js b/dev/jquery.jtable.deletion.js index 99960837..123f38f0 100644 --- a/dev/jquery.jtable.deletion.js +++ b/dev/jquery.jtable.deletion.js @@ -364,6 +364,31 @@ var postData = {}; postData[self._keyField] = self._getKeyValueOfRecord($row.data('record')); + + //Add additonal field to delete request POST + for (var i = 0; i < this._fieldList.length; i++) { + var fieldName = this._fieldList[i]; + var field = this.options.fields[fieldName]; + + //Do not send this field to server if field delete option is not true + if (!field.delete) { + continue; + } + + //Key field is already added to postData + if (self._keyField == fieldName) { + continue; + } + + //Add field to postData to be sent to server + postData[fieldName] = $row.data('record')[fieldName]; + } + + + + + + //deleteAction may be a function, check if it is if (!url && $.isFunction(self.options.actions.deleteAction)) { From 45faf5697f115e466641a1e80b0e9d18ca89ba05 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 28 Mar 2021 17:57:29 +0430 Subject: [PATCH 12/58] Add additional fields to row delete request POST data Add additional fields to row delete POST request data if 'delete' option of the field is true (default value is true) --- dev/jquery.jtable.deletion.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/jquery.jtable.deletion.js b/dev/jquery.jtable.deletion.js index 123f38f0..75acee4b 100644 --- a/dev/jquery.jtable.deletion.js +++ b/dev/jquery.jtable.deletion.js @@ -371,7 +371,7 @@ var field = this.options.fields[fieldName]; //Do not send this field to server if field delete option is not true - if (!field.delete) { + if (field.delete == false) { continue; } From b22eaa172f249c53c56098f35888baf9f51868cc Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 28 Mar 2021 18:05:36 +0430 Subject: [PATCH 13/58] Add additional fields to row delete request POST data Add additional fields to row deletion POST request data by setting 'delete' option of the field to true (default value is true) --- dev/jquery.jtable.deletion.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dev/jquery.jtable.deletion.js b/dev/jquery.jtable.deletion.js index 75acee4b..2b7fc8a4 100644 --- a/dev/jquery.jtable.deletion.js +++ b/dev/jquery.jtable.deletion.js @@ -364,8 +364,7 @@ var postData = {}; postData[self._keyField] = self._getKeyValueOfRecord($row.data('record')); - - //Add additonal field to delete request POST + //Add additional field to delete request POST for (var i = 0; i < this._fieldList.length; i++) { var fieldName = this._fieldList[i]; var field = this.options.fields[fieldName]; @@ -384,11 +383,6 @@ postData[fieldName] = $row.data('record')[fieldName]; } - - - - - //deleteAction may be a function, check if it is if (!url && $.isFunction(self.options.actions.deleteAction)) { From 75d9f9ac4ee163ba6c5397ce1b775c5cfb158f52 Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 9 Oct 2021 17:24:49 +0330 Subject: [PATCH 14/58] new features list added --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9b236f51..dae99c66 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -What is jTable +What is msTable ====== -http://www.jtable.org + [![A screenshot of jTable](https://raw.githubusercontent.com/hikalkan/jtable/master/screenshot.png)](http://jtable.org/) -jTable is a jQuery plugin used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: +msTable is a jQuery plugin (forked from jTable with more features added) used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: * Automatically creates HTML table and loads records from server using AJAX. * Creates 'create new record' jQueryUI dialog form. When user creates a record, it sends data to server using AJAX and adds the same record to the table in the page. @@ -25,6 +25,11 @@ jTable is a jQuery plugin used to create AJAX based CRUD tables without coding H * It is not depended on any server side technology. * It is platform independed and works on all common browsers. +#### What features msTable has that jTable doesn't ? + +* multi columns form for Create and Edit records +* you can send any fields to row-delete-request POST data (jTable only sends single key field) + Notes ====== From c688d2d7d33a498f723a4545fcac0a92b659f21f Mon Sep 17 00:00:00 2001 From: Bird Date: Sat, 9 Oct 2021 17:27:37 +0330 Subject: [PATCH 15/58] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dae99c66..642963a6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -What is msTable +What is msjTable ====== [![A screenshot of jTable](https://raw.githubusercontent.com/hikalkan/jtable/master/screenshot.png)](http://jtable.org/) -msTable is a jQuery plugin (forked from jTable with more features added) used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: +msjTable is a jQuery plugin (forked from jTable with more features added) used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: * Automatically creates HTML table and loads records from server using AJAX. * Creates 'create new record' jQueryUI dialog form. When user creates a record, it sends data to server using AJAX and adds the same record to the table in the page. @@ -25,7 +25,7 @@ msTable is a jQuery plugin (forked from jTable with more features added) used to * It is not depended on any server side technology. * It is platform independed and works on all common browsers. -#### What features msTable has that jTable doesn't ? +#### What features msjTable has that jTable doesn't ? * multi columns form for Create and Edit records * you can send any fields to row-delete-request POST data (jTable only sends single key field) From 325bc49e43cf74b9e0db14272591f583ff95428a Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 14:51:51 +0330 Subject: [PATCH 16/58] Record preview feature added Record preview feature added. --- dev/jquery.jtable.preview.js | 227 +++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 dev/jquery.jtable.preview.js diff --git a/dev/jquery.jtable.preview.js b/dev/jquery.jtable.preview.js new file mode 100644 index 00000000..07498d43 --- /dev/null +++ b/dev/jquery.jtable.preview.js @@ -0,0 +1,227 @@ +/************************************************************************ + * VIEW RECORD extension for jTable * + *************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, + _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Events + recordUpdated: function (event, data) { }, + rowUpdated: function (event, data) { }, + + //Localization + messages: { + viewRecord: 'View' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$viewDiv: null, //Reference to the viewing dialog div (jQuery object) + _$viewingRow: null, //Reference to currently viewing row (jQuery object) + + /************************************************************************ + * CONSTRUCTOR AND INITIALIZATION METHODS * + *************************************************************************/ + + /* Overrides base method to do viewing-specific constructions. + *************************************************************************/ + _create: function () { + base._create.apply(this, arguments); + + if (!this.options.recordPreview) { + return; + } + + this._createViewDialogDiv(); + }, + + /* Creates and prepares preview dialog div + *************************************************************************/ + _createViewDialogDiv: function () { + var self = this; + + //Create a div for dialog and add to container element + self._$viewDiv = $('
') + .appendTo(self._$mainContainer); + + //Prepare dialog + self._$viewDiv.dialog({ + autoOpen: false, + show: self.options.dialogShowEffect, + hide: self.options.dialogHideEffect, + width: 'auto', + minWidth: '300', + modal: true, + title: self.options.messages.viewRecord, + buttons: + [{ //close button + text: self.options.messages.close, + click: function () { + self._$viewDiv.dialog('close'); + } + }], + close: function () { + var $viewForm = self._$viewDiv.find('form:first'); + self._trigger("formClosed", null, { form: $viewForm, formType: 'edit', row: self._$viewingRow }); + $viewForm.remove(); + } + }); + }, + + + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to add a 'view coomand column cell' to header row. + *************************************************************************/ + _addColumnsToHeaderRow: function ($tr) { + base._addColumnsToHeaderRow.apply(this, arguments); + if (self.options.recordPreview == true) { + $tr.append(this._createEmptyCommandHeader()); + } + }, + + /* Overrides base method to add a 'edit command cell' to a row. + *************************************************************************/ + _addCellsToRowUsingRecord: function ($row) { + var self = this; + base._addCellsToRowUsingRecord.apply(this, arguments); + + + // Add View command cell if the viewable option is sef to true + if (self.options.recordPreview == true ) { + var $span = $('').html(self.options.messages.viewRecord); + var $button = $('') + .addClass('jtable-command-button jtable-view-command-button') + .append($span) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self._showViewForm($row); + }); + $('
') + .addClass('jtable-command-column') + .append($button) + .appendTo($row); + } + + + + }, + + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Shows view form for a row. + *************************************************************************/ + _showViewForm: function ($tableRow) { + var self = this; + var record = $tableRow.data('record'); + + //Create view form + var $ViewForm = $(''); + var ColumnCount=self.options.viewFormColumns ? self.options.viewFormColumns : 1; + var CurrentColumnCount=0; + var $ViewFormTable = $('
') + .addClass('jtable-input-field-container') + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('
').appendTo($ViewForm); + var $RowContainer = $('').appendTo($ViewFormTable); + //Create input fields + for (var i = 0; i < self._fieldList.length; i++) { + + var fieldName = self._fieldList[i]; + var field = self.options.fields[fieldName]; + var fieldValue = record[fieldName]; + + + //Do not create element for non-viewable fields + if (field.view == false) { + continue; + } + + + + //Create a container div for this input field and add to form + var $fieldContainer = $('').appendTo($ViewFormTable); + CurrentColumnCount=0; + } + + //Create a label for input + $fieldContainer.append(self._createViewLabelForRecordField(fieldName)); + + //Create input element with it's current value + var currentValue = self._getValueForRecordField(record, fieldName); + $fieldContainer.append( + self._createViewForRecordField({ + fieldName: fieldName, + value: currentValue, + record: record, + formType: 'view', + form: $ViewForm + })); + } + + self._makeCascadeDropDowns($ViewForm, record, 'view'); + + + + //Open dialog + self._$viewingRow = $tableRow; + self._$viewDiv.clear(); + self._$viewDiv.append($ViewForm).dialog('open'); + self._trigger("formCreated", null, { form: $ViewForm, formType: 'view', record: record, row: $tableRow }); + } + , + + + + /* Gets text for a field of a record according to it's type. + *************************************************************************/ + _getValueForRecordField: function (record, fieldName) { + var field = this.options.fields[fieldName]; + var fieldValue = record[fieldName]; + if (field.type == 'date') { + return this._getDisplayTextForDateRecordField(field, fieldValue); + } else { + return fieldValue; + } + } + + + /************************************************************************ + * EVENT RAISING METHODS * + *************************************************************************/ + + + }); + +})(jQuery); \ No newline at end of file From 6a9f17aa4f2952a5de5ed49abd4790f014891ac8 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 14:57:48 +0330 Subject: [PATCH 17/58] Record preview file added to build text --- dev/jquery.jtable.build.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/jquery.jtable.build.txt b/dev/jquery.jtable.build.txt index 49f8ca72..18c5dc8e 100644 --- a/dev/jquery.jtable.build.txt +++ b/dev/jquery.jtable.build.txt @@ -5,9 +5,10 @@ add jquery.jtable.utils.js add jquery.jtable.forms.js add jquery.jtable.creation.js add jquery.jtable.editing.js +add jquery.jtable.preview.js add jquery.jtable.deletion.js add jquery.jtable.selecting.js add jquery.jtable.paging.js add jquery.jtable.sorting.js add jquery.jtable.dynamiccolumns.js -add jquery.jtable.masterchild.js \ No newline at end of file +add jquery.jtable.masterchild.js From 4a95e37c16c448ee038b59b1659d348242dd22ae Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 15:05:58 +0330 Subject: [PATCH 18/58] Record Preview feature added --- dev/jquery.jtable.forms.js | 179 +++++++++++++++++++++++++++++++------ 1 file changed, 153 insertions(+), 26 deletions(-) diff --git a/dev/jquery.jtable.forms.js b/dev/jquery.jtable.forms.js index 799a35b0..116b5530 100644 --- a/dev/jquery.jtable.forms.js +++ b/dev/jquery.jtable.forms.js @@ -1,5 +1,6 @@ -/************************************************************************ -* FORMS extension for jTable (base for edit/create forms) * + +/************************************************************************ +* FORMS extension for jTable (base for edit/create/preview forms) * *************************************************************************/ (function ($) { @@ -30,6 +31,14 @@ .addClass('jtable-input-label') .html(this.options.fields[fieldName].inputTitle || this.options.fields[fieldName].title); }, + /* Creates label for an input element. + *************************************************************************/ + _createViewLabelForRecordField: function (fieldName) { + //TODO: May create label tag instead of a div. + return $('
') + .addClass('jtable-view-label') + .html(this.options.fields[fieldName].title); + }, /* Creates an input element according to field type. *************************************************************************/ @@ -45,7 +54,7 @@ //If value if not supplied, use defaultValue of the field if (value == undefined || value == null) { - value = field.defaultValue; + value = field.defaultValue; } //Use custom function if supplied @@ -68,6 +77,7 @@ .append($input); } + //Create input according to field type if (field.type == 'date') { return this._createDateInputForField(field, fieldName, value); @@ -76,7 +86,7 @@ } else if (field.type == 'password') { return this._createPasswordInputForField(field, fieldName, value); } else if (field.type == 'checkbox') { - return this._createCheckboxForField(field, fieldName, value); + return this._createCheckboxForField(field, fieldName, value, formType); } else if (field.options) { if (field.type == 'radiobutton') { return this._createRadioButtonListForField(field, fieldName, value, record, formType); @@ -88,6 +98,64 @@ } }, + /* Creates an View element according to field type. + *************************************************************************/ + _createViewForRecordField: function (funcParams) { + var fieldName = funcParams.fieldName, + value = funcParams.value, + record = funcParams.record, + formType = funcParams.formType, + form = funcParams.form; + + //Get the field + var field = this.options.fields[fieldName]; + + //If value if not supplied, use defaultValue of the field + if (value == undefined || value == null) { + value = field.defaultViewValue; + } + + //Use custom function if supplied + if (field.customView) { + var $view = $(field.customView({ + value: value, + record: record, + formType: formType, + form: form + })); + + //Add id attribute if does not exists + if (!$view.attr('id')) { + $view.attr('id', 'View-' + fieldName); + } + + //Wrap input element with div + return $('
') + .addClass('jtable-view jtable-custom-view') + .append($view); + } + + + //Create input according to field type + if (field.type == 'date') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'textarea') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'password') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'checkbox') { + return this._createTextViewForCheckboxField(field, fieldName, value,formType); + } else if (field.options) { + if (field.type == 'radiobutton') { + return this._createTextViewForOptions(field, fieldName, value, record, formType); + } else { + return this._createTextViewForOptions(field, fieldName, value, record, formType); + } + } else { + return this._createTextViewForField(field, fieldName, value); + } + }, + //Creates a hidden input element with given name and value. _createInputForHidden: function (fieldName, value) { if (value == undefined) { @@ -105,20 +173,9 @@ if(value != undefined) { $input.val(value); } - + var displayFormat = field.displayFormat || this.options.defaultDateFormat; - var changeMonth = field.changeMonth || this.options.defaultChangeMonth; - var changeYear = field.changeYear || this.options.defaultChangeYear; - var yearRange = field.yearRange || this.options.defaultYearRange; - var maxDate = field.maxDate || this.options.defaultMaxDate; - - $input.datepicker({ - dateFormat: displayFormat, - changeMonth: changeMonth, - changeYear: changeYear, - yearRange: yearRange, - maxDate: maxDate - }); + $input.datepicker({ dateFormat: displayFormat }); return $('
') .addClass('jtable-input jtable-date-input') .append($input); @@ -131,7 +188,7 @@ if (value != undefined) { $textArea.val(value); } - + return $('
') .addClass('jtable-input jtable-textarea-input') .append($textArea); @@ -140,11 +197,11 @@ /* Creates a standart textbox for a field. *************************************************************************/ _createTextInputForField: function (field, fieldName, value) { - var $input = $(''); + var $input = $(''); if (value != undefined) { $input.val(value); } - + return $('
') .addClass('jtable-input jtable-text-input') .append($input); @@ -153,19 +210,74 @@ /* Creates a password input for a field. *************************************************************************/ _createPasswordInputForField: function (field, fieldName, value) { - var $input = $(''); + var $input = $(''); if (value != undefined) { $input.val(value); } - + return $('
') .addClass('jtable-input jtable-password-input') .append($input); }, + /* Creates a standart view span for a field. + *************************************************************************/ + _createTextViewForField: function (field, fieldName, value) { + var $view = $('
'); + if (value != undefined) { + $view.html(value); + } + + return $('
') + .addClass('jtable-view jtable-view-text') + .append($view); + }, + + /* Creates a standart view for DropDownBoxs and Radio Options field. + *************************************************************************/ + _createTextViewForOptions: function (field, fieldName, value, record, source ) { + var $view = $('
'); + var options = this._getOptionsForField(fieldName, { + record: record, + source: source + }); + + var DisplayValue = this._getOptionsSelectedItem(options,value) + + if (DisplayValue != undefined && DisplayValue != null ) { + $view.html(DisplayValue); + } + + return $('
') + .addClass('jtable-view jtable-view-text') + .append($view); + }, + /* Creates a view for checkboxfor a field. + *************************************************************************/ + _createTextViewForCheckboxField: function (field, fieldName, value, formType) { + + var $view = $('
'); + var self = this; + + //If value is undefined, get unchecked state's value + if (value == undefined) { + value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value; + } + var DisplayValue = self._getCheckBoxTextForFieldByValue(fieldName, value) + + if (DisplayValue != undefined && DisplayValue != null ) { + $view.html(DisplayValue); + } + + return $('
') + .addClass('jtable-view jtable-view-text') + .append($view); + + }, + /* Creates a checkboxfor a field. *************************************************************************/ - _createCheckboxForField: function (field, fieldName, value) { + _createCheckboxForField: function (field, fieldName, value, formType) { var self = this; //If value is undefined, get unchecked state's value @@ -178,7 +290,7 @@ .addClass('jtable-input jtable-checkbox-input'); //Create checkbox and check if needed - var $checkBox = $('') + var $checkBox = $('') .appendTo($containerDiv); if (value != undefined) { $checkBox.val(value); @@ -193,6 +305,8 @@ $checkBox.attr('checked', 'checked'); } + + //This method sets checkbox's value and text according to state of the checkbox var refreshCheckBoxValueAndText = function () { var checkboxProps = self._getCheckBoxPropertiesForFieldByState(fieldName, $checkBox.is(':checked')); @@ -247,7 +361,7 @@ return $containerDiv; }, - + /* Fills a dropdown list with given options. *************************************************************************/ _fillDropDownListWithOptions: function ($select, options, value) { @@ -258,6 +372,15 @@ .appendTo($select); } }, + /* get selected item from given options + *************************************************************************/ + _getOptionsSelectedItem: function (options, value) { + for (var i = 0; i < options.length; i++) { + if (options[i].Value == value ) + return options[i].DisplayText ; + } + return null; + }, /* Creates depended values object from given form. *************************************************************************/ @@ -307,6 +430,10 @@ .html(option.DisplayText) .appendTo($radioButtonDiv); + if (source = 'view'){ + $radioButton.attr('disabled', 'disabled'); + } + if (field.setOnTextClick != false) { $textSpan .addClass('jtable-option-text-clickable') @@ -384,7 +511,7 @@ } var field = self.options.fields[fieldName]; - + //check if this combobox depends on others if (!field.dependsOn) { return; From d6e7ccb80a2e44b582575cbcc21c62121113d686 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 15:50:03 +0330 Subject: [PATCH 19/58] record preview form style --- lib/themes/lightcolor/blue/jtable.css | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/lightcolor/blue/jtable.css b/lib/themes/lightcolor/blue/jtable.css index 431e332d..dedbf1aa 100644 --- a/lib/themes/lightcolor/blue/jtable.css +++ b/lib/themes/lightcolor/blue/jtable.css @@ -528,3 +528,37 @@ div.jtable-busy-message { border-color: #2B5177; background-color: #78B1ED; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From 1eb2a92457ee34c0e4951d00a2994b1aa6bfc36e Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 15:51:28 +0330 Subject: [PATCH 20/58] report.png path corrected --- lib/themes/lightcolor/blue/jtable.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/themes/lightcolor/blue/jtable.css b/lib/themes/lightcolor/blue/jtable.css index dedbf1aa..c3e98ad5 100644 --- a/lib/themes/lightcolor/blue/jtable.css +++ b/lib/themes/lightcolor/blue/jtable.css @@ -554,7 +554,7 @@ form.jtable-dialog-form div.jtable-view { max-width : 400px; } div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { - background: url('../../metro/report.png') no-repeat; + background: url('../../report.png') no-repeat; width: 16px; height: 16px; opacity: 0.4; From 95f9f17a75fc14f4f4a1ed9fd63e590e2b41dd48 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 15:54:11 +0330 Subject: [PATCH 21/58] preview icon added --- lib/themes/lightcolor/report.png | Bin 0 -> 327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/themes/lightcolor/report.png diff --git a/lib/themes/lightcolor/report.png b/lib/themes/lightcolor/report.png new file mode 100644 index 0000000000000000000000000000000000000000..c472bf7c4d8286ea8b8c72349ebfc47814780478 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85p>QK$!8;-MT+OLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwA=yjEdAMcX}H978Ppr(WL3$LJ{1`p{J=YHpN@)8bD|i&Pc`1Xk(yng@84+-q@K z7?82$k9(Q<=Ir-(EKW{8^X#5wwC|*}hVmoA$umxJZJRMQXTeQo Date: Tue, 9 Nov 2021 15:56:08 +0330 Subject: [PATCH 22/58] preview style added --- lib/themes/basic/jtable_basic.css | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/themes/basic/jtable_basic.css b/lib/themes/basic/jtable_basic.css index a456673f..81951b1f 100644 --- a/lib/themes/basic/jtable_basic.css +++ b/lib/themes/basic/jtable_basic.css @@ -289,3 +289,39 @@ div.jtable-busy-message { background-color: #ddd; font-size: 1.25em; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + + From ce0c16884b46c626756eaa20e1aadd080680218b Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 15:56:50 +0330 Subject: [PATCH 23/58] preview icon added --- lib/themes/basic/report.png | Bin 0 -> 327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/themes/basic/report.png diff --git a/lib/themes/basic/report.png b/lib/themes/basic/report.png new file mode 100644 index 0000000000000000000000000000000000000000..c472bf7c4d8286ea8b8c72349ebfc47814780478 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85p>QK$!8;-MT+OLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwA=yjEdAMcX}H978Ppr(WL3$LJ{1`p{J=YHpN@)8bD|i&Pc`1Xk(yng@84+-q@K z7?82$k9(Q<=Ir-(EKW{8^X#5wwC|*}hVmoA$umxJZJRMQXTeQo Date: Tue, 9 Nov 2021 16:00:18 +0330 Subject: [PATCH 24/58] preview style added --- lib/themes/lightcolor/gray/jtable.css | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/themes/lightcolor/gray/jtable.css b/lib/themes/lightcolor/gray/jtable.css index 5497e490..a1997079 100644 --- a/lib/themes/lightcolor/gray/jtable.css +++ b/lib/themes/lightcolor/gray/jtable.css @@ -528,3 +528,38 @@ div.jtable-busy-message { border-color: #5f5f5f; background-color: #8e8e8e; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../lightcolor/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 242a3cf972cd66058f309c53004a2e21628af11d Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:03:23 +0330 Subject: [PATCH 25/58] preview style added --- lib/themes/lightcolor/green/jtable.css | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/themes/lightcolor/green/jtable.css b/lib/themes/lightcolor/green/jtable.css index 34d21add..999cf8a0 100644 --- a/lib/themes/lightcolor/green/jtable.css +++ b/lib/themes/lightcolor/green/jtable.css @@ -525,3 +525,38 @@ div.jtable-busy-message { border-color: #167509; background-color: #42d033; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../lightcolor/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 7ffc66d75cb5a37e484ac929b1239cd3f8db6d6d Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:04:10 +0330 Subject: [PATCH 26/58] preview style added --- lib/themes/lightcolor/orange/jtable.css | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/lightcolor/orange/jtable.css b/lib/themes/lightcolor/orange/jtable.css index 07a46b5d..6cd66388 100644 --- a/lib/themes/lightcolor/orange/jtable.css +++ b/lib/themes/lightcolor/orange/jtable.css @@ -526,3 +526,37 @@ div.jtable-busy-message { border-color: #a14100; background-color: #f36301; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../lightcolor/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From 966108d037106fda5173c9cfb5d40b823ae6e327 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:04:39 +0330 Subject: [PATCH 27/58] preview style added --- lib/themes/lightcolor/red/jtable.css | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/lightcolor/red/jtable.css b/lib/themes/lightcolor/red/jtable.css index 0045a4ab..626cb939 100644 --- a/lib/themes/lightcolor/red/jtable.css +++ b/lib/themes/lightcolor/red/jtable.css @@ -523,3 +523,37 @@ div.jtable-busy-message { border-color: #772b2b; background-color: #ea2a2a; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../lightcolor/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From a58a0e038021545bf0f735233c6993a9d9fcee28 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:05:22 +0330 Subject: [PATCH 28/58] preview style added --- lib/themes/lightcolor/blue/jtable.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/themes/lightcolor/blue/jtable.css b/lib/themes/lightcolor/blue/jtable.css index c3e98ad5..92f32f90 100644 --- a/lib/themes/lightcolor/blue/jtable.css +++ b/lib/themes/lightcolor/blue/jtable.css @@ -554,7 +554,7 @@ form.jtable-dialog-form div.jtable-view { max-width : 400px; } div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { - background: url('../../report.png') no-repeat; + background: url('../../lightcolor/report.png') no-repeat; width: 16px; height: 16px; opacity: 0.4; From 6c00fe1038899d36dca14d21dd404ef938fc3df9 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:06:08 +0330 Subject: [PATCH 29/58] preview icon added to metro --- lib/themes/metro/report.png | Bin 0 -> 327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/themes/metro/report.png diff --git a/lib/themes/metro/report.png b/lib/themes/metro/report.png new file mode 100644 index 0000000000000000000000000000000000000000..c472bf7c4d8286ea8b8c72349ebfc47814780478 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85p>QK$!8;-MT+OLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwwA=yjEdAMcX}H978Ppr(WL3$LJ{1`p{J=YHpN@)8bD|i&Pc`1Xk(yng@84+-q@K z7?82$k9(Q<=Ir-(EKW{8^X#5wwC|*}hVmoA$umxJZJRMQXTeQo Date: Tue, 9 Nov 2021 16:08:44 +0330 Subject: [PATCH 30/58] preview style added to metro blue --- lib/themes/metro/blue/jtable.css | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/themes/metro/blue/jtable.css b/lib/themes/metro/blue/jtable.css index 88429fe5..01162c6d 100644 --- a/lib/themes/metro/blue/jtable.css +++ b/lib/themes/metro/blue/jtable.css @@ -493,3 +493,36 @@ div.jtable-busy-message { background-color: #0b67cd; background-position: 8px; } +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From 627d9d518d84e330dc2a57ab67d6d3b1b888390f Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:09:45 +0330 Subject: [PATCH 31/58] preview style added to metro brown --- lib/themes/metro/brown/jtable.css | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/metro/brown/jtable.css b/lib/themes/metro/brown/jtable.css index aa22a8f3..943f0bac 100644 --- a/lib/themes/metro/brown/jtable.css +++ b/lib/themes/metro/brown/jtable.css @@ -493,3 +493,37 @@ div.jtable-busy-message { background-color: #61380a; background-position: 8px; } +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 0cd9e7ac8ca0ff896c2db642c5f97af4277d1fd4 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:10:24 +0330 Subject: [PATCH 32/58] preview style added to metro crismon --- lib/themes/metro/crimson/jtable.css | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/themes/metro/crimson/jtable.css b/lib/themes/metro/crimson/jtable.css index 9c0344b2..42454790 100644 --- a/lib/themes/metro/crimson/jtable.css +++ b/lib/themes/metro/crimson/jtable.css @@ -493,3 +493,38 @@ div.jtable-busy-message { background-color: #a10000; background-position: 8px; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 67f95c4cb35a6289ae8baf2e477dc98680ba8cd5 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:20:28 +0330 Subject: [PATCH 33/58] Update README.md --- README.md | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 642963a6..bef2a535 100644 --- a/README.md +++ b/README.md @@ -7,28 +7,13 @@ What is msjTable msjTable is a jQuery plugin (forked from jTable with more features added) used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: -* Automatically creates HTML table and loads records from server using AJAX. -* Creates 'create new record' jQueryUI dialog form. When user creates a record, it sends data to server using AJAX and adds the same record to the table in the page. -* Creates 'edit record' jQueryUI dialog form. When user edits a record, it updates server using AJAX and updates all cells on the table in the page. -* Allow user to 'delete a record' by jQueryUI dialog based confirmation. When user deletes a record, it deletes the record from server using AJAX and deletes the record from the table in the page. -* Shows animations for create/delete/edit operations on the table. -* Supports server side paging using AJAX. -* Supports server side sorting using AJAX. -* Supports master/child tables. -* Allows user to select rows. -* Allows user to resize columns. -* Allows user to show/hide columns. -* Exposes some events to enable validation with forms. -* It can be localized easily. -* All styling of table and forms are defined in a CSS file, so you can easily change style of everything to use plugin in your pages. CSS file is well defined and commented. -* It comes with pre-defined color themes. -* It is not depended on any server side technology. -* It is platform independed and works on all common browsers. #### What features msjTable has that jTable doesn't ? -* multi columns form for Create and Edit records -* you can send any fields to row-delete-request POST data (jTable only sends single key field) +* Record Preview features +* Multi columns form for Create, Edit and Preview dialogs. +* You can send any required fields to row-delete-request POST data (jTable only sends single key field) + Notes ====== From efc8789bb6c91ecbcea9c0db03b57a4c351802c7 Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:20:54 +0330 Subject: [PATCH 34/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bef2a535..599bf80c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ msjTable is a jQuery plugin (forked from jTable with more features added) used t #### What features msjTable has that jTable doesn't ? -* Record Preview features +* Record Preview feature * Multi columns form for Create, Edit and Preview dialogs. * You can send any required fields to row-delete-request POST data (jTable only sends single key field) From 663d43063e034191e1a15d17b7836d039a6d9cec Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 16:38:17 +0330 Subject: [PATCH 35/58] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 599bf80c..65520876 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ What is msjTable [![A screenshot of jTable](https://raw.githubusercontent.com/hikalkan/jtable/master/screenshot.png)](http://jtable.org/) -msjTable is a jQuery plugin (forked from jTable with more features added) used to create AJAX based CRUD tables without coding HTML or Javascript. It has several features including: - +#### msjTable is a jQuery plugin forked from jTable, to create AJAX based CRUD tables without coding HTML or Javascript. #### What features msjTable has that jTable doesn't ? From c93c114bff6d69c9a563cc02846db9fe74e4db1e Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 9 Nov 2021 19:52:56 +0330 Subject: [PATCH 36/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65520876..95eff11c 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ What is msjTable #### msjTable is a jQuery plugin forked from jTable, to create AJAX based CRUD tables without coding HTML or Javascript. -#### What features msjTable has that jTable doesn't ? +#### Additional features of msjTable : * Record Preview feature * Multi columns form for Create, Edit and Preview dialogs. From 1876c11aade3fbc38eed8ce8723d9cadb309cb2e Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:53:08 +0330 Subject: [PATCH 37/58] Update jtable.css --- lib/themes/metro/darkorange/jtable.css | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/metro/darkorange/jtable.css b/lib/themes/metro/darkorange/jtable.css index 3590e760..3b134223 100644 --- a/lib/themes/metro/darkorange/jtable.css +++ b/lib/themes/metro/darkorange/jtable.css @@ -493,3 +493,37 @@ div.jtable-busy-message { background-color: #b8310a; background-position: 8px; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From 1ee4cbf6799d39698506a41126e0c41412340ec5 Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:53:34 +0330 Subject: [PATCH 38/58] Update jtable.css --- lib/themes/metro/green/jtable.css | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/themes/metro/green/jtable.css b/lib/themes/metro/green/jtable.css index 6ad58287..5706ec22 100644 --- a/lib/themes/metro/green/jtable.css +++ b/lib/themes/metro/green/jtable.css @@ -493,3 +493,38 @@ div.jtable-busy-message { background-color: #008100; background-position: 8px; } + +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 79d61d280a94c6b16934bc4ea310347c3c9622eb Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:54:05 +0330 Subject: [PATCH 39/58] Update jtable.css --- lib/themes/metro/pink/jtable.css | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/themes/metro/pink/jtable.css b/lib/themes/metro/pink/jtable.css index d980c038..d3303876 100644 --- a/lib/themes/metro/pink/jtable.css +++ b/lib/themes/metro/pink/jtable.css @@ -493,3 +493,36 @@ div.jtable-busy-message { background-color: #7d0085; background-position: 8px; } +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From e158fa38b29dd9c53817c7058d76f89e5fa66f11 Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:54:38 +0330 Subject: [PATCH 40/58] Update jtable.css --- lib/themes/metro/purple/jtable.css | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/themes/metro/purple/jtable.css b/lib/themes/metro/purple/jtable.css index 2b9affc4..be540a4d 100644 --- a/lib/themes/metro/purple/jtable.css +++ b/lib/themes/metro/purple/jtable.css @@ -493,3 +493,36 @@ div.jtable-busy-message { background-color: #3e1a98; background-position: 8px; } +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} From 4de62eb5d617aff7b975ba19f3c077a52cf86fb3 Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:55:00 +0330 Subject: [PATCH 41/58] Update jtable.css --- lib/themes/metro/red/jtable.css | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/themes/metro/red/jtable.css b/lib/themes/metro/red/jtable.css index 3c2e20a9..0d22bd94 100644 --- a/lib/themes/metro/red/jtable.css +++ b/lib/themes/metro/red/jtable.css @@ -493,3 +493,37 @@ div.jtable-busy-message { background-color: #c30000; background-position: 8px; } +form.jtable-dialog-form table.jtable-view-field-container-grid { + border-spacing: 25px 15px; + border-collapse: separate; + +} +table.jtable-view-field-container-grid tr.jtable-view-field-container-row td.jtable-view-field-container { + padding: 2px 0px 3px 0px; + border-bottom: 1px solid #ddd; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid tr.jtable-view-field-container-row:last-child td.jtable-view-field-container { + border: none; +} + +form.jtable-dialog-form table.jtable-view-field-container-grid div.jtable-view-label { + padding: 3px 3px; + font-size: 1.1em; + font-weight:bold; + color: #666; +} +form.jtable-dialog-form div.jtable-view { + padding: 4px; + max-width : 400px; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button { + background: url('../../metro/report.png') no-repeat; + width: 16px; + height: 16px; + opacity: 0.4; +} +div.jtable-main-container > table.jtable > tbody > tr.jtable-data-row > td > .jtable-view-command-button:hover { + opacity: 0.8; +} + From 77679df8b9e8eef0d5e0d19b9111f67503b6614a Mon Sep 17 00:00:00 2001 From: Bird Date: Wed, 10 Nov 2021 17:58:41 +0330 Subject: [PATCH 42/58] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 95eff11c..f9293638 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ What is msjTable * Multi columns form for Create, Edit and Preview dialogs. * You can send any required fields to row-delete-request POST data (jTable only sends single key field) +Read More about additional features [here](https://m-shaeri.ir/blog/jquery-jtable-awesome-full-featured-plugin-for-crud-table/) Notes ====== From 2d57345b817c6a1cfa9a1dd3036b8edced55a36b Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:00:31 +0330 Subject: [PATCH 43/58] Update README.md --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index f9293638..171754a4 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,30 @@ What is msjTable * Multi columns form for Create, Edit and Preview dialogs. * You can send any required fields to row-delete-request POST data (jTable only sends single key field) +```javascript +$("#myjtable").jtable({ +..., +recordPreview : true, +..., +fields : { + ..., + first_name : { + title : 'First name', + edit : true, + create : true, + preview : true, + type : 'text' + }, + ... +}); + +``` + + + + + + Read More about additional features [here](https://m-shaeri.ir/blog/jquery-jtable-awesome-full-featured-plugin-for-crud-table/) Notes From ae849bbde91b9f11504b1cfe5638c36fb6404419 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:01:28 +0330 Subject: [PATCH 44/58] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 171754a4..c12031a2 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,7 @@ What is msjTable #### Additional features of msjTable : -* Record Preview feature -* Multi columns form for Create, Edit and Preview dialogs. -* You can send any required fields to row-delete-request POST data (jTable only sends single key field) - +* Record Preview feature : ```javascript $("#myjtable").jtable({ ..., @@ -32,6 +29,11 @@ fields : { ``` +* Multi columns form for Create, Edit and Preview dialogs. +* You can send any required fields to row-delete-request POST data (jTable only sends single key field) + + + From 9e2181536aadd24b817f36bbb8fb838e626f122f Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:04:03 +0330 Subject: [PATCH 45/58] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c12031a2..fff7683a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ What is msjTable ```javascript $("#myjtable").jtable({ ..., -recordPreview : true, +recordPreview : true, // setting recordPreview to true, an view icon will appeare beside edit icon in each row ..., fields : { ..., @@ -21,7 +21,7 @@ fields : { title : 'First name', edit : true, create : true, - preview : true, + preview : true, // specify which field you want to be shown in preview dialog type : 'text' }, ... From cabff18ca59715b13050f2cc7df68bf7d50c04a9 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:04:53 +0330 Subject: [PATCH 46/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fff7683a..83e2cdfa 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ What is msjTable ```javascript $("#myjtable").jtable({ ..., -recordPreview : true, // setting recordPreview to true, an view icon will appeare beside edit icon in each row +recordPreview : true, // setting recordPreview to true, a view icon will appeare beside edit icon in each row ..., fields : { ..., From 421697cc1373b6299316ab2417a3538a6491f495 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:35:32 +0330 Subject: [PATCH 47/58] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 83e2cdfa..fc997c91 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,17 @@ fields : { ``` * Multi columns form for Create, Edit and Preview dialogs. +```javascript +$("#myjtable").jtable({ +..., +createFormColumns : 2, +editFormColumns : 2, +viewFormColumns : 2, +... + +}); +``` + * You can send any required fields to row-delete-request POST data (jTable only sends single key field) From eea5fe7d8e27fb4e385f53e469ae1f161ed84b0b Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:37:49 +0330 Subject: [PATCH 48/58] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fc997c91..f4743990 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ fields : { ```javascript $("#myjtable").jtable({ ..., -createFormColumns : 2, -editFormColumns : 2, -viewFormColumns : 2, +createFormColumns : 2, // set the number of columns in new record dialog +editFormColumns : 2, // set the number of columns in edit record dialog +viewFormColumns : 2, // set the number of columns in record preview dialog ... }); From 5a84c9f2a1c7d8618f16358577215d18b0cb1799 Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:38:24 +0330 Subject: [PATCH 49/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4743990..ea0119fe 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ fields : { preview : true, // specify which field you want to be shown in preview dialog type : 'text' }, - ... + ... }); ``` From ddecd6c1e7d51282a53e9acafe3e505107bfbebf Mon Sep 17 00:00:00 2001 From: Bird Date: Sun, 14 Nov 2021 08:51:58 +0330 Subject: [PATCH 50/58] Send field to server if field delete option is explicitly set to true --- dev/jquery.jtable.deletion.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/jquery.jtable.deletion.js b/dev/jquery.jtable.deletion.js index 2b7fc8a4..395a4e40 100644 --- a/dev/jquery.jtable.deletion.js +++ b/dev/jquery.jtable.deletion.js @@ -369,8 +369,8 @@ var fieldName = this._fieldList[i]; var field = this.options.fields[fieldName]; - //Do not send this field to server if field delete option is not true - if (field.delete == false) { + //Do not send this field to server if field delete option is not explicitly set to true + if (!field.delete) { continue; } From 83e7be68be58919afa61930fb1b39ff61a004813 Mon Sep 17 00:00:00 2001 From: Bird Date: Mon, 15 Nov 2021 12:46:17 +0330 Subject: [PATCH 51/58] Update jTable.jquery.json --- jTable.jquery.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/jTable.jquery.json b/jTable.jquery.json index b66d95c6..bbc8e2d1 100644 --- a/jTable.jquery.json +++ b/jTable.jquery.json @@ -1,6 +1,6 @@ { - "name": "jTable", - "title": "jTable", + "name": "msjTable", + "title": "msjTable", "description": "A JQuery plugin to create AJAX based CRUD tables (grids). It supports paging, sorting, selecting, master/child tables, show/hide/resize columns, localization, themes and more.", "keywords": [ "ajax", @@ -14,14 +14,14 @@ "version": "2.6.0", "author": { "name": "Halil ibrahim Kalkan", - "email": "halil@jtable.org", + "email": "halil@jtable.org", "url": "http://www.halilibrahimkalkan.com" }, "maintainers": [ { - "name": "Halil ibrahim Kalkan", - "email": "hi_kalkan@yahoo.com", - "url": "http://www.halilibrahimkalkan.com" + "name": "Mostafa Shaeri", + + "url": "https://m-shaeri.ir" } ], "licenses": [ @@ -30,7 +30,7 @@ "url": "http://en.wikipedia.org/wiki/MIT_License" } ], - "bugs": "https://github.com/volosoft/jtable/issues", + "bugs": "https://github.com/birddevelper/msjtable/issues", "homepage": "http://www.jtable.org", "docs": "http://jtable.org/Home/Documents", "download": "http://jtable.org/Home/Downloads", @@ -38,4 +38,4 @@ "jquery": ">=1.9.1", "jqueryui": ">=1.9.2" } -} \ No newline at end of file +} From 25d83a989461b4996aadec3362b1c14d760140a4 Mon Sep 17 00:00:00 2001 From: Bird Date: Mon, 15 Nov 2021 12:55:48 +0330 Subject: [PATCH 52/58] Delete jquery.jtable.js --- jquery.jtable.js | 5110 ---------------------------------------------- 1 file changed, 5110 deletions(-) delete mode 100644 jquery.jtable.js diff --git a/jquery.jtable.js b/jquery.jtable.js deleted file mode 100644 index e69f83e0..00000000 --- a/jquery.jtable.js +++ /dev/null @@ -1,5110 +0,0 @@ -/* - -jTable 2.5.0 -http://www.jtable.org - ---------------------------------------------------------------------------- - -Copyright (C) 2011-2014 by Halil İbrahim Kalkan (http://www.halilibrahimkalkan.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - -/************************************************************************ -* 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', - defaultChangeMonth: false, - defaultChangeYear: false, - defaultYearRange: 'c-10:c+10', - defaultMaxDate: null, - 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
') + .addClass('jtable-view-field-container') + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('
(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) - - _extraFieldTypes:[], - - /************************************************************************ - * 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 = []; - this._extraFieldTypes = []; - }, - - /* 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 }); - } - - var extraFieldType = this._findItemByProperty(this._extraFieldTypes, 'type', field.type); - if(extraFieldType && extraFieldType.creator){ - return extraFieldType.creator(record, field); - } - else 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) { - return this._findItemByProperty(options, 'Value', value); - }, - - /* Finds an option object by given value. - *************************************************************************/ - _findItemByProperty: function (items, key, value) { - for (var i = 0; i < items.length; i++) { - if (items[i][key] == value) { - return items[i]; - } - } - - return {}; //no item 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); - try { - return this._formatDate(displayFormat, date); - } catch (e) { - return date; - } - }, - - /* Format the date/time field. - *************************************************************************/ - _formatDate: function (format, date) { - - var pad = function (n, width, z) { - z = z || '0'; - n = n + ''; - return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; - }; - - format = format.replace('ss', pad(date.getSeconds(), 2)); - format = format.replace('s', date.getSeconds()); - format = format.replace('dd', pad(date.getDate(), 2)); - format = format.replace('d', date.getDate()); - format = format.replace('mm', pad(date.getMinutes(), 2)); - format = format.replace('m', date.getMinutes()); - //format = format.replace('MMMM', monthNames[date.getMonth()]); - //format = format.replace('MMM', monthNames[date.getMonth()].substring(0, 3)); - format = format.replace('MM', pad(date.getMonth() + 1, 2)); - format = format.replace(/M(?![ao])/, date.getMonth() + 1); - //format = format.replace('DD', Days[date.getDay()]); - //format = format.replace(/D(?!e)/, Days[date.getDay()].substring(0, 3)); - format = format.replace('yyyy', date.getFullYear()); - format = format.replace('YYYY', date.getFullYear()); - format = format.replace('yy', (date.getFullYear() + "").substring(2)); - format = format.replace('YY', (date.getFullYear() + "").substring(2)); - format = format.replace('HH', pad(date.getHours(), 2)); - format = format.replace('H', date.getHours()); - format = format.replace('hh', pad(date.getHours(), 2)); - format = format.replace('h', date.getHours()); - return format; - }, - - /* 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) - * 2011-10-15T14:42:51 (ISO 8601) - *************************************************************************/ - _parseDate: function (dateString) { - try { - 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 if (dateString.indexOf('T') > 0) { //Format: ISO 8601 2009-10-15T14:42:51 - var dtstr = dateString.replace(/\D/g, " "); - var dtcomps = dtstr.split(" "); - dtcomps[1]--; // modify month between 1 based ISO 8601 and zero based Date - return new Date( - Date.UTC( - dtcomps[0], - dtcomps[1], - dtcomps[2], - dtcomps[3], - dtcomps[4], - dtcomps[5])); - } else { - throw 'Given date is not properly formatted: ' + dateString; - } - } catch (e) { - this._logWarn(e); - 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)); - - -/************************************************************************ -* Some UTULITY methods used by jTable * -*************************************************************************/ -(function ($) { - - $.extend(true, $.hik.jtable.prototype, { - - /* Gets property value of an object recursively. - *************************************************************************/ - _getPropertyOfObject: function (obj, propName) { - if (propName.indexOf('.') < 0) { - return obj[propName]; - } else { - var preDot = propName.substring(0, propName.indexOf('.')); - var postDot = propName.substring(propName.indexOf('.') + 1); - return this._getPropertyOfObject(obj[preDot], postDot); - } - }, - - /* Sets property value of an object recursively. - *************************************************************************/ - _setPropertyOfObject: function (obj, propName, value) { - if (propName.indexOf('.') < 0) { - obj[propName] = value; - } else { - var preDot = propName.substring(0, propName.indexOf('.')); - var postDot = propName.substring(propName.indexOf('.') + 1); - this._setPropertyOfObject(obj[preDot], postDot, value); - } - }, - - /* Inserts a value to an array if it does not exists in the array. - *************************************************************************/ - _insertToArrayIfDoesNotExists: function (array, value) { - if ($.inArray(value, array) < 0) { - array.push(value); - } - }, - - /* Finds index of an element in an array according to given comparision function - *************************************************************************/ - _findIndexInArray: function (value, array, compareFunc) { - - //If not defined, use default comparision - if (!compareFunc) { - compareFunc = function (a, b) { - return a == b; - }; - } - - for (var i = 0; i < array.length; i++) { - if (compareFunc(value, array[i])) { - return i; - } - } - - return -1; - }, - - /* Normalizes a number between given bounds or sets to a defaultValue - * if it is undefined - *************************************************************************/ - _normalizeNumber: function (number, min, max, defaultValue) { - if (number == undefined || number == null || isNaN(number)) { - return defaultValue; - } - - if (number < min) { - return min; - } - - if (number > max) { - return max; - } - - return number; - }, - - /* Formats a string just like string.format in c#. - * Example: - * _formatString('Hello {0}','Halil') = 'Hello Halil' - *************************************************************************/ - _formatString: function () { - if (arguments.length == 0) { - return null; - } - - var str = arguments[0]; - for (var i = 1; i < arguments.length; i++) { - var placeHolder = '{' + (i - 1) + '}'; - str = str.replace(placeHolder, arguments[i]); - } - - return str; - }, - - /* Checks if given object is a jQuery Deferred object. - */ - _isDeferredObject: function (obj) { - return obj.then && obj.done && obj.fail; - }, - - //Logging methods //////////////////////////////////////////////////////// - - _logDebug: function (text) { - if (!window.console) { - return; - } - - console.log('jTable DEBUG: ' + text); - }, - - _logInfo: function (text) { - if (!window.console) { - return; - } - - console.log('jTable INFO: ' + text); - }, - - _logWarn: function (text) { - if (!window.console) { - return; - } - - console.log('jTable WARNING: ' + text); - }, - - _logError: function (text) { - if (!window.console) { - return; - } - - console.log('jTable ERROR: ' + text); - } - - }); - - /* Fix for array.indexOf method in IE7. - * This code is taken from http://www.tutorialspoint.com/javascript/array_indexof.htm */ - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (elt) { - var len = this.length; - var from = Number(arguments[1]) || 0; - from = (from < 0) - ? Math.ceil(from) - : Math.floor(from); - if (from < 0) - from += len; - for (; from < len; from++) { - if (from in this && - this[from] === elt) - return from; - } - return -1; - }; - } - -})(jQuery); - - -/************************************************************************ -* FORMS extension for jTable (base for edit/create forms) * -*************************************************************************/ -(function ($) { - - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Submits a form asynchronously using AJAX. - * This method is needed, since form submitting logic can be overrided - * by extensions. - *************************************************************************/ - _submitFormUsingAjax: function (url, formData, success, error) { - this._ajax({ - url: url, - data: formData, - success: success, - error: error - }); - }, - - /* Creates label for an input element. - *************************************************************************/ - _createInputLabelForRecordField: function (fieldName) { - //TODO: May create label tag instead of a div. - return $('
') - .addClass('jtable-input-label') - .html(this.options.fields[fieldName].inputTitle || this.options.fields[fieldName].title); - }, - - /* Creates an input element according to field type. - *************************************************************************/ - _createInputForRecordField: function (funcParams) { - var fieldName = funcParams.fieldName, - value = funcParams.value, - record = funcParams.record, - formType = funcParams.formType, - form = funcParams.form; - - //Get the field - var field = this.options.fields[fieldName]; - - //If value if not supplied, use defaultValue of the field - if (value == undefined || value == null) { - value = field.defaultValue; - } - - //Use custom function if supplied - if (field.input) { - var $input = $(field.input({ - value: value, - record: record, - formType: formType, - form: form - })); - - //Add id attribute if does not exists - if (!$input.attr('id')) { - $input.attr('id', 'Edit-' + fieldName); - } - - //Wrap input element with div - return $('
') - .addClass('jtable-input jtable-custom-input') - .append($input); - } - - //Create input according to field type - if (field.type == 'date') { - return this._createDateInputForField(field, fieldName, value); - } else if (field.type == 'textarea') { - return this._createTextAreaForField(field, fieldName, value); - } else if (field.type == 'password') { - return this._createPasswordInputForField(field, fieldName, value); - } else if (field.type == 'checkbox') { - return this._createCheckboxForField(field, fieldName, value); - } else if (field.options) { - if (field.type == 'radiobutton') { - return this._createRadioButtonListForField(field, fieldName, value, record, formType); - } else { - return this._createDropDownListForField(field, fieldName, value, record, formType, form); - } - } else { - return this._createTextInputForField(field, fieldName, value); - } - }, - - //Creates a hidden input element with given name and value. - _createInputForHidden: function (fieldName, value) { - if (value == undefined) { - value = ""; - } - - return $('') - .val(value); - }, - - /* Creates a date input for a field. - *************************************************************************/ - _createDateInputForField: function (field, fieldName, value) { - var $input = $(''); - if(value != undefined) { - $input.val(value); - } - - var displayFormat = field.displayFormat || this.options.defaultDateFormat; - var changeMonth = field.changeMonth || this.options.defaultChangeMonth; - var changeYear = field.changeYear || this.options.defaultChangeYear; - var yearRange = field.yearRange || this.options.defaultYearRange; - var maxDate = field.maxDate || this.options.defaultMaxDate; - - $input.datepicker({ - dateFormat: displayFormat, - changeMonth: changeMonth, - changeYear: changeYear, - yearRange: yearRange, - maxDate: maxDate - }); - return $('
') - .addClass('jtable-input jtable-date-input') - .append($input); - }, - - /* Creates a textarea element for a field. - *************************************************************************/ - _createTextAreaForField: function (field, fieldName, value) { - var $textArea = $(''); - if (value != undefined) { - $textArea.val(value); - } - - return $('
') - .addClass('jtable-input jtable-textarea-input') - .append($textArea); - }, - - /* Creates a standart textbox for a field. - *************************************************************************/ - _createTextInputForField: function (field, fieldName, value) { - var $input = $(''); - if (value != undefined) { - $input.val(value); - } - - return $('
') - .addClass('jtable-input jtable-text-input') - .append($input); - }, - - /* Creates a password input for a field. - *************************************************************************/ - _createPasswordInputForField: function (field, fieldName, value) { - var $input = $(''); - if (value != undefined) { - $input.val(value); - } - - return $('
') - .addClass('jtable-input jtable-password-input') - .append($input); - }, - - /* Creates a checkboxfor a field. - *************************************************************************/ - _createCheckboxForField: function (field, fieldName, value) { - var self = this; - - //If value is undefined, get unchecked state's value - if (value == undefined) { - value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value; - } - - //Create a container div - var $containerDiv = $('
') - .addClass('jtable-input jtable-checkbox-input'); - - //Create checkbox and check if needed - var $checkBox = $('') - .appendTo($containerDiv); - if (value != undefined) { - $checkBox.val(value); - } - - //Create display text of checkbox for current state - var $textSpan = $('' + (field.formText || self._getCheckBoxTextForFieldByValue(fieldName, value)) + '') - .appendTo($containerDiv); - - //Check the checkbox if it's value is checked-value - if (self._getIsCheckBoxSelectedForFieldByValue(fieldName, value)) { - $checkBox.attr('checked', 'checked'); - } - - //This method sets checkbox's value and text according to state of the checkbox - var refreshCheckBoxValueAndText = function () { - var checkboxProps = self._getCheckBoxPropertiesForFieldByState(fieldName, $checkBox.is(':checked')); - $checkBox.attr('value', checkboxProps.Value); - $textSpan.html(field.formText || checkboxProps.DisplayText); - }; - - //Register to click event to change display text when state of checkbox is changed. - $checkBox.click(function () { - refreshCheckBoxValueAndText(); - }); - - //Change checkbox state when clicked to text - if (field.setOnTextClick != false) { - $textSpan - .addClass('jtable-option-text-clickable') - .click(function () { - if ($checkBox.is(':checked')) { - $checkBox.attr('checked', false); - } else { - $checkBox.attr('checked', true); - } - - refreshCheckBoxValueAndText(); - }); - } - - return $containerDiv; - }, - - /* Creates a drop down list (combobox) input element for a field. - *************************************************************************/ - _createDropDownListForField: function (field, fieldName, value, record, source, form) { - - //Create a container div - var $containerDiv = $('
') - .addClass('jtable-input jtable-dropdown-input'); - - //Create select element - var $select = $('') - .appendTo($containerDiv); - - //add options - var options = this._getOptionsForField(fieldName, { - record: record, - source: source, - form: form, - dependedValues: this._createDependedValuesUsingForm(form, field.dependsOn) - }); - - this._fillDropDownListWithOptions($select, options, value); - - return $containerDiv; - }, - - /* Fills a dropdown list with given options. - *************************************************************************/ - _fillDropDownListWithOptions: function ($select, options, value) { - $select.empty(); - for (var i = 0; i < options.length; i++) { - $('' + options[i].DisplayText + '') - .val(options[i].Value) - .appendTo($select); - } - }, - - /* Creates depended values object from given form. - *************************************************************************/ - _createDependedValuesUsingForm: function ($form, dependsOn) { - if (!dependsOn) { - return {}; - } - - var dependedValues = {}; - - for (var i = 0; i < dependsOn.length; i++) { - var dependedField = dependsOn[i]; - - var $dependsOn = $form.find('select[name=' + dependedField + ']'); - if ($dependsOn.length <= 0) { - continue; - } - - dependedValues[dependedField] = $dependsOn.val(); - } - - - return dependedValues; - }, - - /* Creates a radio button list for a field. - *************************************************************************/ - _createRadioButtonListForField: function (field, fieldName, value, record, source) { - var $containerDiv = $('
') - .addClass('jtable-input jtable-radiobuttonlist-input'); - - var options = this._getOptionsForField(fieldName, { - record: record, - source: source - }); - - $.each(options, function(i, option) { - var $radioButtonDiv = $('
') - .addClass('jtable-radio-input') - .appendTo($containerDiv); - - var $radioButton = $('') - .val(option.Value) - .appendTo($radioButtonDiv); - - var $textSpan = $('') - .html(option.DisplayText) - .appendTo($radioButtonDiv); - - if (field.setOnTextClick != false) { - $textSpan - .addClass('jtable-option-text-clickable') - .click(function () { - if (!$radioButton.is(':checked')) { - $radioButton.attr('checked', true); - } - }); - } - }); - - return $containerDiv; - }, - - /* Gets display text for a checkbox field. - *************************************************************************/ - _getCheckBoxTextForFieldByValue: function (fieldName, value) { - return this.options.fields[fieldName].values[value]; - }, - - /* Returns true if given field's value must be checked state. - *************************************************************************/ - _getIsCheckBoxSelectedForFieldByValue: function (fieldName, value) { - return (this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[1].Value.toString() == value.toString()); - }, - - /* Gets an object for a checkbox field that has Value and DisplayText - * properties. - *************************************************************************/ - _getCheckBoxPropertiesForFieldByState: function (fieldName, checked) { - return this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[(checked ? 1 : 0)]; - }, - - /* Calls _createCheckBoxStateArrayForField with caching. - *************************************************************************/ - _createCheckBoxStateArrayForFieldWithCaching: function (fieldName) { - var cacheKey = 'checkbox_' + fieldName; - if (!this._cache[cacheKey]) { - - this._cache[cacheKey] = this._createCheckBoxStateArrayForField(fieldName); - } - - return this._cache[cacheKey]; - }, - - /* Creates a two element array of objects for states of a checkbox field. - * First element for unchecked state, second for checked state. - * Each object has two properties: Value and DisplayText - *************************************************************************/ - _createCheckBoxStateArrayForField: function (fieldName) { - var stateArray = []; - var currentIndex = 0; - $.each(this.options.fields[fieldName].values, function (propName, propValue) { - if (currentIndex++ < 2) { - stateArray.push({ 'Value': propName, 'DisplayText': propValue }); - } - }); - - return stateArray; - }, - - /* Searches a form for dependend dropdowns and makes them cascaded. - */ - _makeCascadeDropDowns: function ($form, record, source) { - var self = this; - - $form.find('select') //for each combobox - .each(function () { - var $thisDropdown = $(this); - - //get field name - var fieldName = $thisDropdown.attr('name'); - if (!fieldName) { - return; - } - - var field = self.options.fields[fieldName]; - - //check if this combobox depends on others - if (!field.dependsOn) { - return; - } - - //for each dependency - $.each(field.dependsOn, function (index, dependsOnField) { - //find the depended combobox - var $dependsOnDropdown = $form.find('select[name=' + dependsOnField + ']'); - //when depended combobox changes - $dependsOnDropdown.change(function () { - - //Refresh options - var funcParams = { - record: record, - source: source, - form: $form, - dependedValues: {} - }; - funcParams.dependedValues = self._createDependedValuesUsingForm($form, field.dependsOn); - var options = self._getOptionsForField(fieldName, funcParams); - - //Fill combobox with new options - self._fillDropDownListWithOptions($thisDropdown, options, undefined); - - //Thigger change event to refresh multi cascade dropdowns. - $thisDropdown.change(); - }); - }); - }); - }, - - /* Updates values of a record from given form - *************************************************************************/ - _updateRecordValuesFromForm: function (record, $form) { - for (var i = 0; i < this._fieldList.length; i++) { - var fieldName = this._fieldList[i]; - var field = this.options.fields[fieldName]; - - //Do not update non-editable fields - if (field.edit == false) { - continue; - } - - //Get field name and the input element of this field in the form - var $inputElement = $form.find('[name="' + fieldName + '"]'); - if ($inputElement.length <= 0) { - continue; - } - - //Update field in record according to it's type - if (field.type == 'date') { - var dateVal = $inputElement.val(); - if (dateVal) { - var displayFormat = field.displayFormat || this.options.defaultDateFormat; - try { - var date = $.datepicker.parseDate(displayFormat, dateVal); - record[fieldName] = '/Date(' + date.getTime() + ')/'; - } catch (e) { - //TODO: Handle incorrect/different date formats - this._logWarn('Date format is incorrect for field ' + fieldName + ': ' + dateVal); - record[fieldName] = undefined; - } - } else { - this._logDebug('Date is empty for ' + fieldName); - record[fieldName] = undefined; //TODO: undefined, null or empty string? - } - } else if (field.options && field.type == 'radiobutton') { - var $checkedElement = $inputElement.filter(':checked'); - if ($checkedElement.length) { - record[fieldName] = $checkedElement.val(); - } else { - record[fieldName] = undefined; - } - } else { - record[fieldName] = $inputElement.val(); - } - } - }, - - /* Sets enabled/disabled state of a dialog button. - *************************************************************************/ - _setEnabledOfDialogButton: function ($button, enabled, buttonText) { - if (!$button) { - return; - } - - if (enabled != false) { - $button - .removeAttr('disabled') - .removeClass('ui-state-disabled'); - } else { - $button - .attr('disabled', 'disabled') - .addClass('ui-state-disabled'); - } - - if (buttonText) { - $button - .find('span') - .text(buttonText); - } - } - - }); - -})(jQuery); - - -/************************************************************************ -* CREATE RECORD extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _create: $.hik.jtable.prototype._create - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - - //Events - recordAdded: function (event, data) { }, - - //Localization - messages: { - addNewRecord: 'Add new record' - } - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _$addRecordDiv: null, //Reference to the adding new record dialog div (jQuery object) - - /************************************************************************ - * CONSTRUCTOR * - *************************************************************************/ - - /* Overrides base method to do create-specific constructions. - *************************************************************************/ - _create: function () { - base._create.apply(this, arguments); - - if (!this.options.actions.createAction) { - return; - } - - this._createAddRecordDialogDiv(); - }, - - /* Creates and prepares add new record dialog div - *************************************************************************/ - _createAddRecordDialogDiv: function () { - var self = this; - - //Create a div for dialog and add to container element - self._$addRecordDiv = $('
') - .appendTo(self._$mainContainer); - - //Prepare dialog - self._$addRecordDiv.dialog({ - autoOpen: false, - show: self.options.dialogShowEffect, - hide: self.options.dialogHideEffect, - width: 'auto', - minWidth: '300', - modal: true, - title: self.options.messages.addNewRecord, - buttons: - [{ //Cancel button - text: self.options.messages.cancel, - click: function () { - self._$addRecordDiv.dialog('close'); - } - }, { //Save button - id: 'AddRecordDialogSaveButton', - text: self.options.messages.save, - click: function () { - self._onSaveClickedOnCreateForm(); - } - }], - close: function () { - var $addRecordForm = self._$addRecordDiv.find('form').first(); - var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton'); - self._trigger("formClosed", null, { form: $addRecordForm, formType: 'create' }); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - $addRecordForm.remove(); - } - }); - - if (self.options.addRecordButton) { - //If user supplied a button, bind the click event to show dialog form - self.options.addRecordButton.click(function (e) { - e.preventDefault(); - self._showAddRecordForm(); - }); - } else { - //If user did not supplied a button, create a 'add record button' toolbar item. - self._addToolBarItem({ - icon: true, - cssClass: 'jtable-toolbar-item-add-record', - text: self.options.messages.addNewRecord, - click: function () { - self._showAddRecordForm(); - } - }); - } - }, - - _onSaveClickedOnCreateForm: function () { - var self = this; - - var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton'); - var $addRecordForm = self._$addRecordDiv.find('form'); - - if (self._trigger("formSubmitting", null, { form: $addRecordForm, formType: 'create' }) != false) { - self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving); - self._saveAddRecordForm($addRecordForm, $saveButton); - } - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* Shows add new record dialog form. - *************************************************************************/ - showCreateForm: function () { - this._showAddRecordForm(); - }, - - /* Adds a new record to the table (optionally to the server also) - *************************************************************************/ - addRecord: function (options) { - var self = this; - options = $.extend({ - clientOnly: false, - animationsEnabled: self.options.animationsEnabled, - success: function () { }, - error: function () { } - }, options); - - if (!options.record) { - self._logWarn('options parameter in addRecord method must contain a record property.'); - return; - } - - if (options.clientOnly) { - self._addRow( - self._createRowFromRecord(options.record), { - isNewRow: true, - animationsEnabled: options.animationsEnabled - }); - - options.success(); - return; - } - - var completeAddRecord = function (data) { - if (data.Result != 'OK') { - self._showError(data.Message); - options.error(data); - return; - } - - if (!data.Record) { - self._logError('Server must return the created Record object.'); - options.error(data); - return; - } - - self._onRecordAdded(data); - self._addRow( - self._createRowFromRecord(data.Record), { - isNewRow: true, - animationsEnabled: options.animationsEnabled - }); - - options.success(data); - }; - - //createAction may be a function, check if it is - if (!options.url && $.isFunction(self.options.actions.createAction)) { - - //Execute the function - var funcResult = self.options.actions.createAction($.param(options.record)); - - //Check if result is a jQuery Deferred object - if (self._isDeferredObject(funcResult)) { - //Wait promise - funcResult.done(function (data) { - completeAddRecord(data); - }).fail(function () { - self._showError(self.options.messages.serverCommunicationError); - options.error(); - }); - } else { //assume it returned the creation result - completeAddRecord(funcResult); - } - - } else { //Assume it's a URL string - - //Make an Ajax call to create record - self._submitFormUsingAjax( - options.url || self.options.actions.createAction, - $.param(options.record), - function (data) { - completeAddRecord(data); - }, - function () { - self._showError(self.options.messages.serverCommunicationError); - options.error(); - }); - - } - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Shows add new record dialog form. - *************************************************************************/ - _showAddRecordForm: function () { - var self = this; - - //Create add new record form - var $addRecordForm = $('
'); - - //Create input elements - for (var i = 0; i < self._fieldList.length; i++) { - - var fieldName = self._fieldList[i]; - var field = self.options.fields[fieldName]; - - //Do not create input for fields that is key and not specially marked as creatable - if (field.key == true && field.create != true) { - continue; - } - - //Do not create input for fields that are not creatable - if (field.create == false) { - continue; - } - - if (field.type == 'hidden') { - $addRecordForm.append(self._createInputForHidden(fieldName, field.defaultValue)); - continue; - } - - //Create a container div for this input field and add to form - var $fieldContainer = $('
') - .addClass('jtable-input-field-container') - .appendTo($addRecordForm); - - //Create a label for input - $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); - - //Create input element - $fieldContainer.append( - self._createInputForRecordField({ - fieldName: fieldName, - formType: 'create', - form: $addRecordForm - })); - } - - self._makeCascadeDropDowns($addRecordForm, undefined, 'create'); - - $addRecordForm.submit(function () { - self._onSaveClickedOnCreateForm(); - return false; - }); - - //Open the form - self._$addRecordDiv.append($addRecordForm).dialog('open'); - self._trigger("formCreated", null, { form: $addRecordForm, formType: 'create' }); - }, - - /* Saves new added record to the server and updates table. - *************************************************************************/ - _saveAddRecordForm: function ($addRecordForm, $saveButton) { - var self = this; - - var completeAddRecord = function (data) { - if (data.Result != 'OK') { - self._showError(data.Message); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - return; - } - - if (!data.Record) { - self._logError('Server must return the created Record object.'); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - return; - } - - self._onRecordAdded(data); - self._addRow( - self._createRowFromRecord(data.Record), { - isNewRow: true - }); - self._$addRecordDiv.dialog("close"); - }; - - $addRecordForm.data('submitting', true); //TODO: Why it's used, can remove? Check it. - - //createAction may be a function, check if it is - if ($.isFunction(self.options.actions.createAction)) { - - //Execute the function - var funcResult = self.options.actions.createAction($addRecordForm.serialize()); - - //Check if result is a jQuery Deferred object - if (self._isDeferredObject(funcResult)) { - //Wait promise - funcResult.done(function (data) { - completeAddRecord(data); - }).fail(function () { - self._showError(self.options.messages.serverCommunicationError); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - }); - } else { //assume it returned the creation result - completeAddRecord(funcResult); - } - - } else { //Assume it's a URL string - - //Make an Ajax call to create record - self._submitFormUsingAjax( - self.options.actions.createAction, - $addRecordForm.serialize(), - function (data) { - completeAddRecord(data); - }, - function () { - self._showError(self.options.messages.serverCommunicationError); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - }); - } - }, - - _onRecordAdded: function (data) { - this._trigger("recordAdded", null, { record: data.Record, serverResponse: data }); - } - - }); - -})(jQuery); - - -/************************************************************************ -* EDIT RECORD extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _create: $.hik.jtable.prototype._create, - _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, - _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - - //Events - recordUpdated: function (event, data) { }, - rowUpdated: function (event, data) { }, - - //Localization - messages: { - editRecord: 'Edit Record' - } - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _$editDiv: null, //Reference to the editing dialog div (jQuery object) - _$editingRow: null, //Reference to currently editing row (jQuery object) - - /************************************************************************ - * CONSTRUCTOR AND INITIALIZATION METHODS * - *************************************************************************/ - - /* Overrides base method to do editing-specific constructions. - *************************************************************************/ - _create: function () { - base._create.apply(this, arguments); - - if (!this.options.actions.updateAction) { - return; - } - - this._createEditDialogDiv(); - }, - - /* Creates and prepares edit dialog div - *************************************************************************/ - _createEditDialogDiv: function () { - var self = this; - - //Create a div for dialog and add to container element - self._$editDiv = $('
') - .appendTo(self._$mainContainer); - - //Prepare dialog - self._$editDiv.dialog({ - autoOpen: false, - show: self.options.dialogShowEffect, - hide: self.options.dialogHideEffect, - width: 'auto', - minWidth: '300', - modal: true, - title: self.options.messages.editRecord, - buttons: - [{ //cancel button - text: self.options.messages.cancel, - click: function () { - self._$editDiv.dialog('close'); - } - }, { //save button - id: 'EditDialogSaveButton', - text: self.options.messages.save, - click: function () { - self._onSaveClickedOnEditForm(); - } - }], - close: function () { - var $editForm = self._$editDiv.find('form:first'); - var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton'); - self._trigger("formClosed", null, { form: $editForm, formType: 'edit', row: self._$editingRow }); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - $editForm.remove(); - } - }); - }, - - /* Saves editing form to server. - *************************************************************************/ - _onSaveClickedOnEditForm: function () { - var self = this; - - //row maybe removed by another source, if so, do nothing - if (self._$editingRow.hasClass('jtable-row-removed')) { - self._$editDiv.dialog('close'); - return; - } - - var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton'); - var $editForm = self._$editDiv.find('form'); - if (self._trigger("formSubmitting", null, { form: $editForm, formType: 'edit', row: self._$editingRow }) != false) { - self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving); - self._saveEditForm($editForm, $saveButton); - } - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* Updates a record on the table (optionally on the server also) - *************************************************************************/ - updateRecord: function (options) { - var self = this; - options = $.extend({ - clientOnly: false, - animationsEnabled: self.options.animationsEnabled, - success: function () { }, - error: function () { } - }, options); - - if (!options.record) { - self._logWarn('options parameter in updateRecord method must contain a record property.'); - return; - } - - var key = self._getKeyValueOfRecord(options.record); - if (key == undefined || key == null) { - self._logWarn('options parameter in updateRecord method must contain a record that contains the key field property.'); - return; - } - - var $updatingRow = self.getRowByKey(key); - if ($updatingRow == null) { - self._logWarn('Can not found any row by key "' + key + '" on the table. Updating row must be visible on the table.'); - return; - } - - if (options.clientOnly) { - $.extend($updatingRow.data('record'), options.record); - self._updateRowTexts($updatingRow); - self._onRecordUpdated($updatingRow, null); - if (options.animationsEnabled) { - self._showUpdateAnimationForRow($updatingRow); - } - - options.success(); - return; - } - - var completeEdit = function (data) { - if (data.Result != 'OK') { - self._showError(data.Message); - options.error(data); - return; - } - - $.extend($updatingRow.data('record'), options.record); - self._updateRecordValuesFromServerResponse($updatingRow.data('record'), data); - - self._updateRowTexts($updatingRow); - self._onRecordUpdated($updatingRow, data); - if (options.animationsEnabled) { - self._showUpdateAnimationForRow($updatingRow); - } - - options.success(data); - }; - - //updateAction may be a function, check if it is - if (!options.url && $.isFunction(self.options.actions.updateAction)) { - - //Execute the function - var funcResult = self.options.actions.updateAction($.param(options.record)); - - //Check if result is a jQuery Deferred object - if (self._isDeferredObject(funcResult)) { - //Wait promise - funcResult.done(function (data) { - completeEdit(data); - }).fail(function () { - self._showError(self.options.messages.serverCommunicationError); - options.error(); - }); - } else { //assume it returned the creation result - completeEdit(funcResult); - } - - } else { //Assume it's a URL string - - //Make an Ajax call to create record - self._submitFormUsingAjax( - options.url || self.options.actions.updateAction, - $.param(options.record), - function (data) { - completeEdit(data); - }, - function () { - self._showError(self.options.messages.serverCommunicationError); - options.error(); - }); - - } - }, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides base method to add a 'editing column cell' to header row. - *************************************************************************/ - _addColumnsToHeaderRow: function ($tr) { - base._addColumnsToHeaderRow.apply(this, arguments); - if (this.options.actions.updateAction != undefined) { - $tr.append(this._createEmptyCommandHeader()); - } - }, - - /* Overrides base method to add a 'edit command cell' to a row. - *************************************************************************/ - _addCellsToRowUsingRecord: function ($row) { - var self = this; - base._addCellsToRowUsingRecord.apply(this, arguments); - - if (self.options.actions.updateAction != undefined) { - var $span = $('').html(self.options.messages.editRecord); - var $button = $('') - .addClass('jtable-command-button jtable-edit-command-button') - .append($span) - .click(function (e) { - e.preventDefault(); - e.stopPropagation(); - self._showEditForm($row); - }); - $('') - .addClass('jtable-command-column') - .append($button) - .appendTo($row); - } - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Shows edit form for a row. - *************************************************************************/ - _showEditForm: function ($tableRow) { - var self = this; - var record = $tableRow.data('record'); - - //Create edit form - var $editForm = $('
'); - - //Create input fields - for (var i = 0; i < self._fieldList.length; i++) { - - var fieldName = self._fieldList[i]; - var field = self.options.fields[fieldName]; - var fieldValue = record[fieldName]; - - if (field.key == true) { - if (field.edit != true) { - //Create hidden field for key - $editForm.append(self._createInputForHidden(fieldName, fieldValue)); - continue; - } else { - //Create a special hidden field for key (since key is be editable) - $editForm.append(self._createInputForHidden('jtRecordKey', fieldValue)); - } - } - - //Do not create element for non-editable fields - if (field.edit == false) { - continue; - } - - //Hidden field - if (field.type == 'hidden') { - $editForm.append(self._createInputForHidden(fieldName, fieldValue)); - continue; - } - - //Create a container div for this input field and add to form - var $fieldContainer = $('
').appendTo($editForm); - - //Create a label for input - $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); - - //Create input element with it's current value - var currentValue = self._getValueForRecordField(record, fieldName); - $fieldContainer.append( - self._createInputForRecordField({ - fieldName: fieldName, - value: currentValue, - record: record, - formType: 'edit', - form: $editForm - })); - } - - self._makeCascadeDropDowns($editForm, record, 'edit'); - - $editForm.submit(function () { - self._onSaveClickedOnEditForm(); - return false; - }); - - //Open dialog - self._$editingRow = $tableRow; - self._$editDiv.append($editForm).dialog('open'); - self._trigger("formCreated", null, { form: $editForm, formType: 'edit', record: record, row: $tableRow }); - }, - - /* Saves editing form to the server and updates the record on the table. - *************************************************************************/ - _saveEditForm: function ($editForm, $saveButton) { - var self = this; - - var completeEdit = function (data) { - if (data.Result != 'OK') { - self._showError(data.Message); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - return; - } - - var record = self._$editingRow.data('record'); - - self._updateRecordValuesFromForm(record, $editForm); - self._updateRecordValuesFromServerResponse(record, data); - self._updateRowTexts(self._$editingRow); - - self._$editingRow.attr('data-record-key', self._getKeyValueOfRecord(record)); - - self._onRecordUpdated(self._$editingRow, data); - - if (self.options.animationsEnabled) { - self._showUpdateAnimationForRow(self._$editingRow); - } - - self._$editDiv.dialog("close"); - }; - - - //updateAction may be a function, check if it is - if ($.isFunction(self.options.actions.updateAction)) { - - //Execute the function - var funcResult = self.options.actions.updateAction($editForm.serialize()); - - //Check if result is a jQuery Deferred object - if (self._isDeferredObject(funcResult)) { - //Wait promise - funcResult.done(function (data) { - completeEdit(data); - }).fail(function () { - self._showError(self.options.messages.serverCommunicationError); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - }); - } else { //assume it returned the creation result - completeEdit(funcResult); - } - - } else { //Assume it's a URL string - - //Make an Ajax call to update record - self._submitFormUsingAjax( - self.options.actions.updateAction, - $editForm.serialize(), - function(data) { - completeEdit(data); - }, - function() { - self._showError(self.options.messages.serverCommunicationError); - self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); - }); - } - - }, - - /* This method ensures updating of current record with server response, - * if server sends a Record object as response to updateAction. - *************************************************************************/ - _updateRecordValuesFromServerResponse: function (record, serverResponse) { - if (!serverResponse || !serverResponse.Record) { - return; - } - - $.extend(true, record, serverResponse.Record); - }, - - /* Gets text for a field of a record according to it's type. - *************************************************************************/ - _getValueForRecordField: function (record, fieldName) { - var field = this.options.fields[fieldName]; - var fieldValue = record[fieldName]; - if (field.type == 'date') { - return this._getDisplayTextForDateRecordField(field, fieldValue); - } else { - return fieldValue; - } - }, - - /* Updates cells of a table row's text values from row's record values. - *************************************************************************/ - _updateRowTexts: function ($tableRow) { - var record = $tableRow.data('record'); - var $columns = $tableRow.find('td'); - for (var i = 0; i < this._columnList.length; i++) { - var displayItem = this._getDisplayTextForRecordField(record, this._columnList[i]); - if ((displayItem === 0)) displayItem = "0"; - $columns.eq(this._firstDataColumnOffset + i).html(displayItem || ''); - } - - this._onRowUpdated($tableRow); - }, - - /* Shows 'updated' animation for a table row. - *************************************************************************/ - _showUpdateAnimationForRow: function ($tableRow) { - var className = 'jtable-row-updated'; - if (this.options.jqueryuiTheme) { - className = className + ' ui-state-highlight'; - } - - $tableRow.stop(true, true).addClass(className, 'slow', '', function () { - $tableRow.removeClass(className, 5000); - }); - }, - - /************************************************************************ - * EVENT RAISING METHODS * - *************************************************************************/ - - _onRowUpdated: function ($row) { - this._trigger("rowUpdated", null, { row: $row, record: $row.data('record') }); - }, - - _onRecordUpdated: function ($row, data) { - this._trigger("recordUpdated", null, { record: $row.data('record'), row: $row, serverResponse: data }); - } - - }); - -})(jQuery); - - -/************************************************************************ -* DELETION extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _create: $.hik.jtable.prototype._create, - _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, - _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - - //Options - deleteConfirmation: true, - - //Events - recordDeleted: function (event, data) { }, - - //Localization - messages: { - deleteConfirmation: 'This record will be deleted. Are you sure?', - deleteText: 'Delete', - deleting: 'Deleting', - canNotDeletedRecords: 'Can not delete {0} of {1} records!', - deleteProggress: 'Deleting {0} of {1} records, processing...' - } - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _$deleteRecordDiv: null, //Reference to the adding new record dialog div (jQuery object) - _$deletingRow: null, //Reference to currently deleting row (jQuery object) - - /************************************************************************ - * CONSTRUCTOR * - *************************************************************************/ - - /* Overrides base method to do deletion-specific constructions. - *************************************************************************/ - _create: function () { - base._create.apply(this, arguments); - this._createDeleteDialogDiv(); - }, - - /* Creates and prepares delete record confirmation dialog div. - *************************************************************************/ - _createDeleteDialogDiv: function () { - var self = this; - - //Check if deleteAction is supplied - if (!self.options.actions.deleteAction) { - return; - } - - //Create div element for delete confirmation dialog - self._$deleteRecordDiv = $('

').appendTo(self._$mainContainer); - - //Prepare dialog - self._$deleteRecordDiv.dialog({ - autoOpen: false, - show: self.options.dialogShowEffect, - hide: self.options.dialogHideEffect, - modal: true, - title: self.options.messages.areYouSure, - buttons: - [{ //cancel button - text: self.options.messages.cancel, - click: function () { - self._$deleteRecordDiv.dialog("close"); - } - }, {//delete button - id: 'DeleteDialogButton', - text: self.options.messages.deleteText, - click: function () { - - //row maybe removed by another source, if so, do nothing - if (self._$deletingRow.hasClass('jtable-row-removed')) { - self._$deleteRecordDiv.dialog('close'); - return; - } - - var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton'); - self._setEnabledOfDialogButton($deleteButton, false, self.options.messages.deleting); - self._deleteRecordFromServer( - self._$deletingRow, - function () { - self._removeRowsFromTableWithAnimation(self._$deletingRow); - self._$deleteRecordDiv.dialog('close'); - }, - function (message) { //error - self._showError(message); - self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText); - } - ); - } - }], - close: function () { - var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton'); - self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText); - } - }); - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* This method is used to delete one or more rows from server and the table. - *************************************************************************/ - deleteRows: function ($rows) { - var self = this; - - if ($rows.length <= 0) { - self._logWarn('No rows specified to jTable deleteRows method.'); - return; - } - - if (self._isBusy()) { - self._logWarn('Can not delete rows since jTable is busy!'); - return; - } - - //Deleting just one row - if ($rows.length == 1) { - self._deleteRecordFromServer( - $rows, - function () { //success - self._removeRowsFromTableWithAnimation($rows); - }, - function (message) { //error - self._showError(message); - } - ); - - return; - } - - //Deleting multiple rows - self._showBusy(self._formatString(self.options.messages.deleteProggress, 0, $rows.length)); - - //This method checks if deleting of all records is completed - var completedCount = 0; - var isCompleted = function () { - return (completedCount >= $rows.length); - }; - - //This method is called when deleting of all records completed - var completed = function () { - var $deletedRows = $rows.filter('.jtable-row-ready-to-remove'); - if ($deletedRows.length < $rows.length) { - self._showError(self._formatString(self.options.messages.canNotDeletedRecords, $rows.length - $deletedRows.length, $rows.length)); - } - - if ($deletedRows.length > 0) { - self._removeRowsFromTableWithAnimation($deletedRows); - } - - self._hideBusy(); - }; - - //Delete all rows - var deletedCount = 0; - $rows.each(function () { - var $row = $(this); - self._deleteRecordFromServer( - $row, - function () { //success - ++deletedCount; ++completedCount; - $row.addClass('jtable-row-ready-to-remove'); - self._showBusy(self._formatString(self.options.messages.deleteProggress, deletedCount, $rows.length)); - if (isCompleted()) { - completed(); - } - }, - function () { //error - ++completedCount; - if (isCompleted()) { - completed(); - } - } - ); - }); - }, - - /* Deletes a record from the table (optionally from the server also). - *************************************************************************/ - deleteRecord: function (options) { - var self = this; - options = $.extend({ - clientOnly: false, - animationsEnabled: self.options.animationsEnabled, - url: self.options.actions.deleteAction, - success: function () { }, - error: function () { } - }, options); - - if (options.key == undefined) { - self._logWarn('options parameter in deleteRecord method must contain a key property.'); - return; - } - - var $deletingRow = self.getRowByKey(options.key); - if ($deletingRow == null) { - self._logWarn('Can not found any row by key: ' + options.key); - return; - } - - if (options.clientOnly) { - self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled); - options.success(); - return; - } - - self._deleteRecordFromServer( - $deletingRow, - function (data) { //success - self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled); - options.success(data); - }, - function (message) { //error - self._showError(message); - options.error(message); - }, - options.url - ); - }, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides base method to add a 'deletion column cell' to header row. - *************************************************************************/ - _addColumnsToHeaderRow: function ($tr) { - base._addColumnsToHeaderRow.apply(this, arguments); - if (this.options.actions.deleteAction != undefined) { - $tr.append(this._createEmptyCommandHeader()); - } - }, - - /* Overrides base method to add a 'delete command cell' to a row. - *************************************************************************/ - _addCellsToRowUsingRecord: function ($row) { - base._addCellsToRowUsingRecord.apply(this, arguments); - - var self = this; - if (self.options.actions.deleteAction != undefined) { - var $span = $('').html(self.options.messages.deleteText); - var $button = $('') - .addClass('jtable-command-button jtable-delete-command-button') - .append($span) - .click(function (e) { - e.preventDefault(); - e.stopPropagation(); - self._deleteButtonClickedForRow($row); - }); - $('') - .addClass('jtable-command-column') - .append($button) - .appendTo($row); - } - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* This method is called when user clicks delete button on a row. - *************************************************************************/ - _deleteButtonClickedForRow: function ($row) { - var self = this; - - var deleteConfirm; - var deleteConfirmMessage = self.options.messages.deleteConfirmation; - - //If options.deleteConfirmation is function then call it - if ($.isFunction(self.options.deleteConfirmation)) { - var data = { row: $row, record: $row.data('record'), deleteConfirm: true, deleteConfirmMessage: deleteConfirmMessage, cancel: false, cancelMessage: null }; - self.options.deleteConfirmation(data); - - //If delete progress is cancelled - if (data.cancel) { - - //If a canlellation reason is specified - if (data.cancelMessage) { - self._showError(data.cancelMessage); //TODO: show warning/stop message instead of error (also show warning/error ui icon)! - } - - return; - } - - deleteConfirmMessage = data.deleteConfirmMessage; - deleteConfirm = data.deleteConfirm; - } else { - deleteConfirm = self.options.deleteConfirmation; - } - - if (deleteConfirm != false) { - //Confirmation - self._$deleteRecordDiv.find('.jtable-delete-confirm-message').html(deleteConfirmMessage); - self._showDeleteDialog($row); - } else { - //No confirmation - self._deleteRecordFromServer( - $row, - function () { //success - self._removeRowsFromTableWithAnimation($row); - }, - function (message) { //error - self._showError(message); - } - ); - } - }, - - /* Shows delete comfirmation dialog. - *************************************************************************/ - _showDeleteDialog: function ($row) { - this._$deletingRow = $row; - this._$deleteRecordDiv.dialog('open'); - }, - - /* Performs an ajax call to server to delete record - * and removes row of the record from table if ajax call success. - *************************************************************************/ - _deleteRecordFromServer: function ($row, success, error, url) { - var self = this; - - var completeDelete = function(data) { - if (data.Result != 'OK') { - $row.data('deleting', false); - if (error) { - error(data.Message); - } - - return; - } - - self._trigger("recordDeleted", null, { record: $row.data('record'), row: $row, serverResponse: data }); - - if (success) { - success(data); - } - }; - - //Check if it is already being deleted right now - if ($row.data('deleting') == true) { - return; - } - - $row.data('deleting', true); - - var postData = {}; - postData[self._keyField] = self._getKeyValueOfRecord($row.data('record')); - - //deleteAction may be a function, check if it is - if (!url && $.isFunction(self.options.actions.deleteAction)) { - - //Execute the function - var funcResult = self.options.actions.deleteAction(postData); - - //Check if result is a jQuery Deferred object - if (self._isDeferredObject(funcResult)) { - //Wait promise - funcResult.done(function (data) { - completeDelete(data); - }).fail(function () { - $row.data('deleting', false); - if (error) { - error(self.options.messages.serverCommunicationError); - } - }); - } else { //assume it returned the deletion result - completeDelete(funcResult); - } - - } else { //Assume it's a URL string - //Make ajax call to delete the record from server - this._ajax({ - url: (url || self.options.actions.deleteAction), - data: postData, - success: function (data) { - completeDelete(data); - }, - error: function () { - $row.data('deleting', false); - if (error) { - error(self.options.messages.serverCommunicationError); - } - } - }); - - } - }, - - /* Removes a row from table after a 'deleting' animation. - *************************************************************************/ - _removeRowsFromTableWithAnimation: function ($rows, animationsEnabled) { - var self = this; - - if (animationsEnabled == undefined) { - animationsEnabled = self.options.animationsEnabled; - } - - if (animationsEnabled) { - var className = 'jtable-row-deleting'; - if (this.options.jqueryuiTheme) { - className = className + ' ui-state-disabled'; - } - - //Stop current animation (if does exists) and begin 'deleting' animation. - $rows.stop(true, true).addClass(className, 'slow', '').promise().done(function () { - self._removeRowsFromTable($rows, 'deleted'); - }); - } else { - self._removeRowsFromTable($rows, 'deleted'); - } - } - - }); - -})(jQuery); - - -/************************************************************************ -* SELECTING extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _create: $.hik.jtable.prototype._create, - _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, - _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord, - _onLoadingRecords: $.hik.jtable.prototype._onLoadingRecords, - _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded, - _onRowsRemoved: $.hik.jtable.prototype._onRowsRemoved - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - - //Options - selecting: false, - multiselect: false, - selectingCheckboxes: false, - selectOnRowClick: true, - - //Events - selectionChanged: function (event, data) { } - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _selectedRecordIdsBeforeLoad: null, //This array is used to store selected row Id's to restore them after a page refresh (string array). - _$selectAllCheckbox: null, //Reference to the 'select/deselect all' checkbox (jQuery object) - _shiftKeyDown: false, //True, if shift key is currently down. - - /************************************************************************ - * CONSTRUCTOR * - *************************************************************************/ - - /* Overrides base method to do selecting-specific constructions. - *************************************************************************/ - _create: function () { - if (this.options.selecting && this.options.selectingCheckboxes) { - ++this._firstDataColumnOffset; - this._bindKeyboardEvents(); - } - - //Call base method - base._create.apply(this, arguments); - }, - - /* Registers to keyboard events those are needed for selection - *************************************************************************/ - _bindKeyboardEvents: function () { - var self = this; - //Register to events to set _shiftKeyDown value - $(document) - .keydown(function (event) { - switch (event.which) { - case 16: - self._shiftKeyDown = true; - break; - } - }) - .keyup(function (event) { - switch (event.which) { - case 16: - self._shiftKeyDown = false; - break; - } - }); - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* Gets jQuery selection for currently selected rows. - *************************************************************************/ - selectedRows: function () { - return this._getSelectedRows(); - }, - - /* Makes row/rows 'selected'. - *************************************************************************/ - selectRows: function ($rows) { - this._selectRows($rows); - this._onSelectionChanged(); //TODO: trigger only if selected rows changes? - }, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides base method to add a 'select column' to header row. - *************************************************************************/ - _addColumnsToHeaderRow: function ($tr) { - if (this.options.selecting && this.options.selectingCheckboxes) { - if (this.options.multiselect) { - $tr.append(this._createSelectAllHeader()); - } else { - $tr.append(this._createEmptyCommandHeader()); - } - } - - base._addColumnsToHeaderRow.apply(this, arguments); - }, - - /* Overrides base method to add a 'delete command cell' to a row. - *************************************************************************/ - _addCellsToRowUsingRecord: function ($row) { - if (this.options.selecting) { - this._makeRowSelectable($row); - } - - base._addCellsToRowUsingRecord.apply(this, arguments); - }, - - /* Overrides base event to store selection list - *************************************************************************/ - _onLoadingRecords: function () { - if (this.options.selecting) { - this._storeSelectionList(); - } - - base._onLoadingRecords.apply(this, arguments); - }, - - /* Overrides base event to restore selection list - *************************************************************************/ - _onRecordsLoaded: function () { - if (this.options.selecting) { - this._restoreSelectionList(); - } - - base._onRecordsLoaded.apply(this, arguments); - }, - - /* Overrides base event to check is any selected row is being removed. - *************************************************************************/ - _onRowsRemoved: function ($rows, reason) { - if (this.options.selecting && (reason != 'reloading') && ($rows.filter('.jtable-row-selected').length > 0)) { - this._onSelectionChanged(); - } - - base._onRowsRemoved.apply(this, arguments); - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Creates a header column to select/deselect all rows. - *************************************************************************/ - _createSelectAllHeader: function () { - var self = this; - - var $columnHeader = $('') - .addClass('jtable-command-column-header jtable-column-header-selecting'); - this._jqueryuiThemeAddClass($columnHeader, 'ui-state-default'); - - var $headerContainer = $('
') - .addClass('jtable-column-header-container') - .appendTo($columnHeader); - - self._$selectAllCheckbox = $('') - .appendTo($headerContainer) - .click(function () { - if (self._$tableRows.length <= 0) { - self._$selectAllCheckbox.attr('checked', false); - return; - } - - var allRows = self._$tableBody.find('>tr.jtable-data-row'); - if (self._$selectAllCheckbox.is(':checked')) { - self._selectRows(allRows); - } else { - self._deselectRows(allRows); - } - - self._onSelectionChanged(); - }); - - return $columnHeader; - }, - - /* Stores Id's of currently selected records to _selectedRecordIdsBeforeLoad. - *************************************************************************/ - _storeSelectionList: function () { - var self = this; - - if (!self.options.selecting) { - return; - } - - self._selectedRecordIdsBeforeLoad = []; - self._getSelectedRows().each(function () { - self._selectedRecordIdsBeforeLoad.push(self._getKeyValueOfRecord($(this).data('record'))); - }); - }, - - /* Selects rows whose Id is in _selectedRecordIdsBeforeLoad; - *************************************************************************/ - _restoreSelectionList: function () { - var self = this; - - if (!self.options.selecting) { - return; - } - - var selectedRowCount = 0; - for (var i = 0; i < self._$tableRows.length; ++i) { - var recordId = self._getKeyValueOfRecord(self._$tableRows[i].data('record')); - if ($.inArray(recordId, self._selectedRecordIdsBeforeLoad) > -1) { - self._selectRows(self._$tableRows[i]); - ++selectedRowCount; - } - } - - if (self._selectedRecordIdsBeforeLoad.length > 0 && self._selectedRecordIdsBeforeLoad.length != selectedRowCount) { - self._onSelectionChanged(); - } - - self._selectedRecordIdsBeforeLoad = []; - self._refreshSelectAllCheckboxState(); - }, - - /* Gets all selected rows. - *************************************************************************/ - _getSelectedRows: function () { - return this._$tableBody - .find('>tr.jtable-row-selected'); - }, - - /* Adds selectable feature to a row. - *************************************************************************/ - _makeRowSelectable: function ($row) { - var self = this; - - //Select/deselect on row click - if (self.options.selectOnRowClick) { - $row.click(function () { - self._invertRowSelection($row); - }); - } - - //'select/deselect' checkbox column - if (self.options.selectingCheckboxes) { - var $cell = $('').addClass('jtable-selecting-column'); - var $selectCheckbox = $('').appendTo($cell); - if (!self.options.selectOnRowClick) { - $selectCheckbox.click(function () { - self._invertRowSelection($row); - }); - } - - $row.append($cell); - } - }, - - /* Inverts selection state of a single row. - *************************************************************************/ - _invertRowSelection: function ($row) { - if ($row.hasClass('jtable-row-selected')) { - this._deselectRows($row); - } else { - //Shift key? - if (this._shiftKeyDown) { - var rowIndex = this._findRowIndex($row); - //try to select row and above rows until first selected row - var beforeIndex = this._findFirstSelectedRowIndexBeforeIndex(rowIndex) + 1; - if (beforeIndex > 0 && beforeIndex < rowIndex) { - this._selectRows(this._$tableBody.find('tr').slice(beforeIndex, rowIndex + 1)); - } else { - //try to select row and below rows until first selected row - var afterIndex = this._findFirstSelectedRowIndexAfterIndex(rowIndex) - 1; - if (afterIndex > rowIndex) { - this._selectRows(this._$tableBody.find('tr').slice(rowIndex, afterIndex + 1)); - } else { - //just select this row - this._selectRows($row); - } - } - } else { - this._selectRows($row); - } - } - - this._onSelectionChanged(); - }, - - /* Search for a selected row (that is before given row index) to up and returns it's index - *************************************************************************/ - _findFirstSelectedRowIndexBeforeIndex: function (rowIndex) { - for (var i = rowIndex - 1; i >= 0; --i) { - if (this._$tableRows[i].hasClass('jtable-row-selected')) { - return i; - } - } - - return -1; - }, - - /* Search for a selected row (that is after given row index) to down and returns it's index - *************************************************************************/ - _findFirstSelectedRowIndexAfterIndex: function (rowIndex) { - for (var i = rowIndex + 1; i < this._$tableRows.length; ++i) { - if (this._$tableRows[i].hasClass('jtable-row-selected')) { - return i; - } - } - - return -1; - }, - - /* Makes row/rows 'selected'. - *************************************************************************/ - _selectRows: function ($rows) { - if (!this.options.multiselect) { - this._deselectRows(this._getSelectedRows()); - } - - $rows.addClass('jtable-row-selected'); - this._jqueryuiThemeAddClass($rows, 'ui-state-highlight'); - - if (this.options.selectingCheckboxes) { - $rows.find('>td.jtable-selecting-column >input').prop('checked', true); - } - - this._refreshSelectAllCheckboxState(); - }, - - /* Makes row/rows 'non selected'. - *************************************************************************/ - _deselectRows: function ($rows) { - $rows.removeClass('jtable-row-selected ui-state-highlight'); - if (this.options.selectingCheckboxes) { - $rows.find('>td.jtable-selecting-column >input').prop('checked', false); - } - - this._refreshSelectAllCheckboxState(); - }, - - /* Updates state of the 'select/deselect' all checkbox according to count of selected rows. - *************************************************************************/ - _refreshSelectAllCheckboxState: function () { - if (!this.options.selectingCheckboxes || !this.options.multiselect) { - return; - } - - var totalRowCount = this._$tableRows.length; - var selectedRowCount = this._getSelectedRows().length; - - if (selectedRowCount == 0) { - this._$selectAllCheckbox.prop('indeterminate', false); - this._$selectAllCheckbox.attr('checked', false); - } else if (selectedRowCount == totalRowCount) { - this._$selectAllCheckbox.prop('indeterminate', false); - this._$selectAllCheckbox.attr('checked', true); - } else { - this._$selectAllCheckbox.attr('checked', false); - this._$selectAllCheckbox.prop('indeterminate', true); - } - }, - - /************************************************************************ - * EVENT RAISING METHODS * - *************************************************************************/ - - _onSelectionChanged: function () { - this._trigger("selectionChanged", null, {}); - } - - }); - -})(jQuery); - - -/************************************************************************ -* PAGING extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - load: $.hik.jtable.prototype.load, - _create: $.hik.jtable.prototype._create, - _setOption: $.hik.jtable.prototype._setOption, - _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl, - _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading, - _addRowToTable: $.hik.jtable.prototype._addRowToTable, - _addRow: $.hik.jtable.prototype._addRow, - _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable, - _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - paging: false, - pageList: 'normal', //possible values: 'minimal', 'normal' - pageSize: 10, - pageSizes: [10, 25, 50, 100, 250, 500], - pageSizeChangeArea: true, - gotoPageArea: 'combobox', //possible values: 'textbox', 'combobox', 'none' - - messages: { - pagingInfo: 'Showing {0}-{1} of {2}', - pageSizeChangeLabel: 'Row count', - gotoPageLabel: 'Go to page' - } - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _$bottomPanel: null, //Reference to the panel at the bottom of the table (jQuery object) - _$pagingListArea: null, //Reference to the page list area in to bottom panel (jQuery object) - _$pageSizeChangeArea: null, //Reference to the page size change area in to bottom panel (jQuery object) - _$pageInfoSpan: null, //Reference to the paging info area in to bottom panel (jQuery object) - _$gotoPageArea: null, //Reference to 'Go to page' input area in to bottom panel (jQuery object) - _$gotoPageInput: null, //Reference to 'Go to page' input in to bottom panel (jQuery object) - _totalRecordCount: 0, //Total count of records on all pages - _currentPageNo: 1, //Current page number - - /************************************************************************ - * CONSTRUCTOR AND INITIALIZING METHODS * - *************************************************************************/ - - /* Overrides base method to do paging-specific constructions. - *************************************************************************/ - _create: function() { - base._create.apply(this, arguments); - if (this.options.paging) { - this._loadPagingSettings(); - this._createBottomPanel(); - this._createPageListArea(); - this._createGotoPageInput(); - this._createPageSizeSelection(); - } - }, - - /* Loads user preferences for paging. - *************************************************************************/ - _loadPagingSettings: function() { - if (!this.options.saveUserPreferences) { - return; - } - - var pageSize = this._getCookie('page-size'); - if (pageSize) { - this.options.pageSize = this._normalizeNumber(pageSize, 1, 1000000, this.options.pageSize); - } - }, - - /* Creates bottom panel and adds to the page. - *************************************************************************/ - _createBottomPanel: function() { - this._$bottomPanel = $('
') - .addClass('jtable-bottom-panel') - .insertAfter(this._$table); - - this._jqueryuiThemeAddClass(this._$bottomPanel, 'ui-state-default'); - - $('
').addClass('jtable-left-area').appendTo(this._$bottomPanel); - $('
').addClass('jtable-right-area').appendTo(this._$bottomPanel); - }, - - /* Creates page list area. - *************************************************************************/ - _createPageListArea: function() { - this._$pagingListArea = $('') - .addClass('jtable-page-list') - .appendTo(this._$bottomPanel.find('.jtable-left-area')); - - this._$pageInfoSpan = $('') - .addClass('jtable-page-info') - .appendTo(this._$bottomPanel.find('.jtable-right-area')); - }, - - /* Creates page list change area. - *************************************************************************/ - _createPageSizeSelection: function() { - var self = this; - - if (!self.options.pageSizeChangeArea) { - return; - } - - //Add current page size to page sizes list if not contains it - if (self._findIndexInArray(self.options.pageSize, self.options.pageSizes) < 0) { - self.options.pageSizes.push(parseInt(self.options.pageSize)); - self.options.pageSizes.sort(function(a, b) { return a - b; }); - } - - //Add a span to contain page size change items - self._$pageSizeChangeArea = $('') - .addClass('jtable-page-size-change') - .appendTo(self._$bottomPanel.find('.jtable-left-area')); - - //Page size label - self._$pageSizeChangeArea.append('' + self.options.messages.pageSizeChangeLabel + ': '); - - //Page size change combobox - var $pageSizeChangeCombobox = $('').appendTo(self._$pageSizeChangeArea); - - //Add page sizes to the combobox - for (var i = 0; i < self.options.pageSizes.length; i++) { - $pageSizeChangeCombobox.append(''); - } - - //Select current page size - $pageSizeChangeCombobox.val(self.options.pageSize); - - //Change page size on combobox change - $pageSizeChangeCombobox.change(function() { - self._changePageSize(parseInt($(this).val())); - }); - }, - - /* Creates go to page area. - *************************************************************************/ - _createGotoPageInput: function() { - var self = this; - - if (!self.options.gotoPageArea || self.options.gotoPageArea == 'none') { - return; - } - - //Add a span to contain goto page items - this._$gotoPageArea = $('') - .addClass('jtable-goto-page') - .appendTo(self._$bottomPanel.find('.jtable-left-area')); - - //Goto page label - this._$gotoPageArea.append('' + self.options.messages.gotoPageLabel + ': '); - - //Goto page input - if (self.options.gotoPageArea == 'combobox') { - - self._$gotoPageInput = $('') - .appendTo(this._$gotoPageArea) - .data('pageCount', 1) - .change(function() { - self._changePage(parseInt($(this).val())); - }); - self._$gotoPageInput.append(''); - - } else { //textbox - - self._$gotoPageInput = $('') - .appendTo(this._$gotoPageArea) - .keypress(function(event) { - if (event.which == 13) { //enter - event.preventDefault(); - self._changePage(parseInt(self._$gotoPageInput.val())); - } else if (event.which == 43) { // + - event.preventDefault(); - self._changePage(parseInt(self._$gotoPageInput.val()) + 1); - } else if (event.which == 45) { // - - event.preventDefault(); - self._changePage(parseInt(self._$gotoPageInput.val()) - 1); - } else { - //Allow only digits - var isValid = ( - (47 < event.keyCode && event.keyCode < 58 && event.shiftKey == false && event.altKey == false) - || (event.keyCode == 8) - || (event.keyCode == 9) - ); - - if (!isValid) { - event.preventDefault(); - } - } - }); - - } - }, - - /* Refreshes the 'go to page' input. - *************************************************************************/ - _refreshGotoPageInput: function() { - if (!this.options.gotoPageArea || this.options.gotoPageArea == 'none') { - return; - } - - if (this._totalRecordCount <= 0) { - this._$gotoPageArea.hide(); - } else { - this._$gotoPageArea.show(); - } - - if (this.options.gotoPageArea == 'combobox') { - var oldPageCount = this._$gotoPageInput.data('pageCount'); - var currentPageCount = this._calculatePageCount(); - if (oldPageCount != currentPageCount) { - this._$gotoPageInput.empty(); - - //Skip some pages is there are too many pages - var pageStep = 1; - if (currentPageCount > 10000) { - pageStep = 100; - } else if (currentPageCount > 5000) { - pageStep = 10; - } else if (currentPageCount > 2000) { - pageStep = 5; - } else if (currentPageCount > 1000) { - pageStep = 2; - } - - for (var i = pageStep; i <= currentPageCount; i += pageStep) { - this._$gotoPageInput.append(''); - } - - this._$gotoPageInput.data('pageCount', currentPageCount); - } - } - - //same for 'textbox' and 'combobox' - this._$gotoPageInput.val(this._currentPageNo); - }, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides load method to set current page to 1. - *************************************************************************/ - load: function() { - this._currentPageNo = 1; - - base.load.apply(this, arguments); - }, - - /* Used to change options dynamically after initialization. - *************************************************************************/ - _setOption: function(key, value) { - base._setOption.apply(this, arguments); - - if (key == 'pageSize') { - this._changePageSize(parseInt(value)); - } - }, - - /* Changes current page size with given value. - *************************************************************************/ - _changePageSize: function(pageSize) { - if (pageSize == this.options.pageSize) { - return; - } - - this.options.pageSize = pageSize; - - //Normalize current page - var pageCount = this._calculatePageCount(); - if (this._currentPageNo > pageCount) { - this._currentPageNo = pageCount; - } - if (this._currentPageNo <= 0) { - this._currentPageNo = 1; - } - - //if user sets one of the options on the combobox, then select it. - var $pageSizeChangeCombobox = this._$bottomPanel.find('.jtable-page-size-change select'); - if ($pageSizeChangeCombobox.length > 0) { - if (parseInt($pageSizeChangeCombobox.val()) != pageSize) { - var selectedOption = $pageSizeChangeCombobox.find('option[value=' + pageSize + ']'); - if (selectedOption.length > 0) { - $pageSizeChangeCombobox.val(pageSize); - } - } - } - - this._savePagingSettings(); - this._reloadTable(); - }, - - /* Saves user preferences for paging - *************************************************************************/ - _savePagingSettings: function() { - if (!this.options.saveUserPreferences) { - return; - } - - this._setCookie('page-size', this.options.pageSize); - }, - - /* Overrides _createRecordLoadUrl method to add paging info to URL. - *************************************************************************/ - _createRecordLoadUrl: function() { - var loadUrl = base._createRecordLoadUrl.apply(this, arguments); - loadUrl = this._addPagingInfoToUrl(loadUrl, this._currentPageNo); - return loadUrl; - }, - - /* Overrides _createJtParamsForLoading method to add paging parameters to jtParams object. - *************************************************************************/ - _createJtParamsForLoading: function () { - var jtParams = base._createJtParamsForLoading.apply(this, arguments); - - if (this.options.paging) { - jtParams.jtStartIndex = (this._currentPageNo - 1) * this.options.pageSize; - jtParams.jtPageSize = this.options.pageSize; - } - - return jtParams; - }, - - /* Overrides _addRowToTable method to re-load table when a new row is created. - * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES. - * USE _addRow METHOD. - *************************************************************************/ - _addRowToTable: function ($tableRow, index, isNewRow) { - if (isNewRow && this.options.paging) { - this._reloadTable(); - return; - } - - base._addRowToTable.apply(this, arguments); - }, - - /* Overrides _addRow method to re-load table when a new row is created. - *************************************************************************/ - _addRow: function ($row, options) { - if (options && options.isNewRow && this.options.paging) { - this._reloadTable(); - return; - } - - base._addRow.apply(this, arguments); - }, - - /* Overrides _removeRowsFromTable method to re-load table when a row is removed from table. - *************************************************************************/ - _removeRowsFromTable: function ($rows, reason) { - base._removeRowsFromTable.apply(this, arguments); - - if (this.options.paging) { - if (this._$tableRows.length <= 0 && this._currentPageNo > 1) { - --this._currentPageNo; - } - - this._reloadTable(); - } - }, - - /* Overrides _onRecordsLoaded method to to do paging specific tasks. - *************************************************************************/ - _onRecordsLoaded: function (data) { - if (this.options.paging) { - this._totalRecordCount = data.TotalRecordCount; - this._createPagingList(); - this._createPagingInfo(); - this._refreshGotoPageInput(); - } - - base._onRecordsLoaded.apply(this, arguments); - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Adds jtStartIndex and jtPageSize parameters to a URL as query string. - *************************************************************************/ - _addPagingInfoToUrl: function (url, pageNumber) { - if (!this.options.paging) { - return url; - } - - var jtStartIndex = (pageNumber - 1) * this.options.pageSize; - var jtPageSize = this.options.pageSize; - - return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtStartIndex=' + jtStartIndex + '&jtPageSize=' + jtPageSize); - }, - - /* Creates and shows the page list. - *************************************************************************/ - _createPagingList: function () { - if (this.options.pageSize <= 0) { - return; - } - - this._$pagingListArea.empty(); - if (this._totalRecordCount <= 0) { - return; - } - - var pageCount = this._calculatePageCount(); - - this._createFirstAndPreviousPageButtons(); - if (this.options.pageList == 'normal') { - this._createPageNumberButtons(this._calculatePageNumbers(pageCount)); - } - this._createLastAndNextPageButtons(pageCount); - this._bindClickEventsToPageNumberButtons(); - }, - - /* Creates and shows previous and first page links. - *************************************************************************/ - _createFirstAndPreviousPageButtons: function () { - var $first = $('') - .addClass('jtable-page-number-first') - .html('<<') - .data('pageNumber', 1) - .appendTo(this._$pagingListArea); - - var $previous = $('') - .addClass('jtable-page-number-previous') - .html('<') - .data('pageNumber', this._currentPageNo - 1) - .appendTo(this._$pagingListArea); - - this._jqueryuiThemeAddClass($first, 'ui-button ui-state-default', 'ui-state-hover'); - this._jqueryuiThemeAddClass($previous, 'ui-button ui-state-default', 'ui-state-hover'); - - if (this._currentPageNo <= 1) { - $first.addClass('jtable-page-number-disabled'); - $previous.addClass('jtable-page-number-disabled'); - this._jqueryuiThemeAddClass($first, 'ui-state-disabled'); - this._jqueryuiThemeAddClass($previous, 'ui-state-disabled'); - } - }, - - /* Creates and shows next and last page links. - *************************************************************************/ - _createLastAndNextPageButtons: function (pageCount) { - var $next = $('') - .addClass('jtable-page-number-next') - .html('>') - .data('pageNumber', this._currentPageNo + 1) - .appendTo(this._$pagingListArea); - var $last = $('') - .addClass('jtable-page-number-last') - .html('>>') - .data('pageNumber', pageCount) - .appendTo(this._$pagingListArea); - - this._jqueryuiThemeAddClass($next, 'ui-button ui-state-default', 'ui-state-hover'); - this._jqueryuiThemeAddClass($last, 'ui-button ui-state-default', 'ui-state-hover'); - - if (this._currentPageNo >= pageCount) { - $next.addClass('jtable-page-number-disabled'); - $last.addClass('jtable-page-number-disabled'); - this._jqueryuiThemeAddClass($next, 'ui-state-disabled'); - this._jqueryuiThemeAddClass($last, 'ui-state-disabled'); - } - }, - - /* Creates and shows page number links for given number array. - *************************************************************************/ - _createPageNumberButtons: function (pageNumbers) { - var previousNumber = 0; - for (var i = 0; i < pageNumbers.length; i++) { - //Create "..." between page numbers if needed - if ((pageNumbers[i] - previousNumber) > 1) { - $('') - .addClass('jtable-page-number-space') - .html('...') - .appendTo(this._$pagingListArea); - } - - this._createPageNumberButton(pageNumbers[i]); - previousNumber = pageNumbers[i]; - } - }, - - /* Creates a page number link and adds to paging area. - *************************************************************************/ - _createPageNumberButton: function (pageNumber) { - var $pageNumber = $('') - .addClass('jtable-page-number') - .html(pageNumber) - .data('pageNumber', pageNumber) - .appendTo(this._$pagingListArea); - - this._jqueryuiThemeAddClass($pageNumber, 'ui-button ui-state-default', 'ui-state-hover'); - - if (this._currentPageNo == pageNumber) { - $pageNumber.addClass('jtable-page-number-active jtable-page-number-disabled'); - this._jqueryuiThemeAddClass($pageNumber, 'ui-state-active'); - } - }, - - /* Calculates total page count according to page size and total record count. - *************************************************************************/ - _calculatePageCount: function () { - var pageCount = Math.floor(this._totalRecordCount / this.options.pageSize); - if (this._totalRecordCount % this.options.pageSize != 0) { - ++pageCount; - } - - return pageCount; - }, - - /* Calculates page numbers and returns an array of these numbers. - *************************************************************************/ - _calculatePageNumbers: function (pageCount) { - if (pageCount <= 4) { - //Show all pages - var pageNumbers = []; - for (var i = 1; i <= pageCount; ++i) { - pageNumbers.push(i); - } - - return pageNumbers; - } else { - //show first three, last three, current, previous and next page numbers - var shownPageNumbers = [1, 2, pageCount - 1, pageCount]; - var previousPageNo = this._normalizeNumber(this._currentPageNo - 1, 1, pageCount, 1); - var nextPageNo = this._normalizeNumber(this._currentPageNo + 1, 1, pageCount, 1); - - this._insertToArrayIfDoesNotExists(shownPageNumbers, previousPageNo); - this._insertToArrayIfDoesNotExists(shownPageNumbers, this._currentPageNo); - this._insertToArrayIfDoesNotExists(shownPageNumbers, nextPageNo); - - shownPageNumbers.sort(function (a, b) { return a - b; }); - return shownPageNumbers; - } - }, - - /* Creates and shows paging informations. - *************************************************************************/ - _createPagingInfo: function () { - if (this._totalRecordCount <= 0) { - this._$pageInfoSpan.empty(); - return; - } - - var startNo = (this._currentPageNo - 1) * this.options.pageSize + 1; - var endNo = this._currentPageNo * this.options.pageSize; - endNo = this._normalizeNumber(endNo, startNo, this._totalRecordCount, 0); - - if (endNo >= startNo) { - var pagingInfoMessage = this._formatString(this.options.messages.pagingInfo, startNo, endNo, this._totalRecordCount); - this._$pageInfoSpan.html(pagingInfoMessage); - } - }, - - /* Binds click events of all page links to change the page. - *************************************************************************/ - _bindClickEventsToPageNumberButtons: function () { - var self = this; - self._$pagingListArea - .find('.jtable-page-number,.jtable-page-number-previous,.jtable-page-number-next,.jtable-page-number-first,.jtable-page-number-last') - .not('.jtable-page-number-disabled') - .click(function (e) { - e.preventDefault(); - self._changePage($(this).data('pageNumber')); - }); - }, - - /* Changes current page to given value. - *************************************************************************/ - _changePage: function (pageNo) { - pageNo = this._normalizeNumber(pageNo, 1, this._calculatePageCount(), 1); - if (pageNo == this._currentPageNo) { - this._refreshGotoPageInput(); - return; - } - - this._currentPageNo = pageNo; - this._reloadTable(); - } - - }); - -})(jQuery); - - -/************************************************************************ -* SORTING extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _initializeFields: $.hik.jtable.prototype._initializeFields, - _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions, - _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField, - _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl, - _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - sorting: false, - multiSorting: false, - defaultSorting: '' - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _lastSorting: null, //Last sorting of the table - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides base method to create sorting array. - *************************************************************************/ - _initializeFields: function () { - base._initializeFields.apply(this, arguments); - - this._lastSorting = []; - if (this.options.sorting) { - this._buildDefaultSortingArray(); - } - }, - - /* Overrides _normalizeFieldOptions method to normalize sorting option for fields. - *************************************************************************/ - _normalizeFieldOptions: function (fieldName, props) { - base._normalizeFieldOptions.apply(this, arguments); - props.sorting = (props.sorting != false); - }, - - /* Overrides _createHeaderCellForField to make columns sortable. - *************************************************************************/ - _createHeaderCellForField: function (fieldName, field) { - var $headerCell = base._createHeaderCellForField.apply(this, arguments); - if (this.options.sorting && field.sorting) { - this._makeColumnSortable($headerCell, fieldName, field.initialSortingDirection); - } - - return $headerCell; - }, - - /* Overrides _createRecordLoadUrl to add sorting specific info to URL. - *************************************************************************/ - _createRecordLoadUrl: function () { - var loadUrl = base._createRecordLoadUrl.apply(this, arguments); - loadUrl = this._addSortingInfoToUrl(loadUrl); - return loadUrl; - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Builds the sorting array according to defaultSorting string - *************************************************************************/ - _buildDefaultSortingArray: function () { - var self = this; - - $.each(self.options.defaultSorting.split(","), function (orderIndex, orderValue) { - $.each(self.options.fields, function (fieldName, fieldProps) { - if (fieldProps.sorting) { - var colOffset = orderValue.indexOf(fieldName); - if (colOffset > -1) { - if (orderValue.toUpperCase().indexOf(' DESC', colOffset) > -1) { - self._lastSorting.push({ - fieldName: fieldName, - sortOrder: 'DESC' - }); - } else { - self._lastSorting.push({ - fieldName: fieldName, - sortOrder: 'ASC' - }); - } - } - } - }); - }); - }, - - /* Makes a column sortable. - *************************************************************************/ - _makeColumnSortable: function ($columnHeader, fieldName, initialSortingDirection) { - var self = this; - - $columnHeader - .addClass('jtable-column-header-sortable') - .click(function (e) { - e.preventDefault(); - - if (!self.options.multiSorting || !e.ctrlKey) { - self._lastSorting = []; //clear previous sorting - } - - self._sortTableByColumn($columnHeader); - }); - - if(initialSortingDirection){ - $columnHeader.addClass('jtable-column-header-sorted-' + initialSortingDirection.toLowerCase()); - } - - //Set default sorting - $.each(this._lastSorting, function (sortIndex, sortField) { - if (sortField.fieldName == fieldName) { - if (sortField.sortOrder == 'DESC') { - $columnHeader.addClass('jtable-column-header-sorted-desc'); - } else { - $columnHeader.addClass('jtable-column-header-sorted-asc'); - } - } - }); - }, - - /* Sorts table according to a column header. - *************************************************************************/ - _sortTableByColumn: function ($columnHeader) { - //Remove sorting styles from all columns except this one - if (this._lastSorting.length == 0) { - $columnHeader.siblings().removeClass('jtable-column-header-sorted-asc jtable-column-header-sorted-desc'); - } - - //If current sorting list includes this column, remove it from the list - for (var i = 0; i < this._lastSorting.length; i++) { - if (this._lastSorting[i].fieldName == $columnHeader.data('fieldName')) { - this._lastSorting.splice(i--, 1); - } - } - - //Sort ASC or DESC according to current sorting state - if ($columnHeader.hasClass('jtable-column-header-sorted-asc')) { - $columnHeader.removeClass('jtable-column-header-sorted-asc').addClass('jtable-column-header-sorted-desc'); - this._lastSorting.push({ - 'fieldName': $columnHeader.data('fieldName'), - sortOrder: 'DESC' - }); - } else { - $columnHeader.removeClass('jtable-column-header-sorted-desc').addClass('jtable-column-header-sorted-asc'); - this._lastSorting.push({ - 'fieldName': $columnHeader.data('fieldName'), - sortOrder: 'ASC' - }); - } - - //Load current page again - this._reloadTable(); - }, - - /* Adds jtSorting parameter to a URL as query string. - *************************************************************************/ - _addSortingInfoToUrl: function (url) { - if (!this.options.sorting || this._lastSorting.length == 0) { - return url; - } - - var sorting = []; - $.each(this._lastSorting, function (idx, value) { - sorting.push(value.fieldName + ' ' + value.sortOrder); - }); - - return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtSorting=' + sorting.join(",")); - }, - - /* Overrides _createJtParamsForLoading method to add sorging parameters to jtParams object. - *************************************************************************/ - _createJtParamsForLoading: function () { - var jtParams = base._createJtParamsForLoading.apply(this, arguments); - - if (this.options.sorting && this._lastSorting.length) { - var sorting = []; - $.each(this._lastSorting, function (idx, value) { - sorting.push(value.fieldName + ' ' + value.sortOrder); - }); - - jtParams.jtSorting = sorting.join(","); - } - - return jtParams; - } - - }); - -})(jQuery); - -/************************************************************************ -* DYNAMIC COLUMNS extension for jTable * -* (Show/hide/resize columns) * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _create: $.hik.jtable.prototype._create, - _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions, - _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField, - _createCellForRecordField: $.hik.jtable.prototype._createCellForRecordField - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - - options: { - tableId: undefined, - columnResizable: true, - columnSelectable: true - }, - - /************************************************************************ - * PRIVATE FIELDS * - *************************************************************************/ - - _$columnSelectionDiv: null, - _$columnResizeBar: null, - _cookieKeyPrefix: null, - _currentResizeArgs: null, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides _addRowToTableHead method. - *************************************************************************/ - - _create: function () { - base._create.apply(this, arguments); - - this._createColumnResizeBar(); - this._createColumnSelection(); - - if (this.options.saveUserPreferences) { - this._loadColumnSettings(); - } - - this._normalizeColumnWidths(); - }, - - /* Normalizes some options for a field (sets default values). - *************************************************************************/ - _normalizeFieldOptions: function (fieldName, props) { - base._normalizeFieldOptions.apply(this, arguments); - - //columnResizable - if (this.options.columnResizable) { - props.columnResizable = (props.columnResizable != false); - } - - //visibility - if (!props.visibility) { - props.visibility = 'visible'; - } - }, - - /* Overrides _createHeaderCellForField to make columns dynamic. - *************************************************************************/ - _createHeaderCellForField: function (fieldName, field) { - var $headerCell = base._createHeaderCellForField.apply(this, arguments); - - //Make data columns resizable except the last one - if (this.options.columnResizable && field.columnResizable && (fieldName != this._columnList[this._columnList.length - 1])) { - this._makeColumnResizable($headerCell); - } - - //Hide column if needed - if (field.visibility == 'hidden') { - $headerCell.hide(); - } - - return $headerCell; - }, - - /* Overrides _createHeaderCellForField to decide show or hide a column. - *************************************************************************/ - _createCellForRecordField: function (record, fieldName) { - var $column = base._createCellForRecordField.apply(this, arguments); - - var field = this.options.fields[fieldName]; - if (field.visibility == 'hidden') { - $column.hide(); - } - - return $column; - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* Changes visibility of a column. - *************************************************************************/ - changeColumnVisibility: function (columnName, visibility) { - this._changeColumnVisibilityInternal(columnName, visibility); - this._normalizeColumnWidths(); - if (this.options.saveUserPreferences) { - this._saveColumnSettings(); - } - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Changes visibility of a column. - *************************************************************************/ - _changeColumnVisibilityInternal: function (columnName, visibility) { - //Check if there is a column with given name - var columnIndex = this._columnList.indexOf(columnName); - if (columnIndex < 0) { - this._logWarn('Column "' + columnName + '" does not exist in fields!'); - return; - } - - //Check if visibility value is valid - if (['visible', 'hidden', 'fixed'].indexOf(visibility) < 0) { - this._logWarn('Visibility value is not valid: "' + visibility + '"! Options are: visible, hidden, fixed.'); - return; - } - - //Get the field - var field = this.options.fields[columnName]; - if (field.visibility == visibility) { - return; //No action if new value is same as old one. - } - - //Hide or show the column if needed - var columnIndexInTable = this._firstDataColumnOffset + columnIndex + 1; - if (field.visibility != 'hidden' && visibility == 'hidden') { - this._$table - .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')') - .hide(); - } else if (field.visibility == 'hidden' && visibility != 'hidden') { - this._$table - .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')') - .show() - .css('display', 'table-cell'); - } - - field.visibility = visibility; - }, - - /* Prepares dialog to change settings. - *************************************************************************/ - _createColumnSelection: function () { - var self = this; - - //Create a div for dialog and add to container element - this._$columnSelectionDiv = $('
') - .addClass('jtable-column-selection-container') - .appendTo(self._$mainContainer); - - this._$table.children('thead').bind('contextmenu', function (e) { - if (!self.options.columnSelectable) { - return; - } - - e.preventDefault(); - - //Make an overlay div to disable page clicks - $('
') - .addClass('jtable-contextmenu-overlay') - .click(function () { - $(this).remove(); - self._$columnSelectionDiv.hide(); - }) - .bind('contextmenu', function () { return false; }) - .appendTo(document.body); - - self._fillColumnSelection(); - - //Calculate position of column selection list and show it - - var containerOffset = self._$mainContainer.offset(); - var selectionDivTop = e.pageY - containerOffset.top; - var selectionDivLeft = e.pageX - containerOffset.left; - - var selectionDivMinWidth = 100; //in pixels - var containerWidth = self._$mainContainer.width(); - - //If user clicks right area of header of the table, show list at a little left - if ((containerWidth > selectionDivMinWidth) && (selectionDivLeft > (containerWidth - selectionDivMinWidth))) { - selectionDivLeft = containerWidth - selectionDivMinWidth; - } - - self._$columnSelectionDiv.css({ - left: selectionDivLeft, - top: selectionDivTop, - 'min-width': selectionDivMinWidth + 'px' - }).show(); - }); - }, - - /* Prepares content of settings dialog. - *************************************************************************/ - _fillColumnSelection: function () { - var self = this; - - var $columnsUl = $('
    ') - .addClass('jtable-column-select-list'); - for (var i = 0; i < this._columnList.length; i++) { - var columnName = this._columnList[i]; - var field = this.options.fields[columnName]; - - //Crete li element - var $columnLi = $('
  • ').appendTo($columnsUl); - - //Create label for the checkbox - var $label = $('') - .append($('' + (field.title || columnName) + '')) - .appendTo($columnLi); - - //Create checkbox - var $checkbox = $('') - .prependTo($label) - .click(function () { - var $clickedCheckbox = $(this); - var clickedColumnName = $clickedCheckbox.attr('name'); - var clickedField = self.options.fields[clickedColumnName]; - if (clickedField.visibility == 'fixed') { - return; - } - - self.changeColumnVisibility(clickedColumnName, $clickedCheckbox.is(':checked') ? 'visible' : 'hidden'); - }); - - //Check, if column if shown - if (field.visibility != 'hidden') { - $checkbox.attr('checked', 'checked'); - } - - //Disable, if column is fixed - if (field.visibility == 'fixed') { - $checkbox.attr('disabled', 'disabled'); - } - } - - this._$columnSelectionDiv.html($columnsUl); - }, - - /* creates a vertical bar that is shown while resizing columns. - *************************************************************************/ - _createColumnResizeBar: function () { - this._$columnResizeBar = $('
    ') - .addClass('jtable-column-resize-bar') - .appendTo(this._$mainContainer) - .hide(); - }, - - /* Makes a column sortable. - *************************************************************************/ - _makeColumnResizable: function ($columnHeader) { - var self = this; - - //Create a handler to handle mouse click event - $('
    ') - .addClass('jtable-column-resize-handler') - .appendTo($columnHeader.find('.jtable-column-header-container')) //Append the handler to the column - .mousedown(function (downevent) { //handle mousedown event for the handler - downevent.preventDefault(); - downevent.stopPropagation(); - - var mainContainerOffset = self._$mainContainer.offset(); - - //Get a reference to the next column - var $nextColumnHeader = $columnHeader.nextAll('th.jtable-column-header:visible:first'); - if (!$nextColumnHeader.length) { - return; - } - - //Store some information to be used on resizing - var minimumColumnWidth = 10; //A column's width can not be smaller than 10 pixel. - self._currentResizeArgs = { - currentColumnStartWidth: $columnHeader.outerWidth(), - minWidth: minimumColumnWidth, - maxWidth: $columnHeader.outerWidth() + $nextColumnHeader.outerWidth() - minimumColumnWidth, - mouseStartX: downevent.pageX, - minResizeX: function () { return this.mouseStartX - (this.currentColumnStartWidth - this.minWidth); }, - maxResizeX: function () { return this.mouseStartX + (this.maxWidth - this.currentColumnStartWidth); } - }; - - //Handle mouse move event to move resizing bar - var resizeonmousemove = function (moveevent) { - if (!self._currentResizeArgs) { - return; - } - - var resizeBarX = self._normalizeNumber(moveevent.pageX, self._currentResizeArgs.minResizeX(), self._currentResizeArgs.maxResizeX()); - self._$columnResizeBar.css('left', (resizeBarX - mainContainerOffset.left) + 'px'); - }; - - //Handle mouse up event to finish resizing of the column - var resizeonmouseup = function (upevent) { - if (!self._currentResizeArgs) { - return; - } - - $(document).unbind('mousemove', resizeonmousemove); - $(document).unbind('mouseup', resizeonmouseup); - - self._$columnResizeBar.hide(); - - //Calculate new widths in pixels - var mouseChangeX = upevent.pageX - self._currentResizeArgs.mouseStartX; - var currentColumnFinalWidth = self._normalizeNumber(self._currentResizeArgs.currentColumnStartWidth + mouseChangeX, self._currentResizeArgs.minWidth, self._currentResizeArgs.maxWidth); - var nextColumnFinalWidth = $nextColumnHeader.outerWidth() + (self._currentResizeArgs.currentColumnStartWidth - currentColumnFinalWidth); - - //Calculate widths as percent - var pixelToPercentRatio = $columnHeader.data('width-in-percent') / self._currentResizeArgs.currentColumnStartWidth; - $columnHeader.data('width-in-percent', currentColumnFinalWidth * pixelToPercentRatio); - $nextColumnHeader.data('width-in-percent', nextColumnFinalWidth * pixelToPercentRatio); - - //Set new widths to columns (resize!) - $columnHeader.css('width', $columnHeader.data('width-in-percent') + '%'); - $nextColumnHeader.css('width', $nextColumnHeader.data('width-in-percent') + '%'); - - //Normalize all column widths - self._normalizeColumnWidths(); - - //Finish resizing - self._currentResizeArgs = null; - - //Save current preferences - if (self.options.saveUserPreferences) { - self._saveColumnSettings(); - } - }; - - //Show vertical resize bar - self._$columnResizeBar - .show() - .css({ - top: ($columnHeader.offset().top - mainContainerOffset.top) + 'px', - left: (downevent.pageX - mainContainerOffset.left) + 'px', - height: (self._$table.outerHeight()) + 'px' - }); - - //Bind events - $(document).bind('mousemove', resizeonmousemove); - $(document).bind('mouseup', resizeonmouseup); - }); - }, - - /* Normalizes column widths as percent for current view. - *************************************************************************/ - _normalizeColumnWidths: function () { - - //Set command column width - var commandColumnHeaders = this._$table - .find('>thead th.jtable-command-column-header') - .data('width-in-percent', 1) - .css('width', '1%'); - - //Find data columns - var headerCells = this._$table.find('>thead th.jtable-column-header'); - - //Calculate total width of data columns - var totalWidthInPixel = 0; - headerCells.each(function () { - var $cell = $(this); - if ($cell.is(':visible')) { - totalWidthInPixel += $cell.outerWidth(); - } - }); - - //Calculate width of each column - var columnWidhts = {}; - var availableWidthInPercent = 100.0 - commandColumnHeaders.length; - headerCells.each(function () { - var $cell = $(this); - if ($cell.is(':visible')) { - var fieldName = $cell.data('fieldName'); - var widthInPercent = $cell.outerWidth() * availableWidthInPercent / totalWidthInPixel; - columnWidhts[fieldName] = widthInPercent; - } - }); - - //Set width of each column - headerCells.each(function () { - var $cell = $(this); - if ($cell.is(':visible')) { - var fieldName = $cell.data('fieldName'); - $cell.data('width-in-percent', columnWidhts[fieldName]).css('width', columnWidhts[fieldName] + '%'); - } - }); - }, - - /* Saves field setting to cookie. - * Saved setting will be a string like that: - * fieldName1=visible;23|fieldName2=hidden;17|... - *************************************************************************/ - _saveColumnSettings: function () { - var self = this; - var fieldSettings = ''; - this._$table.find('>thead >tr >th.jtable-column-header').each(function () { - var $cell = $(this); - var fieldName = $cell.data('fieldName'); - var columnWidth = $cell.data('width-in-percent'); - var fieldVisibility = self.options.fields[fieldName].visibility; - var fieldSetting = fieldName + "=" + fieldVisibility + ';' + columnWidth; - fieldSettings = fieldSettings + fieldSetting + '|'; - }); - - this._setCookie('column-settings', fieldSettings.substr(0, fieldSettings.length - 1)); - }, - - /* Loads field settings from cookie that is saved by _saveFieldSettings method. - *************************************************************************/ - _loadColumnSettings: function () { - var self = this; - var columnSettingsCookie = this._getCookie('column-settings'); - if (!columnSettingsCookie) { - return; - } - - var columnSettings = {}; - $.each(columnSettingsCookie.split('|'), function (inx, fieldSetting) { - var splitted = fieldSetting.split('='); - var fieldName = splitted[0]; - var settings = splitted[1].split(';'); - columnSettings[fieldName] = { - columnVisibility: settings[0], - columnWidth: settings[1] - }; - }); - - var headerCells = this._$table.find('>thead >tr >th.jtable-column-header'); - headerCells.each(function () { - var $cell = $(this); - var fieldName = $cell.data('fieldName'); - var field = self.options.fields[fieldName]; - if (columnSettings[fieldName]) { - if (field.visibility != 'fixed') { - self._changeColumnVisibilityInternal(fieldName, columnSettings[fieldName].columnVisibility); - } - - $cell.data('width-in-percent', columnSettings[fieldName].columnWidth).css('width', columnSettings[fieldName].columnWidth + '%'); - } - }); - } - - }); - -})(jQuery); - - -/************************************************************************ -* MASTER/CHILD tables extension for jTable * -*************************************************************************/ -(function ($) { - - //Reference to base object members - var base = { - _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable - }; - - //extension members - $.extend(true, $.hik.jtable.prototype, { - - /************************************************************************ - * DEFAULT OPTIONS / EVENTS * - *************************************************************************/ - options: { - openChildAsAccordion: false - }, - - /************************************************************************ - * PUBLIC METHODS * - *************************************************************************/ - - /* Creates and opens a new child table for given row. - *************************************************************************/ - openChildTable: function ($row, tableOptions, opened) { - var self = this; - - //Apply theming as same as parent table unless explicitily set - if (tableOptions.jqueryuiTheme == undefined) { - tableOptions.jqueryuiTheme = self.options.jqueryuiTheme; - } - - //Show close button as default - tableOptions.showCloseButton = (tableOptions.showCloseButton != false); - - //Close child table when close button is clicked (default behavior) - if (tableOptions.showCloseButton && !tableOptions.closeRequested) { - tableOptions.closeRequested = function () { - self.closeChildTable($row); - }; - } - - //If accordion style, close open child table (if it does exists) - if (self.options.openChildAsAccordion) { - $row.siblings('.jtable-data-row').each(function () { - self.closeChildTable($(this)); - }); - } - - //Close child table for this row and open new one for child table - self.closeChildTable($row, function () { - var $childRowColumn = self.getChildRow($row).children('td').empty(); - var $childTableContainer = $('
    ') - .addClass('jtable-child-table-container') - .appendTo($childRowColumn); - $childRowColumn.data('childTable', $childTableContainer); - $childTableContainer.jtable(tableOptions); - self.openChildRow($row); - $childTableContainer.hide().slideDown('fast', function () { - if (opened) { - opened({ - childTable: $childTableContainer - }); - } - }); - }); - }, - - /* Closes child table for given row. - *************************************************************************/ - closeChildTable: function ($row, closed) { - var self = this; - - var $childRowColumn = this.getChildRow($row).children('td'); - var $childTable = $childRowColumn.data('childTable'); - if (!$childTable) { - if (closed) { - closed(); - } - - return; - } - - $childRowColumn.data('childTable', null); - $childTable.slideUp('fast', function () { - $childTable.jtable('destroy'); - $childTable.remove(); - self.closeChildRow($row); - if (closed) { - closed(); - } - }); - }, - - /* Returns a boolean value indicates that if a child row is open for given row. - *************************************************************************/ - isChildRowOpen: function ($row) { - return (this.getChildRow($row).is(':visible')); - }, - - /* Gets child row for given row, opens it if it's closed (Creates if needed). - *************************************************************************/ - getChildRow: function ($row) { - return $row.data('childRow') || this._createChildRow($row); - }, - - /* Creates and opens child row for given row. - *************************************************************************/ - openChildRow: function ($row) { - var $childRow = this.getChildRow($row); - if (!$childRow.is(':visible')) { - $childRow.show(); - } - - return $childRow; - }, - - /* Closes child row if it's open. - *************************************************************************/ - closeChildRow: function ($row) { - var $childRow = this.getChildRow($row); - if ($childRow.is(':visible')) { - $childRow.hide(); - } - }, - - /************************************************************************ - * OVERRIDED METHODS * - *************************************************************************/ - - /* Overrides _removeRowsFromTable method to remove child rows of deleted rows. - *************************************************************************/ - _removeRowsFromTable: function ($rows, reason) { - //var self = this; - - if (reason == 'deleted') { - $rows.each(function () { - var $row = $(this); - var $childRow = $row.data('childRow'); - if ($childRow) { - //self.closeChildTable($row); //Removed since it causes "Uncaught Error: cannot call methods on jtable prior to initialization; attempted to call method 'destroy'" - $childRow.remove(); - } - }); - } - - base._removeRowsFromTable.apply(this, arguments); - }, - - /************************************************************************ - * PRIVATE METHODS * - *************************************************************************/ - - /* Creates a child row for a row, hides and returns it. - *************************************************************************/ - _createChildRow: function ($row) { - var totalColumnCount = this._$table.find('thead th').length; - var $childRow = $('') - .addClass('jtable-child-row') - .append(''); - $row.after($childRow); - $row.data('childRow', $childRow); - $childRow.hide(); - return $childRow; - } - - }); - -})(jQuery); - From f41c786b27f7698c03611bd7493e1a8507ff3616 Mon Sep 17 00:00:00 2001 From: Bird Date: Mon, 15 Nov 2021 12:56:42 +0330 Subject: [PATCH 53/58] version with new features included --- jquery.jtable.js | 5505 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5505 insertions(+) create mode 100644 jquery.jtable.js diff --git a/jquery.jtable.js b/jquery.jtable.js new file mode 100644 index 00000000..336614b2 --- /dev/null +++ b/jquery.jtable.js @@ -0,0 +1,5505 @@ +/* + +msjTable 2.6.0 +http://www.jtable.org + +--------------------------------------------------------------------------- + +Copyright (C) 2020-2021 by Mostafa Shaeri (https://www.m-shaeri.ir) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +/************************************************************************ +* 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', + defaultChangeMonth: false, + defaultChangeYear: false, + defaultYearRange: 'c-10:c+10', + defaultMaxDate: null, + 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) + + _extraFieldTypes:[], + + /************************************************************************ + * 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 = []; + this._extraFieldTypes = []; + }, + + /* 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 }); + } + + var extraFieldType = this._findItemByProperty(this._extraFieldTypes, 'type', field.type); + if(extraFieldType && extraFieldType.creator){ + return extraFieldType.creator(record, field); + } + else 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) { + return this._findItemByProperty(options, 'Value', value); + }, + + /* Finds an option object by given value. + *************************************************************************/ + _findItemByProperty: function (items, key, value) { + for (var i = 0; i < items.length; i++) { + if (items[i][key] == value) { + return items[i]; + } + } + + return {}; //no item 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); + try { + return this._formatDate(displayFormat, date); + } catch (e) { + return date; + } + }, + + /* Format the date/time field. + *************************************************************************/ + _formatDate: function (format, date) { + + var pad = function (n, width, z) { + z = z || '0'; + n = n + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; + }; + + format = format.replace('ss', pad(date.getSeconds(), 2)); + format = format.replace('s', date.getSeconds()); + format = format.replace('dd', pad(date.getDate(), 2)); + format = format.replace('d', date.getDate()); + format = format.replace('mm', pad(date.getMinutes(), 2)); + format = format.replace('m', date.getMinutes()); + //format = format.replace('MMMM', monthNames[date.getMonth()]); + //format = format.replace('MMM', monthNames[date.getMonth()].substring(0, 3)); + format = format.replace('MM', pad(date.getMonth() + 1, 2)); + format = format.replace(/M(?![ao])/, date.getMonth() + 1); + //format = format.replace('DD', Days[date.getDay()]); + //format = format.replace(/D(?!e)/, Days[date.getDay()].substring(0, 3)); + format = format.replace('yyyy', date.getFullYear()); + format = format.replace('YYYY', date.getFullYear()); + format = format.replace('yy', (date.getFullYear() + "").substring(2)); + format = format.replace('YY', (date.getFullYear() + "").substring(2)); + format = format.replace('HH', pad(date.getHours(), 2)); + format = format.replace('H', date.getHours()); + format = format.replace('hh', pad(date.getHours(), 2)); + format = format.replace('h', date.getHours()); + return format; + }, + + /* 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) + * 2011-10-15T14:42:51 (ISO 8601) + *************************************************************************/ + _parseDate: function (dateString) { + try { + 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 if (dateString.indexOf('T') > 0) { //Format: ISO 8601 2009-10-15T14:42:51 + var dtstr = dateString.replace(/\D/g, " "); + var dtcomps = dtstr.split(" "); + dtcomps[1]--; // modify month between 1 based ISO 8601 and zero based Date + return new Date( + Date.UTC( + dtcomps[0], + dtcomps[1], + dtcomps[2], + dtcomps[3], + dtcomps[4], + dtcomps[5])); + } else { + throw 'Given date is not properly formatted: ' + dateString; + } + } catch (e) { + this._logWarn(e); + 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)); + + +/************************************************************************ +* Some UTULITY methods used by jTable * +*************************************************************************/ +(function ($) { + + $.extend(true, $.hik.jtable.prototype, { + + /* Gets property value of an object recursively. + *************************************************************************/ + _getPropertyOfObject: function (obj, propName) { + if (propName.indexOf('.') < 0) { + return obj[propName]; + } else { + var preDot = propName.substring(0, propName.indexOf('.')); + var postDot = propName.substring(propName.indexOf('.') + 1); + return this._getPropertyOfObject(obj[preDot], postDot); + } + }, + + /* Sets property value of an object recursively. + *************************************************************************/ + _setPropertyOfObject: function (obj, propName, value) { + if (propName.indexOf('.') < 0) { + obj[propName] = value; + } else { + var preDot = propName.substring(0, propName.indexOf('.')); + var postDot = propName.substring(propName.indexOf('.') + 1); + this._setPropertyOfObject(obj[preDot], postDot, value); + } + }, + + /* Inserts a value to an array if it does not exists in the array. + *************************************************************************/ + _insertToArrayIfDoesNotExists: function (array, value) { + if ($.inArray(value, array) < 0) { + array.push(value); + } + }, + + /* Finds index of an element in an array according to given comparision function + *************************************************************************/ + _findIndexInArray: function (value, array, compareFunc) { + + //If not defined, use default comparision + if (!compareFunc) { + compareFunc = function (a, b) { + return a == b; + }; + } + + for (var i = 0; i < array.length; i++) { + if (compareFunc(value, array[i])) { + return i; + } + } + + return -1; + }, + + /* Normalizes a number between given bounds or sets to a defaultValue + * if it is undefined + *************************************************************************/ + _normalizeNumber: function (number, min, max, defaultValue) { + if (number == undefined || number == null || isNaN(number)) { + return defaultValue; + } + + if (number < min) { + return min; + } + + if (number > max) { + return max; + } + + return number; + }, + + /* Formats a string just like string.format in c#. + * Example: + * _formatString('Hello {0}','Halil') = 'Hello Halil' + *************************************************************************/ + _formatString: function () { + if (arguments.length == 0) { + return null; + } + + var str = arguments[0]; + for (var i = 1; i < arguments.length; i++) { + var placeHolder = '{' + (i - 1) + '}'; + str = str.replace(placeHolder, arguments[i]); + } + + return str; + }, + + /* Checks if given object is a jQuery Deferred object. + */ + _isDeferredObject: function (obj) { + return obj.then && obj.done && obj.fail; + }, + + //Logging methods //////////////////////////////////////////////////////// + + _logDebug: function (text) { + if (!window.console) { + return; + } + + console.log('jTable DEBUG: ' + text); + }, + + _logInfo: function (text) { + if (!window.console) { + return; + } + + console.log('jTable INFO: ' + text); + }, + + _logWarn: function (text) { + if (!window.console) { + return; + } + + console.log('jTable WARNING: ' + text); + }, + + _logError: function (text) { + if (!window.console) { + return; + } + + console.log('jTable ERROR: ' + text); + } + + }); + + /* Fix for array.indexOf method in IE7. + * This code is taken from http://www.tutorialspoint.com/javascript/array_indexof.htm */ + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (elt) { + var len = this.length; + var from = Number(arguments[1]) || 0; + from = (from < 0) + ? Math.ceil(from) + : Math.floor(from); + if (from < 0) + from += len; + for (; from < len; from++) { + if (from in this && + this[from] === elt) + return from; + } + return -1; + }; + } + +})(jQuery); + + + +/************************************************************************ +* FORMS extension for jTable (base for edit/create/preview forms) * +*************************************************************************/ +(function ($) { + + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Submits a form asynchronously using AJAX. + * This method is needed, since form submitting logic can be overrided + * by extensions. + *************************************************************************/ + _submitFormUsingAjax: function (url, formData, success, error) { + this._ajax({ + url: url, + data: formData, + success: success, + error: error + }); + }, + + /* Creates label for an input element. + *************************************************************************/ + _createInputLabelForRecordField: function (fieldName) { + //TODO: May create label tag instead of a div. + return $('
    ') + .addClass('jtable-input-label') + .html(this.options.fields[fieldName].inputTitle || this.options.fields[fieldName].title); + }, + /* Creates label for an input element. + *************************************************************************/ + _createViewLabelForRecordField: function (fieldName) { + //TODO: May create label tag instead of a div. + return $('
    ') + .addClass('jtable-view-label') + .html(this.options.fields[fieldName].title); + }, + + /* Creates an input element according to field type. + *************************************************************************/ + _createInputForRecordField: function (funcParams) { + var fieldName = funcParams.fieldName, + value = funcParams.value, + record = funcParams.record, + formType = funcParams.formType, + form = funcParams.form; + + //Get the field + var field = this.options.fields[fieldName]; + + //If value if not supplied, use defaultValue of the field + if (value == undefined || value == null) { + value = field.defaultValue; + } + + //Use custom function if supplied + if (field.input) { + var $input = $(field.input({ + value: value, + record: record, + formType: formType, + form: form + })); + + //Add id attribute if does not exists + if (!$input.attr('id')) { + $input.attr('id', 'Edit-' + fieldName); + } + + //Wrap input element with div + return $('
    ') + .addClass('jtable-input jtable-custom-input') + .append($input); + } + + + //Create input according to field type + if (field.type == 'date') { + return this._createDateInputForField(field, fieldName, value); + } else if (field.type == 'textarea') { + return this._createTextAreaForField(field, fieldName, value); + } else if (field.type == 'password') { + return this._createPasswordInputForField(field, fieldName, value); + } else if (field.type == 'checkbox') { + return this._createCheckboxForField(field, fieldName, value, formType); + } else if (field.options) { + if (field.type == 'radiobutton') { + return this._createRadioButtonListForField(field, fieldName, value, record, formType); + } else { + return this._createDropDownListForField(field, fieldName, value, record, formType, form); + } + } else { + return this._createTextInputForField(field, fieldName, value); + } + }, + + /* Creates an View element according to field type. + *************************************************************************/ + _createViewForRecordField: function (funcParams) { + var fieldName = funcParams.fieldName, + value = funcParams.value, + record = funcParams.record, + formType = funcParams.formType, + form = funcParams.form; + + //Get the field + var field = this.options.fields[fieldName]; + + //If value if not supplied, use defaultValue of the field + if (value == undefined || value == null) { + value = field.defaultViewValue; + } + + //Use custom function if supplied + if (field.customView) { + var $view = $(field.customView({ + value: value, + record: record, + formType: formType, + form: form + })); + + //Add id attribute if does not exists + if (!$view.attr('id')) { + $view.attr('id', 'View-' + fieldName); + } + + //Wrap input element with div + return $('
    ') + .addClass('jtable-view jtable-custom-view') + .append($view); + } + + + //Create input according to field type + if (field.type == 'date') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'textarea') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'password') { + return this._createTextViewForField(field, fieldName, value); + } else if (field.type == 'checkbox') { + return this._createTextViewForCheckboxField(field, fieldName, value,formType); + } else if (field.options) { + if (field.type == 'radiobutton') { + return this._createTextViewForOptions(field, fieldName, value, record, formType); + } else { + return this._createTextViewForOptions(field, fieldName, value, record, formType); + } + } else { + return this._createTextViewForField(field, fieldName, value); + } + }, + + //Creates a hidden input element with given name and value. + _createInputForHidden: function (fieldName, value) { + if (value == undefined) { + value = ""; + } + + return $('') + .val(value); + }, + + /* Creates a date input for a field. + *************************************************************************/ + _createDateInputForField: function (field, fieldName, value) { + var $input = $(''); + if(value != undefined) { + $input.val(value); + } + + var displayFormat = field.displayFormat || this.options.defaultDateFormat; + $input.datepicker({ dateFormat: displayFormat }); + return $('
    ') + .addClass('jtable-input jtable-date-input') + .append($input); + }, + + /* Creates a textarea element for a field. + *************************************************************************/ + _createTextAreaForField: function (field, fieldName, value) { + var $textArea = $(''); + if (value != undefined) { + $textArea.val(value); + } + + return $('
    ') + .addClass('jtable-input jtable-textarea-input') + .append($textArea); + }, + + /* Creates a standart textbox for a field. + *************************************************************************/ + _createTextInputForField: function (field, fieldName, value) { + var $input = $(''); + if (value != undefined) { + $input.val(value); + } + + return $('
    ') + .addClass('jtable-input jtable-text-input') + .append($input); + }, + + /* Creates a password input for a field. + *************************************************************************/ + _createPasswordInputForField: function (field, fieldName, value) { + var $input = $(''); + if (value != undefined) { + $input.val(value); + } + + return $('
    ') + .addClass('jtable-input jtable-password-input') + .append($input); + }, + + /* Creates a standart view span for a field. + *************************************************************************/ + _createTextViewForField: function (field, fieldName, value) { + var $view = $('
    '); + if (value != undefined) { + $view.html(value); + } + + return $('
    ') + .addClass('jtable-view jtable-view-text') + .append($view); + }, + + /* Creates a standart view for DropDownBoxs and Radio Options field. + *************************************************************************/ + _createTextViewForOptions: function (field, fieldName, value, record, source ) { + var $view = $('
    '); + var options = this._getOptionsForField(fieldName, { + record: record, + source: source + }); + + var DisplayValue = this._getOptionsSelectedItem(options,value) + + if (DisplayValue != undefined && DisplayValue != null ) { + $view.html(DisplayValue); + } + + return $('
    ') + .addClass('jtable-view jtable-view-text') + .append($view); + }, + /* Creates a view for checkboxfor a field. + *************************************************************************/ + _createTextViewForCheckboxField: function (field, fieldName, value, formType) { + + var $view = $('
    '); + var self = this; + + //If value is undefined, get unchecked state's value + if (value == undefined) { + value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value; + } + var DisplayValue = self._getCheckBoxTextForFieldByValue(fieldName, value) + + if (DisplayValue != undefined && DisplayValue != null ) { + $view.html(DisplayValue); + } + + return $('
    ') + .addClass('jtable-view jtable-view-text') + .append($view); + + }, + + /* Creates a checkboxfor a field. + *************************************************************************/ + _createCheckboxForField: function (field, fieldName, value, formType) { + var self = this; + + //If value is undefined, get unchecked state's value + if (value == undefined) { + value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value; + } + + //Create a container div + var $containerDiv = $('
    ') + .addClass('jtable-input jtable-checkbox-input'); + + //Create checkbox and check if needed + var $checkBox = $('') + .appendTo($containerDiv); + if (value != undefined) { + $checkBox.val(value); + } + + //Create display text of checkbox for current state + var $textSpan = $('' + (field.formText || self._getCheckBoxTextForFieldByValue(fieldName, value)) + '') + .appendTo($containerDiv); + + //Check the checkbox if it's value is checked-value + if (self._getIsCheckBoxSelectedForFieldByValue(fieldName, value)) { + $checkBox.attr('checked', 'checked'); + } + + + + //This method sets checkbox's value and text according to state of the checkbox + var refreshCheckBoxValueAndText = function () { + var checkboxProps = self._getCheckBoxPropertiesForFieldByState(fieldName, $checkBox.is(':checked')); + $checkBox.attr('value', checkboxProps.Value); + $textSpan.html(field.formText || checkboxProps.DisplayText); + }; + + //Register to click event to change display text when state of checkbox is changed. + $checkBox.click(function () { + refreshCheckBoxValueAndText(); + }); + + //Change checkbox state when clicked to text + if (field.setOnTextClick != false) { + $textSpan + .addClass('jtable-option-text-clickable') + .click(function () { + if ($checkBox.is(':checked')) { + $checkBox.attr('checked', false); + } else { + $checkBox.attr('checked', true); + } + + refreshCheckBoxValueAndText(); + }); + } + + return $containerDiv; + }, + + /* Creates a drop down list (combobox) input element for a field. + *************************************************************************/ + _createDropDownListForField: function (field, fieldName, value, record, source, form) { + + //Create a container div + var $containerDiv = $('
    ') + .addClass('jtable-input jtable-dropdown-input'); + + //Create select element + var $select = $('') + .appendTo($containerDiv); + + //add options + var options = this._getOptionsForField(fieldName, { + record: record, + source: source, + form: form, + dependedValues: this._createDependedValuesUsingForm(form, field.dependsOn) + }); + + this._fillDropDownListWithOptions($select, options, value); + + return $containerDiv; + }, + + /* Fills a dropdown list with given options. + *************************************************************************/ + _fillDropDownListWithOptions: function ($select, options, value) { + $select.empty(); + for (var i = 0; i < options.length; i++) { + $('' + options[i].DisplayText + '') + .val(options[i].Value) + .appendTo($select); + } + }, + /* get selected item from given options + *************************************************************************/ + _getOptionsSelectedItem: function (options, value) { + for (var i = 0; i < options.length; i++) { + if (options[i].Value == value ) + return options[i].DisplayText ; + } + return null; + }, + + /* Creates depended values object from given form. + *************************************************************************/ + _createDependedValuesUsingForm: function ($form, dependsOn) { + if (!dependsOn) { + return {}; + } + + var dependedValues = {}; + + for (var i = 0; i < dependsOn.length; i++) { + var dependedField = dependsOn[i]; + + var $dependsOn = $form.find('select[name=' + dependedField + ']'); + if ($dependsOn.length <= 0) { + continue; + } + + dependedValues[dependedField] = $dependsOn.val(); + } + + + return dependedValues; + }, + + /* Creates a radio button list for a field. + *************************************************************************/ + _createRadioButtonListForField: function (field, fieldName, value, record, source) { + var $containerDiv = $('
    ') + .addClass('jtable-input jtable-radiobuttonlist-input'); + + var options = this._getOptionsForField(fieldName, { + record: record, + source: source + }); + + $.each(options, function(i, option) { + var $radioButtonDiv = $('
    ') + .addClass('jtable-radio-input') + .appendTo($containerDiv); + + var $radioButton = $('') + .val(option.Value) + .appendTo($radioButtonDiv); + + var $textSpan = $('') + .html(option.DisplayText) + .appendTo($radioButtonDiv); + + if (source = 'view'){ + $radioButton.attr('disabled', 'disabled'); + } + + if (field.setOnTextClick != false) { + $textSpan + .addClass('jtable-option-text-clickable') + .click(function () { + if (!$radioButton.is(':checked')) { + $radioButton.attr('checked', true); + } + }); + } + }); + + return $containerDiv; + }, + + /* Gets display text for a checkbox field. + *************************************************************************/ + _getCheckBoxTextForFieldByValue: function (fieldName, value) { + return this.options.fields[fieldName].values[value]; + }, + + /* Returns true if given field's value must be checked state. + *************************************************************************/ + _getIsCheckBoxSelectedForFieldByValue: function (fieldName, value) { + return (this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[1].Value.toString() == value.toString()); + }, + + /* Gets an object for a checkbox field that has Value and DisplayText + * properties. + *************************************************************************/ + _getCheckBoxPropertiesForFieldByState: function (fieldName, checked) { + return this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[(checked ? 1 : 0)]; + }, + + /* Calls _createCheckBoxStateArrayForField with caching. + *************************************************************************/ + _createCheckBoxStateArrayForFieldWithCaching: function (fieldName) { + var cacheKey = 'checkbox_' + fieldName; + if (!this._cache[cacheKey]) { + + this._cache[cacheKey] = this._createCheckBoxStateArrayForField(fieldName); + } + + return this._cache[cacheKey]; + }, + + /* Creates a two element array of objects for states of a checkbox field. + * First element for unchecked state, second for checked state. + * Each object has two properties: Value and DisplayText + *************************************************************************/ + _createCheckBoxStateArrayForField: function (fieldName) { + var stateArray = []; + var currentIndex = 0; + $.each(this.options.fields[fieldName].values, function (propName, propValue) { + if (currentIndex++ < 2) { + stateArray.push({ 'Value': propName, 'DisplayText': propValue }); + } + }); + + return stateArray; + }, + + /* Searches a form for dependend dropdowns and makes them cascaded. + */ + _makeCascadeDropDowns: function ($form, record, source) { + var self = this; + + $form.find('select') //for each combobox + .each(function () { + var $thisDropdown = $(this); + + //get field name + var fieldName = $thisDropdown.attr('name'); + if (!fieldName) { + return; + } + + var field = self.options.fields[fieldName]; + + //check if this combobox depends on others + if (!field.dependsOn) { + return; + } + + //for each dependency + $.each(field.dependsOn, function (index, dependsOnField) { + //find the depended combobox + var $dependsOnDropdown = $form.find('select[name=' + dependsOnField + ']'); + //when depended combobox changes + $dependsOnDropdown.change(function () { + + //Refresh options + var funcParams = { + record: record, + source: source, + form: $form, + dependedValues: {} + }; + funcParams.dependedValues = self._createDependedValuesUsingForm($form, field.dependsOn); + var options = self._getOptionsForField(fieldName, funcParams); + + //Fill combobox with new options + self._fillDropDownListWithOptions($thisDropdown, options, undefined); + + //Thigger change event to refresh multi cascade dropdowns. + $thisDropdown.change(); + }); + }); + }); + }, + + /* Updates values of a record from given form + *************************************************************************/ + _updateRecordValuesFromForm: function (record, $form) { + for (var i = 0; i < this._fieldList.length; i++) { + var fieldName = this._fieldList[i]; + var field = this.options.fields[fieldName]; + + //Do not update non-editable fields + if (field.edit == false) { + continue; + } + + //Get field name and the input element of this field in the form + var $inputElement = $form.find('[name="' + fieldName + '"]'); + if ($inputElement.length <= 0) { + continue; + } + + //Update field in record according to it's type + if (field.type == 'date') { + var dateVal = $inputElement.val(); + if (dateVal) { + var displayFormat = field.displayFormat || this.options.defaultDateFormat; + try { + var date = $.datepicker.parseDate(displayFormat, dateVal); + record[fieldName] = '/Date(' + date.getTime() + ')/'; + } catch (e) { + //TODO: Handle incorrect/different date formats + this._logWarn('Date format is incorrect for field ' + fieldName + ': ' + dateVal); + record[fieldName] = undefined; + } + } else { + this._logDebug('Date is empty for ' + fieldName); + record[fieldName] = undefined; //TODO: undefined, null or empty string? + } + } else if (field.options && field.type == 'radiobutton') { + var $checkedElement = $inputElement.filter(':checked'); + if ($checkedElement.length) { + record[fieldName] = $checkedElement.val(); + } else { + record[fieldName] = undefined; + } + } else { + record[fieldName] = $inputElement.val(); + } + } + }, + + /* Sets enabled/disabled state of a dialog button. + *************************************************************************/ + _setEnabledOfDialogButton: function ($button, enabled, buttonText) { + if (!$button) { + return; + } + + if (enabled != false) { + $button + .removeAttr('disabled') + .removeClass('ui-state-disabled'); + } else { + $button + .attr('disabled', 'disabled') + .addClass('ui-state-disabled'); + } + + if (buttonText) { + $button + .find('span') + .text(buttonText); + } + } + + }); + +})(jQuery); + + +/************************************************************************ +* CREATE RECORD extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Events + recordAdded: function (event, data) { }, + + //Localization + messages: { + addNewRecord: 'Add new record' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$addRecordDiv: null, //Reference to the adding new record dialog div (jQuery object) + + /************************************************************************ + * CONSTRUCTOR * + *************************************************************************/ + + /* Overrides base method to do create-specific constructions. + *************************************************************************/ + _create: function () { + base._create.apply(this, arguments); + + if (!this.options.actions.createAction) { + return; + } + + this._createAddRecordDialogDiv(); + }, + + /* Creates and prepares add new record dialog div + *************************************************************************/ + _createAddRecordDialogDiv: function () { + var self = this; + + //Create a div for dialog and add to container element + self._$addRecordDiv = $('
    ') + .appendTo(self._$mainContainer); + + //Prepare dialog + self._$addRecordDiv.dialog({ + autoOpen: false, + show: self.options.dialogShowEffect, + hide: self.options.dialogHideEffect, + width: self.options.formDialogWidth ? self.options.formDialogWidth : 'auto', + minWidth: '300', + modal: true, + title: self.options.messages.addNewRecord, + buttons: + [{ //Cancel button + text: self.options.messages.cancel, + click: function () { + self._$addRecordDiv.dialog('close'); + } + }, { //Save button + id: 'AddRecordDialogSaveButton', + text: self.options.messages.save, + click: function () { + self._onSaveClickedOnCreateForm(); + } + }], + close: function () { + var $addRecordForm = self._$addRecordDiv.find('form').first(); + var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton'); + self._trigger("formClosed", null, { form: $addRecordForm, formType: 'create' }); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + $addRecordForm.remove(); + } + }); + + if (self.options.addRecordButton) { + //If user supplied a button, bind the click event to show dialog form + self.options.addRecordButton.click(function (e) { + e.preventDefault(); + self._showAddRecordForm(); + }); + } else { + //If user did not supplied a button, create a 'add record button' toolbar item. + self._addToolBarItem({ + icon: true, + cssClass: 'jtable-toolbar-item-add-record', + text: self.options.messages.addNewRecord, + click: function () { + self._showAddRecordForm(); + } + }); + } + }, + + _onSaveClickedOnCreateForm: function () { + var self = this; + + var $saveButton = self._$addRecordDiv.parent().find('#AddRecordDialogSaveButton'); + var $addRecordForm = self._$addRecordDiv.find('form'); + + if (self._trigger("formSubmitting", null, { form: $addRecordForm, formType: 'create' }) != false) { + self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving); + self._saveAddRecordForm($addRecordForm, $saveButton); + } + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* Shows add new record dialog form. + *************************************************************************/ + showCreateForm: function () { + this._showAddRecordForm(); + }, + + /* Adds a new record to the table (optionally to the server also) + *************************************************************************/ + addRecord: function (options) { + var self = this; + options = $.extend({ + clientOnly: false, + animationsEnabled: self.options.animationsEnabled, + success: function () { }, + error: function () { } + }, options); + + if (!options.record) { + self._logWarn('options parameter in addRecord method must contain a record property.'); + return; + } + + if (options.clientOnly) { + self._addRow( + self._createRowFromRecord(options.record), { + isNewRow: true, + animationsEnabled: options.animationsEnabled + }); + + options.success(); + return; + } + + var completeAddRecord = function (data) { + if (data.Result != 'OK') { + self._showError(data.Message); + options.error(data); + return; + } + + if (!data.Record) { + self._logError('Server must return the created Record object.'); + options.error(data); + return; + } + + self._onRecordAdded(data); + self._addRow( + self._createRowFromRecord(data.Record), { + isNewRow: true, + animationsEnabled: options.animationsEnabled + }); + + options.success(data); + }; + + //createAction may be a function, check if it is + if (!options.url && $.isFunction(self.options.actions.createAction)) { + + //Execute the function + var funcResult = self.options.actions.createAction($.param(options.record)); + + //Check if result is a jQuery Deferred object + if (self._isDeferredObject(funcResult)) { + //Wait promise + funcResult.done(function (data) { + completeAddRecord(data); + }).fail(function () { + self._showError(self.options.messages.serverCommunicationError); + options.error(); + }); + } else { //assume it returned the creation result + completeAddRecord(funcResult); + } + + } else { //Assume it's a URL string + + //Make an Ajax call to create record + self._submitFormUsingAjax( + options.url || self.options.actions.createAction, + $.param(options.record), + function (data) { + completeAddRecord(data); + }, + function () { + self._showError(self.options.messages.serverCommunicationError); + options.error(); + }); + + } + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Shows add new record dialog form. + *************************************************************************/ + _showAddRecordForm: function () { + var self = this; + + //Create add new record form + var $addRecordForm = $('
    '); + var ColumnCount=self.options.createFormColumns ? self.options.createFormColumns : 1; + var CurrentColumnCount=0; + var $CreateFormTable = $('').appendTo($addRecordForm); + var $RowContainer = $('').appendTo($CreateFormTable); + //Create input elements + for (var i = 0; i < self._fieldList.length; i++) { + + var fieldName = self._fieldList[i]; + var field = self.options.fields[fieldName]; + + //Do not create input for fields that is key and not specially marked as creatable + if (field.key == true && field.create != true) { + continue; + } + + //Do not create input for fields that are not creatable + if (field.create == false) { + continue; + } + + if (field.type == 'hidden') { + $addRecordForm.append(self._createInputForHidden(fieldName, field.defaultValue)); + continue; + } + + //Create a container table cell for this input field and add to form + var $fieldContainer = $('').appendTo($CreateFormTable); + CurrentColumnCount=0; + } + + //Create a label for input + $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); + + //Create input element + $fieldContainer.append( + self._createInputForRecordField({ + fieldName: fieldName, + formType: 'create', + form: $addRecordForm + })); + } + + self._makeCascadeDropDowns($addRecordForm, undefined, 'create'); + + $addRecordForm.submit(function () { + self._onSaveClickedOnCreateForm(); + return false; + }); + + //Open the form + self._$addRecordDiv.append($addRecordForm).dialog('open'); + self._trigger("formCreated", null, { form: $addRecordForm, formType: 'create' }); + }, + + /* Saves new added record to the server and updates table. + *************************************************************************/ + _saveAddRecordForm: function ($addRecordForm, $saveButton) { + var self = this; + + var completeAddRecord = function (data) { + if (data.Result != 'OK') { + self._showError(data.Message); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + return; + } + + if (!data.Record) { + self._logError('Server must return the created Record object.'); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + return; + } + + self._onRecordAdded(data); + self._addRow( + self._createRowFromRecord(data.Record), { + isNewRow: true + }); + self._$addRecordDiv.dialog("close"); + }; + + $addRecordForm.data('submitting', true); //TODO: Why it's used, can remove? Check it. + + //createAction may be a function, check if it is + if ($.isFunction(self.options.actions.createAction)) { + + //Execute the function + var funcResult = self.options.actions.createAction($addRecordForm.serialize()); + + //Check if result is a jQuery Deferred object + if (self._isDeferredObject(funcResult)) { + //Wait promise + funcResult.done(function (data) { + completeAddRecord(data); + }).fail(function () { + self._showError(self.options.messages.serverCommunicationError); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + }); + } else { //assume it returned the creation result + completeAddRecord(funcResult); + } + + } else { //Assume it's a URL string + + //Make an Ajax call to create record + self._submitFormUsingAjax( + self.options.actions.createAction, + $addRecordForm.serialize(), + function (data) { + completeAddRecord(data); + }, + function () { + self._showError(self.options.messages.serverCommunicationError); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + }); + } + }, + + _onRecordAdded: function (data) { + this._trigger("recordAdded", null, { record: data.Record, serverResponse: data }); + } + + }); + +})(jQuery); + + +/************************************************************************ +* EDIT RECORD extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, + _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Events + recordUpdated: function (event, data) { }, + rowUpdated: function (event, data) { }, + + //Localization + messages: { + editRecord: 'Edit Record' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$editDiv: null, //Reference to the editing dialog div (jQuery object) + _$editingRow: null, //Reference to currently editing row (jQuery object) + + /************************************************************************ + * CONSTRUCTOR AND INITIALIZATION METHODS * + *************************************************************************/ + + /* Overrides base method to do editing-specific constructions. + *************************************************************************/ + _create: function () { + base._create.apply(this, arguments); + + if (!this.options.actions.updateAction) { + return; + } + + this._createEditDialogDiv(); + }, + + /* Creates and prepares edit dialog div + *************************************************************************/ + _createEditDialogDiv: function () { + var self = this; + + //Create a div for dialog and add to container element + self._$editDiv = $('
    ') + .appendTo(self._$mainContainer); + + //Prepare dialog + self._$editDiv.dialog({ + autoOpen: false, + show: self.options.dialogShowEffect, + hide: self.options.dialogHideEffect, + width: self.options.formDialogWidth ? self.options.formDialogWidth : 'auto', + minWidth: '300', + modal: true, + title: self.options.messages.editRecord, + buttons: + [{ //cancel button + text: self.options.messages.cancel, + click: function () { + self._$editDiv.dialog('close'); + } + }, { //save button + id: 'EditDialogSaveButton', + text: self.options.messages.save, + click: function () { + self._onSaveClickedOnEditForm(); + } + }], + close: function () { + var $editForm = self._$editDiv.find('form:first'); + var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton'); + self._trigger("formClosed", null, { form: $editForm, formType: 'edit', row: self._$editingRow }); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + $editForm.remove(); + } + }); + }, + + /* Saves editing form to server. + *************************************************************************/ + _onSaveClickedOnEditForm: function () { + var self = this; + + //row maybe removed by another source, if so, do nothing + if (self._$editingRow.hasClass('jtable-row-removed')) { + self._$editDiv.dialog('close'); + return; + } + + var $saveButton = self._$editDiv.parent().find('#EditDialogSaveButton'); + var $editForm = self._$editDiv.find('form'); + if (self._trigger("formSubmitting", null, { form: $editForm, formType: 'edit', row: self._$editingRow }) != false) { + self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving); + self._saveEditForm($editForm, $saveButton); + } + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* Updates a record on the table (optionally on the server also) + *************************************************************************/ + updateRecord: function (options) { + var self = this; + options = $.extend({ + clientOnly: false, + animationsEnabled: self.options.animationsEnabled, + success: function () { }, + error: function () { } + }, options); + + if (!options.record) { + self._logWarn('options parameter in updateRecord method must contain a record property.'); + return; + } + + var key = self._getKeyValueOfRecord(options.record); + if (key == undefined || key == null) { + self._logWarn('options parameter in updateRecord method must contain a record that contains the key field property.'); + return; + } + + var $updatingRow = self.getRowByKey(key); + if ($updatingRow == null) { + self._logWarn('Can not found any row by key "' + key + '" on the table. Updating row must be visible on the table.'); + return; + } + + if (options.clientOnly) { + $.extend($updatingRow.data('record'), options.record); + self._updateRowTexts($updatingRow); + self._onRecordUpdated($updatingRow, null); + if (options.animationsEnabled) { + self._showUpdateAnimationForRow($updatingRow); + } + + options.success(); + return; + } + + var completeEdit = function (data) { + if (data.Result != 'OK') { + self._showError(data.Message); + options.error(data); + return; + } + + $.extend($updatingRow.data('record'), options.record); + self._updateRecordValuesFromServerResponse($updatingRow.data('record'), data); + + self._updateRowTexts($updatingRow); + self._onRecordUpdated($updatingRow, data); + if (options.animationsEnabled) { + self._showUpdateAnimationForRow($updatingRow); + } + + options.success(data); + }; + + //updateAction may be a function, check if it is + if (!options.url && $.isFunction(self.options.actions.updateAction)) { + + //Execute the function + var funcResult = self.options.actions.updateAction($.param(options.record)); + + //Check if result is a jQuery Deferred object + if (self._isDeferredObject(funcResult)) { + //Wait promise + funcResult.done(function (data) { + completeEdit(data); + }).fail(function () { + self._showError(self.options.messages.serverCommunicationError); + options.error(); + }); + } else { //assume it returned the creation result + completeEdit(funcResult); + } + + } else { //Assume it's a URL string + + //Make an Ajax call to create record + self._submitFormUsingAjax( + options.url || self.options.actions.updateAction, + $.param(options.record), + function (data) { + completeEdit(data); + }, + function () { + self._showError(self.options.messages.serverCommunicationError); + options.error(); + }); + + } + }, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to add a 'editing column cell' to header row. + *************************************************************************/ + _addColumnsToHeaderRow: function ($tr) { + base._addColumnsToHeaderRow.apply(this, arguments); + if (this.options.actions.updateAction != undefined) { + $tr.append(this._createEmptyCommandHeader()); + } + }, + + /* Overrides base method to add a 'edit command cell' to a row. + *************************************************************************/ + _addCellsToRowUsingRecord: function ($row) { + var self = this; + base._addCellsToRowUsingRecord.apply(this, arguments); + + if (self.options.actions.updateAction != undefined) { + var $span = $('').html(self.options.messages.editRecord); + var $button = $('') + .addClass('jtable-command-button jtable-edit-command-button') + .append($span) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self._showEditForm($row); + }); + $('') + .addClass('jtable-command-column') + .append($button) + .appendTo($row); + } + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Shows edit form for a row. + *************************************************************************/ + _showEditForm: function ($tableRow) { + var self = this; + var record = $tableRow.data('record'); + + //Create edit form + var $editForm = $(''); + var ColumnCount=self.options.editFormColumns ? self.options.editFormColumns : 1; + var CurrentColumnCount=0; + var $EditFormTable = $('
    ') + .addClass('jtable-input-field-container') + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('
    ').appendTo($editForm); + var $RowContainer = $('').appendTo($EditFormTable); + //Create input fields + for (var i = 0; i < self._fieldList.length; i++) { + + var fieldName = self._fieldList[i]; + var field = self.options.fields[fieldName]; + var fieldValue = record[fieldName]; + + if (field.key == true) { + if (field.edit != true) { + //Create hidden field for key + $editForm.append(self._createInputForHidden(fieldName, fieldValue)); + continue; + } else { + //Create a special hidden field for key (since key is be editable) + $editForm.append(self._createInputForHidden('jtRecordKey', fieldValue)); + } + } + + //Do not create element for non-editable fields + if (field.edit == false) { + continue; + } + + //Hidden field + if (field.type == 'hidden') { + $editForm.append(self._createInputForHidden(fieldName, fieldValue)); + continue; + } + + //Create a container div for this input field and add to form + var $fieldContainer = $('').appendTo($EditFormTable); + CurrentColumnCount=0; + } + + //Create a label for input + $fieldContainer.append(self._createInputLabelForRecordField(fieldName)); + + //Create input element with it's current value + var currentValue = self._getValueForRecordField(record, fieldName); + $fieldContainer.append( + self._createInputForRecordField({ + fieldName: fieldName, + value: currentValue, + record: record, + formType: 'edit', + form: $editForm + })); + } + + self._makeCascadeDropDowns($editForm, record, 'edit'); + + $editForm.submit(function () { + self._onSaveClickedOnEditForm(); + return false; + }); + + //Open dialog + self._$editingRow = $tableRow; + self._$editDiv.append($editForm).dialog('open'); + self._trigger("formCreated", null, { form: $editForm, formType: 'edit', record: record, row: $tableRow }); + }, + + /* Saves editing form to the server and updates the record on the table. + *************************************************************************/ + _saveEditForm: function ($editForm, $saveButton) { + var self = this; + + var completeEdit = function (data) { + if (data.Result != 'OK') { + self._showError(data.Message); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + return; + } + + var record = self._$editingRow.data('record'); + + self._updateRecordValuesFromForm(record, $editForm); + self._updateRecordValuesFromServerResponse(record, data); + self._updateRowTexts(self._$editingRow); + + self._$editingRow.attr('data-record-key', self._getKeyValueOfRecord(record)); + + self._onRecordUpdated(self._$editingRow, data); + + if (self.options.animationsEnabled) { + self._showUpdateAnimationForRow(self._$editingRow); + } + + self._$editDiv.dialog("close"); + }; + + + //updateAction may be a function, check if it is + if ($.isFunction(self.options.actions.updateAction)) { + + //Execute the function + var funcResult = self.options.actions.updateAction($editForm.serialize()); + + //Check if result is a jQuery Deferred object + if (self._isDeferredObject(funcResult)) { + //Wait promise + funcResult.done(function (data) { + completeEdit(data); + }).fail(function () { + self._showError(self.options.messages.serverCommunicationError); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + }); + } else { //assume it returned the creation result + completeEdit(funcResult); + } + + } else { //Assume it's a URL string + + //Make an Ajax call to update record + self._submitFormUsingAjax( + self.options.actions.updateAction, + $editForm.serialize(), + function(data) { + completeEdit(data); + }, + function() { + self._showError(self.options.messages.serverCommunicationError); + self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save); + }); + } + + }, + + /* This method ensures updating of current record with server response, + * if server sends a Record object as response to updateAction. + *************************************************************************/ + _updateRecordValuesFromServerResponse: function (record, serverResponse) { + if (!serverResponse || !serverResponse.Record) { + return; + } + + $.extend(true, record, serverResponse.Record); + }, + + /* Gets text for a field of a record according to it's type. + *************************************************************************/ + _getValueForRecordField: function (record, fieldName) { + var field = this.options.fields[fieldName]; + var fieldValue = record[fieldName]; + if (field.type == 'date') { + return this._getDisplayTextForDateRecordField(field, fieldValue); + } else { + return fieldValue; + } + }, + + /* Updates cells of a table row's text values from row's record values. + *************************************************************************/ + _updateRowTexts: function ($tableRow) { + var record = $tableRow.data('record'); + var $columns = $tableRow.find('td'); + for (var i = 0; i < this._columnList.length; i++) { + var displayItem = this._getDisplayTextForRecordField(record, this._columnList[i]); + if ((displayItem === 0)) displayItem = "0"; + $columns.eq(this._firstDataColumnOffset + i).html(displayItem || ''); + } + + this._onRowUpdated($tableRow); + }, + + /* Shows 'updated' animation for a table row. + *************************************************************************/ + _showUpdateAnimationForRow: function ($tableRow) { + var className = 'jtable-row-updated'; + if (this.options.jqueryuiTheme) { + className = className + ' ui-state-highlight'; + } + + $tableRow.stop(true, true).addClass(className, 'slow', '', function () { + $tableRow.removeClass(className, 5000); + }); + }, + + /************************************************************************ + * EVENT RAISING METHODS * + *************************************************************************/ + + _onRowUpdated: function ($row) { + this._trigger("rowUpdated", null, { row: $row, record: $row.data('record') }); + }, + + _onRecordUpdated: function ($row, data) { + this._trigger("recordUpdated", null, { record: $row.data('record'), row: $row, serverResponse: data }); + } + + }); + +})(jQuery); + + +/************************************************************************ + * VIEW RECORD extension for jTable * + *************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, + _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Events + recordUpdated: function (event, data) { }, + rowUpdated: function (event, data) { }, + + //Localization + messages: { + viewRecord: 'View' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$viewDiv: null, //Reference to the viewing dialog div (jQuery object) + _$viewingRow: null, //Reference to currently viewing row (jQuery object) + + /************************************************************************ + * CONSTRUCTOR AND INITIALIZATION METHODS * + *************************************************************************/ + + /* Overrides base method to do viewing-specific constructions. + *************************************************************************/ + _create: function () { + base._create.apply(this, arguments); + + if (!this.options.recordPreview) { + return; + } + + this._createViewDialogDiv(); + }, + + /* Creates and prepares preview dialog div + *************************************************************************/ + _createViewDialogDiv: function () { + var self = this; + + //Create a div for dialog and add to container element + self._$viewDiv = $('
    ') + .appendTo(self._$mainContainer); + + //Prepare dialog + self._$viewDiv.dialog({ + autoOpen: false, + show: self.options.dialogShowEffect, + hide: self.options.dialogHideEffect, + width: 'auto', + minWidth: '300', + modal: true, + title: self.options.messages.viewRecord, + buttons: + [{ //close button + text: self.options.messages.close, + click: function () { + self._$viewDiv.dialog('close'); + } + }], + close: function () { + var $viewForm = self._$viewDiv.find('form:first'); + self._trigger("formClosed", null, { form: $viewForm, formType: 'edit', row: self._$viewingRow }); + $viewForm.remove(); + } + }); + }, + + + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to add a 'view coomand column cell' to header row. + *************************************************************************/ + _addColumnsToHeaderRow: function ($tr) { + base._addColumnsToHeaderRow.apply(this, arguments); + if (self.options.recordPreview == true) { + $tr.append(this._createEmptyCommandHeader()); + } + }, + + /* Overrides base method to add a 'edit command cell' to a row. + *************************************************************************/ + _addCellsToRowUsingRecord: function ($row) { + var self = this; + base._addCellsToRowUsingRecord.apply(this, arguments); + + + // Add View command cell if the viewable option is sef to true + if (self.options.recordPreview == true ) { + var $span = $('').html(self.options.messages.viewRecord); + var $button = $('') + .addClass('jtable-command-button jtable-view-command-button') + .append($span) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self._showViewForm($row); + }); + $('') + .addClass('jtable-command-column') + .append($button) + .appendTo($row); + } + + + + }, + + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Shows view form for a row. + *************************************************************************/ + _showViewForm: function ($tableRow) { + var self = this; + var record = $tableRow.data('record'); + + //Create view form + var $ViewForm = $(''); + var ColumnCount=self.options.viewFormColumns ? self.options.viewFormColumns : 1; + var CurrentColumnCount=0; + var $ViewFormTable = $('
    ') + .addClass('jtable-input-field-container') + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('
    ').appendTo($ViewForm); + var $RowContainer = $('').appendTo($ViewFormTable); + //Create input fields + for (var i = 0; i < self._fieldList.length; i++) { + + var fieldName = self._fieldList[i]; + var field = self.options.fields[fieldName]; + var fieldValue = record[fieldName]; + + + //Do not create element for non-viewable fields + if (field.view == false) { + continue; + } + + + + //Create a container div for this input field and add to form + var $fieldContainer = $('').appendTo($ViewFormTable); + CurrentColumnCount=0; + } + + //Create a label for input + $fieldContainer.append(self._createViewLabelForRecordField(fieldName)); + + //Create input element with it's current value + var currentValue = self._getValueForRecordField(record, fieldName); + $fieldContainer.append( + self._createViewForRecordField({ + fieldName: fieldName, + value: currentValue, + record: record, + formType: 'view', + form: $ViewForm + })); + } + + self._makeCascadeDropDowns($ViewForm, record, 'view'); + + + + //Open dialog + self._$viewingRow = $tableRow; + self._$viewDiv.clear(); + self._$viewDiv.append($ViewForm).dialog('open'); + self._trigger("formCreated", null, { form: $ViewForm, formType: 'view', record: record, row: $tableRow }); + } + , + + + + /* Gets text for a field of a record according to it's type. + *************************************************************************/ + _getValueForRecordField: function (record, fieldName) { + var field = this.options.fields[fieldName]; + var fieldValue = record[fieldName]; + if (field.type == 'date') { + return this._getDisplayTextForDateRecordField(field, fieldValue); + } else { + return fieldValue; + } + } + + + /************************************************************************ + * EVENT RAISING METHODS * + *************************************************************************/ + + + }); + +})(jQuery); + +/************************************************************************ +* DELETION extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, + _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Options + deleteConfirmation: true, + + //Events + recordDeleted: function (event, data) { }, + + //Localization + messages: { + deleteConfirmation: 'This record will be deleted. Are you sure?', + deleteText: 'Delete', + deleting: 'Deleting', + canNotDeletedRecords: 'Can not delete {0} of {1} records!', + deleteProggress: 'Deleting {0} of {1} records, processing...' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$deleteRecordDiv: null, //Reference to the adding new record dialog div (jQuery object) + _$deletingRow: null, //Reference to currently deleting row (jQuery object) + + /************************************************************************ + * CONSTRUCTOR * + *************************************************************************/ + + /* Overrides base method to do deletion-specific constructions. + *************************************************************************/ + _create: function () { + base._create.apply(this, arguments); + this._createDeleteDialogDiv(); + }, + + /* Creates and prepares delete record confirmation dialog div. + *************************************************************************/ + _createDeleteDialogDiv: function () { + var self = this; + + //Check if deleteAction is supplied + if (!self.options.actions.deleteAction) { + return; + } + + //Create div element for delete confirmation dialog + self._$deleteRecordDiv = $('

    ').appendTo(self._$mainContainer); + + //Prepare dialog + self._$deleteRecordDiv.dialog({ + autoOpen: false, + show: self.options.dialogShowEffect, + hide: self.options.dialogHideEffect, + modal: true, + title: self.options.messages.areYouSure, + buttons: + [{ //cancel button + text: self.options.messages.cancel, + click: function () { + self._$deleteRecordDiv.dialog("close"); + } + }, {//delete button + id: 'DeleteDialogButton', + text: self.options.messages.deleteText, + click: function () { + + //row maybe removed by another source, if so, do nothing + if (self._$deletingRow.hasClass('jtable-row-removed')) { + self._$deleteRecordDiv.dialog('close'); + return; + } + + var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton'); + self._setEnabledOfDialogButton($deleteButton, false, self.options.messages.deleting); + self._deleteRecordFromServer( + self._$deletingRow, + function () { + self._removeRowsFromTableWithAnimation(self._$deletingRow); + self._$deleteRecordDiv.dialog('close'); + }, + function (message) { //error + self._$deleteRecordDiv.dialog('close'); + self._showError(message); + self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText); + } + ); + } + }], + close: function () { + var $deleteButton = self._$deleteRecordDiv.parent().find('#DeleteDialogButton'); + self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText); + } + }); + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* This method is used to delete one or more rows from server and the table. + *************************************************************************/ + deleteRows: function ($rows) { + var self = this; + + if ($rows.length <= 0) { + self._logWarn('No rows specified to jTable deleteRows method.'); + return; + } + + if (self._isBusy()) { + self._logWarn('Can not delete rows since jTable is busy!'); + return; + } + + //Deleting just one row + if ($rows.length == 1) { + self._deleteRecordFromServer( + $rows, + function () { //success + self._removeRowsFromTableWithAnimation($rows); + }, + function (message) { //error + self._showError(message); + } + ); + + return; + } + + //Deleting multiple rows + self._showBusy(self._formatString(self.options.messages.deleteProggress, 0, $rows.length)); + + //This method checks if deleting of all records is completed + var completedCount = 0; + var isCompleted = function () { + return (completedCount >= $rows.length); + }; + + //This method is called when deleting of all records completed + var completed = function () { + var $deletedRows = $rows.filter('.jtable-row-ready-to-remove'); + if ($deletedRows.length < $rows.length) { + self._showError(self._formatString(self.options.messages.canNotDeletedRecords, $rows.length - $deletedRows.length, $rows.length)); + } + + if ($deletedRows.length > 0) { + self._removeRowsFromTableWithAnimation($deletedRows); + } + + self._hideBusy(); + }; + + //Delete all rows + var deletedCount = 0; + $rows.each(function () { + var $row = $(this); + self._deleteRecordFromServer( + $row, + function () { //success + ++deletedCount; ++completedCount; + $row.addClass('jtable-row-ready-to-remove'); + self._showBusy(self._formatString(self.options.messages.deleteProggress, deletedCount, $rows.length)); + if (isCompleted()) { + completed(); + } + }, + function () { //error + ++completedCount; + if (isCompleted()) { + completed(); + } + } + ); + }); + }, + + /* Deletes a record from the table (optionally from the server also). + *************************************************************************/ + deleteRecord: function (options) { + var self = this; + options = $.extend({ + clientOnly: false, + animationsEnabled: self.options.animationsEnabled, + url: self.options.actions.deleteAction, + success: function () { }, + error: function () { } + }, options); + + if (options.key == undefined) { + self._logWarn('options parameter in deleteRecord method must contain a key property.'); + return; + } + + var $deletingRow = self.getRowByKey(options.key); + if ($deletingRow == null) { + self._logWarn('Can not found any row by key: ' + options.key); + return; + } + + if (options.clientOnly) { + self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled); + options.success(); + return; + } + + self._deleteRecordFromServer( + $deletingRow, + function (data) { //success + self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled); + options.success(data); + }, + function (message) { //error + self._showError(message); + options.error(message); + }, + options.url + ); + }, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to add a 'deletion column cell' to header row. + *************************************************************************/ + _addColumnsToHeaderRow: function ($tr) { + base._addColumnsToHeaderRow.apply(this, arguments); + if (this.options.actions.deleteAction != undefined) { + $tr.append(this._createEmptyCommandHeader()); + } + }, + + /* Overrides base method to add a 'delete command cell' to a row. + *************************************************************************/ + _addCellsToRowUsingRecord: function ($row) { + base._addCellsToRowUsingRecord.apply(this, arguments); + + var self = this; + if (self.options.actions.deleteAction != undefined) { + var $span = $('').html(self.options.messages.deleteText); + var $button = $('') + .addClass('jtable-command-button jtable-delete-command-button') + .append($span) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self._deleteButtonClickedForRow($row); + }); + $('') + .addClass('jtable-command-column') + .append($button) + .appendTo($row); + } + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* This method is called when user clicks delete button on a row. + *************************************************************************/ + _deleteButtonClickedForRow: function ($row) { + var self = this; + + var deleteConfirm; + var deleteConfirmMessage = self.options.messages.deleteConfirmation; + + //If options.deleteConfirmation is function then call it + if ($.isFunction(self.options.deleteConfirmation)) { + var data = { row: $row, record: $row.data('record'), deleteConfirm: true, deleteConfirmMessage: deleteConfirmMessage, cancel: false, cancelMessage: null }; + self.options.deleteConfirmation(data); + + //If delete progress is cancelled + if (data.cancel) { + + //If a canlellation reason is specified + if (data.cancelMessage) { + self._showError(data.cancelMessage); //TODO: show warning/stop message instead of error (also show warning/error ui icon)! + } + + return; + } + + deleteConfirmMessage = data.deleteConfirmMessage; + deleteConfirm = data.deleteConfirm; + } else { + deleteConfirm = self.options.deleteConfirmation; + } + + if (deleteConfirm != false) { + //Confirmation + self._$deleteRecordDiv.find('.jtable-delete-confirm-message').html(deleteConfirmMessage); + self._showDeleteDialog($row); + } else { + //No confirmation + self._deleteRecordFromServer( + $row, + function () { //success + self._removeRowsFromTableWithAnimation($row); + }, + function (message) { //error + self._showError(message); + } + ); + } + }, + + /* Shows delete comfirmation dialog. + *************************************************************************/ + _showDeleteDialog: function ($row) { + this._$deletingRow = $row; + this._$deleteRecordDiv.dialog('open'); + }, + + /* Performs an ajax call to server to delete record + * and removes row of the record from table if ajax call success. + *************************************************************************/ + _deleteRecordFromServer: function ($row, success, error, url) { + var self = this; + + var completeDelete = function(data) { + if (data.Result != 'OK') { + $row.data('deleting', false); + if (error) { + error(data.Message); + } + + return; + } + + self._trigger("recordDeleted", null, { record: $row.data('record'), row: $row, serverResponse: data }); + + if (success) { + success(data); + } + }; + + //Check if it is already being deleted right now + if ($row.data('deleting') == true) { + return; + } + + $row.data('deleting', true); + + var postData = {}; + postData[self._keyField] = self._getKeyValueOfRecord($row.data('record')); + + //Add additional field to delete request POST + for (var i = 0; i < this._fieldList.length; i++) { + var fieldName = this._fieldList[i]; + var field = this.options.fields[fieldName]; + + //Do not send this field to server if field delete option is not explicitly set to true + if (!field.delete) { + continue; + } + + //Key field is already added to postData + if (self._keyField == fieldName) { + continue; + } + + //Add field to postData to be sent to server + postData[fieldName] = $row.data('record')[fieldName]; + } + + //deleteAction may be a function, check if it is + if (!url && $.isFunction(self.options.actions.deleteAction)) { + + //Execute the function + var funcResult = self.options.actions.deleteAction(postData); + + //Check if result is a jQuery Deferred object + if (self._isDeferredObject(funcResult)) { + //Wait promise + funcResult.done(function (data) { + completeDelete(data); + }).fail(function () { + $row.data('deleting', false); + if (error) { + error(self.options.messages.serverCommunicationError); + } + }); + } else { //assume it returned the deletion result + completeDelete(funcResult); + } + + } else { //Assume it's a URL string + //Make ajax call to delete the record from server + this._ajax({ + url: (url || self.options.actions.deleteAction), + data: postData, + success: function (data) { + completeDelete(data); + }, + error: function () { + $row.data('deleting', false); + if (error) { + error(self.options.messages.serverCommunicationError); + } + } + }); + + } + }, + + /* Removes a row from table after a 'deleting' animation. + *************************************************************************/ + _removeRowsFromTableWithAnimation: function ($rows, animationsEnabled) { + var self = this; + + if (animationsEnabled == undefined) { + animationsEnabled = self.options.animationsEnabled; + } + + if (animationsEnabled) { + var className = 'jtable-row-deleting'; + if (this.options.jqueryuiTheme) { + className = className + ' ui-state-disabled'; + } + + //Stop current animation (if does exists) and begin 'deleting' animation. + $rows.stop(true, true).addClass(className, 'slow', '').promise().done(function () { + self._removeRowsFromTable($rows, 'deleted'); + }); + } else { + self._removeRowsFromTable($rows, 'deleted'); + } + } + + }); + +})(jQuery); + + +/************************************************************************ +* SELECTING extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow, + _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord, + _onLoadingRecords: $.hik.jtable.prototype._onLoadingRecords, + _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded, + _onRowsRemoved: $.hik.jtable.prototype._onRowsRemoved + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + + //Options + selecting: false, + multiselect: false, + selectingCheckboxes: false, + selectOnRowClick: true, + + //Events + selectionChanged: function (event, data) { } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _selectedRecordIdsBeforeLoad: null, //This array is used to store selected row Id's to restore them after a page refresh (string array). + _$selectAllCheckbox: null, //Reference to the 'select/deselect all' checkbox (jQuery object) + _shiftKeyDown: false, //True, if shift key is currently down. + + /************************************************************************ + * CONSTRUCTOR * + *************************************************************************/ + + /* Overrides base method to do selecting-specific constructions. + *************************************************************************/ + _create: function () { + if (this.options.selecting && this.options.selectingCheckboxes) { + ++this._firstDataColumnOffset; + this._bindKeyboardEvents(); + } + + //Call base method + base._create.apply(this, arguments); + }, + + /* Registers to keyboard events those are needed for selection + *************************************************************************/ + _bindKeyboardEvents: function () { + var self = this; + //Register to events to set _shiftKeyDown value + $(document) + .keydown(function (event) { + switch (event.which) { + case 16: + self._shiftKeyDown = true; + break; + } + }) + .keyup(function (event) { + switch (event.which) { + case 16: + self._shiftKeyDown = false; + break; + } + }); + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* Gets jQuery selection for currently selected rows. + *************************************************************************/ + selectedRows: function () { + return this._getSelectedRows(); + }, + + /* Makes row/rows 'selected'. + *************************************************************************/ + selectRows: function ($rows) { + this._selectRows($rows); + this._onSelectionChanged(); //TODO: trigger only if selected rows changes? + }, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to add a 'select column' to header row. + *************************************************************************/ + _addColumnsToHeaderRow: function ($tr) { + if (this.options.selecting && this.options.selectingCheckboxes) { + if (this.options.multiselect) { + $tr.append(this._createSelectAllHeader()); + } else { + $tr.append(this._createEmptyCommandHeader()); + } + } + + base._addColumnsToHeaderRow.apply(this, arguments); + }, + + /* Overrides base method to add a 'delete command cell' to a row. + *************************************************************************/ + _addCellsToRowUsingRecord: function ($row) { + if (this.options.selecting) { + this._makeRowSelectable($row); + } + + base._addCellsToRowUsingRecord.apply(this, arguments); + }, + + /* Overrides base event to store selection list + *************************************************************************/ + _onLoadingRecords: function () { + if (this.options.selecting) { + this._storeSelectionList(); + } + + base._onLoadingRecords.apply(this, arguments); + }, + + /* Overrides base event to restore selection list + *************************************************************************/ + _onRecordsLoaded: function () { + if (this.options.selecting) { + this._restoreSelectionList(); + } + + base._onRecordsLoaded.apply(this, arguments); + }, + + /* Overrides base event to check is any selected row is being removed. + *************************************************************************/ + _onRowsRemoved: function ($rows, reason) { + if (this.options.selecting && (reason != 'reloading') && ($rows.filter('.jtable-row-selected').length > 0)) { + this._onSelectionChanged(); + } + + base._onRowsRemoved.apply(this, arguments); + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Creates a header column to select/deselect all rows. + *************************************************************************/ + _createSelectAllHeader: function () { + var self = this; + + var $columnHeader = $('') + .addClass('jtable-command-column-header jtable-column-header-selecting'); + this._jqueryuiThemeAddClass($columnHeader, 'ui-state-default'); + + var $headerContainer = $('
    ') + .addClass('jtable-column-header-container') + .appendTo($columnHeader); + + self._$selectAllCheckbox = $('') + .appendTo($headerContainer) + .click(function () { + if (self._$tableRows.length <= 0) { + self._$selectAllCheckbox.attr('checked', false); + return; + } + + var allRows = self._$tableBody.find('>tr.jtable-data-row'); + if (self._$selectAllCheckbox.is(':checked')) { + self._selectRows(allRows); + } else { + self._deselectRows(allRows); + } + + self._onSelectionChanged(); + }); + + return $columnHeader; + }, + + /* Stores Id's of currently selected records to _selectedRecordIdsBeforeLoad. + *************************************************************************/ + _storeSelectionList: function () { + var self = this; + + if (!self.options.selecting) { + return; + } + + self._selectedRecordIdsBeforeLoad = []; + self._getSelectedRows().each(function () { + self._selectedRecordIdsBeforeLoad.push(self._getKeyValueOfRecord($(this).data('record'))); + }); + }, + + /* Selects rows whose Id is in _selectedRecordIdsBeforeLoad; + *************************************************************************/ + _restoreSelectionList: function () { + var self = this; + + if (!self.options.selecting) { + return; + } + + var selectedRowCount = 0; + for (var i = 0; i < self._$tableRows.length; ++i) { + var recordId = self._getKeyValueOfRecord(self._$tableRows[i].data('record')); + if ($.inArray(recordId, self._selectedRecordIdsBeforeLoad) > -1) { + self._selectRows(self._$tableRows[i]); + ++selectedRowCount; + } + } + + if (self._selectedRecordIdsBeforeLoad.length > 0 && self._selectedRecordIdsBeforeLoad.length != selectedRowCount) { + self._onSelectionChanged(); + } + + self._selectedRecordIdsBeforeLoad = []; + self._refreshSelectAllCheckboxState(); + }, + + /* Gets all selected rows. + *************************************************************************/ + _getSelectedRows: function () { + return this._$tableBody + .find('>tr.jtable-row-selected'); + }, + + /* Adds selectable feature to a row. + *************************************************************************/ + _makeRowSelectable: function ($row) { + var self = this; + + //Select/deselect on row click + if (self.options.selectOnRowClick) { + $row.click(function () { + self._invertRowSelection($row); + }); + } + + //'select/deselect' checkbox column + if (self.options.selectingCheckboxes) { + var $cell = $('
    ').addClass('jtable-selecting-column'); + var $selectCheckbox = $('').appendTo($cell); + if (!self.options.selectOnRowClick) { + $selectCheckbox.click(function () { + self._invertRowSelection($row); + }); + } + + $row.append($cell); + } + }, + + /* Inverts selection state of a single row. + *************************************************************************/ + _invertRowSelection: function ($row) { + if ($row.hasClass('jtable-row-selected')) { + this._deselectRows($row); + } else { + //Shift key? + if (this._shiftKeyDown) { + var rowIndex = this._findRowIndex($row); + //try to select row and above rows until first selected row + var beforeIndex = this._findFirstSelectedRowIndexBeforeIndex(rowIndex) + 1; + if (beforeIndex > 0 && beforeIndex < rowIndex) { + this._selectRows(this._$tableBody.find('tr').slice(beforeIndex, rowIndex + 1)); + } else { + //try to select row and below rows until first selected row + var afterIndex = this._findFirstSelectedRowIndexAfterIndex(rowIndex) - 1; + if (afterIndex > rowIndex) { + this._selectRows(this._$tableBody.find('tr').slice(rowIndex, afterIndex + 1)); + } else { + //just select this row + this._selectRows($row); + } + } + } else { + this._selectRows($row); + } + } + + this._onSelectionChanged(); + }, + + /* Search for a selected row (that is before given row index) to up and returns it's index + *************************************************************************/ + _findFirstSelectedRowIndexBeforeIndex: function (rowIndex) { + for (var i = rowIndex - 1; i >= 0; --i) { + if (this._$tableRows[i].hasClass('jtable-row-selected')) { + return i; + } + } + + return -1; + }, + + /* Search for a selected row (that is after given row index) to down and returns it's index + *************************************************************************/ + _findFirstSelectedRowIndexAfterIndex: function (rowIndex) { + for (var i = rowIndex + 1; i < this._$tableRows.length; ++i) { + if (this._$tableRows[i].hasClass('jtable-row-selected')) { + return i; + } + } + + return -1; + }, + + /* Makes row/rows 'selected'. + *************************************************************************/ + _selectRows: function ($rows) { + if (!this.options.multiselect) { + this._deselectRows(this._getSelectedRows()); + } + + $rows.addClass('jtable-row-selected'); + this._jqueryuiThemeAddClass($rows, 'ui-state-highlight'); + + if (this.options.selectingCheckboxes) { + $rows.find('>td.jtable-selecting-column >input').prop('checked', true); + } + + this._refreshSelectAllCheckboxState(); + }, + + /* Makes row/rows 'non selected'. + *************************************************************************/ + _deselectRows: function ($rows) { + $rows.removeClass('jtable-row-selected ui-state-highlight'); + if (this.options.selectingCheckboxes) { + $rows.find('>td.jtable-selecting-column >input').prop('checked', false); + } + + this._refreshSelectAllCheckboxState(); + }, + + /* Updates state of the 'select/deselect' all checkbox according to count of selected rows. + *************************************************************************/ + _refreshSelectAllCheckboxState: function () { + if (!this.options.selectingCheckboxes || !this.options.multiselect) { + return; + } + + var totalRowCount = this._$tableRows.length; + var selectedRowCount = this._getSelectedRows().length; + + if (selectedRowCount == 0) { + this._$selectAllCheckbox.prop('indeterminate', false); + this._$selectAllCheckbox.attr('checked', false); + } else if (selectedRowCount == totalRowCount) { + this._$selectAllCheckbox.prop('indeterminate', false); + this._$selectAllCheckbox.attr('checked', true); + } else { + this._$selectAllCheckbox.attr('checked', false); + this._$selectAllCheckbox.prop('indeterminate', true); + } + }, + + /************************************************************************ + * EVENT RAISING METHODS * + *************************************************************************/ + + _onSelectionChanged: function () { + this._trigger("selectionChanged", null, {}); + } + + }); + +})(jQuery); + + +/************************************************************************ +* PAGING extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + load: $.hik.jtable.prototype.load, + _create: $.hik.jtable.prototype._create, + _setOption: $.hik.jtable.prototype._setOption, + _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl, + _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading, + _addRowToTable: $.hik.jtable.prototype._addRowToTable, + _addRow: $.hik.jtable.prototype._addRow, + _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable, + _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + paging: false, + pageList: 'normal', //possible values: 'minimal', 'normal' + pageSize: 10, + pageSizes: [10, 25, 50, 100, 250, 500], + pageSizeChangeArea: true, + gotoPageArea: 'combobox', //possible values: 'textbox', 'combobox', 'none' + + messages: { + pagingInfo: 'Showing {0}-{1} of {2}', + pageSizeChangeLabel: 'Row count', + gotoPageLabel: 'Go to page' + } + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$bottomPanel: null, //Reference to the panel at the bottom of the table (jQuery object) + _$pagingListArea: null, //Reference to the page list area in to bottom panel (jQuery object) + _$pageSizeChangeArea: null, //Reference to the page size change area in to bottom panel (jQuery object) + _$pageInfoSpan: null, //Reference to the paging info area in to bottom panel (jQuery object) + _$gotoPageArea: null, //Reference to 'Go to page' input area in to bottom panel (jQuery object) + _$gotoPageInput: null, //Reference to 'Go to page' input in to bottom panel (jQuery object) + _totalRecordCount: 0, //Total count of records on all pages + _currentPageNo: 1, //Current page number + + /************************************************************************ + * CONSTRUCTOR AND INITIALIZING METHODS * + *************************************************************************/ + + /* Overrides base method to do paging-specific constructions. + *************************************************************************/ + _create: function() { + base._create.apply(this, arguments); + if (this.options.paging) { + this._loadPagingSettings(); + this._createBottomPanel(); + this._createPageListArea(); + this._createGotoPageInput(); + this._createPageSizeSelection(); + } + }, + + /* Loads user preferences for paging. + *************************************************************************/ + _loadPagingSettings: function() { + if (!this.options.saveUserPreferences) { + return; + } + + var pageSize = this._getCookie('page-size'); + if (pageSize) { + this.options.pageSize = this._normalizeNumber(pageSize, 1, 1000000, this.options.pageSize); + } + }, + + /* Creates bottom panel and adds to the page. + *************************************************************************/ + _createBottomPanel: function() { + this._$bottomPanel = $('
    ') + .addClass('jtable-bottom-panel') + .insertAfter(this._$table); + + this._jqueryuiThemeAddClass(this._$bottomPanel, 'ui-state-default'); + + $('
    ').addClass('jtable-left-area').appendTo(this._$bottomPanel); + $('
    ').addClass('jtable-right-area').appendTo(this._$bottomPanel); + }, + + /* Creates page list area. + *************************************************************************/ + _createPageListArea: function() { + this._$pagingListArea = $('') + .addClass('jtable-page-list') + .appendTo(this._$bottomPanel.find('.jtable-left-area')); + + this._$pageInfoSpan = $('') + .addClass('jtable-page-info') + .appendTo(this._$bottomPanel.find('.jtable-right-area')); + }, + + /* Creates page list change area. + *************************************************************************/ + _createPageSizeSelection: function() { + var self = this; + + if (!self.options.pageSizeChangeArea) { + return; + } + + //Add current page size to page sizes list if not contains it + if (self._findIndexInArray(self.options.pageSize, self.options.pageSizes) < 0) { + self.options.pageSizes.push(parseInt(self.options.pageSize)); + self.options.pageSizes.sort(function(a, b) { return a - b; }); + } + + //Add a span to contain page size change items + self._$pageSizeChangeArea = $('') + .addClass('jtable-page-size-change') + .appendTo(self._$bottomPanel.find('.jtable-left-area')); + + //Page size label + self._$pageSizeChangeArea.append('' + self.options.messages.pageSizeChangeLabel + ': '); + + //Page size change combobox + var $pageSizeChangeCombobox = $('').appendTo(self._$pageSizeChangeArea); + + //Add page sizes to the combobox + for (var i = 0; i < self.options.pageSizes.length; i++) { + $pageSizeChangeCombobox.append(''); + } + + //Select current page size + $pageSizeChangeCombobox.val(self.options.pageSize); + + //Change page size on combobox change + $pageSizeChangeCombobox.change(function() { + self._changePageSize(parseInt($(this).val())); + }); + }, + + /* Creates go to page area. + *************************************************************************/ + _createGotoPageInput: function() { + var self = this; + + if (!self.options.gotoPageArea || self.options.gotoPageArea == 'none') { + return; + } + + //Add a span to contain goto page items + this._$gotoPageArea = $('') + .addClass('jtable-goto-page') + .appendTo(self._$bottomPanel.find('.jtable-left-area')); + + //Goto page label + this._$gotoPageArea.append('' + self.options.messages.gotoPageLabel + ': '); + + //Goto page input + if (self.options.gotoPageArea == 'combobox') { + + self._$gotoPageInput = $('') + .appendTo(this._$gotoPageArea) + .data('pageCount', 1) + .change(function() { + self._changePage(parseInt($(this).val())); + }); + self._$gotoPageInput.append(''); + + } else { //textbox + + self._$gotoPageInput = $('') + .appendTo(this._$gotoPageArea) + .keypress(function(event) { + if (event.which == 13) { //enter + event.preventDefault(); + self._changePage(parseInt(self._$gotoPageInput.val())); + } else if (event.which == 43) { // + + event.preventDefault(); + self._changePage(parseInt(self._$gotoPageInput.val()) + 1); + } else if (event.which == 45) { // - + event.preventDefault(); + self._changePage(parseInt(self._$gotoPageInput.val()) - 1); + } else { + //Allow only digits + var isValid = ( + (47 < event.keyCode && event.keyCode < 58 && event.shiftKey == false && event.altKey == false) + || (event.keyCode == 8) + || (event.keyCode == 9) + ); + + if (!isValid) { + event.preventDefault(); + } + } + }); + + } + }, + + /* Refreshes the 'go to page' input. + *************************************************************************/ + _refreshGotoPageInput: function() { + if (!this.options.gotoPageArea || this.options.gotoPageArea == 'none') { + return; + } + + if (this._totalRecordCount <= 0) { + this._$gotoPageArea.hide(); + } else { + this._$gotoPageArea.show(); + } + + if (this.options.gotoPageArea == 'combobox') { + var oldPageCount = this._$gotoPageInput.data('pageCount'); + var currentPageCount = this._calculatePageCount(); + if (oldPageCount != currentPageCount) { + this._$gotoPageInput.empty(); + + //Skip some pages is there are too many pages + var pageStep = 1; + if (currentPageCount > 10000) { + pageStep = 100; + } else if (currentPageCount > 5000) { + pageStep = 10; + } else if (currentPageCount > 2000) { + pageStep = 5; + } else if (currentPageCount > 1000) { + pageStep = 2; + } + + for (var i = pageStep; i <= currentPageCount; i += pageStep) { + this._$gotoPageInput.append(''); + } + + this._$gotoPageInput.data('pageCount', currentPageCount); + } + } + + //same for 'textbox' and 'combobox' + this._$gotoPageInput.val(this._currentPageNo); + }, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides load method to set current page to 1. + *************************************************************************/ + load: function() { + this._currentPageNo = 1; + + base.load.apply(this, arguments); + }, + + /* Used to change options dynamically after initialization. + *************************************************************************/ + _setOption: function(key, value) { + base._setOption.apply(this, arguments); + + if (key == 'pageSize') { + this._changePageSize(parseInt(value)); + } + }, + + /* Changes current page size with given value. + *************************************************************************/ + _changePageSize: function(pageSize) { + if (pageSize == this.options.pageSize) { + return; + } + + this.options.pageSize = pageSize; + + //Normalize current page + var pageCount = this._calculatePageCount(); + if (this._currentPageNo > pageCount) { + this._currentPageNo = pageCount; + } + if (this._currentPageNo <= 0) { + this._currentPageNo = 1; + } + + //if user sets one of the options on the combobox, then select it. + var $pageSizeChangeCombobox = this._$bottomPanel.find('.jtable-page-size-change select'); + if ($pageSizeChangeCombobox.length > 0) { + if (parseInt($pageSizeChangeCombobox.val()) != pageSize) { + var selectedOption = $pageSizeChangeCombobox.find('option[value=' + pageSize + ']'); + if (selectedOption.length > 0) { + $pageSizeChangeCombobox.val(pageSize); + } + } + } + + this._savePagingSettings(); + this._reloadTable(); + }, + + /* Saves user preferences for paging + *************************************************************************/ + _savePagingSettings: function() { + if (!this.options.saveUserPreferences) { + return; + } + + this._setCookie('page-size', this.options.pageSize); + }, + + /* Overrides _createRecordLoadUrl method to add paging info to URL. + *************************************************************************/ + _createRecordLoadUrl: function() { + var loadUrl = base._createRecordLoadUrl.apply(this, arguments); + loadUrl = this._addPagingInfoToUrl(loadUrl, this._currentPageNo); + return loadUrl; + }, + + /* Overrides _createJtParamsForLoading method to add paging parameters to jtParams object. + *************************************************************************/ + _createJtParamsForLoading: function () { + var jtParams = base._createJtParamsForLoading.apply(this, arguments); + + if (this.options.paging) { + jtParams.jtStartIndex = (this._currentPageNo - 1) * this.options.pageSize; + jtParams.jtPageSize = this.options.pageSize; + } + + return jtParams; + }, + + /* Overrides _addRowToTable method to re-load table when a new row is created. + * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES. + * USE _addRow METHOD. + *************************************************************************/ + _addRowToTable: function ($tableRow, index, isNewRow) { + if (isNewRow && this.options.paging) { + this._reloadTable(); + return; + } + + base._addRowToTable.apply(this, arguments); + }, + + /* Overrides _addRow method to re-load table when a new row is created. + *************************************************************************/ + _addRow: function ($row, options) { + if (options && options.isNewRow && this.options.paging) { + this._reloadTable(); + return; + } + + base._addRow.apply(this, arguments); + }, + + /* Overrides _removeRowsFromTable method to re-load table when a row is removed from table. + *************************************************************************/ + _removeRowsFromTable: function ($rows, reason) { + base._removeRowsFromTable.apply(this, arguments); + + if (this.options.paging) { + if (this._$tableRows.length <= 0 && this._currentPageNo > 1) { + --this._currentPageNo; + } + + this._reloadTable(); + } + }, + + /* Overrides _onRecordsLoaded method to to do paging specific tasks. + *************************************************************************/ + _onRecordsLoaded: function (data) { + if (this.options.paging) { + this._totalRecordCount = data.TotalRecordCount; + this._createPagingList(); + this._createPagingInfo(); + this._refreshGotoPageInput(); + } + + base._onRecordsLoaded.apply(this, arguments); + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Adds jtStartIndex and jtPageSize parameters to a URL as query string. + *************************************************************************/ + _addPagingInfoToUrl: function (url, pageNumber) { + if (!this.options.paging) { + return url; + } + + var jtStartIndex = (pageNumber - 1) * this.options.pageSize; + var jtPageSize = this.options.pageSize; + + return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtStartIndex=' + jtStartIndex + '&jtPageSize=' + jtPageSize); + }, + + /* Creates and shows the page list. + *************************************************************************/ + _createPagingList: function () { + if (this.options.pageSize <= 0) { + return; + } + + this._$pagingListArea.empty(); + if (this._totalRecordCount <= 0) { + return; + } + + var pageCount = this._calculatePageCount(); + + this._createFirstAndPreviousPageButtons(); + if (this.options.pageList == 'normal') { + this._createPageNumberButtons(this._calculatePageNumbers(pageCount)); + } + this._createLastAndNextPageButtons(pageCount); + this._bindClickEventsToPageNumberButtons(); + }, + + /* Creates and shows previous and first page links. + *************************************************************************/ + _createFirstAndPreviousPageButtons: function () { + var $first = $('') + .addClass('jtable-page-number-first') + .html('<<') + .data('pageNumber', 1) + .appendTo(this._$pagingListArea); + + var $previous = $('') + .addClass('jtable-page-number-previous') + .html('<') + .data('pageNumber', this._currentPageNo - 1) + .appendTo(this._$pagingListArea); + + this._jqueryuiThemeAddClass($first, 'ui-button ui-state-default', 'ui-state-hover'); + this._jqueryuiThemeAddClass($previous, 'ui-button ui-state-default', 'ui-state-hover'); + + if (this._currentPageNo <= 1) { + $first.addClass('jtable-page-number-disabled'); + $previous.addClass('jtable-page-number-disabled'); + this._jqueryuiThemeAddClass($first, 'ui-state-disabled'); + this._jqueryuiThemeAddClass($previous, 'ui-state-disabled'); + } + }, + + /* Creates and shows next and last page links. + *************************************************************************/ + _createLastAndNextPageButtons: function (pageCount) { + var $next = $('') + .addClass('jtable-page-number-next') + .html('>') + .data('pageNumber', this._currentPageNo + 1) + .appendTo(this._$pagingListArea); + var $last = $('') + .addClass('jtable-page-number-last') + .html('>>') + .data('pageNumber', pageCount) + .appendTo(this._$pagingListArea); + + this._jqueryuiThemeAddClass($next, 'ui-button ui-state-default', 'ui-state-hover'); + this._jqueryuiThemeAddClass($last, 'ui-button ui-state-default', 'ui-state-hover'); + + if (this._currentPageNo >= pageCount) { + $next.addClass('jtable-page-number-disabled'); + $last.addClass('jtable-page-number-disabled'); + this._jqueryuiThemeAddClass($next, 'ui-state-disabled'); + this._jqueryuiThemeAddClass($last, 'ui-state-disabled'); + } + }, + + /* Creates and shows page number links for given number array. + *************************************************************************/ + _createPageNumberButtons: function (pageNumbers) { + var previousNumber = 0; + for (var i = 0; i < pageNumbers.length; i++) { + //Create "..." between page numbers if needed + if ((pageNumbers[i] - previousNumber) > 1) { + $('') + .addClass('jtable-page-number-space') + .html('...') + .appendTo(this._$pagingListArea); + } + + this._createPageNumberButton(pageNumbers[i]); + previousNumber = pageNumbers[i]; + } + }, + + /* Creates a page number link and adds to paging area. + *************************************************************************/ + _createPageNumberButton: function (pageNumber) { + var $pageNumber = $('') + .addClass('jtable-page-number') + .html(pageNumber) + .data('pageNumber', pageNumber) + .appendTo(this._$pagingListArea); + + this._jqueryuiThemeAddClass($pageNumber, 'ui-button ui-state-default', 'ui-state-hover'); + + if (this._currentPageNo == pageNumber) { + $pageNumber.addClass('jtable-page-number-active jtable-page-number-disabled'); + this._jqueryuiThemeAddClass($pageNumber, 'ui-state-active'); + } + }, + + /* Calculates total page count according to page size and total record count. + *************************************************************************/ + _calculatePageCount: function () { + var pageCount = Math.floor(this._totalRecordCount / this.options.pageSize); + if (this._totalRecordCount % this.options.pageSize != 0) { + ++pageCount; + } + + return pageCount; + }, + + /* Calculates page numbers and returns an array of these numbers. + *************************************************************************/ + _calculatePageNumbers: function (pageCount) { + if (pageCount <= 4) { + //Show all pages + var pageNumbers = []; + for (var i = 1; i <= pageCount; ++i) { + pageNumbers.push(i); + } + + return pageNumbers; + } else { + //show first three, last three, current, previous and next page numbers + var shownPageNumbers = [1, 2, pageCount - 1, pageCount]; + var previousPageNo = this._normalizeNumber(this._currentPageNo - 1, 1, pageCount, 1); + var nextPageNo = this._normalizeNumber(this._currentPageNo + 1, 1, pageCount, 1); + + this._insertToArrayIfDoesNotExists(shownPageNumbers, previousPageNo); + this._insertToArrayIfDoesNotExists(shownPageNumbers, this._currentPageNo); + this._insertToArrayIfDoesNotExists(shownPageNumbers, nextPageNo); + + shownPageNumbers.sort(function (a, b) { return a - b; }); + return shownPageNumbers; + } + }, + + /* Creates and shows paging informations. + *************************************************************************/ + _createPagingInfo: function () { + if (this._totalRecordCount <= 0) { + this._$pageInfoSpan.empty(); + return; + } + + var startNo = (this._currentPageNo - 1) * this.options.pageSize + 1; + var endNo = this._currentPageNo * this.options.pageSize; + endNo = this._normalizeNumber(endNo, startNo, this._totalRecordCount, 0); + + if (endNo >= startNo) { + var pagingInfoMessage = this._formatString(this.options.messages.pagingInfo, startNo, endNo, this._totalRecordCount); + this._$pageInfoSpan.html(pagingInfoMessage); + } + }, + + /* Binds click events of all page links to change the page. + *************************************************************************/ + _bindClickEventsToPageNumberButtons: function () { + var self = this; + self._$pagingListArea + .find('.jtable-page-number,.jtable-page-number-previous,.jtable-page-number-next,.jtable-page-number-first,.jtable-page-number-last') + .not('.jtable-page-number-disabled') + .click(function (e) { + e.preventDefault(); + self._changePage($(this).data('pageNumber')); + }); + }, + + /* Changes current page to given value. + *************************************************************************/ + _changePage: function (pageNo) { + pageNo = this._normalizeNumber(pageNo, 1, this._calculatePageCount(), 1); + if (pageNo == this._currentPageNo) { + this._refreshGotoPageInput(); + return; + } + + this._currentPageNo = pageNo; + this._reloadTable(); + } + + }); + +})(jQuery); + + +/************************************************************************ +* SORTING extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _initializeFields: $.hik.jtable.prototype._initializeFields, + _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions, + _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField, + _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl, + _createJtParamsForLoading: $.hik.jtable.prototype._createJtParamsForLoading + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + sorting: false, + multiSorting: false, + defaultSorting: '' + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _lastSorting: null, //Last sorting of the table + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides base method to create sorting array. + *************************************************************************/ + _initializeFields: function () { + base._initializeFields.apply(this, arguments); + + this._lastSorting = []; + if (this.options.sorting) { + this._buildDefaultSortingArray(); + } + }, + + /* Overrides _normalizeFieldOptions method to normalize sorting option for fields. + *************************************************************************/ + _normalizeFieldOptions: function (fieldName, props) { + base._normalizeFieldOptions.apply(this, arguments); + props.sorting = (props.sorting != false); + }, + + /* Overrides _createHeaderCellForField to make columns sortable. + *************************************************************************/ + _createHeaderCellForField: function (fieldName, field) { + var $headerCell = base._createHeaderCellForField.apply(this, arguments); + if (this.options.sorting && field.sorting) { + this._makeColumnSortable($headerCell, fieldName, field.initialSortingDirection); + } + + return $headerCell; + }, + + /* Overrides _createRecordLoadUrl to add sorting specific info to URL. + *************************************************************************/ + _createRecordLoadUrl: function () { + var loadUrl = base._createRecordLoadUrl.apply(this, arguments); + loadUrl = this._addSortingInfoToUrl(loadUrl); + return loadUrl; + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Builds the sorting array according to defaultSorting string + *************************************************************************/ + _buildDefaultSortingArray: function () { + var self = this; + + $.each(self.options.defaultSorting.split(","), function (orderIndex, orderValue) { + $.each(self.options.fields, function (fieldName, fieldProps) { + if (fieldProps.sorting) { + var colOffset = orderValue.indexOf(fieldName); + if (colOffset > -1) { + if (orderValue.toUpperCase().indexOf(' DESC', colOffset) > -1) { + self._lastSorting.push({ + fieldName: fieldName, + sortOrder: 'DESC' + }); + } else { + self._lastSorting.push({ + fieldName: fieldName, + sortOrder: 'ASC' + }); + } + } + } + }); + }); + }, + + /* Makes a column sortable. + *************************************************************************/ + _makeColumnSortable: function ($columnHeader, fieldName, initialSortingDirection) { + var self = this; + + $columnHeader + .addClass('jtable-column-header-sortable') + .click(function (e) { + e.preventDefault(); + + if (!self.options.multiSorting || !e.ctrlKey) { + self._lastSorting = []; //clear previous sorting + } + + self._sortTableByColumn($columnHeader); + }); + + if(initialSortingDirection){ + $columnHeader.addClass('jtable-column-header-sorted-' + initialSortingDirection.toLowerCase()); + } + + //Set default sorting + $.each(this._lastSorting, function (sortIndex, sortField) { + if (sortField.fieldName == fieldName) { + if (sortField.sortOrder == 'DESC') { + $columnHeader.addClass('jtable-column-header-sorted-desc'); + } else { + $columnHeader.addClass('jtable-column-header-sorted-asc'); + } + } + }); + }, + + /* Sorts table according to a column header. + *************************************************************************/ + _sortTableByColumn: function ($columnHeader) { + //Remove sorting styles from all columns except this one + if (this._lastSorting.length == 0) { + $columnHeader.siblings().removeClass('jtable-column-header-sorted-asc jtable-column-header-sorted-desc'); + } + + //If current sorting list includes this column, remove it from the list + for (var i = 0; i < this._lastSorting.length; i++) { + if (this._lastSorting[i].fieldName == $columnHeader.data('fieldName')) { + this._lastSorting.splice(i--, 1); + } + } + + //Sort ASC or DESC according to current sorting state + if ($columnHeader.hasClass('jtable-column-header-sorted-asc')) { + $columnHeader.removeClass('jtable-column-header-sorted-asc').addClass('jtable-column-header-sorted-desc'); + this._lastSorting.push({ + 'fieldName': $columnHeader.data('fieldName'), + sortOrder: 'DESC' + }); + } else { + $columnHeader.removeClass('jtable-column-header-sorted-desc').addClass('jtable-column-header-sorted-asc'); + this._lastSorting.push({ + 'fieldName': $columnHeader.data('fieldName'), + sortOrder: 'ASC' + }); + } + + //Load current page again + this._reloadTable(); + }, + + /* Adds jtSorting parameter to a URL as query string. + *************************************************************************/ + _addSortingInfoToUrl: function (url) { + if (!this.options.sorting || this._lastSorting.length == 0) { + return url; + } + + var sorting = []; + $.each(this._lastSorting, function (idx, value) { + sorting.push(value.fieldName + ' ' + value.sortOrder); + }); + + return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtSorting=' + sorting.join(",")); + }, + + /* Overrides _createJtParamsForLoading method to add sorging parameters to jtParams object. + *************************************************************************/ + _createJtParamsForLoading: function () { + var jtParams = base._createJtParamsForLoading.apply(this, arguments); + + if (this.options.sorting && this._lastSorting.length) { + var sorting = []; + $.each(this._lastSorting, function (idx, value) { + sorting.push(value.fieldName + ' ' + value.sortOrder); + }); + + jtParams.jtSorting = sorting.join(","); + } + + return jtParams; + } + + }); + +})(jQuery); + +/************************************************************************ +* DYNAMIC COLUMNS extension for jTable * +* (Show/hide/resize columns) * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _create: $.hik.jtable.prototype._create, + _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions, + _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField, + _createCellForRecordField: $.hik.jtable.prototype._createCellForRecordField + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + + options: { + tableId: undefined, + columnResizable: true, + columnSelectable: true + }, + + /************************************************************************ + * PRIVATE FIELDS * + *************************************************************************/ + + _$columnSelectionDiv: null, + _$columnResizeBar: null, + _cookieKeyPrefix: null, + _currentResizeArgs: null, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides _addRowToTableHead method. + *************************************************************************/ + + _create: function () { + base._create.apply(this, arguments); + + this._createColumnResizeBar(); + this._createColumnSelection(); + + if (this.options.saveUserPreferences) { + this._loadColumnSettings(); + } + + this._normalizeColumnWidths(); + }, + + /* Normalizes some options for a field (sets default values). + *************************************************************************/ + _normalizeFieldOptions: function (fieldName, props) { + base._normalizeFieldOptions.apply(this, arguments); + + //columnResizable + if (this.options.columnResizable) { + props.columnResizable = (props.columnResizable != false); + } + + //visibility + if (!props.visibility) { + props.visibility = 'visible'; + } + }, + + /* Overrides _createHeaderCellForField to make columns dynamic. + *************************************************************************/ + _createHeaderCellForField: function (fieldName, field) { + var $headerCell = base._createHeaderCellForField.apply(this, arguments); + + //Make data columns resizable except the last one + if (this.options.columnResizable && field.columnResizable && (fieldName != this._columnList[this._columnList.length - 1])) { + this._makeColumnResizable($headerCell); + } + + //Hide column if needed + if (field.visibility == 'hidden') { + $headerCell.hide(); + } + + return $headerCell; + }, + + /* Overrides _createHeaderCellForField to decide show or hide a column. + *************************************************************************/ + _createCellForRecordField: function (record, fieldName) { + var $column = base._createCellForRecordField.apply(this, arguments); + + var field = this.options.fields[fieldName]; + if (field.visibility == 'hidden') { + $column.hide(); + } + + return $column; + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* Changes visibility of a column. + *************************************************************************/ + changeColumnVisibility: function (columnName, visibility) { + this._changeColumnVisibilityInternal(columnName, visibility); + this._normalizeColumnWidths(); + if (this.options.saveUserPreferences) { + this._saveColumnSettings(); + } + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Changes visibility of a column. + *************************************************************************/ + _changeColumnVisibilityInternal: function (columnName, visibility) { + //Check if there is a column with given name + var columnIndex = this._columnList.indexOf(columnName); + if (columnIndex < 0) { + this._logWarn('Column "' + columnName + '" does not exist in fields!'); + return; + } + + //Check if visibility value is valid + if (['visible', 'hidden', 'fixed'].indexOf(visibility) < 0) { + this._logWarn('Visibility value is not valid: "' + visibility + '"! Options are: visible, hidden, fixed.'); + return; + } + + //Get the field + var field = this.options.fields[columnName]; + if (field.visibility == visibility) { + return; //No action if new value is same as old one. + } + + //Hide or show the column if needed + var columnIndexInTable = this._firstDataColumnOffset + columnIndex + 1; + if (field.visibility != 'hidden' && visibility == 'hidden') { + this._$table + .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')') + .hide(); + } else if (field.visibility == 'hidden' && visibility != 'hidden') { + this._$table + .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')') + .show() + .css('display', 'table-cell'); + } + + field.visibility = visibility; + }, + + /* Prepares dialog to change settings. + *************************************************************************/ + _createColumnSelection: function () { + var self = this; + + //Create a div for dialog and add to container element + this._$columnSelectionDiv = $('
    ') + .addClass('jtable-column-selection-container') + .appendTo(self._$mainContainer); + + this._$table.children('thead').bind('contextmenu', function (e) { + if (!self.options.columnSelectable) { + return; + } + + e.preventDefault(); + + //Make an overlay div to disable page clicks + $('
    ') + .addClass('jtable-contextmenu-overlay') + .click(function () { + $(this).remove(); + self._$columnSelectionDiv.hide(); + }) + .bind('contextmenu', function () { return false; }) + .appendTo(document.body); + + self._fillColumnSelection(); + + //Calculate position of column selection list and show it + + var containerOffset = self._$mainContainer.offset(); + var selectionDivTop = e.pageY - containerOffset.top; + var selectionDivLeft = e.pageX - containerOffset.left; + + var selectionDivMinWidth = 100; //in pixels + var containerWidth = self._$mainContainer.width(); + + //If user clicks right area of header of the table, show list at a little left + if ((containerWidth > selectionDivMinWidth) && (selectionDivLeft > (containerWidth - selectionDivMinWidth))) { + selectionDivLeft = containerWidth - selectionDivMinWidth; + } + + self._$columnSelectionDiv.css({ + left: selectionDivLeft, + top: selectionDivTop, + 'min-width': selectionDivMinWidth + 'px' + }).show(); + }); + }, + + /* Prepares content of settings dialog. + *************************************************************************/ + _fillColumnSelection: function () { + var self = this; + + var $columnsUl = $('
      ') + .addClass('jtable-column-select-list'); + for (var i = 0; i < this._columnList.length; i++) { + var columnName = this._columnList[i]; + var field = this.options.fields[columnName]; + + //Crete li element + var $columnLi = $('
    • ').appendTo($columnsUl); + + //Create label for the checkbox + var $label = $('') + .append($('' + (field.title || columnName) + '')) + .appendTo($columnLi); + + //Create checkbox + var $checkbox = $('') + .prependTo($label) + .click(function () { + var $clickedCheckbox = $(this); + var clickedColumnName = $clickedCheckbox.attr('name'); + var clickedField = self.options.fields[clickedColumnName]; + if (clickedField.visibility == 'fixed') { + return; + } + + self.changeColumnVisibility(clickedColumnName, $clickedCheckbox.is(':checked') ? 'visible' : 'hidden'); + }); + + //Check, if column if shown + if (field.visibility != 'hidden') { + $checkbox.attr('checked', 'checked'); + } + + //Disable, if column is fixed + if (field.visibility == 'fixed') { + $checkbox.attr('disabled', 'disabled'); + } + } + + this._$columnSelectionDiv.html($columnsUl); + }, + + /* creates a vertical bar that is shown while resizing columns. + *************************************************************************/ + _createColumnResizeBar: function () { + this._$columnResizeBar = $('
      ') + .addClass('jtable-column-resize-bar') + .appendTo(this._$mainContainer) + .hide(); + }, + + /* Makes a column sortable. + *************************************************************************/ + _makeColumnResizable: function ($columnHeader) { + var self = this; + + //Create a handler to handle mouse click event + $('
      ') + .addClass('jtable-column-resize-handler') + .appendTo($columnHeader.find('.jtable-column-header-container')) //Append the handler to the column + .mousedown(function (downevent) { //handle mousedown event for the handler + downevent.preventDefault(); + downevent.stopPropagation(); + + var mainContainerOffset = self._$mainContainer.offset(); + + //Get a reference to the next column + var $nextColumnHeader = $columnHeader.nextAll('th.jtable-column-header:visible:first'); + if (!$nextColumnHeader.length) { + return; + } + + //Store some information to be used on resizing + var minimumColumnWidth = 10; //A column's width can not be smaller than 10 pixel. + self._currentResizeArgs = { + currentColumnStartWidth: $columnHeader.outerWidth(), + minWidth: minimumColumnWidth, + maxWidth: $columnHeader.outerWidth() + $nextColumnHeader.outerWidth() - minimumColumnWidth, + mouseStartX: downevent.pageX, + minResizeX: function () { return this.mouseStartX - (this.currentColumnStartWidth - this.minWidth); }, + maxResizeX: function () { return this.mouseStartX + (this.maxWidth - this.currentColumnStartWidth); } + }; + + //Handle mouse move event to move resizing bar + var resizeonmousemove = function (moveevent) { + if (!self._currentResizeArgs) { + return; + } + + var resizeBarX = self._normalizeNumber(moveevent.pageX, self._currentResizeArgs.minResizeX(), self._currentResizeArgs.maxResizeX()); + self._$columnResizeBar.css('left', (resizeBarX - mainContainerOffset.left) + 'px'); + }; + + //Handle mouse up event to finish resizing of the column + var resizeonmouseup = function (upevent) { + if (!self._currentResizeArgs) { + return; + } + + $(document).unbind('mousemove', resizeonmousemove); + $(document).unbind('mouseup', resizeonmouseup); + + self._$columnResizeBar.hide(); + + //Calculate new widths in pixels + var mouseChangeX = upevent.pageX - self._currentResizeArgs.mouseStartX; + var currentColumnFinalWidth = self._normalizeNumber(self._currentResizeArgs.currentColumnStartWidth + mouseChangeX, self._currentResizeArgs.minWidth, self._currentResizeArgs.maxWidth); + var nextColumnFinalWidth = $nextColumnHeader.outerWidth() + (self._currentResizeArgs.currentColumnStartWidth - currentColumnFinalWidth); + + //Calculate widths as percent + var pixelToPercentRatio = $columnHeader.data('width-in-percent') / self._currentResizeArgs.currentColumnStartWidth; + $columnHeader.data('width-in-percent', currentColumnFinalWidth * pixelToPercentRatio); + $nextColumnHeader.data('width-in-percent', nextColumnFinalWidth * pixelToPercentRatio); + + //Set new widths to columns (resize!) + $columnHeader.css('width', $columnHeader.data('width-in-percent') + '%'); + $nextColumnHeader.css('width', $nextColumnHeader.data('width-in-percent') + '%'); + + //Normalize all column widths + self._normalizeColumnWidths(); + + //Finish resizing + self._currentResizeArgs = null; + + //Save current preferences + if (self.options.saveUserPreferences) { + self._saveColumnSettings(); + } + }; + + //Show vertical resize bar + self._$columnResizeBar + .show() + .css({ + top: ($columnHeader.offset().top - mainContainerOffset.top) + 'px', + left: (downevent.pageX - mainContainerOffset.left) + 'px', + height: (self._$table.outerHeight()) + 'px' + }); + + //Bind events + $(document).bind('mousemove', resizeonmousemove); + $(document).bind('mouseup', resizeonmouseup); + }); + }, + + /* Normalizes column widths as percent for current view. + *************************************************************************/ + _normalizeColumnWidths: function () { + + //Set command column width + var commandColumnHeaders = this._$table + .find('>thead th.jtable-command-column-header') + .data('width-in-percent', 1) + .css('width', '1%'); + + //Find data columns + var headerCells = this._$table.find('>thead th.jtable-column-header'); + + //Calculate total width of data columns + var totalWidthInPixel = 0; + headerCells.each(function () { + var $cell = $(this); + if ($cell.is(':visible')) { + totalWidthInPixel += $cell.outerWidth(); + } + }); + + //Calculate width of each column + var columnWidhts = {}; + var availableWidthInPercent = 100.0 - commandColumnHeaders.length; + headerCells.each(function () { + var $cell = $(this); + if ($cell.is(':visible')) { + var fieldName = $cell.data('fieldName'); + var widthInPercent = $cell.outerWidth() * availableWidthInPercent / totalWidthInPixel; + columnWidhts[fieldName] = widthInPercent; + } + }); + + //Set width of each column + headerCells.each(function () { + var $cell = $(this); + if ($cell.is(':visible')) { + var fieldName = $cell.data('fieldName'); + $cell.data('width-in-percent', columnWidhts[fieldName]).css('width', columnWidhts[fieldName] + '%'); + } + }); + }, + + /* Saves field setting to cookie. + * Saved setting will be a string like that: + * fieldName1=visible;23|fieldName2=hidden;17|... + *************************************************************************/ + _saveColumnSettings: function () { + var self = this; + var fieldSettings = ''; + this._$table.find('>thead >tr >th.jtable-column-header').each(function () { + var $cell = $(this); + var fieldName = $cell.data('fieldName'); + var columnWidth = $cell.data('width-in-percent'); + var fieldVisibility = self.options.fields[fieldName].visibility; + var fieldSetting = fieldName + "=" + fieldVisibility + ';' + columnWidth; + fieldSettings = fieldSettings + fieldSetting + '|'; + }); + + this._setCookie('column-settings', fieldSettings.substr(0, fieldSettings.length - 1)); + }, + + /* Loads field settings from cookie that is saved by _saveFieldSettings method. + *************************************************************************/ + _loadColumnSettings: function () { + var self = this; + var columnSettingsCookie = this._getCookie('column-settings'); + if (!columnSettingsCookie) { + return; + } + + var columnSettings = {}; + $.each(columnSettingsCookie.split('|'), function (inx, fieldSetting) { + var splitted = fieldSetting.split('='); + var fieldName = splitted[0]; + var settings = splitted[1].split(';'); + columnSettings[fieldName] = { + columnVisibility: settings[0], + columnWidth: settings[1] + }; + }); + + var headerCells = this._$table.find('>thead >tr >th.jtable-column-header'); + headerCells.each(function () { + var $cell = $(this); + var fieldName = $cell.data('fieldName'); + var field = self.options.fields[fieldName]; + if (columnSettings[fieldName]) { + if (field.visibility != 'fixed') { + self._changeColumnVisibilityInternal(fieldName, columnSettings[fieldName].columnVisibility); + } + + $cell.data('width-in-percent', columnSettings[fieldName].columnWidth).css('width', columnSettings[fieldName].columnWidth + '%'); + } + }); + } + + }); + +})(jQuery); + + +/************************************************************************ +* MASTER/CHILD tables extension for jTable * +*************************************************************************/ +(function ($) { + + //Reference to base object members + var base = { + _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable + }; + + //extension members + $.extend(true, $.hik.jtable.prototype, { + + /************************************************************************ + * DEFAULT OPTIONS / EVENTS * + *************************************************************************/ + options: { + openChildAsAccordion: false + }, + + /************************************************************************ + * PUBLIC METHODS * + *************************************************************************/ + + /* Creates and opens a new child table for given row. + *************************************************************************/ + openChildTable: function ($row, tableOptions, opened) { + var self = this; + + //Apply theming as same as parent table unless explicitily set + if (tableOptions.jqueryuiTheme == undefined) { + tableOptions.jqueryuiTheme = self.options.jqueryuiTheme; + } + + //Show close button as default + tableOptions.showCloseButton = (tableOptions.showCloseButton != false); + + //Close child table when close button is clicked (default behavior) + if (tableOptions.showCloseButton && !tableOptions.closeRequested) { + tableOptions.closeRequested = function () { + self.closeChildTable($row); + }; + } + + //If accordion style, close open child table (if it does exists) + if (self.options.openChildAsAccordion) { + $row.siblings('.jtable-data-row').each(function () { + self.closeChildTable($(this)); + }); + } + + //Close child table for this row and open new one for child table + self.closeChildTable($row, function () { + var $childRowColumn = self.getChildRow($row).children('td').empty(); + var $childTableContainer = $('
      ') + .addClass('jtable-child-table-container') + .appendTo($childRowColumn); + $childRowColumn.data('childTable', $childTableContainer); + $childTableContainer.jtable(tableOptions); + self.openChildRow($row); + $childTableContainer.hide().slideDown('fast', function () { + if (opened) { + opened({ + childTable: $childTableContainer + }); + } + }); + }); + }, + + /* Closes child table for given row. + *************************************************************************/ + closeChildTable: function ($row, closed) { + var self = this; + + var $childRowColumn = this.getChildRow($row).children('td'); + var $childTable = $childRowColumn.data('childTable'); + if (!$childTable) { + if (closed) { + closed(); + } + + return; + } + + $childRowColumn.data('childTable', null); + $childTable.slideUp('fast', function () { + $childTable.jtable('destroy'); + $childTable.remove(); + self.closeChildRow($row); + if (closed) { + closed(); + } + }); + }, + + /* Returns a boolean value indicates that if a child row is open for given row. + *************************************************************************/ + isChildRowOpen: function ($row) { + return (this.getChildRow($row).is(':visible')); + }, + + /* Gets child row for given row, opens it if it's closed (Creates if needed). + *************************************************************************/ + getChildRow: function ($row) { + return $row.data('childRow') || this._createChildRow($row); + }, + + /* Creates and opens child row for given row. + *************************************************************************/ + openChildRow: function ($row) { + var $childRow = this.getChildRow($row); + if (!$childRow.is(':visible')) { + $childRow.show(); + } + + return $childRow; + }, + + /* Closes child row if it's open. + *************************************************************************/ + closeChildRow: function ($row) { + var $childRow = this.getChildRow($row); + if ($childRow.is(':visible')) { + $childRow.hide(); + } + }, + + /************************************************************************ + * OVERRIDED METHODS * + *************************************************************************/ + + /* Overrides _removeRowsFromTable method to remove child rows of deleted rows. + *************************************************************************/ + _removeRowsFromTable: function ($rows, reason) { + //var self = this; + + if (reason == 'deleted') { + $rows.each(function () { + var $row = $(this); + var $childRow = $row.data('childRow'); + if ($childRow) { + //self.closeChildTable($row); //Removed since it causes "Uncaught Error: cannot call methods on jtable prior to initialization; attempted to call method 'destroy'" + $childRow.remove(); + } + }); + } + + base._removeRowsFromTable.apply(this, arguments); + }, + + /************************************************************************ + * PRIVATE METHODS * + *************************************************************************/ + + /* Creates a child row for a row, hides and returns it. + *************************************************************************/ + _createChildRow: function ($row) { + var totalColumnCount = this._$table.find('thead th').length; + var $childRow = $('
      ') + .addClass('jtable-child-row') + .append(''); + $row.after($childRow); + $row.data('childRow', $childRow); + $childRow.hide(); + return $childRow; + } + + }); + +})(jQuery); + From 69de4505f71384e9f779ce489bf5b10e80f75f1d Mon Sep 17 00:00:00 2001 From: Bird Date: Thu, 18 Nov 2021 07:32:14 +0330 Subject: [PATCH 54/58] Update README.md --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ea0119fe..523b226b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,19 @@ viewFormColumns : 2, // set the number of columns in record preview dialog * You can send any required fields to row-delete-request POST data (jTable only sends single key field) - +$("#myjtable").jtable({ +..., +fields : { + ..., + first_name : { + title : 'First name', + edit : true, + create : true, + delete : true, // setting delete option of field to true, sends this field beside key field to delete request. + type : 'text' + }, + ... +}); From de50119867343a647655ab94c699f3abdff993f1 Mon Sep 17 00:00:00 2001 From: Bird Date: Thu, 18 Nov 2021 07:32:42 +0330 Subject: [PATCH 55/58] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 523b226b..f361fc92 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ viewFormColumns : 2, // set the number of columns in record preview dialog ``` * You can send any required fields to row-delete-request POST data (jTable only sends single key field) - +```javascript $("#myjtable").jtable({ ..., fields : { @@ -57,7 +57,7 @@ fields : { ... }); - +``` From 8071f77904ca826c076a51bb1a3d5170ae99497f Mon Sep 17 00:00:00 2001 From: Bird Date: Thu, 18 Nov 2021 07:33:27 +0330 Subject: [PATCH 56/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f361fc92..028d83e6 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ fields : { title : 'First name', edit : true, create : true, - delete : true, // setting delete option of field to true, sends this field beside key field to delete request. + delete : true, // setting delete option of field to true, sends this field beside key field as parameters to delete request. type : 'text' }, ... From 2c780920c3fd4c2352d047c18eaf7a713f7368a7 Mon Sep 17 00:00:00 2001 From: Bird Date: Thu, 18 Nov 2021 07:34:14 +0330 Subject: [PATCH 57/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 028d83e6..4f662c3e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ viewFormColumns : 2, // set the number of columns in record preview dialog }); ``` -* You can send any required fields to row-delete-request POST data (jTable only sends single key field) +* You can send any required fields to row-delete-request as POST data (jTable only sends single key field) ```javascript $("#myjtable").jtable({ ..., From 4fa953f888eaee18e0f38e8a7db120499f49141a Mon Sep 17 00:00:00 2001 From: Bird Date: Fri, 19 Nov 2021 17:38:19 +0330 Subject: [PATCH 58/58] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f662c3e..d99b7f68 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ fields : { -Read More about additional features [here](https://m-shaeri.ir/blog/jquery-jtable-awesome-full-featured-plugin-for-crud-table/) +Read More about additional features and its usage [here](https://m-shaeri.ir/blog/jquery-jtable-awesome-full-featured-plugin-for-crud-table/) Notes ======
      ') + .addClass('jtable-view-field-container') + .appendTo($RowContainer); + + CurrentColumnCount++; + if(CurrentColumnCount==ColumnCount) { + $RowContainer=$('