Вход Регистрация
Файл: ezEditTable/ezEditTable-uncompressed.js
Строк: 3678
<?php
/*------------------------------------------------------------------------
    - ezEditTable v2.3.1 (edittable.free.fr)
    - Copyright (c) 2012 Max Guglielmi
    - License required for use
------------------------------------------------------------------------*/

var EditTable = function(id){
/*========================================================================
    - EditTable object constructor
    - Params:
        - id: table id (string)
        - startRow (optional): start row index (number)
        - config (optional): configuration object (literal object)
========================================================================*/
    
if(arguments.length === 0){ return; }
    
this.id = id;
    
this.version = '2.3.1';
    
this.table = this.Get(id);
    
this.tBody = this.table.tBodies[0];
    
this.startRow = 0;
    
this.config = null;
    
this.nbCells = null;
    if(!
window['et_activeGrid']){ window.et_activeGrid = ''; }//global var for active grid in page

    
if(this.table === null || this.table.nodeName.LCase() !== 'table'){ return; }
    var 
tBodyRows = this.Tag(this.tBody, 'tr');
    if(
tBodyRows.length > 0){ this.startRow = tBodyRows[0].rowIndex; }

    if(
arguments.length > 1){
        for(var 
i=0; i<arguments.length; i++){
            var 
argtype = typeof arguments[i];
            if(
argtype.LCase() === 'number'){ this.startRow = arguments[i]; }
            if(
argtype.LCase() === 'object'){ this.config = arguments[i]; }
        }
    }

    
//cols nb
    
this.nbCells = this.GetCellsNb(this.startRow);

    
/********************************************
    *         Configuration object                 *
    *********************************************/
    
var f = this.config || {};
    
/*** Common properties ***/
    
this.selection = f.selection===false ? false : true; //enables/disables selection model
    
this.keyNav = f.key_navigation!==undefined ? f.key_navigation : true; //enables/disables keyboard navigation
    
this.editable = f.editable!==undefined ? f.editable : false; //enables/disables cell edition model
    
this.tableCss = f.table_css!==undefined ? f.table_css : 'ezEditableTable'; //default css class applied to html table
    
this.unselectableCss = f.unselectable_css!==undefined ? f.unselectable_css : 'ezUnselectable'; //default css class applied to html table to make text unselectable
    
this.basePath = f.base_path!==undefined ? f.base_path : 'ezEditTable/'; //default script base path
    
this.activityIndicatorCss = f.activity_indicator_css!==undefined ? f.activity_indicator_css : 'ezOpacity'; //server activity indicator default css class
    
this.onServerActivityStart = this.IsFn(f.on_server_activity_start) ? f.on_server_activity_start : null; //function called on server-side operations start
    
this.onServerActivityStop = this.IsFn(f.on_server_activity_stop) ? f.on_server_activity_stop : null; //function called when server-side operation completed

    //Selection properties
    
this.selectionModel = f.selection_model!==undefined ? f.selection_model.LCase() : 'single'; //selection model: multiple or single
    
this.defaultSelection = f.default_selection!==undefined ? f.default_selection.LCase() : 'row'; //default selection: row, cell, both
    
this.keySelection = this.editable ? true : f.key_selection!==undefined  ? f.key_selection : true; //multiple selection: select muliple rows by holding Ctrl key down
    
this.selectRowAtStart = f.select_row_at_start!==undefined ? f.select_row_at_start : false; //select a row at start
    
this.rowIndexAtStart = f.row_index_at_start!==undefined ? parseInt(f.row_index_at_start, 10) : this.startRow; //select row at start
    
this.scrollIntoView = f.scroll_into_view!==undefined ? f.scroll_into_view : false; //scrolls the element into view with key navigation
    
this.activeRowCss = f.active_row_css!==undefined ? f.active_row_css : 'ezActiveRow'; //default css class applied to active row
    
this.selectedRowCss = f.selected_row_css!==undefined ? f.selected_row_css : 'ezSelectedRow'; //default css class applied to selected row (multiple selection)
    
this.activeCellCss = f.active_cell_css!==undefined ? f.active_cell_css : 'ezActiveCell'; //default css class applied to selected row
    
this.nbRowsPerPage = f.nb_rows_per_page!==undefined ? f.nb_rows_per_page : 10; //nb of rows when pageup/pagedown is pressed

    //Selection callbacks
    
this.onSelectionInit = this.IsFn(f.on_selection_initialized) ? f.on_selection_initialized : null; //selection object initialized callback
    
this.onBeforeSelectedRow = this.IsFn(f.on_before_selected_row) ? f.on_before_selected_row : null; //callback invoked before a row is selected
    
this.onAfterSelectedRow = this.IsFn(f.on_after_selected_row) ? f.on_after_selected_row : null; //callback invoked after a row is selected
    
this.onBeforeSelectedCell = this.IsFn(f.on_before_selected_cell) ? f.on_before_selected_cell : null; //callback invoked before a row is selected
    
this.onAfterSelectedCell = this.IsFn(f.on_after_selected_cell) ? f.on_after_selected_cell : null; //callback invoked after a cell is selected
    
this.onBeforeDeselectedRow = this.IsFn(f.on_before_deselected_row) ? f.on_before_deselected_row : null; //callback invoked before a row is deselected
    
this.onAfterDeselectedRow = this.IsFn(f.on_after_deselected_row) ? f.on_after_deselected_row : null; //callback invoked after a row is deselected
    
this.onBeforeDeselectedCell = this.IsFn(f.on_before_deselected_cell) ? f.on_before_deselected_cell : null; //callback invoked before a cell is deselected
    
this.onAfterDeselectedCell = this.IsFn(f.on_after_deselected_cell) ? f.on_after_deselected_cell : null; //callback invoked after a cell is deselected
    
this.onValidateRow = this.IsFn(f.on_validate_row) ? f.on_validate_row : null; //callback invoked after a row is double-clicked or enter key pressed
    
this.onValidateCell = this.IsFn(f.on_validate_cell) ? f.on_validate_cell : null; //callback invoked after a cell is double-clicked or enter key pressed

    //Editable properties
    
this.editorModel = f.editor_model!==undefined ? f.editor_model.LCase() : 'cell'; //'cell' or 'row' (template later)
    
this.openEditorAction = f.open_editor_action!==undefined ? f.open_editor_action.LCase() : 'dblclick'; //editor is shown on a dblclick or click
    
this.ajax = window['jQuery'] && f.ajax!==false ? true : false; //enable/disable AJAX, if jquery is available has to be explicitly set to false to disable it
    
this.autoSave = f.auto_save!==undefined ? f.auto_save : this.editable; //save pending changes on selection change
    
this.autoSaveModel = f.auto_save_model!==undefined ? f.auto_save_model : 'row'; //'cell' or 'row', save on cell or row selection change
    
this.autoSaveType = f.auto_save_type!==undefined ? f.auto_save_type : 'both'; //'update', 'insert' or 'both' save only inserts, updates or both
    
this.editableOnKeystroke = f.editable_on_keystroke!==undefined ? f.editable_on_keystroke : false; //enable/disable inline editing by key stroke
    
this.newRowPrefix = f.new_row_prefix!==undefined ? f.new_row_prefix : 'tr'; //prefix for new row element id
    
this.formSubmitInterval = f.form_submit_interval!==undefined ? f.form_submit_interval : 50; //interval separating form submissions (overload server security policy)
    
this.newRowPos = f.new_row_pos!==undefined ? f.new_row_pos : 'top'; //new row position 'top', 'bottom' or integer for row index

    //Cells editor properties
    
this.edtTypes = {
        
none:'none', input:'input', textarea:'textarea', select:'select', multiple:'multiple',
        
bool:'boolean', uploader:'uploader', command:'command', custom:'custom'
    
};
    
this.editors = [];
    
this.cellEditors = this.IsArray(f.cell_editors) ? f.cell_editors : []; //editors configuration objects
    
this.editorTypes = []; this.editorCss = []; this.editorStyles = []; this.editorAttributes = []; this.customEditor = [];
    
this.editorCustomSlcOptions = []; this.editorCustomSlcValues = []; this.editorSortSlcOptions = []; this.editorValuesSeparator = [];
    
this.editorAllowEmptyValue = []; this.editorCmdColIndex = null; this.editorCmdBtns = {}; this.editorUploader = [];
    
this.uplURI = []; this.uplPath = []; this.uplShowUpload = []; this.uplShowLink = []; this.linkCss = []; this.uplSqlField = [];
    
this.uplLoaderImg = []; this.uplOkImg = []; this.uplMaxFileSize = []; this.uplValidExt = []; this.uplCss = []; this.uplOutputCss = [];
    
this.uplDisplayCss = []; this.uplJsSuccess = []; this.uplRecordIdColIndex = []; this.showUpload = []; this.onBeforeOpenUploader = [];
    
this.onAfterOpenUploader = []; this.onBeforeCloseUploader = []; this.onAfterCloseUploader = [];
    for(var 
i=0; i<this.nbCells; i++){
        var 
editor = this.cellEditors[i];
        if(!
editor) continue;
        
this.editorTypes[i] = editor['type']; this.editorCss[i] = editor['css'];
        
this.editorAttributes[i] = editor['attributes']; this.editorStyles[i] = editor['style'];
        
this.editorCustomSlcOptions[i] = editor['custom_slc_options']; this.editorCustomSlcValues[i] = editor['custom_slc_values'];
        
this.editorSortSlcOptions[i] = editor['sort_slc_options']; this.editorValuesSeparator[i] = editor['values_separator'];
        
this.customEditor[i] = editor['target']; this.editorAllowEmptyValue[i] = editor['allow_empty_value'];

        switch(
this.editorTypes[i]){
            case 
this.edtTypes.command:
                
this.editorCmdColIndex = editor['command_column_index']!==undefined ? parseInt(editor['command_column_index'], 10) : (this.nbCells-1);
                
this.editorCmdBtns = editor['buttons'] || {};
            break;
            case 
this.edtTypes.uploader:
                var 
upl = this.editorUploader[i] = editor['uploader'] || {};
                
this.hasUploader = true;
                
//Uploader settings
                
this.uplURI[i] = upl.hasOwnProperty('uri') ? upl['uri'] : null; //upload URI
                
this.uplPath[i] = upl.hasOwnProperty('path') ? upl['path'] : null; //upload folder path
                
this.uplShowUpload[i] = upl.hasOwnProperty('show_upload') ? upl['show_upload'] : true; //show uploaded file
                
this.uplSqlField[i] = upl.hasOwnProperty('sql_field') ? upl['sql_field'] : 'IMAGENAME'; //SQL field name storing upload filename
                
this.uplRecordIdColIndex[i] = upl.hasOwnProperty('record_id_column_index') ? upl['record_id_column_index'] : null; //Record id in a column
                
this.uplShowLink[i] = upl.hasOwnProperty('show_link') ? upl['show_link'] : true; //Uploaded file appears as a link in Uploader box and in cell
                
this.linkCss[i] = upl.hasOwnProperty('link_css') ? upl['link_css'] : ''; //Uploaded file link css
                
this.uplLoaderImg[i] = upl.hasOwnProperty('loader_image') ? upl['loader_image'] : this.basePath + 'themes/img_loader.gif'; //local loader image path
                
this.uplOkImg[i] = upl.hasOwnProperty('ok_image') ? upl['ok_image'] : this.basePath +'themes/icn_tick.png'; //local tick image path
                
this.uplMaxFileSize[i] = upl.hasOwnProperty('max_file_size') ? upl['max_file_size'] : '102400'; //default max file size in bytes (100Kb)
                
this.uplValidExt[i] =  upl.hasOwnProperty('valid_extensions') ? upl['valid_extensions'] : 'jpg, jpeg, gif, png'; //default file extension allowed
                
this.uplCss[i] = upl.hasOwnProperty('css') ? upl.css : 'ezUploaderEditor'; //uploader container css
                
this.uplOutputCss[i] = upl.hasOwnProperty('output_css') ? upl['output_css'] : 'ezUploaderEditorOutput'; //uploader output message css
                
this.uplDisplayCss[i] = upl.hasOwnProperty('display_css') ? upl['display_css'] : 'ezUploaderEditorDisplay'; //image displayer css
                
this.uplJsSuccess[i] = upl.hasOwnProperty('javascript_code_success') ? upl['javascript_code_success']
                                        : 
'<script>window.parent["{1}"].SetUploadSuccess(true); window.parent["{1}"].SetUploadName("{0}");' +
                                            
'window.parent["{1}"].ShowUpload();</script>'; //js code invoked from server-side when file successfully uploaded

                //Uploader callbacks and delegates
                
this.showUpload[i] =  upl.hasOwnProperty('show_upload')    && this.IsFn(upl['show_upload']) ? upl['show_upload'] : null; //delegate function for showing uploaded file
                
this.onBeforeOpenUploader[i] = upl.hasOwnProperty('on_before_open') && this.IsFn(upl['on_before_open']) ? upl['on_before_open'] : null;
                
this.onAfterOpenUploader[i] = upl.hasOwnProperty('on_after_open') && this.IsFn(upl['on_after_open']) ? upl['on_after_open'] : null;
                
this.onBeforeCloseUploader[i] = upl.hasOwnProperty('on_before_close') && this.IsFn(upl['on_before_close']) ? upl['on_before_close'] : null;
                
this.onAfterCloseUploader[i] = upl.hasOwnProperty('on_after_close') && this.IsFn(upl['on_after_close']) ? upl['on_after_close'] : null;
            break;
        }
    }
    if(
this.editorTypes.indexOf(this.edtTypes.command) != -1){ this.editorModel = 'row'; }
    
this.inputEditorCss = f.input_editor_css!==undefined ? f.input_editor_css : 'ezInputEditor'; //default css class applied to input editor
    
this.textareaEditorCss = f.textarea_editor_css!==undefined ? f.textarea_editor_css : 'ezTextareaEditor'; //default css class applied to textarea editor
    
this.selectEditorCss = f.select_editor_css!==undefined ? f.select_editor_css : 'ezSelectEditor'; //default css class applied to select editor
    
this.commandEditorCss = f.command_editor_css!==undefined ? f.command_editor_css : 'ezCommandEditor'; //default css class applied to command editor
    
this.modifiedCellCss = f.modified_cell_css!==undefined ? f.modified_cell_css : 'ezModifiedCell'; //default css class applied to modified cell

    //Command type editor config
    
this.cmdEnabledBtns = this.editorCmdBtns.hasOwnProperty('enable') ? this.editorCmdBtns.enable : ['update', 'insert', 'delete', 'submit', 'cancel']; //command buttons to be enabled
    
this.cmdUpdateBtn = this.editorCmdBtns.hasOwnProperty('update') ? this.editorCmdBtns.update : {}; //update command button
    
this.cmdInsertBtn = this.editorCmdBtns.hasOwnProperty('insert') ? this.editorCmdBtns.insert : {}; //insert command button
    
this.cmdDeleteBtn = this.editorCmdBtns.hasOwnProperty('delete') ? this.editorCmdBtns['delete'] : {}; //delete command button
    
this.cmdSubmitBtn = this.editorCmdBtns.hasOwnProperty('submit') ? this.editorCmdBtns['submit'] : {}; //submit command button
    
this.cmdCancelBtn = this.editorCmdBtns.hasOwnProperty('cancel') ? this.editorCmdBtns.cancel : {}; //cancel command button

    
this.cmdUpdateBtnText = this.cmdUpdateBtn.hasOwnProperty('text') ? this.cmdUpdateBtn.text : ''; //Default text is empty because icon is set by default
    
this.cmdInsertBtnText = this.cmdInsertBtn.hasOwnProperty('text') ? this.cmdInsertBtn.text : ''; //Default text is empty because icon is set by default
    
this.cmdDeleteBtnText = this.cmdDeleteBtn.hasOwnProperty('text') ? this.cmdDeleteBtn.text : ''; //Default text is empty because icon is set by default
    
this.cmdSubmitBtnText = this.cmdSubmitBtn.hasOwnProperty('text') ? this.cmdSubmitBtn.text : 'Submit';
    
this.cmdCancelBtnText = this.cmdCancelBtn.hasOwnProperty('text') ? this.cmdCancelBtn.text : 'Cancel';

    
this.cmdUpdateBtnTitle = this.cmdUpdateBtn.hasOwnProperty('title') ? this.cmdUpdateBtn.title : 'Edit record';
    
this.cmdInsertBtnTitle = this.cmdInsertBtn.hasOwnProperty('title') ? this.cmdInsertBtn.title : 'Create record';
    
this.cmdDeleteBtnTitle = this.cmdDeleteBtn.hasOwnProperty('title') ? this.cmdDeleteBtn.title : 'Delete record';
    
this.cmdSubmitBtnTitle = this.cmdSubmitBtn.hasOwnProperty('title') ? this.cmdSubmitBtn.title : 'Submit record';
    
this.cmdCancelBtnTitle = this.cmdCancelBtn.hasOwnProperty('title') ? this.cmdCancelBtn.title : 'Cancel';

    
this.cmdUpdateBtnIcon = this.cmdUpdateBtn.hasOwnProperty('icon') ? this.cmdUpdateBtn.icon : '<img src="'+this.basePath+'themes/icn_edit.gif" alt="" />';
    
this.cmdInsertBtnIcon = this.cmdInsertBtn.hasOwnProperty('icon') ? this.cmdInsertBtn.icon : '<img src="'+this.basePath+'themes/icn_add.gif" alt="" />';
    
this.cmdDeleteBtnIcon = this.cmdDeleteBtn.hasOwnProperty('icon') ? this.cmdDeleteBtn.icon : '<img src="'+this.basePath+'themes/icn_del.gif" alt="" />';
    
this.cmdSubmitBtnIcon = this.cmdSubmitBtn.hasOwnProperty('icon') ? this.cmdSubmitBtn.icon : '';
    
this.cmdCancelBtnIcon = this.cmdCancelBtn.hasOwnProperty('icon') ? this.cmdCancelBtn.icon : '';

    
this.cmdUpdateBtnCss = this.cmdUpdateBtn.hasOwnProperty('css') ? this.cmdUpdateBtn.css : null;
    
this.cmdInsertBtnCss = this.cmdInsertBtn.hasOwnProperty('css') ? this.cmdInsertBtn.css : null;
    
this.cmdDeleteBtnCss = this.cmdDeleteBtn.hasOwnProperty('css') ? this.cmdDeleteBtn.css : null;
    
this.cmdSubmitBtnCss = this.cmdSubmitBtn.hasOwnProperty('css') ? this.cmdSubmitBtn.css : null;
    
this.cmdCancelBtnCss = this.cmdCancelBtn.hasOwnProperty('css') ? this.cmdCancelBtn.css : null;

    
this.cmdUpdateBtnStyle = this.cmdUpdateBtn.hasOwnProperty('style') ? this.cmdUpdateBtn.style : null;
    
this.cmdInsertBtnStyle = this.cmdInsertBtn.hasOwnProperty('style') ? this.cmdInsertBtn.style : null;
    
this.cmdDeleteBtnStyle = this.cmdDeleteBtn.hasOwnProperty('style') ? this.cmdDeleteBtn.style : null;
    
this.cmdSubmitBtnStyle = this.cmdSubmitBtn.hasOwnProperty('style') ? this.cmdSubmitBtn.style : null;
    
this.cmdCancelBtnStyle = this.cmdCancelBtn.hasOwnProperty('style') ? this.cmdCancelBtn.style : null;

    
this.cmdInsertBtnScroll = this.cmdInsertBtn.hasOwnProperty('scrollIntoView') ? this.cmdInsertBtn.scrollIntoView : false; //scroll new added row into view

    //Editor callbacks, delegates
    
this.onEditableInit = this.IsFn(f.on_editable_initialized) ? f.on_editable_initialized : null;
    
this.onBeforeOpenEditor = this.IsFn(f.on_before_open_editor) ? f.on_before_open_editor : null;
    
this.onAfterOpenEditor = this.IsFn(f.on_after_open_editor) ? f.on_after_open_editor : null;
    
this.onBeforeCloseEditor = this.IsFn(f.on_before_close_editor) ? f.on_before_close_editor : null;
    
this.onAfterCloseEditor = this.IsFn(f.on_after_close_editor) ? f.on_after_close_editor : null;
    
this.setCustomEditorValue = this.IsFn(f.set_custom_editor_value) ? f.set_custom_editor_value : null;
    
this.getCustomEditorValue = this.IsFn(f.get_custom_editor_value) ? f.get_custom_editor_value : null;
    
this.setCellModifiedValue = this.IsFn(f.set_cell_modified_value) ? f.set_cell_modified_value : null;
    
this.validateModifiedValue = this.IsFn(f.validate_modified_value) ? f.validate_modified_value : null;
    
this.openCustomEditor = this.IsFn(f.open_custom_editor) ? f.open_custom_editor : null;
    
this.closeCustomEditor = this.IsFn(f.close_custom_editor) ? f.close_custom_editor : null;
    
this.onAddedDomRow = this.IsFn(f.on_added_dom_row) ? f.on_added_dom_row : null;

    
//Server actions config
    
this.actions = this.IsObj(f.actions) ? f.actions : {};
    
this.updateConfig = this.actions['update']!==undefined ? this.actions['update'] : {};
    
this.insertConfig = this.actions['insert']!==undefined ? this.actions['insert'] : {};
    
this.deleteConfig = this.actions['delete']!==undefined ? this.actions['delete'] : {};
    
this.updateURI = this.updateConfig.hasOwnProperty('uri') ? this.updateConfig['uri'] : null;
    
this.insertURI = this.insertConfig.hasOwnProperty('uri') ? this.insertConfig['uri'] : null;
    
this.deleteURI = this.deleteConfig.hasOwnProperty('uri') ? this.deleteConfig['uri'] : null;
    
this.updateFormMethod = this.updateConfig.hasOwnProperty('form_method') ? this.updateConfig['form_method'].LCase() : 'post';
    
this.insertFormMethod = this.insertConfig.hasOwnProperty('form_method') ? this.insertConfig['form_method'].LCase() : 'post';
    
this.deleteFormMethod = this.deleteConfig.hasOwnProperty('form_method') ? this.deleteConfig['form_method'].LCase() : 'post';
    
this.updateSubmitMethod = this.updateConfig.hasOwnProperty('submit_method') ? this.updateConfig['submit_method'].LCase() : this.ajax ? 'ajax': 'form';
    
this.insertSubmitMethod = this.insertConfig.hasOwnProperty('submit_method') ? this.insertConfig['submit_method'].LCase() : this.ajax ? 'ajax': 'form';
    
this.deleteSubmitMethod = this.deleteConfig.hasOwnProperty('submit_method') ? this.deleteConfig['submit_method'].LCase() : this.ajax ? 'ajax': 'form';
    
this.bulkDelete = this.deleteConfig.hasOwnProperty('bulk_delete') ? this.deleteConfig['bulk_delete'] : false; //enables delete of selected rows
    
this.defaultRecord = this.insertConfig.hasOwnProperty('default_record')
                            && 
this.IsArray(this.insertConfig['default_record']) ? this.insertConfig['default_record'] : null;
    
this.updateParams = this.updateConfig.hasOwnProperty('param_names')
                            && 
this.IsArray(this.updateConfig['param_names']) ? this.updateConfig['param_names'] : null;
    
this.insertParams = this.insertConfig.hasOwnProperty('param_names')
                            && 
this.IsArray(this.insertConfig['param_names']) ? this.insertConfig['param_names'] : null;
    
this.deleteParams = this.deleteConfig.hasOwnProperty('param_names')
                            && 
this.IsArray(this.deleteConfig['param_names']) ? this.deleteConfig['param_names'] : null;

    
//Server actions delegates and callbacks
    
this.onUpdateSubmit = this.updateConfig.hasOwnProperty('on_update_submit')
                            && 
this.IsFn(this.updateConfig['on_update_submit']) ? this.updateConfig['on_update_submit'] : null;
    
this.onInsertSubmit = this.insertConfig.hasOwnProperty('on_insert_submit')
                            && 
this.IsFn(this.insertConfig['on_insert_submit']) ? this.insertConfig['on_insert_submit'] : null;
    
this.onDeleteSubmit = this.deleteConfig.hasOwnProperty('on_delete_submit')
                            && 
this.IsFn(this.deleteConfig['on_delete_submit']) ? this.deleteConfig['on_delete_submit'] : null;
    
this.onBeforeUpdateSubmit = this.updateConfig.hasOwnProperty('on_before_submit')
                            && 
this.IsFn(this.updateConfig['on_before_submit']) ? this.updateConfig['on_before_submit'] : null;
    
this.onBeforeInsertSubmit = this.insertConfig.hasOwnProperty('on_before_submit')
                            && 
this.IsFn(this.insertConfig['on_before_submit']) ? this.insertConfig['on_before_submit'] : null;
    
this.onBeforeDeleteSubmit = this.deleteConfig.hasOwnProperty('on_before_submit')
                            && 
this.IsFn(this.deleteConfig['on_before_submit']) ? this.deleteConfig['on_before_submit'] : null;
    
this.onAfterUpdateSubmit = this.updateConfig.hasOwnProperty('on_after_submit')
                            && 
this.IsFn(this.updateConfig['on_after_submit']) ? this.updateConfig['on_after_submit'] : null;
    
this.onAfterInsertSubmit = this.insertConfig.hasOwnProperty('on_after_submit')
                            && 
this.IsFn(this.insertConfig['on_after_submit']) ? this.insertConfig['on_after_submit'] : null;
    
this.onAfterDeleteSubmit = this.deleteConfig.hasOwnProperty('on_after_submit')
                            && 
this.IsFn(this.deleteConfig['on_after_submit']) ? this.deleteConfig['on_after_submit'] : null;
    
this.onUpdateError = this.updateConfig.hasOwnProperty('on_submit_error')
                            && 
this.IsFn(this.updateConfig['on_submit_error']) ? this.updateConfig['on_submit_error'] : null;
    
this.onInsertError = this.insertConfig.hasOwnProperty('on_submit_error')
                            && 
this.IsFn(this.insertConfig['on_submit_error']) ? this.insertConfig['on_submit_error'] : null;
    
this.onDeleteError = this.deleteConfig.hasOwnProperty('on_submit_error')
                            && 
this.IsFn(this.deleteConfig['on_submit_error']) ? this.deleteConfig['on_submit_error'] : null;
    
this.checkUpdateResponseSanity = this.updateConfig.hasOwnProperty('check_response_sanity')
                            && 
this.IsFn(this.updateConfig['check_response_sanity']) ? this.updateConfig['check_response_sanity'] : null;
    
this.checkInsertResponseSanity = this.insertConfig.hasOwnProperty('check_response_sanity')
                            && 
this.IsFn(this.insertConfig['check_response_sanity']) ? this.insertConfig['check_response_sanity'] : null;
    
this.checkDeleteResponseSanity = this.deleteConfig.hasOwnProperty('check_response_sanity')
                            && 
this.IsFn(this.deleteConfig['check_response_sanity']) ? this.deleteConfig['check_response_sanity'] : null;
    
this.processUpdateResponse = this.updateConfig.hasOwnProperty('process_response')
                            && 
this.IsFn(this.updateConfig['process_response']) ? this.updateConfig['process_response'] : null;
    
this.processInsertResponse = this.insertConfig.hasOwnProperty('process_response')
                            && 
this.IsFn(this.insertConfig['process_response']) ? this.insertConfig['process_response'] : null;
    
this.processDeleteResponse = this.deleteConfig.hasOwnProperty('process_response')
                            && 
this.IsFn(this.deleteConfig['process_response']) ? this.deleteConfig['process_response'] : null;

    
//Messages
    
this.msgSubmitOK = f.msg_submit_ok!==undefined ? f.msg_submit_ok : 'Changes were successfully submitted to server!';
    
this.msgConfirmDelSelectedRows = f.msg_confirm_delete_selected_rows!==undefined ? f.msg_confirm_delete_selected_rows : 'Do you want to delete the selected row(s)?';
    
this.msgErrOccur = f.msg_error_occured!==undefined ? f.msg_error_occured : 'An error occured!';
    
this.msgSaveUnsuccess = f.msg_submit_unsuccessful!==undefined ? f.msg_submit_unsuccessful : 'Changes could not be saved!';
    
this.msgUndefinedSubmitUrl = f.undefined_submit_url!==undefined ? f.undefined_submit_url : 'Changes could not be saved! Endpoint URL is not defined';
    
this.msgNewRowNoUploader = f.msg_new_row_no_uploader!==undefined ? f.msg_new_row_no_uploader : 'Please save the newly added rows before using the Uploader.';
    
this.msgInvalidData = f.msg_invalid_data!==undefined ? f.msg_invalid_data : 'Returned data is invalid.';

    
//Containers
    
this.ifrmContainer = {}; //submit forms container

    //Values
    
this.valuesSeparator = ', ';
    
this.defaultRecordUndefinedValue = '...';
    
this.newRowClass = 'ezNewRow';
    
this.recordKeyValue = 'new';

    
//Attributes
    
this.attrValue = 'data-ez-slc-value';
    
this.attrText = 'data-ez-slc-text';
    
this.attrCont = 'data-ez-html';
    
this.attrData = 'data-ez-data';
    
this.attrUplname = 'data-ez-uplname';
    
this.attrColIndex = 'data-ez-col-index';
    
this.attrRowIndex = 'data-ez-row-index';

    
//Counters
    
this.savedRowsNb = { 'insert': 0, 'update': 0, 'delete': 0 };

    
//prefixes
    
this.prfxEdt = 'edt_';
    
this.prfxIFrm = 'iframe_';
    
this.prfxFrm = 'form_';
    
this.prfxScr = 'scr_';
    
this.prfxParam = 'col_';
    
//upload editor
    
this.prfxUplCont = 'upl_';
    
this.prfxUplForm = 'upl_form_';
    
this.prfxUplIframe = 'upl_ifrm_';
    
this.prfxUplInfo = 'upl_info_';
    
this.prfxUplOutput = 'upl_output_';
    
this.prfxUplBtn = 'upl_btn_';
    
this.prfxUplBtnClose = 'upl_btn_close_';
    
this.prfxUplImgDisplay = 'upl_img_display_';
    
this.prfxUplWinRef = 'et_upl_';

    
//Upload form
    
this.uplFileInp = 'UPL_FILE';
    
this.uplKeyInput = 'RECORD_KEY';
    
this.uplFldPath = 'IMAGES_FOLDER_PATH';
    
this.uplSqlFieldName = 'SQL_FIELD';
    
this.uplFileSize = 'MAX_FILE_SIZE';
    
this.uplValidExts = 'VALID_EXTENSIONS';
    
this.uplJsCode = 'JS_CODE';

    
this.Editable = new Editable(this);
    
this.Selection = new Selection(this);
};

