You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2018/04/08 16:49:46 UTC

[34/46] logging-log4j-audit git commit: Fixed the catalog editor. Support using a branch

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

<TRUNCATED>