var 
Editable = function(editTable){
    
this.o = editTable;
};

var 
Selection = function(editTable){
    
this.o = editTable;
};

var 
Uploader = function(editTable, colIndex){
    
this.o = editTable;
    
this.colIndex = colIndex;
    
window[this.o.prfxUplWinRef + colIndex + this.o.id] = this; //global reference for server-side purposes
};

Uploader.prototype = {
    
divUpl: null, formUpl: null, fileUpl: null, hiddenFileSize: null, hiddenFolderPath: null, hiddenValidExt: null, hiddenKey: null,
    
ifrmUpl: null, divUplInfo: null, divUplOutput: null, divUplBtnsCont: null, divUplBtn: null, divUplBtnClose: null, divUplDisplay: null,
    
initialValue: null,    isUploadSuccessful: false,
    
Init: function(){
        if(!
this.o.hasUploader){ return; }
        
this.SetUploader();
    },
    
SetUploader: function(){
        
this.divUpl = this.o.CreateElm('div',
            [
'id', this.o.prfxUplCont + this.colIndex + this.o.id], ['style', 'display:none; z-index:10001;'], ['class', this.o.uplCss[this.colIndex]]
        );
        
this.formUpl = this.o.CreateElm('form',
            [
'id', this.o.prfxUplForm + this.colIndex + this.o.id], ['name', this.o.prfxUplForm + this.colIndex + this.o.id], ['method', 'POST'],
            [
'action', this.o.uplURI[this.colIndex]], ['target', this.o.prfxUplIframe + this.colIndex + this.o.id], ['enctype', 'multipart/form-data']
        );
        
this.fileUpl = this.o.CreateElm('input',['id', this.o.uplFileInp], ['name', this.o.uplFileInp], ['type', 'file']);
        
this.hiddenFileSize = this.o.CreateElm('input', ['name', this.o.uplFileSize], ['type', 'hidden'], ['value', this.o.uplMaxFileSize[this.colIndex]]); //server-side info: max file size
        
this.hiddenFolderPath = this.o.CreateElm('input', ['name', this.o.uplFldPath], ['type', 'hidden'], ['value', this.o.uplPath[this.colIndex]]); //server-side info: images folder path
        
this.hiddenValidExt = this.o.CreateElm('input', ['name', this.o.uplValidExts], ['type', 'hidden'], ['value', this.o.uplValidExt[this.colIndex]]); //server-side info: allowed file extensions
        
this.hiddenKey = this.o.CreateElm('input', ['name', this.o.uplKeyInput], ['type', 'hidden'], ['value', this.o.recordKeyValue]); //server-side info: record key
        
this.hiddenSqlField = this.o.CreateElm('input', ['name', this.o.uplSqlFieldName], ['type', 'hidden'], ['value', this.o.uplSqlField[this.colIndex]]); //server-side info: SQL field name storing upload filename
        
this.hiddenUplWinRef = this.o.CreateElm('input', ['name', this.o.uplJsCode], ['type', 'hidden'], ['value', this.o.uplJsSuccess[this.colIndex]]); //server-side info: js code invoked from server when upload successful
        
this.ifrmUpl = this.o.CreateElm('iframe', ['id', this.o.prfxUplIframe + this.colIndex + this.o.id], ['name', this.o.prfxUplIframe + this.colIndex + this.o.id], ['style', 'display:none; left:-10001;']);
        
this.divUplInfo = this.o.CreateElm('div',['id', this.o.prfxUplInfo + this.colIndex + this.o.id]);
        
this.divUplOutput = this.o.CreateElm('div',['id', this.o.prfxUplOutput + this.colIndex + this.o.id], ['class', this.o.uplOutputCss[this.colIndex]]);
        
this.divUplBtnsCont = this.o.CreateElm('div',['style', 'text-align:right']);
        
this.divUplBtn = this.o.CreateElm('button',['id', this.o.prfxUplBtn + this.colIndex + this.o.id], ['style', 'display:none;']);
        
this.divUplBtnClose = this.o.CreateElm('button',['id', this.o.prfxUplBtnClose + this.colIndex + this.o.id]);
        
this.divUplDisplay = this.o.CreateElm('div',['id', this.o.prfxUplImgDisplay + this.colIndex + this.o.id], ['class', this.o.uplDisplayCss[this.colIndex]]);

        
//IE<8 class, name, style attributes are not set correctly...
        
if(!this.o.Css.Has(this.divUpl, this.o.uplCss[this.colIndex])){
            
this.o.Css.Add(this.divUpl, this.o.uplCss[this.colIndex]);
            
this.o.Css.Add(this.divUplOutput, this.o.uplOutputCss[this.colIndex]);
            
this.o.Css.Add(this.divUplDisplay, this.o.uplDisplayCss[this.colIndex]);
            
this.divUpl.style.cssText = 'display:none; z-index:10001;';
            
this.divUplBtnsCont.style.cssText = 'text-align:right';
            
this.divUplBtn.style.cssText = 'display:none;';
            
this.formUpl = document.createElement(
                
'<form id="'+this.o.prfxUplForm + this.colIndex + this.o.id+'" name="'+this.o.prfxUplForm + this.colIndex + this.o.id+'" method="POST" ' +
                
'action="'+this.o.uplURI[this.colIndex]+'" target="'+this.o.prfxUplIframe + this.colIndex + this.o.id+'" enctype="multipart/form-data"></form>'
            
);
            
this.ifrmUpl = document.createElement(
                
'<iframe name="'+this.o.prfxUplIframe + this.colIndex + this.o.id+'" id="'+this.o.prfxUplIframe + this.colIndex + this.o.id +
                
'" style="display:none; left:-10001;"></iframe>'
            
);
        }

        
this.divUplInfo.innerHTML = parseInt(this.o.uplMaxFileSize[this.colIndex]/1024, 10) + 'Kb max (' + this.o.uplValidExt[this.colIndex].toString() + ')';
        
this.divUplBtn.appendChild(this.o.CreateText('Upload'));
        
this.divUplBtnClose.appendChild(this.o.CreateText('Close'));
        
this.divUplBtnsCont.appendChild(this.divUplBtn);
        
this.divUplBtnsCont.appendChild(this.divUplBtnClose);

        
this.formUpl.appendChild(this.fileUpl);
        
this.formUpl.appendChild(this.hiddenFileSize);
        
this.formUpl.appendChild(this.hiddenFolderPath);
        
this.formUpl.appendChild(this.hiddenUplWinRef);
        
this.formUpl.appendChild(this.hiddenValidExt);
        
this.formUpl.appendChild(this.hiddenKey);
        
this.formUpl.appendChild(this.hiddenSqlField);

        
this.divUpl.appendChild(this.formUpl);
        
this.divUpl.appendChild(this.ifrmUpl);
        
this.divUpl.appendChild(this.divUplInfo);
        
this.divUpl.appendChild(this.divUplOutput);
        
this.divUpl.appendChild(this.divUplBtnsCont);
        
this.divUpl.appendChild(this.divUplDisplay);
        
this.o.table.parentNode.insertBefore(this.divUpl, this.o.table);

        
//Events
        
var x = this;
        
this.ifrmUpl.onload = this.ifrmUpl.onreadystatechange = function(){ //Iframe onload event
            
if(!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete'){
                try{
                    var 
iframeDoc = this.contentDocument || this.contentWindow.document;
                    if(
iframeDoc.location.href != 'about:blank'){
                        
x.Output(iframeDoc.body.innerHTML);
                        
x.iframe.src = 'about:blank';
                        
x.HideUploadButton();
                    }
                } catch(
ex){}
            }
        };

        
this.o.Event.Add(this.fileUpl, 'click', function(e){ x.OnUplClick(); });
        
this.o.Event.Add(this.divUplBtn, 'click', function(e){ x.Upload(); });
        
this.o.Event.Add(this.divUplBtnClose, 'click', function(e){ x.Close(x.o.Selection.GetActiveRow().cells[x.colIndex]); });

        
//Js code passed to server
        
var js = this.o.uplJsSuccess[this.colIndex].replace(/{1}/g, this.o.prfxUplWinRef + this.colIndex + this.o.id);
        
this.hiddenUplWinRef.value = js;
    },
    
GetValue: function(){ return this.fileUpl.value; },
    
HasValueChanged: function(){ return this.initialValue != this.GetValue(); },
    
OnUplClick: function(){ this.ShowUploadButton(); },
    
Upload: function(){ this.ShowLoader(); this.formUpl.submit(); },
    
SetRecordKey: function(key){ this.hiddenKey.value = key; },
    
GetRecordKey: function(){ return this.hiddenKey.value; },
    
ShowUploadButton: function(){ this.divUplBtn.style.display = 'inline'; },
    
HideUploadButton: function(){ this.divUplBtn.style.display = 'none'; },
    
ShowUploadContainer: function(){ this.divUplDisplay.style.display = 'block'; },
    
HideUploadContainer: function(){ this.divUplDisplay.style.display = 'none'; },
    
ShowUpload: function(){
        if(!
this.o.uplShowUpload[this.colIndex]){ return; }
        var 
row = this.o.Selection.GetActiveRow();
        if(
this.o.showUpload[this.colIndex]){
            
this.ShowUploadContainer();
            
this.o.showUpload[this.colIndex].call(this, this.o, this.divUplDisplay, this.GetUploadName(), this.o.uplPath[this.colIndex]);
            return;
        }
        if(
row){
            var 
uplname = this.GetUploadName();
            if(
uplname){
                if(
this.o.uplShowLink[this.colIndex]){
                    
this.divUplDisplay.innerHTML = this.GetUploadLinkHtml();
                } else {
                    
this.divUplDisplay.innerHTML = '<img src="'+this.o.uplPath[this.colIndex]+uplname+'" alt="'+uplname+'" />';
                }
                
this.ShowUploadContainer();
                
this.divUpl.scrollIntoView(false);
            } else { 
this.ClearUpload(); }
        }
    },
    
ClearUpload: function(){
        
this.divUplDisplay.innerHTML = '';
        
this.HideUploadContainer();
    },
    
GetUploadName: function(){
        var 
cell = this.o.Selection.GetActiveRow().cells[this.colIndex];
        if(
cell){ return cell.getAttribute(this.o.attrUplname); }
        return 
null;
    },
    
SetUploadName: function(name){
        var 
cell = this.o.Selection.GetActiveRow().cells[this.colIndex];
        if(
cell){ cell.setAttribute(this.o.attrUplname, name); }
    },
    
GetUploadLinkHtml: function(){
        var 
uplname = this.GetUploadName();
        return 
'<a href="'+ this.o.uplPath[this.colIndex]+ uplname +'" target="blank" class="'+ this.o.linkCss[this.colIndex] +'">'+
                
uplname.replace(this.GetRecordKey()+'_', '') + '</a>';
    },
    
Open: function(cell){
        if(!
cell){ return; }
        if(
this.o.Css.Has(cell.parentNode, this.o.newRowClass) || !cell.parentNode.getAttribute('id')){ alert(this.o.msgNewRowNoUploader); return; }
        
cell.appendChild(this.divUpl);
        if(
this.o.onBeforeOpenUploader[this.colIndex]){
            
this.o.onBeforeOpenUploader[this.colIndex].call(this, this.o, this.divUpl, cell);
        }
        var 
key = this.o.uplRecordIdColIndex[this.colIndex]
                    ? 
this.o.Selection.GetActiveRow()[this.o.uplRecordIdColIndex[this.colIndex]]
                    : 
this.o.Selection.GetActiveRow().getAttribute('id').replace(this.o.newRowPrefix, '');
        if(
key !== ''){
            
this.divUpl.style.display = '';
            
this.SetRecordKey(key);
            
this.initialValue = this.GetValue();
            
this.SetUploadSuccess(false);
            
this.ShowUpload();
        }
        if(
this.o.onAfterOpenUploader[this.colIndex]){
            
this.o.onAfterOpenUploader[this.colIndex].call(this, this.o, this.divUpl, cell);
        }
    },
    
Close: function(cell){
        if(
this.divUpl.style.display == 'none'){ return; }
        if(
this.o.onBeforeCloseUploader[this.colIndex]){
            
this.o.onBeforeCloseUploader[this.colIndex].call(this, this.o, this.divUpl, cell);
        }
        if(
cell && this.IsUploadSuccessful()){
            if(
this.o.uplShowLink[this.colIndex]){
                var 
div = this.o.CreateElm('div');
                
div.innerHTML = this.divUplDisplay.innerHTML;
                
cell.appendChild(div);
            } else {
                var 
img = this.o.Tag(cell, 'img')[0];
                if(
img){ img.src = this.o.uplOkImg[this.colIndex]; }
            }
        }
        
this.Output('');
        
this.SetRecordKey('');
        
this.ClearUpload();
        
this.HideUploadButton();
        
this.divUpl.style.display = 'none';
        
this.o.StandardBody().appendChild(this.divUpl);
        if(
this.o.onAfterCloseUploader[this.colIndex]){
            
this.o.onAfterCloseUploader[this.colIndex].call(this, this.o, this.divUpl, cell);
        }
    },
    
Output: function(msg){ this.divUplOutput.innerHTML = msg; },
    
SetUploadSuccess: function(val){ this.isUploadSuccessful = val; },
    
IsUploadSuccessful: function(val){ return this.isUploadSuccessful; },
    
ShowLoader: function(){    this.Output('<img src="'+this.o.uplLoaderImg[this.colIndex]+'" alt="Please wait..." />'); },
    
HideLoader: function(){ this.Output(''); }
};

Editable.prototype = {
    
onEditAdded: false, activeCellEditor: null, openCellEditor: null, activeRow: null,
    
modifiedRows: [], newRows: [], addedRows: [], deletedRows: [],
    
Init: function(){
        if(!
this.o.editable){ return; }
        
this.SetEvents();
        
this.SetCellsEditor();
        if(
this.o.onEditableInit){ this.o.onEditableInit.call(null, this.o); }
    },
    
Set: function(){ this.o.editable = true; this.SetEvents(); },
    
Remove: function(){ this.o.editable = false; this.RemoveEvents(); },
    
SetEvents: function(){
        if(!
this.onEditAdded){
            var 
x = this;
            
this.o.Event.Bind(this.o.table, this.o.openEditorAction, function(e){ x.Edit.call(x, e); });
            
this.onEditAdded = true;
        }
    },
    
RemoveEvents: function(){
        if(
this.onEditAdded){
            var 
x = this;
            
this.o.Event.Unbind(this.o.table, this.o.openEditorAction, function(e){ x.Edit.call(x, e); });
            
this.onEditAdded = false;
        }
    },
    
SetCellsEditor: function(){
        for (var 
i = 0; i < this.o.nbCells; i++){
            if(
this.o.editorTypes.length == this.o.nbCells){
                switch (
this.o.editorTypes[i]){
                    case 
this.o.edtTypes.none:
                        
this.o.editors[i] = null;
                        break;
                    case 
this.o.edtTypes.input:
                        
this.o.editors[i] = this.CreateInputEditor(i);
                        break;
                    case 
this.o.edtTypes.textarea:
                        
this.o.editors[i] = this.CreateMultilineEditor(i);
                        break;
                    case 
this.o.edtTypes.select:
                    case 
this.o.edtTypes.multiple:
                        
this.o.editors[i] = this.CreateSelectEditor(i);
                        break;
                    case 
this.o.edtTypes.bool:
                        
this.o.editors[i] = {};
                        break;
                    case 
this.o.edtTypes.uploader:
                        
this.o.editors[i] = this.CreateUploaderEditor(i);
                        break;
                    case 
this.o.edtTypes.command:
                        
this.SetCommandEditor(i);
                        
this.o.editors[i] = null;
                        break;
                    case 
this.o.edtTypes.custom:
                        
this.o.editors[i] = this.o.Get(this.o.customEditor[i]);
                        break;
                    default:
                        
this.o.editors[i] = null;
                        break;
                }
            } else {
                
//If editor type not set, default type is input
                
this.o.editorTypes[i] = this.o.edtTypes.input;
                
this.o.editors[i] = this.CreateInputEditor(i);
            }
        }
    },
    
CreateInputEditor: function(colIndex){
        if(
colIndex === undefined) return null;
        var 
inp = this.o.CreateElm(this.o.edtTypes.input, ['id', this.o.prfxEdt + colIndex + '_' + this.o.id], ['type', 'text'],
            [
'class', this.o.inputEditorCss], [this.o.attrColIndex, colIndex]);
        var 
attr = this.o.editorAttributes[colIndex];
        if(
attr) for (var i = 0; i < attr.length; i++){ inp.setAttribute(attr[i][0], attr[i][1]); } //additional attributes
        
if(inp.className === '') inp.className = this.o.inputEditorCss; //for older IE versions...
        
if(this.o.editorCss[colIndex]) this.o.Css.Add(inp, this.o.editorCss[colIndex]); //additional css
        
if(this.o.editorStyles[colIndex]) inp.style.cssText = this.o.editorStyles[colIndex]; //inline style
        
var x = this;
        
this.o.Event.Add(inp, 'focus', function(e){ x.Event.OnInputFocus.call(x, e); });
        
this.o.Event.Add(inp, 'blur', function(e){ x.Event.OnBlur.call(x, e); });
        return 
inp;
    },
    
CreateMultilineEditor: function(colIndex){
        if(
colIndex === undefined) return null;
        var 
txa = this.o.CreateElm(this.o.edtTypes.textarea, ['id', this.o.prfxEdt + colIndex + '_' + this.o.id],
            [
'class', this.o.textareaEditorCss], [this.o.attrColIndex, colIndex]);
        var 
attr = this.o.editorAttributes[colIndex];
        if(
attr) for (var i = 0; i < attr.length; i++){ txa.setAttribute(attr[i][0], attr[i][1]); } //additional attributes
        
if(txa.className === '') txa.className = this.o.textareaEditorCss; //for older IE versions...
        
if(this.o.editorCss[colIndex]) this.o.Css.Add(txa, this.o.editorCss[colIndex]); //additional css
        
if(this.o.editorStyles[colIndex]) txa.style.cssText = this.o.editorStyles[colIndex]; //inline style
        
var x = this;
        
this.o.Event.Add(txa, 'focus', function(e){ x.Event.OnInputFocus.call(x, e); });
        
this.o.Event.Add(txa, 'blur', function(e){ x.Event.OnBlur.call(x, e); });
        return 
txa;
    },
    
CreateSelectEditor: function(colIndex){
        if(
colIndex === undefined) return null;
        var 
slc = this.o.CreateElm(this.o.edtTypes.select, ['id', this.o.prfxEdt + colIndex + '_' + this.o.id],
            [
'class', this.o.selectEditorCss], [this.o.attrColIndex, colIndex]);
        if(
this.o.IsEditorType(colIndex, this.o.edtTypes.multiple)){ slc.setAttribute('multiple', 'multiple'); }
        var 
attr = this.o.editorAttributes[colIndex];
        if(
attr) for (var i = 0; i < attr.length; i++){ slc.setAttribute(attr[i][0], attr[i][1]); } //additional attributes
        
if(slc.className === '') slc.className = this.o.selectEditorCss; //for older IE versions...
        
if(this.o.editorCss[colIndex]) this.o.Css.Add(slc, this.o.editorCss[colIndex]); //additional css
        
if(this.o.editorStyles[colIndex]) slc.style.cssText = this.o.editorStyles[colIndex]; //inline style
        
var optArray = [], valArray = []; //options arrays
        
if(this.o.editorCustomSlcOptions[colIndex]){//custom values
            
for (var i = 0; i < this.o.editorCustomSlcOptions[colIndex].length; i++){
                var 
data = this.o.editorCustomSlcOptions[colIndex][i];
                if(
this.o.editorCustomSlcValues[colIndex]){
                    var 
val = this.o.editorCustomSlcValues[colIndex][i];
                    if(
valArray.indexOf(val) == -1) valArray.push(val);
                }
                if(
optArray.indexOf(data) == -1) optArray.push(data);
            }
        } else {
//automatic column values
            
for (var i = this.o.startRow; i < this.o.GetRowsNb(); i++){
                var 
row = this.o.table.rows[i];
                var 
cell = row.cells[colIndex];
                if(!
row || !cell) continue;
                var 
data = this.o.GetText(cell);
                if(
optArray.indexOf(data) == -1) optArray.push(data);
            }
        }
        if(
this.o.editorSortSlcOptions[colIndex]){
            var 
sortType = this.o.editorSortSlcOptions[colIndex].LCase();
            if(
sortType == 'numdesc'){
                try { 
optArray.sort(this.o.Sort.NumDesc); } catch (e){ }
            } else if(
sortType == 'numasc'){
                try { 
optArray.sort(this.o.Sort.NumAsc); } catch (e){ }
            } else {
                try { 
optArray.sort(this.o.Sort.IgnoreCase); } catch (e){ }
            }
        }
        for (var 
j = 0; j < optArray.length; j++){
            var 
opt = this.o.CreateElm('option', ['value', valArray[j] || optArray[j]]);
            
opt.appendChild(this.o.CreateText(optArray[j]));
            
slc.appendChild(opt);
        }
        var 
x = this;
        
this.o.Event.Add(slc, 'change', function(e){
            var 
cell = x.o.GetCell(e);
            if(
cell){
                
cell.setAttribute(x.o.attrText, slc.options[slc.selectedIndex].text); //for inserts
                
cell.setAttribute(x.o.attrValue, slc.options[slc.selectedIndex].value); //for inserts
                
slc.setAttribute(x.o.attrText, slc.options[slc.selectedIndex].text);
                
slc.setAttribute(x.o.attrValue, slc.options[slc.selectedIndex].value);
            }
        });
        
this.o.Event.Add(slc, 'blur', function(e){ x.Event.OnBlur.call(x, e); });
        return 
slc;
    },
    
SetCommandEditor: function(colIndex){
        if(
colIndex === undefined || !this.o.IsEditorType(colIndex, this.o.edtTypes.command)){ return; }
        
this.edtBtns = []; this.addBtns = []; this.delBtns = []; this.submitBtns = []; this.cancelBtns = [];
        var 
x = this.o.Editable,
            
o = this.o;
        for (var 
i = this.o.startRow; i < this.o.GetRowsNb(); i++){
            var 
row = this.o.table.rows[i];
            var 
cell = row.cells[colIndex];
            if(!
row || !cell) continue;

            var 
div = this.o.CreateElm('div', ['class', this.o.commandEditorCss]);
            if(
this.o.cmdEnabledBtns.indexOf('update') != -1){
                var 
editBtn = this.o.CreateElm('button', ['id', 'editBtn_' + i + '_' + this.o.id], ['title', this.o.cmdUpdateBtnTitle],
                                [
'css', this.o.cmdUpdateBtnCss], [this.o.attrColIndex, i]);
                if(
this.o.cmdUpdateBtnStyle) editBtn.style.cssText = this.o.cmdUpdateBtnStyle; //inline style
                
editBtn.innerHTML = this.o.cmdUpdateBtnIcon + this.o.cmdUpdateBtnText;
                
div.appendChild(editBtn);
                
this.o.Event.Add(editBtn, 'click', function(e){ x.Edit.call(x, e); });
                if(
this.edtBtns.indexOf(editBtn) == -1) this.edtBtns[i] = editBtn;
            }
            if(
this.o.cmdEnabledBtns.indexOf('insert') != -1){
                var 
createBtn = this.o.CreateElm('button', ['id', 'createBtn_' + i + '_' + this.o.id], ['title', this.o.cmdInsertBtnTitle],
                                [
'css', this.o.cmdInsertBtnCss], [this.o.attrColIndex, i]);
                if(
this.o.cmdInsertBtnStyle) createBtn.style.cssText = this.o.cmdInsertBtnStyle; //inline style
                
createBtn.innerHTML = this.o.cmdInsertBtnIcon + this.o.cmdInsertBtnText;
                
div.appendChild(createBtn);
                
this.o.Event.Add(createBtn, 'click', function(e){ x.AddNewRow(); x.SetCommandEditor(x.o.editorCmdColIndex); });
                if(
this.addBtns.indexOf(createBtn) == -1) this.addBtns[i] = createBtn;
            }
            if(
this.o.cmdEnabledBtns.indexOf('delete') != -1){
                var 
delBtn = this.o.CreateElm('button', ['id', 'delBtn_' + i + '_' + this.o.id], ['title', this.o.cmdDeleteBtnTitle],
                                [
'css', this.o.cmdDeleteBtnCss], [this.o.attrColIndex, i]);
                if(
this.o.cmdDeleteBtnStyle) delBtn.style.cssText = this.o.cmdDeleteBtnStyle; //inline style
                
delBtn.innerHTML = this.o.cmdDeleteBtnIcon + this.o.cmdDeleteBtnText;
                
div.appendChild(delBtn);
                
this.o.Event.Add(delBtn, 'click', function(e){ x.SubmitDeletedRows(); });
                if(
this.delBtns.indexOf(delBtn) == -1) this.delBtns[i] = delBtn;
            }
            if(
this.o.cmdEnabledBtns.indexOf('submit') != -1){
                var 
postBtn = this.o.CreateElm('button', ['id', 'postBtn_' + i + '_' + this.o.id], ['title', this.o.cmdSubmitBtnTitle],
                                [
'style', 'display:none;'], ['css', this.o.cmdSubmitBtnCss], [this.o.attrColIndex, i]);
                
postBtn.style.display = 'none'; //older versions of IE
                
if(this.o.cmdSubmitBtnStyle) postBtn.style.cssText += this.o.cmdSubmitBtnStyle; //inline style
                
postBtn.innerHTML = this.o.cmdSubmitBtnIcon + this.o.cmdSubmitBtnText;
                
div.appendChild(postBtn);
                
this.o.Event.Add(postBtn, 'click', function(e){
                    
o.Event.Stop(e);
                    
x.CloseRowEditor();
                    
x.SubmitAll();
                });
                if(
this.submitBtns.indexOf(postBtn) == -1) this.submitBtns[i] = postBtn;
            }
            if(
this.o.cmdEnabledBtns.indexOf('cancel') != -1){
                var 
cancelBtn = this.o.CreateElm('button', ['id', 'cancelBtn_' + i + '_' + this.o.id], ['title', this.o.cmdCancelBtnTitle],
                                [
'style', 'display:none;'], ['css', this.o.cmdCancelBtnCss], [this.o.attrColIndex, i]);
                
cancelBtn.style.display = 'none'; //older versions of IE
                
if(this.o.cmdCancelBtnStyle) cancelBtn.style.cssText += this.o.cmdCancelBtnStyle; //inline style
                
cancelBtn.innerHTML = this.o.cmdCancelBtnIcon + this.o.cmdCancelBtnText;
                
div.appendChild(cancelBtn);
                
this.o.Event.Add(cancelBtn, 'click', function(e){ o.Event.Stop(e); x.CloseRowEditor(); });
                if(
this.cancelBtns.indexOf(cancelBtn) == -1) this.cancelBtns[i] = cancelBtn;
            }
            
cell.innerHTML = '';
            
cell.appendChild(div);
        }
    },
    
CreateUploaderEditor: function(colIndex){
        var 
uploader = new Uploader(this.o, colIndex);
        
uploader.Init();
        return 
uploader;
    },
    
OpenCellEditor: function(cell){
        if(!
cell){ return; }
        var 
cellIndex = cell.cellIndex;
        var 
editor = this.o.editors[cellIndex];
        if(
this.o.onBeforeOpenEditor){ this.o.onBeforeOpenEditor.call(null, this.o, cell, editor); }
        
this.activeCellEditor = cell;
        
this.openCellEditor = cellIndex;
        if(!
this.o.IsEditorType(cellIndex, this.o.edtTypes.uploader)){
            var 
data = this.o.GetText(cell);
            
this.SetCellCache(cell, data);
            
this.SetEditorValue(cellIndex, data);
            if(!
this.o.IsEditorType(cellIndex, this.o.edtTypes.custom)){
                
cell.innerHTML = '';
                
cell.appendChild(editor);
                if(
this.o.editorModel == 'cell'){ this.SetEditorFocus(cellIndex); }
            } else {
                if(
this.o.openCustomEditor){ this.o.openCustomEditor.call(null, this.o, cell, editor); }
            }
        } else { 
editor.Open(cell); }
        if(
this.o.onAfterOpenEditor){ this.o.onAfterOpenEditor.call(null, this.o, cell, editor); }
    },
    
OpenRowEditor: function(row){
        if(!
row) return;
        
this.activeRow = row;
        for (var 
i = 0; i < this.o.nbCells; i++){
            if(!
this.o.editors[i] || this.o.IsEditorType(i, this.o.edtTypes.bool)
                || 
this.o.IsEditorType(i, this.o.edtTypes.command)){ continue; }
            var 
c = row.cells[i];
            
this.OpenCellEditor(c);
            if(
this.o.Selection.activeCell && this.o.Selection.activeCell.cellIndex === i){
                
this.SetEditorFocus(i);
            }
        }
        
this.ShowCommandBtns(row.rowIndex, false);
    },
    
CloseRowEditor: function(){
        if(!
this.activeRow) return;
        var 
row = this.activeRow;
        for (var 
i = 0; i < this.o.nbCells; i++){
            if(!
this.o.editors[i] || this.o.IsEditorType(i, this.o.edtTypes.bool)) continue;
            
this.activeCellEditor = row.cells[i];
            
this.CloseCellEditor(i);
        }
        
this.ShowCommandBtns(row.rowIndex, true);
        if(
this.o.autoSave){ this.AutoSubmit(); }
        
this.activeRow = null;
    },
    
ShowCommandBtns: function(rowIndex, showIcons){
        if(
rowIndex === undefined || showIcons === undefined || this.o.editorModel != 'row') return;
        if(!
this.edtBtns || !this.addBtns || !this.delBtns || !this.submitBtns || !this.cancelBtns) return;
        if(
showIcons){
            if(
this.edtBtns[rowIndex]) this.edtBtns[rowIndex].style.display = 'inline';
            if(
this.addBtns[rowIndex]) this.addBtns[rowIndex].style.display = 'inline';
            if(
this.delBtns[rowIndex]) this.delBtns[rowIndex].style.display = 'inline';
            if(
this.submitBtns[rowIndex]) this.submitBtns[rowIndex].style.display = 'none';
            if(
this.cancelBtns[rowIndex]) this.cancelBtns[rowIndex].style.display = 'none';
        } else {
            if(
this.edtBtns[rowIndex]) this.edtBtns[rowIndex].style.display = 'none';
            if(
this.addBtns[rowIndex]) this.addBtns[rowIndex].style.display = 'none';
            if(
this.delBtns[rowIndex]) this.delBtns[rowIndex].style.display = 'none';
            if(
this.submitBtns[rowIndex]) this.submitBtns[rowIndex].style.display = 'inline';
            if(
this.cancelBtns[rowIndex]) this.cancelBtns[rowIndex].style.display = 'inline';
        }
    },
    
CloseCellEditor: function(colIndex){
        if(
colIndex === undefined || !this.activeCellEditor){ return; }
        if(
this.o.onBeforeCloseEditor){ this.o.onBeforeCloseEditor.call(null, this.o, this.activeCellEditor, this.o.editors[colIndex]); }
        var 
edtVal = this.GetEditorValue(colIndex),
            
cache = this.GetCellCache(this.activeCellEditor),
            
cellVal = cache[1], cellHtml = cache[0],
            
editor = this.o.editors[colIndex],
            
val;
        if(
this.o.IsEditorType(colIndex, this.o.edtTypes.uploader)){
            
editor.Close(this.activeCellEditor);
        } else {
            if(
this.o.IsEditorType(colIndex, this.o.edtTypes.multiple)){//multiple selections need to be cleared
                
for (var j = 0; j < editor.options.length; j++){
                    if(
editor.options[j].selected){ editor.options[j].selected = false; }
                }
            }
            if(
edtVal != cellVal){
                var 
reg = new RegExp(cellVal.RegexpEscape(), 'g');
                if(
reg.test(cellHtml) && cellVal !== ''){ val = cellHtml.replace(reg, edtVal); } //value to be written
                
else { val = edtVal; }
            }
            if(
this.o.setCellModifiedValue){ this.o.setCellModifiedValue.call(null, this.o, this.activeCellEditor, val); }
            else {
                try { 
this.activeCellEditor.removeChild(editor); } catch (e){ if(this.activeCellEditor) this.activeCellEditor.innerHTML = ''; } //for older versions of IE
                
try {
                    if(!
this.o.validateModifiedValue ||
                        
this.o.validateModifiedValue.call(null, this.o, colIndex, cellVal, edtVal, this.activeCellEditor, editor)){ //validation ok
                        
if(this.o.editorAllowEmptyValue[colIndex]){ //empty value is allowed
                            
this.activeCellEditor.innerHTML = (val !== undefined ? val : cellHtml);
                        } else { 
this.activeCellEditor.innerHTML = (val !== undefined && val.Trim() !== '' ? val : cellHtml); }
                        if(
edtVal != cellVal){
                            
//select editor with option value different from option text
                            
if(this.o.IsEditorType(colIndex, this.o.edtTypes.select)){ val = editor.getAttribute(this.o.attrValue); }
                            if(
this.o.editorModel == 'row'){ this.SetModifiedCell(this.activeCellEditor, this.activeCellEditor.innerHTML, cellVal); }
                            else { 
this.SetModifiedCell(this.activeCellEditor, val, cellVal); }
                        }
                    } else { 
this.activeCellEditor.innerHTML = cellVal || cellHtml; } //resets cached value
                
} catch (e){ } //Temp solution for mysterious Chrome bug
            
}
            if(
this.o.onAfterCloseEditor){ this.o.onAfterCloseEditor.call(null, this.o, this.activeCellEditor, editor); }
            
this.RemoveCellCache(this.activeCellEditor);
            if(
this.o.IsEditorType(colIndex, this.o.edtTypes.custom)){
                if(
this.o.closeCustomEditor){ this.o.closeCustomEditor.call(null, this.o, this.activeCellEditor, editor); }
            }
        }
        if(
this.o.autoSave && this.o.editorModel === 'cell' && this.o.autoSaveModel === 'cell'){ this.AutoSubmit(); }
        
this.activeCellEditor = null;
        
this.openCellEditor = null;
    },
    
IsEditorOpen: function(colIndex){ return this.openCellEditor == colIndex; },
    
IsRowEditorOpen: function(){ return this.activeRow !== null; },
    
SetEditorFocus: function(colIndex){
        if(
this.o.editors[colIndex] && (this.IsEditorOpen(colIndex) || this.activeRow) &&
            (!
this.o.IsEditorType(colIndex, this.o.edtTypes.custom) && !this.o.IsEditorType(colIndex, this.o.edtTypes.command)
            && !
this.o.IsEditorType(colIndex, this.o.edtTypes.bool))){ this.o.editors[colIndex].focus(); }
    },
    
BlurEditor: function(colIndex){ if(this.o.editors[colIndex] && (this.IsEditorOpen(colIndex) || this.activeRow)) this.o.editors[colIndex].blur(); },
    
SetModifiedCell: function(cell, val, oldVal){
        if(!
cell) return;
        var 
row = cell.parentNode;
        if(
this.o.Css.Has(row, this.o.newRowClass)){ return; } //modified values of added rows don't need to be treated
        
var r = {}; r.values = []; r.urlParams = ''; r.modified = [];
        var 
modRow = this.GetModifiedRow(row.rowIndex);
        if(!
modRow){
            for (var 
i = 0; i < row.cells.length; i++){
                if(
cell.cellIndex == i){ this.o.Css.Add(cell, this.o.modifiedCellCss); }
                var 
cache = this.GetCellCache(row.cells[i]);
                var 
t = cell.cellIndex == i ? val : (this.o.editorModel == 'row' && !this.o.IsEditorType(i, this.o.edtTypes.none) ? cache[1] || this.o.GetText(row.cells[i]) : this.o.GetText(row.cells[i]));
                if(
this.o.IsEditorType(i, this.o.edtTypes.bool) && this.o.Tag(row.cells[i], 'input').length > 0)
                    
t = this.o.Tag(row.cells[i], 'input')[0].checked;
                var 
paramName = this.o.prfxParam + i; //param name for server-side purposes
                
r.values.push(t);
                
r.modified.push(cell.cellIndex == i ? true : false);
                
r.urlParams += '&' + paramName + '=' + encodeURIComponent(t); //params for submission
            
}
            
this.modifiedRows.push([row.rowIndex, r]);
        } else {
            var 
obj = modRow[1];
            
obj.values[cell.cellIndex] = val;
            
obj.modified[cell.cellIndex] = true;
            var 
paramName = this.o.prfxParam + cell.cellIndex;
            var 
oldParam = paramName + '=' + encodeURIComponent(oldVal);
            var 
newParam = paramName + '=' + encodeURIComponent(val);
            
obj.urlParams = obj.urlParams.replace(oldParam, newParam);
        }
    },
    
GetModifiedRow: function(rowIndex){
        if(
rowIndex === undefined){ return null; }
        for (var 
i = 0; i < this.modifiedRows.length; i++){
            if(
this.modifiedRows[i][0] == rowIndex){
                return 
this.modifiedRows[i];
            }
        } return 
null;
    },
    
GetModifiedRows: function(){ return this.modifiedRows; },
    
GetAddedRows: function(){ return this.addedRows; },
    
SetRowsObject: function(rows, cmd){
        if(!
rows) return;
        for (var 
i = 0; i < rows.length; i++){
            var 
row = rows[i];
            if(!
row) continue;
            var 
r = {}; r.values = []; r.urlParams = ''; r.modified = [];
            for (var 
j = 0; j < row.cells.length; j++){
                var 
cell = row.cells[j];
                var 
t = this.o.GetText(row.cells[j]);
                if(
this.o.IsEditorType(j, this.o.edtTypes.bool) && this.o.Tag(cell, 'input').length > 0){
                    
t = this.o.Tag(cell, 'input')[0].checked;
                } else if(
this.o.IsEditorType(j, this.o.edtTypes.select)){ t = cell.getAttribute(this.o.attrValue); }
                var 
paramName = this.o.prfxParam + j; //param name for server-side purposes
                
r.values.push(t);
                
r.modified.push((cmd == 'delete' ? false : true));
                
r.urlParams += '&' + paramName + '=' + encodeURIComponent(t); //params for submission
            
}
            if(
cmd == 'delete') this.deletedRows.push([row.rowIndex, r]);
            else if(
cmd == 'insert') this.addedRows.push([row.rowIndex, r]);
            else 
this.modifiedRows.push([row.rowIndex, r]);
        }
    },
    
GetDeletedRows: function(){ return this.deletedRows; },
    
RemoveModifiedRow: function(rowIndex){
        if(
rowIndex === undefined) return;
        for (var 
i = 0; i < this.GetModifiedRows().length; i++){
            if(
this.GetModifiedRows()[i][0] == rowIndex){
                
this.modifiedRows.splice(i, 1); break;
            }
        }
    },
    
RemoveModifiedCellMark: function(rowIndex, cellIndexes){
        if(
rowIndex === undefined) return;
        var 
row = this.o.table.rows[rowIndex], cells = row.cells;
        for (var 
i = 0; i < cells.length; i++){
            var 
cell = cells[i];
            if(!
cellIndexes || cellIndexes.indexOf(i) != -1)
                
this.o.Css.Remove(cell, this.o.modifiedCellCss);
        }
    },
    
SetCellCache: function(cell, data, htmlCont){
        if(!
cell || data === undefined) return;
        var 
html = htmlCont || htmlCont === '' ? htmlCont : cell.innerHTML;
        
cell.setAttribute(this.o.attrCont, escape(html));
        
cell.setAttribute(this.o.attrData, escape(data));
    },
    
GetCellCache: function(cell){
        if(!
cell) return [];
        var 
a, b;
        if(
cell.attributes[this.o.attrCont] !== undefined) a = unescape(cell.getAttribute(this.o.attrCont));
        if(
cell.attributes[this.o.attrData] !== undefined) b = unescape(cell.getAttribute(this.o.attrData));
        return [
a, b];
    },
    
RemoveCellCache: function(cell){
        if(!
cell) return;
        if(
cell.attributes[this.o.attrCont] !== undefined) cell.removeAttribute(this.o.attrCont);
        if(
cell.attributes[this.o.attrData] !== undefined) cell.removeAttribute(this.o.attrData);
    },
    
GetEditorValue: function(colIndex){
        var 
editor = this.o.editors[colIndex];
        var 
editorType = this.o.editorTypes[colIndex];
        var 
val = '';
        if(!
editor || !editorType) return val;
        switch (
editorType.LCase()){
            case 
this.o.edtTypes.input:
            case 
this.o.edtTypes.textarea:
                
val = editor.value;
                break;
            case 
this.o.edtTypes.select:
                
val = editor.getAttribute(this.o.attrText) || editor.value;
                break;
            case 
this.o.edtTypes.multiple:
                var 
sep = !this.o.editorValuesSeparator[colIndex] ? this.o.valuesSeparator : this.o.editorValuesSeparator[colIndex];
                for (var 
j = 0; j < editor.options.length; j++)
                    if(
editor.options[j].selected)
                        
val = val.concat(editor.options[j].value, sep);
                
val = val.substring(0, val.length - sep.length);
                break;
            case 
this.o.edtTypes.custom:
                if(
this.o.getCustomEditorValue) val = this.o.getCustomEditorValue.call(null, this.o, editor, colIndex);
                break;
        }
        return 
val;
    },
    
SetEditorValue: function(colIndex, val){
        var 
editor = this.o.editors[colIndex];
        var 
editorType = this.o.editorTypes[colIndex];
        switch (
editorType.LCase()){
            case 
this.o.edtTypes.input:
            case 
this.o.edtTypes.textarea:
            case 
this.o.edtTypes.select:
                
editor.value = val;
                break;
            case 
this.o.edtTypes.multiple:
                for (var 
j = 0; j < editor.options.length; j++)
                    if(
editor.options[j].value == val) editor.options[j].selected = true;
                break;
            case 
this.o.edtTypes.custom:
                if(
this.o.setCustomEditorValue) this.o.setCustomEditorValue.call(null, this.o, editor, colIndex, val);
                break;
        }
    },
    
GetCheckBox: function(cell){
        if(!
cell) return null;
        var 
chk = this.o.Tag(cell, 'input')[0];
        if(
chk.getAttribute('type').LCase() == 'checkbox') return chk;
        else return 
null;
    },
    
SetCheckBoxValue: function(e, cell){
        if(!
cell) return;
        var 
x = this.o.Editable;
        var 
checkbox = x.GetCheckBox(cell);
        if(!
checkbox || checkbox.type.LCase() != 'checkbox') return;
        if(
this.o.Event.GetElement(e) != checkbox){//in case user clicks on cell instead of checkbox
            
if(checkbox.checked) checkbox.checked = false;
            else 
checkbox.checked = true;
        }
        var 
data = !checkbox.checked;
        
x.SetCellCache(cell, data, '');
        
x.SetModifiedCell(cell);
        if(
x.o.autoSave){ x.SubmitAll(); } //checkbox value set straightaway, prevents treatedRows inconsistency
    
},
    
AddNewRow: function(){
        var 
row,
            
rowIdx = this.o.startRow;

        if(
this.o.newRowPos === 'bottom'){
            
rowIdx = -1;
        }
        else if(
typeof this.o.newRowPos === 'number' &&
            
this.o.newRowPos >= -1){
            
rowIdx = this.o.newRowPos;
        }

        try{
            
row = this.o.table.insertRow(rowIdx);
          } catch(
e) {
              
row = this.o.table.insertRow(this.o.startRow);
            
console.log(e);
        }
        
row.setAttribute('id', this.o.CreateId()); //temp id for new row
        
this.o.Css.Add(row, this.o.newRowClass);
        for (var 
i = 0; i < this.o.nbCells; i++){
            var 
cell = row.insertCell(i);
            if(
this.o.defaultRecord){ cell.innerHTML = this.o.defaultRecord[i]; }
            else { 
cell.innerHTML = this.o.defaultRecordUndefinedValue; }
        }
        if(
this.o.cmdInsertBtnScroll){ row.scrollIntoView(false); }
        
this.newRows.push(row);
        if(
this.o.onAddedDomRow){ this.o.onAddedDomRow.call(null, this.o, this.newRows, row); }
    },
    
SubmitEditedRows: function(){ this.Submit('update'); },
    
SubmitAddedRows: function(){ this.SetRowsObject(this.newRows, 'insert'); this.Submit('insert'); },
    
SubmitDeletedRows: function(){
        if(
this.o.selection){
            if(!
this.o.Selection.activeRow && this.o.Selection.selectedRows.length === 0) return;
            var 
rows = (this.o.bulkDelete) ? this.o.Selection.selectedRows : [this.o.Selection.activeRow];
            if(
rows.length === 0){ return; }
            
this.SetRowsObject(rows, 'delete');
            if(
confirm(this.o.msgConfirmDelSelectedRows)){ this.Submit('delete'); }
            else { 
this.deletedRows = []; }
        }
    },
    
SubmitAll: function(){ this.submitAll = true; this.SubmitAddedRows(); this.SubmitEditedRows(); },
    
AutoSubmit: function(){
        switch(
this.o.autoSaveType){
            case 
'both': this.SubmitAll(); break;
            case 
'insert': this.SubmitAddedRows(); break;
            case 
'update': default: this.SubmitEditedRows(); break;
        }
    },
    
Submit: function(cmd){
        
cmd = cmd.LCase();
        var 
x = this;
        var 
treatedRows, uri, submitMethod, formMethod, params;
        var 
beforeSubmitCallBack, afterSubmitCallBack, onSubmit, onSubmitError;
        var 
checkResponseSanity, processResponse;
        switch ((
cmd || '')){
            case 
'insert':
                
treatedRows = this.GetAddedRows();
                
uri = this.o.insertURI;
                
submitMethod = this.o.insertSubmitMethod;
                
formMethod = this.o.insertFormMethod;
                
params = this.o.insertParams;
                
beforeSubmitCallBack = this.o.onBeforeInsertSubmit;
                
afterSubmitCallBack = this.o.onAfterInsertSubmit;
                
onSubmit = this.o.onInsertSubmit;
                
onSubmitError = this.o.onInsertError;
                
checkResponseSanity = this.o.checkInsertResponseSanity;
                
processResponse = this.o.processInsertResponse;
                break;
            case 
'delete':
                
treatedRows = this.GetDeletedRows();
                
uri = this.o.deleteURI;
                
submitMethod = this.o.deleteSubmitMethod;
                
formMethod = this.o.deleteFormMethod;
                
params = this.o.deleteParams;
                
beforeSubmitCallBack = this.o.onBeforeDeleteSubmit;
                
afterSubmitCallBack = this.o.onAfterDeleteSubmit;
                
onSubmit = this.o.onDeleteSubmit;
                
onSubmitError = this.o.onDeleteError;
                
checkResponseSanity = this.o.checkDeleteResponseSanity;
                
processResponse = this.o.processDeleteResponse;
                break;
            case 
'update':
            default:
                
treatedRows = this.GetModifiedRows();
                
uri = this.o.updateURI;
                
submitMethod = this.o.updateSubmitMethod;
                
formMethod = this.o.updateFormMethod;
                
params = this.o.updateParams;
                
beforeSubmitCallBack = this.o.onBeforeUpdateSubmit;
                
afterSubmitCallBack = this.o.onAfterUpdateSubmit;
                
onSubmit = this.o.onUpdateSubmit;
                
onSubmitError = this.o.onUpdateError;
                
checkResponseSanity = this.o.checkUpdateResponseSanity;
                
processResponse = this.o.processUpdateResponse;
                break;
        }
        if(
beforeSubmitCallBack){ beforeSubmitCallBack.call(null, this.o, treatedRows); }
        if(
onSubmit){ onSubmit.call(null, this.o, treatedRows); }
        else {
            if((!
uri || uri === '') && treatedRows.length > 0){
                
alert(cmd.toUpperCase() + ': ' + this.o.msgUndefinedSubmitUrl);
                
treatedRows = []; x.o.savedRowsNb[cmd] = 0;
                
SubmitComplete(false);
            } else {
                for (var 
i = 0; i < treatedRows.length; i++){
                    var 
modArr = treatedRows[i], rowIndex = modArr[0], row = modArr[1];
                    if(
rowIndex < 0) continue;
                    var 
rowValues = row.values;
                    var 
urlParams = row.urlParams, paramParts = urlParams.split('&');
                    var 
rowId = this.o.table.rows[rowIndex].getAttribute('id');
                    if(
params && this.o.IsArray(params)){//params are replaced if defined
                        
for (var j = 0; j < params.length; j++){
                            if(
params[j] === ''){ continue; }
                            
urlParams = urlParams.replace(paramParts[j + 1].split('=')[0], params[j]);
                        }
                        
paramParts = urlParams.split('&');
                    }
                    
this.o.Css.Add(this.o.table, this.o.activityIndicatorCss);
                    if(
this.o.onServerActivityStart) this.o.onServerActivityStart.call(null, this.o, this.o.table.rows[rowIndex]);

                    if(
submitMethod === 'script'){//GET method by using script tag inclusion
                        
var prm = (uri.indexOf('?') === -1 ? '?rowId=' : '&rowId=') + rowId + urlParams;
                        try {
//window.open(uri+prm);
                            
this.o.IncludeFile(this.o.prfxScr + rowIndex + '_' + this.o.id, uri + prm,
                                function(
eg, scriptElm){
                                    
eg.savedRowsNb[cmd]++; var rIndex = scriptElm.id.replace(eg.prfxScr, '').replace('_' + eg.id, '');
                                    
eg.Editable.RemoveModifiedCellMark(parseInt(rIndex, 10));
                                    
SubmitComplete(true);
                                });
                        } catch (
e){
                            
this.o.Css.Remove(this.o.table, this.o.activityIndicatorCss);
                            if(
this.o.onServerActivityStop) this.o.onServerActivityStop.call(null, this.o, this.o.table.rows[rowIndex]);
                            if(
this.o.onSubmitError) this.o.onSubmitError.call(null, this.o, e, e.description);
                            else 
alert(this.o.msgErrOccur + 'n' + e.description + 'n' + this.o.msgSaveUnsuccess);
                        }
                    }
                    else if(
submitMethod !== 'script' && this.o.ajax){
                        var 
xhr = (function(rowIndex, arrIndex, rowId, urlParams) {
                            var 
_self = this,
                                
div;
                            
// Submit data with intervals
                            
if(arrIndex===0){
                                
submitData(rowId, urlParams, rowIndex, arrIndex);
                            } else {
                                
setTimeout(function() {
                                    
submitData(rowId, urlParams, rowIndex, arrIndex); },
                                    (
arrIndex*x.o.formSubmitInterval)
                                );
                            }

                            function 
submitData(rowId, urlParams, rowIndex, arrIndex){
                                var 
prm = 'rowId=' + rowId + '&rIndex=' + rowIndex + urlParams;

                                $.
ajax({
                                       
url: uri,
                                      
type: formMethod,
                                      
data: prm
                                
}).done(function(data, status, xhr){
                                    
// Determine response content-type
                                    
var dataType = xhr.getResponseHeader('content-type') || 'application/json';

                                    if(
dataType.indexOf('application/json') !== -1){
                                        var 
sane = sanityCheck(data);
                                        if(
sane){
                                            
// Process the response if needed
                                            
if(processResponse){
                                                
processResponse.call(_self.o, data);
                                            }

                                            
// Ensure id attribute is set for newly created rows, so they can be updated immediately
                                            
if(data.result && data.result.id && cmd === 'insert'){
                                                if(
_self.o.table.rows[rowIndex]){
                                                    
_self.o.table.rows[rowIndex].setAttribute('id', _self.o.newRowPrefix + data.result.id);
                                                }
                                            }

                                            
release();
                                        } else {
                                            
fail(null, 'Invalid Data', _self.o.msgInvalidData);
                                        }
                                    } else {
                                        
div = _self.o.CreateElm('div', ['id', 'xhr_' + rowIndex + '_' + _self.o.id]);
                                        
document.body.appendChild(div);
                                        $(
div).html(data);
                                        
release();
                                    }
                                }).
always(function(data){
                                    if(
div){ document.body.removeChild(div); }
                                }).
fail(fail);
                            }

                            function 
sanityCheck(data) {
                                if(
checkResponseSanity){
                                    return 
checkResponseSanity.call(_self.o, data);
                                }
                                return (
data && data.hasOwnProperty('result') &&
                                        
data.result.hasOwnProperty('success') &&
                                        
data.result.hasOwnProperty('id'));
                            }

                            function 
fail(jqXHR, textStatus, errDesc) {
                                
// remove reference from collection of rows to be treated
                                
if(cmd === 'insert'){
                                    
_self.o.Editable.addedRows.splice(arrIndex, 1);
                                    
_self.o.Editable.newRows.splice(arrIndex, 1);
                                }
                                
treatedRows.splice(arrIndex, 1);
                                
_self.o.Css.Remove(_self.o.table, _self.o.activityIndicatorCss);
                                if(
_self.o.onServerActivityStop) _self.o.onServerActivityStop.call(null, _self.o, _self.o.table.rows[rowIndex]);
                                if(
_self.o.onSubmitError) _self.o.onSubmitError.call(null, _self.o, e, e.description);
                                else 
alert(_self.o.msgErrOccur + 'n' + errDesc + 'n' + _self.o.msgSaveUnsuccess);
                            }

                            function 
release(){
                                
_self.o.savedRowsNb[cmd]++;
                                
_self.o.Editable.RemoveModifiedCellMark(rowIndex);
                                
_self.o.Css.Remove(_self.o.table.rows[rowIndex], _self.o.newRowClass);
                                
SubmitComplete(true);
                            }
                        }).
call(this, rowIndex, i, rowId, urlParams);
                    } else {
//GET or POST method by using form and iframe elements
                        
if(!this.o.ifrmContainer[cmd]) this.o.ifrmContainer[cmd] = this.o.CreateElm('div', ['id', 'cont_' + this.o.id + cmd], ['style', 'display:none;']);
                        var 
iframeId = this.o.prfxIFrm + rowIndex + '_' + this.o.id + cmd;
                        var 
iframe; //below for older versions of IE, name attribute dynamically created problem, very ugly solution...
                        
try { var iframe = document.createElement('<iframe src="about:blank" name="' + iframeId + '" id="' + iframeId + '" '+ this.o.attrRowIndex +'="' + rowIndex + '"></iframe>'); }
                        catch (
e){ var iframe = this.o.CreateElm('iframe', ['id', iframeId], ['name', iframeId], ['src', 'about:blank'], [this.o.attrRowIndex, rowIndex]); }
                        
iframe.style.cssText = 'display:none; width:0; height:0;';

                        var 
form = this.o.CreateElm( 'form',
                            [
'id', this.o.prfxFrm + rowIndex + '_' + this.o.id + cmd], ['method', formMethod], ['action', uri],
                            [
'target', iframeId], ['accept-charset', 'utf-8'] );
                        for (var 
j = 1; j < paramParts.length; j++){
                            var 
paramName = paramParts[j].split('=')[0];
                            var 
paramValue = paramParts[j].split('=')[1];
                            var 
hiddenField = this.o.CreateElm('input', ['type', 'hidden'], ['name', paramName], ['value', paramValue]);
                            
form.appendChild(hiddenField);
                        }
                        var 
hiddenField = this.o.CreateElm('input', ['type', 'hidden'], ['name', 'rowId'], ['value', rowId]);
                        
form.appendChild(hiddenField);

                        
document.body.appendChild(this.o.ifrmContainer[cmd]);
                        
this.o.ifrmContainer[cmd].appendChild(iframe);
                        
this.o.ifrmContainer[cmd].appendChild(form);
                    }
                } 
//for i

                
if(treatedRows.length > 0 && this.o.ifrmContainer[cmd]){
                    var 
ifrms = this.o.Tag(this.o.ifrmContainer[cmd], 'iframe'),
                        
frms = this.o.Tag(this.o.ifrmContainer[cmd], 'form');
                    for(var 
j=0; j<ifrms.length; j++){
                        (function(
k){
                            var 
form = frms[k], iframe = ifrms[k];
                            if(
k===0){ form.submit(); } //1st form is submitted
                            
else{ setTimeout(function(){ form.submit(); }, (k*x.o.formSubmitInterval)); } //intervalled submits

                            
iframe.onload = iframe.onreadystatechange = function(){ //Iframe onload event
                                
try{
                                    var 
rIndex = this.getAttribute(x.o.attrRowIndex),
                                        
iframeDoc = this.contentDocument || (this.contentWindow && this.contentWindow.document);

                                    if(
iframeDoc && iframeDoc.readyState === 'complete'){
                                        
//Below for FF, it doesn't seem to submit multiple forms correctly, this operation needs to be enforced
                                        
if(iframeDoc.location.href === 'about:blank'){
                                            var 
form = x.o.Get(this.id.replace(x.o.prfxIFrm, x.o.prfxFrm));
                                            if(
form){ form.submit(); }
                                        } 
//form is re-submitted
                                        
else {
                                            
x.o.savedRowsNb[cmd]++;
                                            
x.o.Editable.RemoveModifiedCellMark(rIndex);
                                            
x.o.Css.Remove(x.o.table.rows[rIndex], x.o.newRowClass);
                                            
SubmitComplete(true);
                                        }
                                    }
                                } catch(
e) {
                                    
x.o.Css.Remove(x.o.table, x.o.activityIndicatorCss);
                                    if(
x.o.onServerActivityStop) x.o.onServerActivityStop.call(null, x.o, x.o.table.rows[rowIndex]);
                                    if(
onSubmitError) onSubmitError.call(null, x.o, e, e.description);
                                    else 
alert(x.o.msgErrOccur +'n'+e.description+'n' + x.o.msgSaveUnsuccess);
                                }
                            }; 
//onload
                        
})(j); //closure
                    
} // for j
                
}
            } 
//else uri
        
}
        function 
SubmitComplete(verbose){
            if(
x.o.savedRowsNb[cmd] === treatedRows.length){
                
treatedRows = []; x.o.savedRowsNb[cmd] = 0;
                if(
cmd == 'insert'){ x.o.Editable.newRows = []; x.o.Editable.addedRows = []; }
                else if(
cmd == 'delete'){
                    if(
uri){
                        var 
selectedRow = x.o.Selection.GetActiveRow(),
                            
selectedCell = x.o.Selection.GetActiveCell(),
                            
curSlcRowIndex = null, curSlcCellIndex = null, slcRowIndex = null;
                        if(
selectedRow){
                            
curSlcRowIndex = selectedRow.rowIndex;
                            if(
curSlcRowIndex-1 >= x.o.startRow) {
                                
slcRowIndex = curSlcRowIndex-1;
                            } else {
                                
slcRowIndex = x.o.startRow;
                            }
                        }
                        if(
selectedCell){
                            
curSlcCellIndex = selectedCell.cellIndex;
                        }
                        
x.o.Selection.ClearSelections();
                        var 
a = [];
                        for (var 
k = 0; k < x.o.Editable.deletedRows.length; k++){
                            
a.push(x.o.Editable.deletedRows[k][0]);
                        }
                        
a.sort(x.o.Sort.NumDesc); //rows indexes need to be sorted in desc manner for rows deleting operation
                        
for (var k = 0; k < a.length; k++){
                            
x.o.table.deleteRow(a[k]);
                        }
                        if(
slcRowIndex !== null){
                            
x.o.Selection.SelectRowByIndex(slcRowIndex);
                        }
                        if(
curSlcCellIndex !== null){
                            if(
selectedRow){
                                var 
cell = selectedRow.cells[curSlcCellIndex];
                                
x.o.Selection.SelectCell(cell);
                            }
                        }
                       }
                    
x.o.Editable.deletedRows = [];
                    
x.o.Editable.SetCommandEditor(x.o.editorCmdColIndex);
                }
                else 
x.o.Editable.modifiedRows = [];
                if(
x.o.savedRowsNb.update === 0 && x.o.savedRowsNb.insert === 0 && x.o.savedRowsNb['delete'] === 0){
                    if(
afterSubmitCallBack){ afterSubmitCallBack.call(null, x.o, treatedRows); }
                    else { 
// message displayed if autosave false
                        
if(verbose && !x.o.autoSave && (!x.o.Editable.submitAll ||
                            (
x.o.Editable.submitAll && cmd === 'update'))){
                                
alert(x.o.msgSubmitOK);
                        }
                    }
                    
x.o.Editable.submitAll = false;
                }
                if(
x.o.onServerActivityStop) x.o.onServerActivityStop.call(null, x.o, x.o.table.rows[rowIndex]);
                if(
submitMethod === 'form' && !x.o.ajax && x.o.ifrmContainer[cmd]){
                    
x.o.ifrmContainer[cmd].innerHTML = '';
                }
                
x.o.Css.Remove(x.o.table, x.o.activityIndicatorCss);
            }
        }
    },
    
Edit: function(e){
        var 
row, cell;
        if(
e && e.type && e.type.LCase().indexOf('click') !== -1){
            
row = this.o.GetRow(e);
            
cell = this.o.GetCell(e);
        } else {
            if(!
this.o.selection) return;
            if(!
this.o.Selection.activeRow && !this.o.Selection.activeCell) return;
            
row = this.o.Selection.activeRow;
            
cell = this.o.Selection.activeCell;
        }
        if(!
row || row.rowIndex < this.o.startRow) return;

        if(
this.o.editorModel === 'cell' && cell){
            var 
cellIndex = cell.cellIndex;
            if(!
this.activeCellEditor && this.o.editors[cellIndex]){
                if(
this.o.IsEditorType(cellIndex, this.o.edtTypes.bool)){
                    
this.SetCheckBoxValue(e, cell);
                } else { 
this.OpenCellEditor(cell); }
            }
        }
        if(
this.o.editorModel === 'row' && !this.IsRowEditorOpen()){ this.OpenRowEditor(row); }
    },
    
Event: {
        
OnInputFocus: function(e){
            var 
elm = this.o.Event.GetElement(e);
            
elm.select();
        },
        
OnBlur: function(e){
            var 
elm = this.o.Event.GetElement(e);
            var 
colIndex = elm.getAttribute(this.o.attrColIndex);
            if(
colIndex === null){
                var 
cell = this.o.GetElement(e, 'td');
                
colIndex = cell.cellIndex;
            }
            if(
this.o.editorModel == 'cell'){ this.CloseCellEditor(colIndex); }
        }
    }
};

Selection.prototype = {
    
onClickAdded: false, activeRow: null, activeCell: null,    selectedRows: [],
    
Init: function(){
        if(!
this.o.selection) return;
        
this.SetEvents();
        if(
this.o.selectRowAtStart){
            
this.SelectRowByIndex(this.o.rowIndexAtStart);
            if(
this.activeRow) this.SelectCell(this.activeRow.cells[0]);
        }
        if(
this.o.onSelectionInit) this.o.onSelectionInit.call(null, this.o);
    },
    
Set: function(){
        
this.o.selection = true;
        
this.o.keyNav = true;
        
this.SetEvents();
    },
    
Remove: function(){
        
this.o.selection = false;
        
this.o.keyNav = false;
        
this.RemoveEvents();
    },
    
SetEvents: function(){
        if(!
this.onClickAdded){
            var 
x = this;
            
this.o.Event.Bind(this.o.table, 'click', function(e){ x.OnClick.call(x, e); });
            if(
this.o.onValidateRow || this.o.onValidateCell) this.o.Event.Bind(this.o.table, 'dblclick', function(e){ x.OnDblClick.call(x, e); });
            
this.onClickAdded = true;
        }
        if(
this.o.keyNav){ this.o.Event.Bind(this.o.StandardBody(), 'keydown', function(e){ x.OnKeyDown.call(x, e); }); }
    },
    
RemoveEvents: function(){
        if(
this.onClickAdded){
            var 
x = this;
            
this.o.Event.Unbind(this.o.table, 'click', function(e){ x.OnClick.call(x, e); });
            if(
this.o.onValidateRow || this.o.onValidateCell) this.o.Event.Unbind(this.o.table, 'dblclick', function(e){ x.OnDblClick.call(x, e); });
            
this.o.Event.Unbind(this.o.StandardBody(), 'keydown', function(e){ x.OnKeyDown.call(x, e); });
            
this.onClickAdded = false;
        }
    },
    
GetActiveRow: function(){ return this.activeRow; },
    
GetActiveCell: function(){ return this.activeCell; },
    
GetSelectedRows: function(){ return this.selectedRows; },
    
GetSelectedValues: function(){
        var 
values = [];
        for(var 
i=0; i<this.GetSelectedRows().length; i++){
            var 
row = this.GetSelectedRows()[i];
            var 
r = this.GetRowValues(row);
            
values.push(r);
        }
        return 
values;
    },
    
GetActiveRowValues: function(){
        if(!
this.GetActiveRow()) return [];
        return 
this.GetRowValues(this.GetActiveRow());
    },
    
GetRowValues: function(row){
        if(!
row) return [];
        var 
values = [];
        for(var 
i=0; i<row.cells.length; i++){
            var 
cell = row.cells[i];
            
values.push(this.o.GetText(cell));
        }
        return 
values;
    },
    
SelectRowByIndex: function(rowIndex){
        if(
rowIndex === undefined || isNaN(rowIndex)){ rowIndex = 0; }
        var 
row = this.o.table.rows[rowIndex];
        if(!
row){ return; }
        
this.SelectRow(row);
    },
    
SelectRowsByIndexes: function(rowIndexes){
        if(!
this.o.IsArray(rowIndexes)){ return; }
        for(var 
i=0; i<rowIndexes.length; i++){
            
this.SelectRowByIndex(rowIndexes[i]);
        }
    },
    
SelectRow: function(row, e){
        if(
this.o.defaultSelection == 'cell' || row.rowIndex<0){ return; }
        if(
this.o.onBeforeSelectedRow){ this.o.onBeforeSelectedRow.call(null, this.o, row, e); }
        
this.o.Css.Remove(this.activeRow, this.o.activeRowCss);
        if(
this.o.selectionModel === 'multiple'){
            
this.o.Css.Add(row, this.o.selectedRowCss);
            if(
this.selectedRows.indexOf(row) == -1) this.selectedRows.push(row);
        }
        
this.o.Css.Add(row, this.o.activeRowCss);
        
this.activeRow = row;
        
et_activeGrid = this.o.id;
        if(
this.o.onAfterSelectedRow) this.o.onAfterSelectedRow.call(null, this.o, row, e);
    },
    
DeselectRow: function(row, e){
        if(
this.o.defaultSelection == 'cell') return;
        if(!
this.IsRowSelected(row)) return;
        if(
this.o.onBeforeDeselectedRow) this.o.onBeforeDeselectedRow.call(null, this.o, row, e);
        
this.o.Css.Remove(row, this.o.activeRowCss);
        
this.o.Css.Remove(row, this.o.selectedRowCss);
        if(
this.o.selectionModel == 'multiple'){
            for(var 
i=0; i<this.GetSelectedRows().length; i++){
                var 
r = this.selectedRows[i];
                if(
row == r){
                    
this.selectedRows.splice(i, 1);
                    break;
                }
            }
        }
        
this.o.Css.Remove(this.activeRow, this.o.activeRowCss);
        
this.activeRow = null;
        if(
this.o.onAfterDeselectedRow) this.o.onAfterDeselectedRow.call(null, this.o, row, e);
    },
    
SelectCell: function(cell, e){
        if(
this.o.defaultSelection == 'row') return;
        if(
this.o.onBeforeSelectedCell) this.o.onBeforeSelectedCell.call(null, this.o, cell, e);
        
this.o.Css.Add(cell, this.o.activeCellCss);
        
this.activeCell = cell;
        try{ if(
this.o.defaultSelection == 'cell' && cell.parentNode && cell.parentNode.nodeName.LCase()=='tr')
                
this.activeRow = cell.parentNode; } catch(ex){} //ugly try-catch solution for old IE
        
et_activeGrid = this.o.id;
        if(
this.o.onAfterSelectedCell) this.o.onAfterSelectedCell.call(null, this.o, cell, e);
    },
    
DeselectCell: function(cell, e){
        if(
this.o.defaultSelection == 'row') return;
        if(
this.o.onBeforeDeselectedCell) this.o.onBeforeDeselectedCell.call(null, this.o, cell, e);
        if(
this.IsCellSelected(cell)){
            
this.o.Css.Remove(cell, this.o.activeCellCss);
            
this.activeCell = null;
            if(
this.o.defaultSelection == 'cell') this.activeRow = null;
        }
        if(
this.o.onAfterDeselectedCell) this.o.onAfterDeselectedCell.call(null, this.o, cell, e);
    },
    
ClearSelections: function(){
        var 
selRow = this.activeRow;
        var 
selCell = this.activeCell;
        if(
selCell) this.DeselectCell(selCell);
        if(
selRow) this.DeselectRow(selRow);
        for(var 
i=0; i<this.GetSelectedRows().length; i++){
            var 
row = this.selectedRows[i];
            if(
this.o.onBeforeDeselectedRow) this.o.onBeforeDeselectedRow.call(null, this.o, row);
            
this.o.Css.Remove(row, this.o.selectedRowCss);
            
this.o.Css.Remove(row, this.o.activeRowCss);
            if(
this.o.onAfterDeselectedRow) this.o.onAfterDeselectedRow.call(null, this.o, row);
        }
        
this.selectedRows = [];
    },
    
IsRowSelected: function(row){
        if(
this.o.selectionModel == 'single'){
            return 
row == this.activeRow;
        } else {
            for(var 
i=0; i<this.GetSelectedRows().length; i++){
                var 
r = this.selectedRows[i];
                if(
r == row) return true;
            }
            return 
false;
        }
    },
    
IsCellSelected: function(cell){
        return 
cell == this.activeCell;
    },
    
OnDblClick: function(e){
        var 
row = this.o.GetRow(e);
        var 
cell = this.o.GetCell(e);
        if(!
row || row.rowIndex < this.o.startRow) return;
        if(!
this.o.editable){
            if(
this.o.onValidateRow && this.o.defaultSelection != 'cell') this.o.onValidateRow.call(null, this.o, this.activeRow);
            if(
this.o.onValidateCell && this.o.defaultSelection != 'row') this.o.onValidateCell.call(null, this.o, this.activeCell);
        }
    },
    
OnClick: function(e){
        var 
row = this.o.GetRow(e);
        var 
cell = this.o.GetCell(e);
        if(!
row || row.rowIndex < this.o.startRow) return;
        if(
this.o.autoSave && this.o.autoSaveModel === 'row'){
            if(
this.activeRow && this.activeRow.rowIndex !== row.rowIndex){
                
this.o.Editable.AutoSubmit();
            }
        }
        if(
this.o.selectionModel == 'single'){
            
this.ClearSelections();
            
this.SelectRow(row);
            
this.SelectCell(cell);
        } else {
            if(!
this.o.keySelection){
                
//Selection keys are disabled, multiple selection is performed with clicks
                
if(this.selectedRows.length > 0){
                    if(!
this.IsRowSelected(row)) this.SelectRow(row);
                    else 
this.DeselectRow(row);
                } else 
this.SelectRow(row);
            }
            else if(
this.o.keySelection && (!e.ctrlKey && !e.shiftKey)){
                
this.ClearSelections();
                
this.SelectRow(row);
            }
            else if(
this.o.keySelection && e.ctrlKey && this.selectedRows.length > 0)
                
this.SelectRow(row);
            else if(
this.o.keySelection && e.shiftKey && this.selectedRows.length > 0){
                if(!
this.activeRow) return;
                var 
prevRowIndex = this.activeRow.rowIndex;
                
this.SelectRow(row);
                var 
curRowIndex = this.activeRow.rowIndex;
                if(
prevRowIndex < curRowIndex){
                    for(var 
i=prevRowIndex+1; i<curRowIndex; i++){
                        var 
r = this.o.table.rows[i];
                        if(
r){
                            if(!
this.IsRowSelected(r)) this.SelectRow(r);
                            else 
this.DeselectRow(r);
                        }
                    }
                    if(!
this.IsRowSelected(this.o.table.rows[prevRowIndex+1]))
                        
this.DeselectRow(this.o.table.rows[prevRowIndex]);
                } else {
                    for(var 
i=prevRowIndex-1; i>curRowIndex; i--){
                        var 
r = this.o.table.rows[i];
                        if(
r){
                            if(!
this.IsRowSelected(r)) this.SelectRow(r);
                            else 
this.DeselectRow(r);
                        }
                    }
                    if(!
this.IsRowSelected(this.o.table.rows[prevRowIndex-1]))
                        
this.DeselectRow(this.o.table.rows[prevRowIndex]);
                }
                
this.SelectRow(row);
            }
            else{ 
this.SelectRow(row); }
            
this.DeselectCell(this.activeCell);
            if(
this.IsRowSelected(row)){ this.SelectCell(cell); }
        }

        if(
this.o.editable){
            if(
this.o.editorModel=='cell'){
                var 
activeCellEditor = this.o.Editable.activeCellEditor;
                if(!
activeCellEditor && cell && this.o.editors[cell.cellIndex]){
                    
//Boolean is set if cell clicked even if editor action is dblclick
                    
if(this.o.IsEditorType(cell.cellIndex, this.o.edtTypes.bool) && this.o.openEditorAction==='dblclick'){
                        
this.o.Editable.SetCheckBoxValue(e, cell);
                    }
                }
                
//Custom or uploader editor is closed if cell selection changes
                
if(activeCellEditor && (this.o.IsEditorType(activeCellEditor.cellIndex, this.o.edtTypes.custom) ||
                    
this.o.IsEditorType(activeCellEditor.cellIndex, this.o.edtTypes.uploader))
                    && (
cell && cell.cellIndex!=activeCellEditor.cellIndex || row.rowIndex!=activeCellEditor.parentNode.rowIndex)){
                    
this.o.Editable.CloseCellEditor(activeCellEditor.cellIndex);
                }
            }
            if(
this.o.editorModel=='row'){
                if(
row != this.o.Editable.activeRow) this.o.Editable.CloseRowEditor();
            }
        }
    },
    
OnKeyDown: function(e){
        if(!
this.activeRow) return;
        var 
t = this.o.GetTableFromElement(this.activeRow);
        if(!
t || t.nodeName.LCase() != 'table' || t['id'] != et_activeGrid) return;
        var 
keyCode = this.o.Event.GetKey(e);
        var 
maxRowIndex = (this.o.table.rows.length - 1);
        var 
nbCells = (this.o.GetCellsNb() - 1);
        var 
curRowIndex = this.activeRow.rowIndex;
        var 
rowIndex, cellIndex;

        var 
x = this;
        var 
navigate = function(cmd){
            if(!
x.activeRow){
                
rowIndex = x.o.startRow; cellIndex = 0;
            } else {
                var 
curCellIndex = (x.activeCell ? x.activeCell.cellIndex : 0);
                if(
x.o.selectionModel == 'single' || (x.o.selectionModel == 'multiple' && !e.shiftKey) || !x.o.keySelection) x.ClearSelections();
                else if(
x.o.selectionModel == 'multiple' && e.shiftKey) x.DeselectCell(x.activeCell, e);
                
cellIndex = curCellIndex;
                if(
cmd === 'down') rowIndex = (curRowIndex < maxRowIndex) ? curRowIndex + 1 : maxRowIndex;
                else if(
cmd === 'up') rowIndex = (curRowIndex == x.o.startRow) ? x.o.startRow : curRowIndex - 1;
                else if(
cmd === 'pgdown') rowIndex = (curRowIndex+x.o.nbRowsPerPage < maxRowIndex) ? curRowIndex + x.o.nbRowsPerPage : maxRowIndex;
                else if(
cmd === 'pgup') rowIndex = (curRowIndex-x.o.nbRowsPerPage <= x.o.startRow) ? x.o.startRow : curRowIndex - x.o.nbRowsPerPage;
                else if(
cmd === 'home') rowIndex = x.o.startRow;
                else if(
cmd === 'end') rowIndex = maxRowIndex;
                else if(
cmd === 'right'){
                    if(
x.o.defaultSelection != 'row'){
                        
rowIndex = curRowIndex;
                        
cellIndex = (curCellIndex+1 > nbCells) ? 0 : curCellIndex+1;
                        if((
curCellIndex+1) > nbCells) rowIndex = (curRowIndex < maxRowIndex) ? curRowIndex + 1 : maxRowIndex;
                    } else 
rowIndex = (curRowIndex < maxRowIndex) ? curRowIndex + 1 : maxRowIndex;
                }
                else if(
cmd === 'left'){
                    if(
x.o.defaultSelection != 'row'){
                        
rowIndex = curRowIndex;
                        
cellIndex = (curCellIndex-1 < 0) ? nbCells : curCellIndex-1;
                        if(
curCellIndex-1 < 0) rowIndex = (curRowIndex == x.o.startRow) ? x.o.startRow : curRowIndex - 1;
                    } else 
rowIndex = (curRowIndex == x.o.startRow) ? x.o.startRow : curRowIndex - 1;
                }
            }
            var 
row = x.o.table.rows[rowIndex];
            if(
x.o.keySelection && e.shiftKey && x.selectedRows.length > 0 && (cmd=='pgdown' || cmd=='pgup' || cmd=='home' || cmd=='end')){
                if(!
x.activeRow) return;
                if(
curRowIndex < rowIndex){
                    for(var 
i=curRowIndex+1; i<rowIndex; i++){
                        var 
r = x.o.table.rows[i];
                        if(
r){
                            if(!
x.IsRowSelected(r)) x.SelectRow(r, e);
                            else 
x.DeselectRow(r, e);
                        }
                    }
                    if(!
x.IsRowSelected(x.o.table.rows[curRowIndex+1]))
                        
x.DeselectRow(x.o.table.rows[curRowIndex], e);
                } else {
                    for(var 
i=curRowIndex-1; i>rowIndex; i--){
                        var 
r = x.o.table.rows[i];
                        if(
r){
                            if(!
x.IsRowSelected(r)) x.SelectRow(r, e);
                            else 
x.DeselectRow(r, e);
                        }
                    }
                    if(!
x.IsRowSelected(x.o.table.rows[curRowIndex-1]))
                        
x.DeselectRow(x.o.table.rows[curRowIndex], e);
                }
                
x.SelectRow(row, e);
            } else {
                if(
x.o.keySelection && e.shiftKey && x.IsRowSelected(row)) x.DeselectRow(x.o.table.rows[curRowIndex], e);
                
x.SelectRow(row, e);
            }

            if(
x.o.defaultSelection != 'row'){
                var 
cell = row.cells[cellIndex];
                
x.SelectCell(cell, e);
                if(
x.o.scrollIntoView) cell.scrollIntoView(false);
            }
            if(
x.o.scrollIntoView && x.o.defaultSelection=='row') row.scrollIntoView(false);
            if(
x.o.autoSave && x.o.autoSaveModel === 'row'){
                if(
rowIndex !== curRowIndex){ x.o.Editable.AutoSubmit(); }
            }
            
x.o.Event.Cancel(e);
        };

        var 
ed = this.o.Editable;
        function 
closeEditor(){
            if(
x.o.editable && ed.activeCellEditor && x.o.editorModel=='cell')
                
ed.CloseCellEditor(ed.activeCellEditor.cellIndex);

            if(
x.o.editable && ed.activeRow && x.o.editorModel=='row')
                if(
ed.activeRow != x.activeRow) ed.CloseRowEditor();
        }

        switch(
keyCode){
            case 
40: //arrow down
                
if(!x.o.editable || (x.o.editable && !ed.activeCellEditor && !ed.activeRow)) navigate('down');
            break;
            case 
38: //arrow up
                
if(!x.o.editable || (x.o.editable && !ed.activeCellEditor && !ed.activeRow)) navigate('up');
            break;
            case 
37: //arrow left
                
if(!x.o.editable || (x.o.editable && !ed.activeCellEditor && !ed.activeRow)) navigate('left');
            break;
            case 
39: //arrow right
                
if(!x.o.editable || (x.o.editable && !ed.activeCellEditor) && !ed.activeRow) navigate('right');
            break;
            case 
34: //pagedown
                
navigate('pgdown'); closeEditor();
            break;
            case 
33: //pageup
                
navigate('pgup'); closeEditor();
            break;
            case 
36: //home
                
navigate('home'); closeEditor();
            break;
            case 
35: //end
                
navigate('end'); closeEditor();
            break;
            case 
9: //tab
                
if(e.shiftKey) navigate('left');
                else 
navigate('right');
                if(
x.o.editorModel == 'row'){
                    if(
x.activeCell && x.selectionModel!='row') ed.SetEditorFocus(x.activeCell.cellIndex);
                    if(
x.activeRow && ed.activeRow && x.activeRow.rowIndex != ed.activeRow.rowIndex) closeEditor();
                } else{ 
closeEditor(); }
            break;
            case 
13: //enter
                
if(!x.o.editable){
                    if(!
x.o.onValidateRow && !x.o.onValidateCell) navigate('down');
                    else{
                        if(
x.o.onValidateRow && x.o.defaultSelection != 'cell') x.o.onValidateRow.call(null, x.o, x.activeRow);
                        if(
x.o.onValidateCell && x.o.defaultSelection != 'row') x.o.onValidateCell.call(null, x.o, x.activeCell);
                    }
                } else {
                    if(!
ed.activeCellEditor){ ed.Edit.call(ed, e); x.o.Event.Cancel(e); }
                    else
                        if(
x.o.IsEditorType(ed.activeCellEditor.cellIndex, x.o.edtTypes.input)){ closeEditor(); }
                }
            break;
            case 
113: //F2
            
case 32: //space bar
                
if(x.o.editable && (!ed.activeCellEditor)){ ed.Edit.call(ed, e); x.o.Event.Cancel(e); }
            break;
            case 
45: //insert
                
if(x.o.editable && (!ed.activeCellEditor)){
                    
ed.AddNewRow();
                    
ed.SetCommandEditor(x.o.editorCmdColIndex);
                    
x.o.Event.Cancel(e);
                }
            break;
            case 
46: //delete
                
if(x.o.editable && (!ed.activeCellEditor)){ ed.SubmitDeletedRows(); x.o.Event.Cancel(e); }
            break;
            case 
27: //escape
                
if(x.o.editable && x.o.editorModel == 'cell'){ if(ed.activeCellEditor) closeEditor(); }
                if(
x.o.editable && x.o.editorModel == 'row') closeEditor();
            break;
            default: 
//key stroke opens editor if edition is enabled
                
if((x.o.editable && x.o.editableOnKeystroke &&
                    
x.o.editorModel == 'cell' && x.o.selectionModel == 'single' &&
                    !
ed.activeCellEditor && !ed.activeRow)){
                    
ed.Edit.call(ed, e);
                    
x.o.Event.Cancel(e);
                }
            break;
        }
        if(
x.o.editable && x.o.openEditorAction=='click'){ ed.Edit.call(ed, e); }
    }
};
//Selection

EditTable.prototype = {
    
Init: function(){
        
this.Css.Add(this.table, this.tableCss+' '+this.unselectableCss);
        
this.Selection.Init();
        
this.Editable.Init();
    },
    
GetCellsNb: function(rowIndex){
        var 
tr = (rowIndex === undefined) ? this.table.rows[this.startRow] : this.table.rows[rowIndex];
        return 
tr.cells.length;
    },
    
GetRowsNb: function(){ return this.table.rows.length; },
    
GetRow: function(e){ return this.GetElement(e, 'tr'); },
    
GetRowByIndex: function(rowIndex){ return this.table.rows[rowIndex]; },
    
GetCell: function(e){ return this.GetElement(e, 'td') || this.GetElement(e, 'th'); },
    
GetTableFromElement: function(elm){
        if(!
elm) return null;
        while(
elm.parentNode){
            if(
elm.nodeName.UCase() === 'TABLE'){ return elm; }
            
elm = elm.parentNode;
        }
        return 
null;
    },
    
GetElement: function(e, tagName){
        var 
elm, target = this.Event.GetElement(e);
        while(
target.parentNode){
            if(
target.nodeName.UCase() === tagName.UCase()
                && 
this.IsParentValid(target)){
                
elm = target; break;
            }
            
target = target.parentNode;
        }
        return 
elm;
    },
    
IsParentValid: function(elm){
        while(
elm.parentNode){
            if(
elm.nodeName.UCase() === 'TABLE'){
                if(
elm.id == this.id) return true;
                else return 
false;
            }
            
elm = elm.parentNode;
        }
        return 
false;
    },
    
IsSelectable: function(){ return this.selection; },
    
IsEditable: function(){ return this.editable; },
    
ClearSelections: function(){ this.Selection.ClearSelections(); },
    
IsEditorType: function(colIndex, type){ return this.editorTypes[colIndex] === type; },
    
IsObj: function(o){ return (o && o.constructor == Object); },
    
IsFn: function(fn){ return (fn && fn.constructor == Function); },
    
IsArray: function(a){ return (a && a.constructor == Array); },
    
Get: function(id){ return document.getElementById(id); },
    
Tag: function(o, tagname){ if(!o){ return null; } else { return o.getElementsByTagName(tagname); } },
    
GetText: function(n){
        if(!
n){ return ''; }
        var 
s = n.textContent || n.innerText || n.innerHTML.replace(/<[^<>]+>/g, '');
        return 
s.replace(/^s+/, '').replace(/s+$/, '').Trim();
    },
    
CreateElm: function(tag){
        if(
tag===undefined || tag===null || tag==='') return;
        var 
el = document.createElement(tag);
        if(
arguments.length>1){
            for(var 
i=0; i<arguments.length; i++){
                var 
argtype = typeof arguments[i];
                if(
argtype.LCase() == 'object' && arguments[i].length == 2){
                    
el.setAttribute(arguments[i][0],arguments[i][1]);
                }
            }
        }
        return 
el;
    },
    
CreateText: function(text){ return document.createTextNode(text); },
    
CreateId: function(prefix){ return (prefix || this.newRowPrefix) + new Date().getTime(); },
    
StandardBody: function(){
        return (
document.compatMode == 'CSS1Compat') ? document.documentElement : document.body;
    },
    
Css: {
        
Has: function(elm,cl){
            if(!
elm){ return false; }
            return 
elm.className.match(new RegExp('(\s|^)'+cl+'(\s|$)'));
        },
        
Add: function(elm,cl){
            if(!
elm){ return; }
            if(!
this.Has(elm,cl)){ elm.className += ' '+cl; }
        },
        
Remove: function(elm,cl){
            if(!
elm){ return; }
            if(!
this.Has(elm,cl)){ return; }
            var 
reg = new RegExp('(\s|^)'+cl+'(\s|$)');
            
elm.className = elm.className.replace(reg,'');
        }
    },
    
Event: {
        
evt: {},
        
Bind: function(el, type, handler){
            if(!(
el in this.evt)){
                
this.evt[el] = {};
            }
            if(!(
type in this.evt[el])){
                
this.evt[el][type] = [];
            }
            
this.evt[el][type].push([handler, true]);
            
this.Add(el, type, handler);
        },
        
Unbind: function(el, type, fn){
            if(
el in this.evt){
                var 
handlers = this.evt[el];
                if(
type in handlers){
                    var 
eventHandlers = handlers[type];
                    for(var 
i = eventHandlers.length; i--;){
                        var 
handler = eventHandlers[i];
                        if(
handler[0].toString()==fn.toString()){
                            
this.Remove(el, type, handler[0]);
                        }
                    }
                }
            }
        },
        
Add: function(el, type, fn, capture){
            if(
el.attachEvent){
                
el.attachEvent('on'+type, fn);
            } else if(
el.addEventListener){
                
el.addEventListener(type, fn, (capture===undefined ? false : capture));
            } else { 
el['on'+type] = fn; }
        },
        
Remove: function(el, type, fn, capture){
            if(
el.detachEvent){
                
el.detachEvent('on'+type, fn);
            } else if(
el.removeEventListener){
                
el.removeEventListener(type, fn, (capture===undefined ? false : capture));
            } else { 
el['on'+type] = null; }
        },
        
Get: function(e){ return e || window.event;    },
        
GetElement: function(e){
            return (
e && e.target) || (event && event.srcElement);
        },
        
GetKey: function(e){
            var 
evt = this.Get(e);
            var 
key = (evt.charCode) ? evt.charCode:
                        ((
evt.keyCode) ? evt.keyCode: ((evt.which) ? evt.which : 0));
            return 
key;
        },
        
Stop: function(e){
            var 
evt = this.Get(e);
            if(
evt.stopPropagation){ evt.stopPropagation(); }
            else{ 
evt.cancelBubble = true; }
        },
        
Cancel: function(e){
            var 
evt = this.Get(e);
            if(
evt.preventDefault){ evt.preventDefault(); }
            else{ 
evt.returnValue = false; }
        }
    },
    
IncludeFile: function(fileId, filePath, callback, type){
        var 
ftype = (type===undefined) ? 'script' : type;
        var 
x = this, isLoaded = false, file;
        var 
head = this.Tag(document,'head')[0];
        if(
ftype.LCase() == 'link'){
            
file = this.CreateElm('link', ['id',fileId],['type','text/css'],['rel','stylesheet'],['href',filePath]);
        } else {
            
file = this.CreateElm('script', ['id',fileId],['type','text/javascript'],['src',filePath]);
        }
        
file.onload = file.onreadystatechange = function(){
        
//Browser <> IE onload event works only for scripts, not for stylesheets
            
if(!isLoaded &&
                (!
this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')){
                
isLoaded = true;
                if(
typeof callback === 'function'){
                    
head.removeChild(file);
                    
callback.call(null, x, this);
                }
            }
        };
        
head.appendChild(file);
    },
    
Sort:{
        
NumAsc: function(a, b){ return (a-b); },
        
NumDesc: function(a, b){ return (b-a); },
        
IgnoreCase: function(a, b){
            var 
x = a.LCase();
            var 
y = b.LCase();
            return ((
x < y) ? -1 : ((x > y) ? 1 : 0));
        }
    }
};

if(
typeof String.prototype.LCase === 'undefined'){
    
String.prototype.LCase = function(){ return this.toLowerCase(); };
}
if(
typeof String.prototype.UCase === 'undefined'){
    
String.prototype.UCase = function(){ return this.toUpperCase(); };
}
if(
typeof String.prototype.Trim === 'undefined'){
    
String.prototype.Trim = function(){//optimised by Anthony Maes
        
return this.replace(/(^[sxA0]*)|([sxA0]*$)/g,'');
    };
}
if(
typeof String.prototype.RegexpEscape === 'undefined'){
    
String.prototype.RegexpEscape = function(){
        var 
s = this;
        function 
escape(e){
            
a = new RegExp('\'+e,'g');
            s = s.replace(a,'
\'+e);
        }
        chars = new Array('
\','[','^','$','.','|','?','*','+','(',')','�');
        for(var e=0; e<chars.length; e++) escape(chars[e]);
        return s;
    };
}

if(typeof Array.prototype.indexOf === '
undefined'){
    Array.prototype.indexOf = function(obj, start){
        for (var i = (start || 0), j = this.length; i < j; i++)
            if(this[i] === obj){ return i; }
        return -1;
    };
}

function setEditTable(id){
/*========================================================================
    - Calls EditTable Constructor
    - Params:
        - id: table id (string)
        - startRow (optional): start row index (number)
        - config (optional): configuration object (literal object)
    - Returns EditTable object
========================================================================*/
    if(arguments.length === 0){ return; }
    var et = new EditTable(arguments[0],arguments[1],arguments[2]);
    et.Init();
    return et;
}
?>
Онлайн: 5
Реклама