// JScript File
var objList = new Object();
var fncList = new Object();

// todo: TableEdit should be shown/hidden and moved/sized to appear over the cell

function text2inputvalue(input, text){
    var tmp = input.value;
    input.value = text;
    var ret = input.value;
    input.value = tmp;
    return ret;
}

function Exec(funct, params)
{
    if (funct && funct != "")
        try {
            if (typeof (funct) == "string") {
                if (funct.indexOf("(") == -1) 
                {
                    if (fncList[funct] == null) fncList[funct] = eval(funct);
                    return (fncList[funct])(params);
                }
                else return eval(funct);
            }
            else if (typeof (funct) == "function")
                return (funct)(params);
        } catch (err) { OnErr({msg: err.message, val: funct}); }
    return null;
}

function ExecEx(obj, prp, params)
{
    if (!obj) return false;

    if (obj.getAttribute && obj.getAttribute(prp))
        return Exec(obj.getAttribute(prp), params);
    else
        return Exec(obj[prp], params);
}


function disableCtrl(ctrl, disable)
{
    if (ctrl == null)
        return;
        
    var type = fieldType(ctrl); 
    if (type == fType.DDEx) return DDExDisable(ctrl.id, disable);       
    
    if(Browser() == "ie")
    {
        if (ctrl.disabled == null) return;
    	ctrl.disabled = disable;
    }
    else if(Browser() == "ff")
    {
        if(ctrl.setAttribute == null) return;
        if (!disable)
            ctrl.removeAttribute("disabled");
        else
            ctrl.setAttribute("disabled", "disabled");
    }


	if (disable)
		ctrl.tabInd = ctrl.tabIndex;
	else
		ctrl.tabIndex = ctrl.tabInd;

    for(var i=0; ctrl.childNodes && i<ctrl.childNodes.length; i++)
        if(ctrl && !eq(ctrl.tagName, "select"))
            disableCtrl(ctrl.childNodes[i], disable);
}

var fType = { None: 0, Text: 1, Select: 2, Check: 3, Textarea: 4, Datetime: 5, Money: 6, DDEx: 7, tinyMCE: 8, Html: 9, Multsel: 10, Custom: 11, Delete: 12, Auto: 13, Upload: 14, Img: 15 };

function fieldType(input)
{
    if(typeof(input.getAttribute) == "undefined")return fType.None;
    
    var tag = input.tagName;
    var typ = input.getAttribute("type");
    var edt = input.getAttribute("cnEditType");

    if(eq(edt,"TINYMCE"))  return fType.tinyMCE;
    if(eq(tag,"TEXTAREA")) return fType.Textarea;
    if(eq(tag,"SELECT"))   return fType.Select;
    if (eq(tag, "IMG")) return fType.Img;
    if(eq(edt,"DDEX"))     return fType.DDEx;
    if(eq(edt,"BOOL"))     return fType.Check;
    if(eq(edt,"AUTO"))     return fType.Auto;
    if(eq(edt,"HTML"))     return fType.Html;
    if(!eq(tag,"INPUT"))   return fType.None;
    if(eq(edt,"MONEY"))    return fType.Money;
    if(eq(edt,"DATETIME")) return fType.Datetime;
    if(!typ || eq(typ,"TEXT")) return fType.Text;
    if (eq(typ, "CHECKBOX")) return fType.Check;
    if (eq(edt, "UPLOAD")) return fType.Upload;
	return fType.None;
}

function fldType(name)
{
    if(name == null) return fType.None;
    switch (name.toUpperCase())
    {
        case "BOOL":     return fType.Check;
        case "DATETIME": return fType.Datetime;
        case "TEXT":     return fType.Text;
        case "READONLY": return fType.None;
        case "TEXTAREA": return fType.Textarea;
        case "MONEY":    return fType.Money;
        case "HTML":     return fType.Html;
        case "DELETE":   return fType.Delete;
        case "CUSTOM":   return fType.Custom;
        case "MULTSEL":  return fType.Multsel;
        case "PULLDOWN": return fType.Select;
        case "DDEX":     return fType.DDEx;
        case "AUTO":     return fType.Auto;
        case "UPLOAD":   return fType.Upload;
    }
    return fType.None;
}


function Quote(str) {
    return ("" + str).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;");;
}

function UnQuote(str) {
    return ("" + str).replace("&amp;", "&").replace("&gt;", ">").replace("&lt;", "<"); ;
}

function ClearInput(input)
{
    if(input == null) return;
    var type = fieldType(input);
    switch (type)
    {
        case fType.Check :
    	    input.checked = false;
    	    break;
        case fType.Select :
    	    input.value = input.getAttribute("cnDefVal") ? input.getAttribute("cnDefVal") : "";
    	    break;
        case fType.DDEx :
            DDExClear(input.id); // SRE only
            break;
        case fType.tinyMCE:
            if(typeof(tinyMCE) != "undefined" && tinyMCE.get(input.id)) tinyMCE.get(input.id).setContent("");
            break;
        case fType.Auto:
            AutoCompleteClear(input.id);
            break;
        case fType.Upload:
            input.innerHTML = "";
            break;
        default:
            try { input.value = input.getAttribute("cnDefVal") ? input.getAttribute("cnDefVal") : ""; input.innerHTML = input.getAttribute("cnDefVal") ? input.getAttribute("cnDefVal") : ""; } 
    	    catch (ex) { } 
    }
}


//----------------------------------------------------- Transport staff ----------------------------------------------------

var Progress;
var webServiceCalls = 0;
var TryToSendBatch = null;
var theBatch = new Array();
var resFunct = new Array();

function CustServerCall(cmdCode, objName, args, onRes)
{
    if(onRes) resFunct.push(onRes);
    ServerCall("Custom", new Object(), {cmdCode: cmdCode, objName: objName, args: args, onRes: resFunct.length - 1});
}

//=== DON'T CALL THE FOLLOWING FUNCTION FROM OUTSIDE OF PAT!!! ===================
function ServerCall(cmdCode, obj, args)
{
    if(!eq(cmdCode,"Custom") && ExecEx(obj, "onServerCall", {obj: obj, cmdCode: cmdCode, args: args}) == false || obj.sleep == true) return;
    if(obj && obj.Progress) obj.Progress.Show(true);
	theBatch.push({cmdCode: cmdCode, objName: obj.name, args: args});
	window.clearTimeout(TryToSendBatch);
	TryToSendBatch = window.setTimeout(SendBatch, 1);
}
//================================================================================

function SendBatch()
{
    if (theBatch.length == 0) return;
	//Tables.BatchCallWebService(theBatch, OnSendBatchRes, OnServiceError); // Old Ajax Transport
    // ------------- JQuery Transport -------------
    ExecuteService(Project.servicePath, JSON2.stringify({ batch: theBatch }), OnSendBatchRes, OnServiceError);
	theBatch.length = 0;
    if (Progress && ++webServiceCalls == 1) Progress.Show(true);
}

// ------------- JQuery Transport -------------
function ExecuteService(url, params, callbackSuccess, callbackError) {
    $.ajax({
        type: "POST",
        url: url,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        data: params,
        beforeSend: function(p) { p.setRequestHeader("Content-type", "application/json; charset=utf-8"); },
        success: callbackSuccess,
        error: callbackError
    });
}

function OnSendBatchRes(res)
{
	if (res != null)
	{
        if (typeof (res.d) != "undefined") res = res.d; // .Net 3.5 serialization feature
	    for (var i = 0; i < res.length; i++)
	        if (eq(res[i].cmdCode,"Custom"))
	            try { 
                    resFunct[res[i]["args"].onRes](res[i]); 
                } 
                catch (err) { 
                    OnErr({msg: err.message, val: res[i].objName}); 
                }
	        else {
	            var obj = objList[res[i]["objName"]];
	            if (ExecEx(obj, "onServerRes", AddHash(res[i], "obj", obj)) != false) {
	                try { obj.OnServiceRes(res[i]); } catch (err) { OnErr({msg: err.message}); }
	                ExecEx(obj, "onDataLoaded", AddHash(res[i], "obj", obj)); // perform after objects process results
	                if(obj && obj.Progress) obj.Progress.Show(false);
	            }
	        }

		if(Progress && --webServiceCalls == 0 && theBatch.length == 0) Progress.Show(false);
	}
}

function OnServiceError(err)
{
    if (err != null)
    {
        var e = JSON2.parse(err.responseText)
        if(e != null) OnErr({msg: e.Message}); // + "<br>" + e.StackTrace);
    }
    if(Progress && --webServiceCalls == 0) Progress.Show(false);
}


//----------------------------------------------------- Error handling ---------------------------------------------------------------------

var OnErrorCustom = null;
function OnError(errorText)
{
    var errTxt = $get("ErrorText");
    var errWnd = typeof(placeHolderID) != "undefined" && placeHolderID != null ? $get(placeHolderID) : null;
    if(OnErrorCustom ? Exec(OnErrorCustom, errorText) : true)
        { if (errWnd && errTxt) {errTxt.innerHTML = errorText; errWnd.show();} else alert(errorText); }
    return false;
}

var OnErrCustom = null;
function OnErr(p)
{
    p.stack = CallStack();
    if(OnErrCustom ? Exec(OnErrCustom, p) : true)
        OnError((p.obj && p.obj.name ? p.obj.name + " : " : "" ) + p.msg + (p.val != null && p.val != "" ? " : " + p.val : ""));
    return false;
}


if (window.onerror) {
    window.onerror = function(msg, file, line) {
        return OnErr({ msg: msg, file: file, line: line });
    }
}
//----------------------------------------------------- Notifications ---------------------------------------------------------------------
function DataChangeNotify(List, params)
{
    var names  = (List != null && List != "" ? List.replace(/\s/g, "").split(",") : new Array());
    for(var i=0; i<names.length; i++)
        if(objList[names[i]]) objList[names[i]].OnDataChangeNotification(params);
}

function RowChangeNotify(List, params)
{
    var names  = (List != null && List != "" ? List.replace(/\s/g, "").split(",") : new Array());
    for(var i=0; i<names.length; i++)
    {
        var parts = names[i].split("/");
        var paramsCurr = CloneHash(params);
        if(parts.length > 1) paramsCurr = AddHash(paramsCurr, "rowidname", parts[1]);
        if(objList[parts[0]]) objList[parts[0]].OnRowChangeNotification(paramsCurr);
    }
}

//---------------------------------------------- PAT Object stuff ----------------------------------------------

var PatObjType = { Table:0, SRE:1, DD:2, DDEx:3, List:4, ACmplt:5, Tabs:6, PopUp:7, Upload: 7, Gallery: 8 }

function PatObj_Sleep(name, sleep)
{
    if(objList[name]) objList[name].sleep = sleep;
}

function PatObj_WakeUp(name, args)
{
    if(objList[name] && objList[name].sleep)
    {
        objList[name].sleep = false;
        if (objList[name].Refresh) objList[name].Refresh(args);
    }
}

function PatObj_isInited(name, type)
{
    return (objList[name] != null && (type == null || eq(objList[name].type, type)))
}

function PatObj_Init(type, name, args, prms)
{
    if(args == null) args = new Object();
    if(prms == null) prms = new Object();
    //if(objList[name]) - TO DO
    objList[name] = new Object();
    objList[name].type = type;
    
    PatObj_LoadGlobalPrms(name, prms);
    
    switch(type)
    {
        case PatObjType.Table:  TableEditor(name, args, prms);  break;
        case PatObjType.SRE:    RowEditor(name, args, prms);    break;
        case PatObjType.DD:     DropDown(name, args, prms);     break;
        case PatObjType.DDEx:   DropDownEx(name, args, prms);   break;
        case PatObjType.List:   ListsEditor(name, args, prms);  break;
        case PatObjType.ACmplt: AutoComplete(name, args, prms); break;
        case PatObjType.Tabs:   TabsObj(name, args, prms);      break;
        case PatObjType.PopUp:  PopUpObj(name, args, prms);     break;
        case PatObjType.Gallery: PatGallery(name, args, prms);  break;
    }
    
    return objList[name];
}

function PatObj_LoadGlobalPrms(name, prms) // We can't just copy prms to pat obj, becouse same attributes requared additional operations
{
    var obj = objList[name];
    if(obj == null) return;
    
    if(prms.Prgr == true) obj.Progress = new PATProgress(name + "_progress", AddHash(prms, "obj", obj));
    if(prms.sleep != null)obj.sleep = prms.sleep;
    obj.ReadOnly = (prms.ReadOnly == null ? false : prms.ReadOnly);
    if(prms.multSel != null)obj.multSel = prms.multSel;
    if(prms.onSelRow != null) obj.onSelRow = prms.onSelRow;

    if(prms.onServerCall != null)obj.onServerCall = prms.onServerCall;
    if(prms.onServerRes  != null)obj.onServerRes  = prms.onServerRes;
    if(prms.onDataLoaded != null)obj.onDataLoaded = prms.onDataLoaded;

	if(prms.onUpdateClickRes != null) obj.onUpdateClickRes = prms.onUpdateClickRes;

    if (prms.DataChangeNotifyList != null) obj.DataChangeNotifyList = prms.DataChangeNotifyList;
    if (prms.RowChangeNotifyList != null) obj.RowChangeNotifyList = prms.RowChangeNotifyList;
}

function PatObj_Refresh(name, args)
{
    if(objList[name] && objList[name].Refresh) objList[name].Refresh(args);
}

function PatObj_InitOrRefresh(type, name, args, prms)
{
    if(objList[name] == null)
        PatObj_Init(type, name, args, prms);
    else 
        if(objList[name].Refresh) objList[name].Refresh(args, prms == undefined ? null : prms.rowId);
    //if(objList[name].type != type) - TO DO
}

function PatObj_SetArgs(name, args, prms)
{
    if (prms) PatObj_LoadGlobalPrms(name, prms);
    if(objList[name] && args) objList[name].args = AddHash(objList[name].args, args);
}
function PatObj_GetArgs(name)
{   
    return objList[name].args;
}
//------------------------------------------------ Table Editor ------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function TableInit(name, args, prms)
{
    return PatObj_Init(PatObjType.Table, name, args, prms);
}

function TableRefresh(name, args)
{
    if(objList[name]) objList[name].Refresh(args);
}

function TableRefreshRow(name, rowId)
{
    if(objList[name]) objList[name].RefreshRow(rowId);
}

function TableSetCurRow(name, rowId)
{
    if(objList[name]) objList[name].SetCurRow(rowId);
}

function TableSort(name, sNames)
{
    if(objList[name]) objList[name].JsSort(sNames);
}

function TableHideRows(name, hide, exeptCur, exeptSel)
{
    if(objList[name]) objList[name].HideRows(hide, exeptCur, exeptSel);
}

function TableGetRowsCnt(name)
{
    return objList[name].rowsTotal != null ? objList[name].rowsTotal : 0;
}

function TableSetCurPage(name, page)
{
    if(objList[name] && objList[name].SetCurPage(page)) {
        var sleep = objList[name].sleep; 
        objList[name].sleep = false;
        objList[name].LoadData();
        objList[name].sleep = sleep;
    }
}

function TableSelectRow(name, rowId, sel)
{
    if(objList[name]) objList[name].SelectRow(rowId, sel);
}

function TableSelectAllRows(name, sel)
{
    if(objList[name]) objList[name].SelectAllRows(sel);
}

function TableClearAllSelection(name)
{
    if(objList[name])objList[name].ClearAllSelection();
}

function TableSetRowsPerPage(name, rowsnum)
{
    if(objList[name] && objList[name].SetPageSize(rowsnum)) {
        var sleep = objList[name].sleep;
        objList[name].sleep = false;
        objList[name].LoadData();
        objList[name].sleep = sleep;
    }
}

function TableDelRow(name, row, confirm) 
{
    if(objList[name]) objList[name].DelRow(row, confirm);
}

function TableMakeInsert(name)
{
    if(objList[name]) objList[name].InsertRow();
}

function TableMakeInsertEF(name)
{
    if(objList[name]) objList[name].InsertRowEF();
}

function TableClearEF(name)
{
    if(objList[name]) objList[name].ClearEF();
}

function TableShowEF(name, show)
{
    if(objList[name]) objList[name].ShowEF(show);
}

function TableGetCurRowID(name)
{
    if(objList[name]) return objList[name].GetCurrentRowID();
}

function TableGetSelRows(name)
{
    if(objList[name]) return objList[name].GetSelRows();
}

//DO NOT USE. Should be replaced with PatObj_GetArgs
function TableGetArgs(name)
{
    if(objList[name]) return objList[name].args;
}

function TableHideShowCol(name, index, show)
{
    if (objList[name]) return objList[name].HideShowCol(index, show);
}

function TableGetCurRowValueByColumn(tableName, columnName) //ask MI
{
    if(objList[tableName]) return objList[tableName].GetCurRowValueByColumn(columnName);
}
function TableGetDataValue(tableName, columnName, rowID)
{
    if(objList[tableName]) return objList[tableName].GetDataValue(columnName, rowID);
}
function TableSelectTopmost(tableName)
{
    if(objList[tableName]) return objList[tableName].SelectTopmost();
}
function TableGoRowDown(tableName)
{
    if(objList[tableName]) return objList[tableName].GoRowDown();
}
function TableGoRowUp(tableName)
{
    if(objList[tableName]) return objList[tableName].GoRowUp();
}
function TableGoPageDown(tableName)
{
    if(objList[tableName]) return objList[tableName].GoPageDown();
}
function TableGoPageUp(tableName)
{
    if(objList[tableName]) return objList[tableName].GoPageUp();
}
function TableLock(name, lock, lockIns)
{
    if(objList[name]) objList[name].LockTable(lock, lockIns);
}
function TableBodyLockState(name)
{
    if(objList[name]) return objList[name].LockedBody;
}
function TableInsLockState(name)
{
    if(objList[name]) return objList[name].LockedInsR;
}
function TableEndEdit(name)
{
    if(objList[name] && objList[name].EndEdit) return objList[name].EndEdit(objList[name].theEditor, null, null);
}
//should be removed
function DisableTable(name, disable, disableIns)
{
    if(objList[name]) objList[name].LockTable(disable, disableIns);
}

function TableEditor(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
    self.args = args;

    self.colDefs = new Array();

    self.TableRows = new Array();

    self.curRow = null;
    self.pagesCount = 1;
    
    self.selRowsId = new Object();

    self.topDiv = $get(self.name);
    self.topPager = $get(self.name + "TopPager");
    self.botPager = $get(self.name + "BottomPager");
    self.rowsPP = $get(self.name + "RowsPP");
    self.tabPattern = $get(self.name + "Table");
    self.headRow = $get(self.name + "Header");
    self.gotoPage = $get(self.name + "GoToPage");
    self.rowsCnt = $get(self.name + "RowsCnt");

    if(self.topDiv == null) return;
    if(self.headRow) self.headRow.className = (self.topDiv.getAttribute("TableHeadCss") ? self.topDiv.getAttribute("TableHeadCss") : "def_tablehead_css");
    self.tabPattern.style.display = "none";

    if(self.topDiv.getAttribute("onServerCall")) self.onServerCall = self.topDiv.getAttribute("onServerCall");
    if(self.topDiv.getAttribute("onServerRes")) self.onServerRes = self.topDiv.getAttribute("onServerRes");
    if(self.topDiv.getAttribute("onDataLoaded")) self.onDataLoaded = self.topDiv.getAttribute("onDataLoaded");
    
    self.SpellCheckInsRow = prms.SpellCheckInsRow == false ? false : true;

    self.tableHolders = new Array();
    for(var i = 1; ; i++)
    {
        var th = $get(self.name + "_TableHolder" + i);
        if(th == null) break;
        self.tableHolders.push(th);
    }
    if(self.tableHolders.length == 0) {
        var tc = document.createElement("div");
        self.tableHolders.push(tc);
        tc.id = self.name + "_TableHolder1";
        tc.style.margin = "0px"; // the developer is not aware that this div will be created so it should have no visual impact.
        tc.style.border = "0px";
        tc.style.padding = "0px";
        self.topDiv.insertBefore(tc, self.tabPattern);
    }
    self.insRowHolder = $get(self.name + "_InsRowHolder");
    
    self.GetSelRows = function ()
    {
        return self.selRowsId;
    }
    
    self.isSelected = function (row)
    {
        for (var id in self.selRowsId)
            if(row.dbid == id)
                return true;
        return false;
    }
    
    self.SetCurPage = function (page)
        {
            if(page == self.currentPage) return false;
            if(page && (!ValidateInt(page) || page < 1 || objList[name].pagesCount < page)) return OnErr({obj: self, msg: "Invalid page number"});
            self.currentPage = page;
            return true;
        }

    self.SetPageSize = function (size)
        {
            if(size == self.PageSize) return false;
            if (size && (!ValidateInt(size) || size < 1)) return OnErr({obj: self, msg: "Invalid page size", val: size});
            self.currentPage = 1;
            self.PageSize = size;
            self.RowsCnt = (self.PageSize == null ? null : self.PageSize * self.tableHolders.length); 
            return true;
        }
    self.SetPageSize(prms.PageSize ? prms.PageSize : self.topDiv.getAttribute("PageSize"));

    if (self.topDiv.getAttribute("DataChangeNotifyList")) self.DataChangeNotifyList = self.topDiv.getAttribute("DataChangeNotifyList");
    if (self.topDiv.getAttribute("RowChangeNotifyList")) self.RowChangeNotifyList = self.topDiv.getAttribute("RowChangeNotifyList");
    
    self.delConfirm = self.topDiv.getAttribute("delConfirm")?self.topDiv.getAttribute("delConfirm"):"";
    
    self.onSelRow = self.onSelRow != null ? self.onSelRow : self.topDiv.getAttribute("onSelRow");
    self.onRowSelected = self.topDiv.getAttribute("onRowSelected");
    self.onSelRowsChanged = self.topDiv.getAttribute("onSelRowsChanged");
    self.onNewRow = self.topDiv.getAttribute("onNewRow");

    self.pagerFunction = self.topDiv.getAttribute("PagerFunction");
    self.pagerText = self.topDiv.getAttribute("pagerText");

    self.MaxRows = self.topDiv.getAttribute("MaxRows");
    self.RefreshRowOnChange = eq(self.topDiv.getAttribute("RefreshRowOnChange"), "True");

    self.DbRowId = (self.topDiv.getAttribute("DbRowId") ? self.topDiv.getAttribute("DbRowId") : "id");

    self.OneClickEdit = eq(self.topDiv.getAttribute("OneClickEdit"), "True");

    self.ReadOnly = (self.topDiv.getAttribute("ReadOnly") ? eq(self.topDiv.getAttribute("ReadOnly"),"True") : self.ReadOnly);
    self.DeleteMode = !eq(self.topDiv.getAttribute("DeleteMode"), "False");
    self.LockedBody = false;
    self.LockedInsR = false;

    self.MultiColumnsSorting = eq(self.topDiv.getAttribute("MultiColumnsSorting"), "True");

//    self.tabPattern.className = "def_table_css ";
//    if (self.topDiv.getAttribute("TableCss"))
//        self.tabPattern.className += self.topDiv.getAttribute("TableCss");
    self.tabPattern.className = (self.topDiv.getAttribute("TableCss") ? self.topDiv.getAttribute("TableCss") : "def_table_css");

    self.RowCss = (self.topDiv.getAttribute("RowCss") ? self.topDiv.getAttribute("RowCss") : "def_row_css");
    self.SelRowCss = (self.topDiv.getAttribute("SelRowCss") ? self.topDiv.getAttribute("SelRowCss") : "def_selrow_css");
    self.MouseOverRowCss = (self.topDiv.getAttribute("MouseOverRowCss") ? self.topDiv.getAttribute("MouseOverRowCss") : "def_mouseoverrow_css"); 
    self.RowTip = self.topDiv.getAttribute("RowTip");
    self.RowBackColor = self.topDiv.getAttribute("RowBackColor");
    self.OddRowBackColor = (self.topDiv.getAttribute("OddRowBackColor") ? self.topDiv.getAttribute("OddRowBackColor") : "");

    self.CustomRowTip = self.topDiv.getAttribute("CustomRowTip");

    self.tipdiv = document.createElement("div");
    self.tipdiv.className = (self.topDiv.getAttribute("CustomRowTipCss") ? self.topDiv.getAttribute("CustomRowTipCss") : "def_tip_css");
    self.topDiv.appendChild(self.tipdiv);
    self.tipdiv.style.display = "none";
    
    self.onRowMouseOver = self.topDiv.getAttribute("onRowMouseOver");
    self.onRowMouseOut = self.topDiv.getAttribute("onRowMouseOut");

    self.PagerCss = (self.topDiv.getAttribute("PagerCss") ? self.topDiv.getAttribute("PagerCss") : "def_pager_css");
    self.SelPagerCss = (self.topDiv.getAttribute("SelPagerCss") ? self.topDiv.getAttribute("SelPagerCss") : "def_selpager_css");
    self.PagerSepCss = (self.topDiv.getAttribute("PagerSepCss") ? self.topDiv.getAttribute("PagerSepCss") : "def_pagersep_css");
    self.TopPagerCss = (self.topDiv.getAttribute("TopPagerCss") ? self.topDiv.getAttribute("TopPagerCss") : "def_toppager_css");
    self.BotPagerCss = (self.topDiv.getAttribute("BotPagerCss") ? self.topDiv.getAttribute("BotPagerCss") : "def_botpager_css");

    self.TextAreaCss = (self.topDiv.getAttribute("TextAreaCss") ? self.topDiv.getAttribute("TextAreaCss") : "def_textarea_css");

    self.ReadonlyBy  = self.topDiv.getAttribute("ReadonlyBy");
    self.ReadonlyCss = (self.topDiv.getAttribute("ReadonlyCss") ? self.topDiv.getAttribute("ReadonlyCss") : "def_readonly_cell_css");

    self.multSel = !eq(self.topDiv.getAttribute("multSel"), "False");

    self.InsertRowOnTab = eq(self.topDiv.getAttribute("InsertRowOnTab"), "True");

    self.pdCached = eq(self.topDiv.getAttribute("pdCached"), "True");
    
    self.HtmBlocks = eq(self.topDiv.getAttribute("HtmBlocks"), "True");        
    
    self.SortNames = (self.topDiv.getAttribute("SortNames") ? self.topDiv.getAttribute("SortNames") : "");
    var t = document.createElement("TABLE");
    self.rowPattern = (typeof(t.insertRow) == "undefined" ? insertRow(t) : t.insertRow(-1));
    self.rowPattern.className = self.RowCss;

    for(var k = 0; k < self.headRow.cells.length; k++)
    {
        var r = self.headRow;
        self.rowPattern.insertCell(-1).setAttribute("columnIndex", k);
        r.cells[k].setAttribute("columnIndex", k);
        self.colDefs[k] = new Object();
        self.colDefs[k].cnIndex        = k;
        self.colDefs[k].cnTitle        = r.cells[k].innerHTML;
        self.colDefs[k].cnTip          = r.cells[k].getAttribute("cnTip");
        self.colDefs[k].cnCstmTip      = r.cells[k].getAttribute("cnCstmTip");
        self.colDefs[k].cnWordWrap     = eq(r.cells[k].getAttribute("cnWordWrap"),"True");
        self.colDefs[k].cnSorting      = eq(r.cells[k].getAttribute("cnSorting"),"True");
        self.colDefs[k].cnReverseSorting = eq(r.cells[k].getAttribute("cnReverseSorting"),"True");
        self.colDefs[k].cnTitleCss     = r.cells[k].getAttribute("cnTitleCss");
        self.colDefs[k].cnEditType     = fldType(r.cells[k].getAttribute("cnEditType"));
        self.colDefs[k].cnCellCss      = r.cells[k].getAttribute("cnCellCss");
        self.colDefs[k].cnDefVal       = r.cells[k].getAttribute("cnDefVal");
        self.colDefs[k].cnDBName       = r.cells[k].getAttribute("cnDBName");
        self.colDefs[k].cnMaxLen       = r.cells[k].getAttribute("cnMaxLen");
        self.colDefs[k].cnMaxLength    = r.cells[k].getAttribute("cnMaxLength"); // Max input len
        self.colDefs[k].cnValidate     = r.cells[k].getAttribute("cnValidate");
        self.colDefs[k].cnFormat       = r.cells[k].getAttribute("cnFormat");
        self.colDefs[k].cnFormatHTML   = r.cells[k].getAttribute("cnFormatHTML");        
        self.colDefs[k].cnImpCss       = r.cells[k].getAttribute("cnImpCss");
        self.colDefs[k].cnRelTableName = r.cells[k].getAttribute("cnRelTableName");
        self.colDefs[k].cnCached       = eq(r.cells[k].getAttribute("cnCached"),"True");
        self.colDefs[k].cnDepend       = r.cells[k].getAttribute("cnDepend");
        self.colDefs[k].cnRelBackColor = r.cells[k].getAttribute("cnRelBackColor");
        self.colDefs[k].cnRelShowInPD  = r.cells[k].getAttribute("cnRelShowInPD");
        self.colDefs[k].cnPdNullValue  = (r.cells[k].getAttribute("cnPdNullValue") != null ? r.cells[k].getAttribute("cnPdNullValue") : "-1");
        self.colDefs[k].cnBackColor    = r.cells[k].getAttribute("cnBackColor");
        self.colDefs[k].cnCellTemplFnc = r.cells[k].getAttribute("cnCellTemplateFunction");
        self.colDefs[k].cnCellTemplIH  = r.cells[k].innerHTML;
        self.colDefs[k].cnReadonly     = eq(r.cells[k].getAttribute("cnReadonly"),"True");
        self.colDefs[k].cnButtons      = eq(r.cells[k].getAttribute("cnButtons"),"True");
        self.colDefs[k].cnReadonlyBy   = r.cells[k].getAttribute("cnReadonlyBy");
        self.colDefs[k].cnRelNamesStr  = r.cells[k].getAttribute("cnRelNames");
        self.colDefs[k].cnRelValuesStr = r.cells[k].getAttribute("cnRelValues");
        self.colDefs[k].cnRelColorsStr = r.cells[k].getAttribute("cnRelColors");
        self.colDefs[k].cnRelSplitter  = (r.cells[k].getAttribute("cnRelSplitter") ? r.cells[k].getAttribute("cnRelSplitter") : ",");
        self.colDefs[k].cnCustomValue  = r.cells[k].getAttribute("cnCustomValue");
        self.colDefs[k].cnInEditForm   = !eq(r.cells[k].getAttribute("InEditForm"), "False");
        self.colDefs[k].cnPopUpCptn    = r.cells[k].getAttribute("cnPopUpCptn");
        self.colDefs[k].RefreshTableOnChange = eq(r.cells[k].getAttribute("RefreshTableOnChange"), "True");
        self.colDefs[k].RefreshFieldOnChange = r.cells[k].getAttribute("RefreshFieldOnChange");
        self.colDefs[k].cnMakeLinks = eq(r.cells[k].getAttribute("cnMakeLinks"), "True");
        self.colDefs[k].cnMakeLinksAttrs = r.cells[k].getAttribute("cnMakeLinksAttrs") ? jQuery.parseJSON(r.cells[k].getAttribute("cnMakeLinksAttrs")) : {};
        self.colDefs[k].cnFileUploadExtensions = r.cells[k].getAttribute("cnFileUploadExtensions") ? r.cells[k].getAttribute("cnFileUploadExtensions").split(',') : [];
        self.colDefs[k].cnFileUploadPath = r.cells[k].getAttribute("cnFileUploadPath") ? r.cells[k].getAttribute("cnFileUploadPath") : "";
        self.colDefs[k].cnFileThumbFolder = r.cells[k].getAttribute("cnFileThumbFolder") ? r.cells[k].getAttribute("cnFileThumbFolder") : "";
        //------------------------------- special Auto Complete attributes -----------------------------------//
        // standart attributes will be used by Auto Complete: cnRelTableName, cnRelNames, cnRelColors, cnRelBackColor
        self.colDefs[k].cnMaxOpts = r.cells[k].getAttribute("cnMaxOpts");
        self.colDefs[k].cnColumn  = r.cells[k].getAttribute("cnColumn");
        self.colDefs[k].cnVColumn = r.cells[k].getAttribute("cnVColumn");
        self.colDefs[k].cnFilter  = r.cells[k].getAttribute("cnFilter");
        self.colDefs[k].divW      = r.cells[k].getAttribute("divW");
        self.colDefs[k].divH      = r.cells[k].getAttribute("divH");
        self.colDefs[k].showTime      = r.cells[k].getAttribute("cnShowTime");
        //----------------------------------------------------------------------------------------------------//
        self.colDefs[k].cnRelNames    = null;
        self.colDefs[k].cnRelValues   = null;
        self.colDefs[k].cnRelColors   = null;
        self.colDefs[k].cnShowInPD    = null;
        self.colDefs[k].cnSpellCheck  = eq(r.cells[k].getAttribute("spellcheck"), "True");
        self.colDefs[k].cnNoGrammarCheck = eq(r.cells[k].getAttribute("nogrammarcheck"), "True");
        if (self.colDefs[k].cnEditType == fType.Delete && self.colDefs[k].cnCellCss == null) 
        {
            self.colDefs[k].cnCellCss = "def_delcolumn_css";
            r.cells[k].className = self.colDefs[k].cnCellCss;
        }
        if(self.colDefs[k].cnTitleCss) r.cells[k].className = self.colDefs[k].cnTitleCss;
        self.colDefs[k].cnSortDr = "";
        self.colDefs[k].cnValues = new Object();
        self.colDefs[k].cnKeys = new Object();
        self.colDefs[k].cnFilterBy = r.cells[k].getAttribute("cnFilterBy");
    }

    self.PopUpInsert = eq(self.topDiv.getAttribute("PopUpInsert"), "True");
    self.PopUpInsertBtn = !eq(self.topDiv.getAttribute("PopUpInsertBtn"), "False");
    
    if(self.PopUpInsert)
    {
        self.popupdiv = document.createElement("div");
        self.topDiv.appendChild(self.popupdiv);
        self.PopUpInsertCaption = (self.topDiv.getAttribute("PopUpInsertCaption") ? self.topDiv.getAttribute("PopUpInsertCaption") : "Create new entry");
        var PopUpInsertCaptionStr = " Caption3d='" + self.PopUpInsertCaption + "'";
        var PopUpInsertCss = " style='" + (self.topDiv.getAttribute("PopUpInsertStyle") ? self.topDiv.getAttribute("PopUpInsertStyle") : "") + "'";
        var PopUpInsertClass = " class='" + (self.topDiv.getAttribute("PopUpInsertCss") ? self.topDiv.getAttribute("PopUpInsertClass") : "") + "'";
        
        self.popupdiv.innerHTML = "<div id='" + self.name + "_Inner_PopUp' Style3d='True' OnHide=\"TableClearEF('" + self.name + "');\" " + PopUpInsertCaptionStr + PopUpInsertCss + PopUpInsertClass + ">" +
            "<table style='width:100%; height:100%;'><tr><td id='" + self.name + "_EditFormHolder'></td></tr>" +
                "<tr><td align='center' style='padding-top:20px;'>" +
                    "<input type='button' value='Create' onclick=\"TableMakeInsertEF('" + self.name + "');\"  />" +
                    "<input type='button' value='Cancel' onclick=\"PopUpShow('" + self.name + "_Inner', false);\"  />" +
                "</td></tr></table></div>";

        PopUpInit(self.name + "_Inner");
    }

    
    self.RefreshBtn = eq(self.topDiv.getAttribute("RefreshBtn"), "True");
    self.RefreshImg = (self.topDiv.getAttribute("RefreshImg") ? self.topDiv.getAttribute("RefreshImg") : patImg_Refresh);
    self.RefreshCaption = (self.topDiv.getAttribute("RefreshCaption") ? self.topDiv.getAttribute("RefreshCaption") : "Refresh");
    
    self.HideRowsBtn = eq(self.topDiv.getAttribute("HideRowsBtn"), "True");
    self.HideRowsImg = (self.topDiv.getAttribute("HideRowsImg") ? self.topDiv.getAttribute("HideRowsImg") : patImg_HideRows);
    self.HideRowsCpt = (self.topDiv.getAttribute("HideRowsCpt") ? self.topDiv.getAttribute("HideRowsCpt") : "Hide rows");
    self.ShowRowsImg = (self.topDiv.getAttribute("ShowRowsImg") ? self.topDiv.getAttribute("ShowRowsImg") : patImg_ShowRows);
    self.ShowRowsCpt = (self.topDiv.getAttribute("ShowRowsCpt") ? self.topDiv.getAttribute("ShowRowsCpt") : "Show rows");
    
    self.HideRowsOnSelect = eq(self.topDiv.getAttribute("HideRowsOnSelect"), "True");
    
    self.InsertImg = (self.topDiv.getAttribute("InsertImg") ? self.topDiv.getAttribute("InsertImg") : patImg_Add);
    self.insRow = null;
    self.insRowDef = null;

    if(!eq(self.topDiv.getAttribute("InsertRow"), "False") && !self.PopUpInsert || eq(self.topDiv.getAttribute("InsertRow"), "True"))
    {
        self.insRow = document.createElement("tr");
        self.insRow.dbid = "-1";
        self.insRow.className = self.RowCss;
        for (var j = 0; j < self.colDefs.length; j++) 
        {
            var cell = document.createElement("td");
            self.insRow.appendChild(cell);
            cell.setAttribute("columnIndex", j);
            if(self.colDefs[j].cnEditType == fType.Delete)
                cell.innerHTML = "<img style='border:0' title='Insert Row' alt='Insert Row' src='" + self.InsertImg + "' onclick=\"if(this.getAttribute('disabled') != 'disabled'){ TableMakeInsert('" + self.name + "'); event.cancelBubble = true;}\" />";
        }
    }

    self.EditFormInput = function(coldef) {
        var theEditor;
        switch (coldef.cnEditType) {
            case fType.Text:
                theEditor = document.createElement("INPUT");
                theEditor.type = "text";
                if (coldef.cnMaxLength) theEditor.setAttribute("maxlength", coldef.cnMaxLength);

                if (coldef.cnRelTableName) {
                    theEditor.setAttribute("autocomplete", "off");

                    theEditor.setAttribute("cnRelTableName", coldef.cnRelTableName);
                    if (coldef.cnRelColors) theEditor.setAttribute("cnRelColors", coldef.cnRelColors);
                    if (coldef.cnRelNames) theEditor.setAttribute("cnRelNames", coldef.cnRelNames);
                    if (coldef.cnRelBackColor) theEditor.setAttribute("cnRelBackColor", coldef.cnRelBackColor);
                    if (coldef.cnMaxOpts) theEditor.setAttribute("cnMaxOpts", coldef.cnMaxOpts);
                    if (coldef.cnColumn) theEditor.setAttribute("cnColumn", coldef.cnColumn);
                    if (coldef.cnVColumn) theEditor.setAttribute("cnVColumn", coldef.cnVColumn);
                    if (coldef.cnFilter) theEditor.setAttribute("cnFilter", coldef.cnFilter);
                    if (coldef.divW) theEditor.setAttribute("divW", coldef.divW);
                    if (coldef.divH) theEditor.setAttribute("divH", coldef.divH);

                    theEditor.setAttribute("onHide", "TableEndEdit('" + self.name + "')");

                    window.setTimeout(function() { if (theEditor) AutoCompleteInit(theEditor.id); }, 5);
                }

                break;
            case fType.Money:
                theEditor = document.createElement("INPUT");
                theEditor.type = "text";
                theEditor.onkeydown = function(evt) { return ImpEventMoneyFormatCheck(this, (evt ? evt : window.event)); };
                break;
            case fType.Textarea:
            case fType.Html:
                theEditor = document.createElement("textarea");
                theEditor.className = (self.topDiv.getAttribute("EditformTaCss") ? self.topDiv.getAttribute("EditformTaCss") : "def_editform_ta_css"); ;
                break;
            case fType.Datetime:
                theEditor = document.createElement("INPUT");
                theEditor.type = "text";
                jCalendar({base:theEditor, showdate: true, showtime: coldef.showTime});
                break;
            case fType.Select:
                theEditor = document.createElement("SELECT");

                break;
            case fType.Check:
                theEditor = document.createElement("INPUT");
                theEditor.type = "checkbox";
                break;
            case fType.Upload:
                theEditor = document.createElement("DIV");
                break;
        }

        theEditor.id = self.name + "_editform_" + coldef.cnIndex;
        return theEditor;
    }
    
    self.EditFormHolder = $get(self.name + "_EditFormHolder");
    if(self.EditFormHolder)
    {
        self.EditForm = document.createElement("div");
        for (var j = 0; j < self.colDefs.length; j++) 
        {
            var coldef = self.colDefs[j];
            if(coldef.cnEditType == fType.Delete || coldef.cnEditType == fType.Multsel || coldef.cnEditType == fType.Custom || coldef.cnEditType == fType.None || !coldef.cnInEditForm || coldef.cnReadonly) continue;

            var theEditor = self.EditFormInput(coldef);
            coldef.cnEditFormInput = theEditor;

            var elDiv = document.createElement("div");
            elDiv.className = (self.topDiv.getAttribute("EditformElCss") ? self.topDiv.getAttribute("EditformElCss") : "def_editform_el_css");
            if(coldef.cnEditType == fType.Textarea) elDiv.className = (self.topDiv.getAttribute("EditformElTaCss") ? self.topDiv.getAttribute("EditformElTaCss") : "def_editform_elta_css");
            
            self.EditForm.appendChild(elDiv);

            var nameDiv = document.createElement("div");
            nameDiv.className = (self.topDiv.getAttribute("EditformNameCss") ? self.topDiv.getAttribute("EditformNameCss") : "def_editform_name_css");
            nameDiv.innerHTML = (coldef.cnPopUpCptn != null && coldef.cnPopUpCptn != "") ? coldef.cnPopUpCptn : coldef.cnTitle;
            elDiv.appendChild(nameDiv);
            
            var valDiv = document.createElement("div");
            valDiv.className = (self.topDiv.getAttribute("EditformValCss") ? self.topDiv.getAttribute("EditformValCss") : "def_editform_val_css");
            valDiv.appendChild(theEditor);
            elDiv.appendChild(valDiv);
        }
        self.EditFormHolder.appendChild(self.EditForm);
    }

    self.ClearEF = function ()
    {
        for (var j = 0; j < self.colDefs.length; j++) 
            if(self.colDefs[j].cnEditFormInput) 
                ClearInput(self.colDefs[j].cnEditFormInput);
    }

    self.ShowEF = function (show)
    {
        show = (show == null ? true : show);
        PopUpShow(self.name + "_Inner", show);
    }
    
    self.DeleteImg = (self.topDiv.getAttribute("DeleteImg") ? self.topDiv.getAttribute("DeleteImg") : patImg_Cancel);
    self.DeleteGrayImg = (self.topDiv.getAttribute("DeleteGrayImg") ? self.topDiv.getAttribute("DeleteGrayImg") : patImg_CancelD);

    self.GetCurrentRowID = function ()
        {
            var id = null;
            if (self.curRow != null)
                id = self.curRow.dbid;
            return id;
        }
        
    self.GetCurrentRowNum = function ()
        {
            if (self.curRow)
				for (var i = 0; i < self.TableRows.length; i++)
					if (self.TableRows[i] == self.curRow)
						return i;
			return null;
        }
        
    self.SelectTopmost = function ()
		{
			self.SetCurRowByNum(0);
		}
    self.GoRowDown = function ()
        {
            var curRowNum = self.GetCurrentRowNum();
			if (curRowNum == null)
				self.SetCurRowByNum(0);
			else {
				if (curRowNum < self.TableRows.length - 1)
					self.SetCurRowByNum(curRowNum + 1);
				else 
					self.GoPageDown();
			}
        }        
    self.GoRowUp = function ()
        {
            var curRowNum = self.GetCurrentRowNum();
			if (curRowNum == null)
				self.SetCurRowByNum(self.TableRows.length - 1);
			else
				if (curRowNum > 0)
					self.SetCurRowByNum(curRowNum - 1);
				else
					self.GoPageUp();
        }        
    self.GoPageDown = function ()
        {			
            if (self.currentPage < self.pagesCount) {
				self.SetCurPage(self.currentPage + 1);
				self.LoadData();
			}
        }
    self.GoPageUp = function ()
        {
            if (self.currentPage > 1) {
				self.SetCurPage(self.currentPage - 1);
				self.LoadData();
			}
        }
    self.HideShowCol = function(index, show) 
    {
        try {
            var col = self.rowPattern.cells[index];
            col.style.display = show ? "" : "none";
            col = self.headRow.cells[index];
            col.style.display = show ? "" : "none";
            for (var i = 0; i < self.tableHolders.length; i++) {
                var tab = self.tableHolders[i].getElementsByTagName("TABLE")[0];
                for (var i = 0; tab.rows && i < tab.rows.length; i++) {
                    col = tab.rows[i].cells[index];
                    col.style.display = show ? "" : "none";
                }
            }
        } catch (e) { OnErr({obj: self, msg: "Can't hide column", val: index}); }
    }

    self.HideRows = function(hide, exeptCur, exeptSel)
    {
        for (var i = 0; i < self.TableRows.length; i++)
            if(!exeptCur || self.TableRows[i] != self.curRow && !exeptSel || !self.isSelected(self.TableRows[i]))
                self.TableRows[i].style.display = hide ? "none" : "";
        if(!exeptCur || self.insRow != self.curRow && exeptSel || self.isSelected(self.insRow))
            if(self.insRow) self.insRow.style.display = hide ? "none" : "";
            
        if(self.hrBtn) self.hrBtn.style.display = hide ? "none" : "";
        if(self.srBtn) self.srBtn.style.display = hide ? "" : "none";
            
        if(self.topPager) self.topPager.style.display = hide ? "none" : "";
        if(self.botPager) self.botPager.style.display = hide ? "none" : "";
        disableCtrl(self.rowsPP, hide);
        disableCtrl(self.gotoPage, hide);
    }

    self.HideRows_ = function()
    {
        self.HideRows(true, true, true);
    }

    self.ShowRows_ = function()
    {
        self.HideRows(false, true, true);
    }


    self.GetDataRow = function (rowID)
        {
            if (self && self.table && self.table.rows)
                for (var index =0; index < self.table.rows.length; index++)
                    if (self.table.rows[index][self.DbRowId] == rowID)
                        return self.table.rows[index];
            return null;
        }

    self.GetDataValue = function (columnName, rowID)
        {
            var row = self.GetDataRow(rowID);
            if(row) return row[columnName];
            return null;
        }
    self.GetCurRowValueByColumn = function (columnName)
        {
            if(self.curRow) 
                return self.GetDataValue(columnName, self.curRow.dbid);
            return null;
        }

    self.OnDataChangeNotification = function (params) 
        { 
            if(params != null && eq(params["act"],"sre_setdata") && params["rowId"] != null)
                self.RefreshRow(params["rowId"])
            else
                self.LoadData();
        }

    self.OnRowChangeNotification = function (params) 
        { 
            if(params != null && params["rowId"] != null && params["rowidname"] != null)
                self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
		    self.currentPage = 1;
			self.LoadData();
        }

    self.OnServiceRes = function (res)
        {
            switch(res["cmdCode"].toUpperCase()) 
            {
                case "GETSINGLEROW": self.OnGetSingleRowRes(res); break;
                case "GETPAGE":      self.OnGetPageRes(res); break;
                case "GETPDTABLE":   self.OnGetPDTableRes(res); break;
                case "DELROW":       self.OnDelRowRes(res); break;
                case "SETDATA":      self.OnSetDataRes(res); break;
                case "INSROW":       self.OnInsRowRes(res); break;
            }
        }

        self.Refresh = function (args) {
            //self.currentPage = 1;
            self.ClearPDCache();
            self.LoadData(args);
        }

    self.Refresh_ = function (obj, evt) 
        { 
            self.Refresh(null);
        }

    self.RefreshRow = function (rowId, fieldDbName) 
        { 
            ServerCall("GetSingleRow", self, {rowId: rowId, field: fieldDbName, args: self.args});
        }

    self.SetCurRow = function (rowId) 
        { 
            if(!rowId) return self.SelectRowByRow(null, null);
            
            var row = self.Row(rowId);
		    if(row) return self.SelectRowByRow(row, null);
            
            self.ClearAllSelection();
            ServerCall("GetPage", self, {rowId: rowId, pagesize: self.RowsCnt, sort: self.SortNames, args: self.args}); 
        }

    self.SetCurRowByNum = function (rowNum) {
		self.SetCurRow(self.TableRows[rowNum].dbid);
	}

    self.OnGetSingleRowRes = function(res) 
        {
            if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});
            var tabData = res["tableRes"];
            var rowId = res["args"]["rowId"];
            var field = res["args"]["field"];

            if (tabData == null || tabData.rows == null || tabData.rows.length == 0) return;

            var newRow = self.DataRow(tabData, rowId);
            var oldRow = self.DataRow(self.table, rowId);
            if(newRow && oldRow)
    	        for (var clmn in newRow)
    		        oldRow[clmn] = newRow[clmn];

            var row = self.Row(rowId);
            for (var j = 0; j < self.colDefs.length; j++) {
                if (field != null && trim(field) != "" && !(new RegExp("^(.+,\\s*)?" + self.colDefs[j].cnDBName + "(,\\s*.+)?$")).test(field)) continue;
                self.setCell(row, self.colDefs[j], tabData.rows[0][self.colDefs[j].cnDBName], tabData.rows[0]);
                if (self.colDefs[j].cnEditType == fType.Select || self.colDefs[j].cnEditType == fType.DDEx) self.SetPullDownCell(row.cells[j], self.colDefs[j]);
            }
        }

    self.DataRow = function (datatable, id)
    {
        if(!datatable || !datatable.rows) return null;
        for (var i = 0; i < datatable.rows.length; i++)
	        if(datatable.rows[i][self.DbRowId] == id)
	            return datatable.rows[i];
        return null;
	}
    
    self.GetColdefByDbName = function (dbName) 
    {
        for (var i=0;i<self.colDefs.length;i++)
        {
            if(self.colDefs[i].cnDBName == dbName)
                return self.colDefs[i];
        }
        return null;
    }
    
    self.JsSort = function (sortStr) 
    {
        var names = sortStr.split(",");
        for (var i=0; i<names.length; i++)
        {
            var prts = trim(names[i]).split(" ");
            if(prts.length > 0)
                self.SetSortDirImg(self.GetColdefByDbName(prts[0]), prts.length == 2 ? prts[1] : "asc");
        }

        self.Sort(sortStr);
    }

    self.SetSortDirImg = function (coldef, sDr) // Dir: "asc" "desc" ""
    {
        if(coldef == null) return;
        coldef.cnSortDr = sDr;
        var img = coldef.headCell.childNodes[0].childNodes[0].childNodes[0].childNodes[1].childNodes[0];
        if(!eq(img.tagName, "img")) return;
        img.src = (sDr == "" ? patImg_UpDn : sDr == "asc" ? patImg_Dn : patImg_Up);
        img.alt = (sDr == "" ? "sort asc" : sDr == "asc" ? "sort desc" : "no sorting");
    }


    self.SortOnHeadClick = function (cell, evt) 
        {
            if(!eq(cell.tagName, "td")) return;
            
            var coldef = self.colDefs[cell.getAttribute("columnIndex")];
                        
            if(!self.MultiColumnsSorting)
            {
                for(var k = 0; k < cell.parentNode.cells.length; k++)
                {
                    if(!self.colDefs[k].cnSorting || cell.getAttribute("columnIndex") == k || cell.parentNode.cells[k].childNodes.length == 0 || cell.parentNode.cells[k].childNodes[0].nodeName == "#text" || self.colDefs[k].cnEditType == fType.Delete || self.colDefs[k].cnButtons) continue;
                    var img = cell.parentNode.cells[k].childNodes[0].childNodes[0].childNodes[0].childNodes[1].childNodes[0];
                    if(eq(img.tagName, "img"))
                    {
                        img.src = patImg_UpDn;
                        img.alt = "no sorting";
                        self.colDefs[k].cnSortDr = "";
                    }
                }
            }

            var sDr = "";
            if (coldef.cnReverseSorting)
            {
                sDr = (coldef.cnSortDr == "" ? "desc" : coldef.cnSortDr == "desc" ? "asc" : "");
            }
            else
            {
                sDr = (coldef.cnSortDr == "" ? "asc" : coldef.cnSortDr == "asc" ? "desc" : "");
            }
            
            coldef.cnSortDr = sDr;
            var img = cell.childNodes[0].childNodes[0].childNodes[0].childNodes[1].childNodes[0];
            if(!eq(img.tagName, "img")) return;
            img.src = (sDr == "" ? patImg_UpDn : sDr == "asc" ? patImg_Dn : patImg_Up);
            img.alt = (sDr == "" ? "sort asc" : sDr == "asc" ? "sort desc" : "no sorting");
            
            
            if(self.MultiColumnsSorting)
            {
                var sNames = (self.SortNames != null ? self.SortNames.split(",") : new Array());
                self.SortNames = "";
                for(var i=0; i<sNames.length; i++) {
                    if(sNames[i].substring(0,coldef.cnDBName.length) != coldef.cnDBName) {
                        var name = Project.type == ProjectTypes.php ? '`' + sNames[i] + '`' : sNames[i];
                        self.SortNames += (self.SortNames == "" ? "" : ",") + name;
                    }
                }
            }
            else
                self.SortNames = "";
            var name = Project.type == ProjectTypes.php ? '`' + coldef.cnDBName + '`' : coldef.cnDBName;
            self.SortNames += (sDr != "" ? (self.SortNames == "" ? "" : ",") + name + " " + sDr : "") ;

            self.Sort(self.SortNames);
        }
        
    self.Sort = function (sNames) 
    {
        self.SortNames = sNames;
        self.LoadData(); 
    }

    self.LoadData = function (args) 
        {
            if(args) AddHash(self.args, args);
            ServerCall("GetPage", self, {page: self.currentPage, pagesize: self.RowsCnt, sort: self.SortNames, args: self.args});
        }

        self.OnGetPageRes = function(res)
        {
            if (res["errCode"] != null) return OnErr({ obj: self, msg: res["errCode"] });
            var tabData = res["tableRes"];
            self.table = tabData;
            self.rowsTotal = res["scalarRes"];
            self.pagesCount = (self.RowsCnt == null || self.RowsCnt == 0 ? 1 : 1 + parseInt((self.rowsTotal - 1) / self.RowsCnt));
            if (self.pagesCount < 1) self.pagesCount = 1;
            if (self.currentPage > self.pagesCount) TableSetCurPage(self.name, self.pagesCount);

            if (self.HtmBlocks)
            {
                if (self.BlocksHolder == null)
                {
                    self.BlocksHolder = document.createElement("div");
                    self.topDiv.insertBefore(self.BlocksHolder, self.tabPattern);
                }
                self.BlocksHolder.innerHTML = "";

                if (tabData == null || tabData.rows == null) return;
                for (var i = 0; i < tabData.rows.length; i++)
                    for (var j = 0; j < self.colDefs.length; j++)
                    self.BlocksHolder.innerHTML += tabData.rows[i][self.colDefs[j].cnDBName];

                return;
            }

            var curRowId, curRowRestored = false;
            if (self.curRow != null)
            {
                curRowId = self.curRow.dbid;
                self.UnselectCurRow(true);
            }
            if (res["args"]["rowId"] != null)
            {
                curRowId = res["args"]["rowId"];
                ExecEx(self, "onSelRow", { obj: self, curRowId: curRowId });
            }

            self.currentPage = (res["args"]["page"] == null ? 1 : res["args"]["page"]);

            var len = (tabData != null && tabData.rows != null ? tabData.rows.length : 0);
            for (var i = 0; i < len; i++)
                if (tabData.rows[i][self.DbRowId] == "-1")
            {
                self.insRowDef = tabData.rows[i];
                //Array.removeAt(tabData.rows, i);
                tabData.rows.splice(i,1);
                len--;
            }

            if (self.MaxRows != null && self.insRow != null)
                self.insRow.style.display = (self.MaxRows <= len ? "none" : "");

            self.SizeTable(len);

            self.hiddenCols = self.args["hidden"] ? self.args["hidden"].split(",") : new Array();

            for (var i = 0; i < self.colDefs.length; i++)
            {
                var cd = self.colDefs[i];

                if (cd.Hidden != true && self.Hidden(cd))
                {
                    cd.Hidden = true;
                    self.HideShowCol(i, false);
                }
                else if (cd.Hidden == true && !self.Hidden(cd))
                {
                    cd.Hidden = false;
                    self.HideShowCol(i, true);
                }


                var val = cd.cnDefVal == null ? "" : cd.cnDefVal;
                if (self.insRowDef != null) val = self.insRowDef[cd.cnDBName] == null ? val : self.insRowDef[cd.cnDBName];
                if (self.insRow) self.setCell(self.insRow, cd, val, self.insRowDef);
                if (cd.cnEditFormInput) cd.cnEditFormInput.value = self.FormatValue(val, cd);
                if (self.insRow && (cd.cnEditType == fType.Select || cd.cnEditType == fType.DDEx)) self.SetPullDownCell(self.insRow.cells[i], cd);
            }

            if (self.insRow)
            {
                AddHandler(self.insRow, "click", self.SelectRowByRow);
                AddHandler(self.insRow, "mouseover", self.rowMouseOver);
                AddHandler(self.insRow, "mouseout", self.rowMouseOut);
            }

            for (var i = 0; i < len; i++)
            {
                var row = self.TableRows[i];
                row.dbid = tabData.rows[i][self.DbRowId];

                AddHandler(row, "click", self.SelectRowByRow);
                AddHandler(row, "mouseover", self.rowMouseOver);
                AddHandler(row, "mouseout", self.rowMouseOut);

                for (var j = 0; j < self.colDefs.length; j++)
                    if (!self.colDefs[j].Hidden)
                        self.setCell(row, self.colDefs[j], tabData.rows[i][self.colDefs[j].cnDBName], tabData.rows[i]);

                if (curRowId && row.dbid == curRowId) { self.SelectCurRow(row); curRowRestored = true; }
                self.SelectRow(row.dbid, self.selRowsId[row.dbid] && self.multSel);
                if (self.OddRowBackColor && i % 2 == 0) row.style.backgroundColor = self.OddRowBackColor;
            }

            if (curRowId != null && curRowId != "")
                if (!curRowRestored)
            {
                ExecEx(self, "onSelRow", { obj: self, curRowId: "-1" });
                RowChangeNotify(self.RowChangeNotifyList, { rowId: "-1", rowidname: self.DbRowId });
                ExecEx(self, "onRowSelected", { obj: self, curRowId: "-1" });
            }
            else if (res["args"]["rowId"] != null) // row was setted
            {
                ExecEx(self, "onSelRow", { obj: self, curRowId: curRowId });
                RowChangeNotify(self.RowChangeNotifyList, { rowId: curRowId, rowidname: self.DbRowId });
                ExecEx(self, "onRowSelected", { obj: self, curRowId: curRowId });
            }

            for (var i = 0; i < self.colDefs.length; i++)
            {
                var cd = self.colDefs[i];
                if (cd.cnEditType != fType.Select && cd.cnEditType != fType.DDEx) continue;
                if ((self.pdCached || cd.cnCached) && cd.cnDataLoaded) { self.SetPDData(cd); continue; }
                if (cd.cnRelTableName != null)
                    ServerCall("GetPDTable", self, { pdName: cd.cnRelTableName, args: self.args, columnIndex: i });
                else
                    self.OnGetPDTableRes({ args: { pdName: null, columnIndex: i} });
            }

            if (self.LockedBody) self.LockTable(self.LockedBody, self.LockedInsR);
            if (res.args.args.SelectFirstInsCell == 1)
            {
                self.selectCell(self.getNextEditCellInRow(self.insRow, 0), self.insRow);
                delete res.args.args.SelectFirstInsCell;
            }
            ExecEx(self, "onRowSelected", { obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "") });
        }

        self.Hidden = function(coldef) 
        {
            for (var i = 0; i < self.hiddenCols.length; i++)
                if (coldef.cnDBName == self.hiddenCols[i])
                    return true;
            return false;
        }
        
        self.OnGetPDTableRes = function(res) {
            if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});
            var columnIndex = res["args"]["columnIndex"];
            var pdName = res["args"]["pdName"];
            var cd = self.colDefs[columnIndex];
            cd.dataRes = res["tableRes"];

            if(cd.cnRelNames) delete cd.cnRelNames;
            if(cd.cnRelValues) delete cd.cnRelValues;
            if(cd.cnRelColors) delete cd.cnRelColors;
            if(cd.cnShowInPD) delete cd.cnShowInPD;
            if(cd.cnDataRow) delete cd.cnDataRow;
            cd.cnRelNames = (cd.cnRelNamesStr != null ? cd.cnRelNamesStr.split(cd.cnRelSplitter) : new Array());
            cd.cnRelValues = (cd.cnRelValuesStr != null ? cd.cnRelValuesStr.split(cd.cnRelSplitter) : new Array());
            cd.cnRelColors = (cd.cnRelColorsStr != null ? cd.cnRelColorsStr.split(cd.cnRelSplitter) : new Array());
            cd.cnShowInPD = new Array();
            cd.cnDataRow = new Array();
            if (cd.dataRes && cd.dataRes.rows && cd.dataRes.rows.length) {
                for (var i = 0; i < cd.dataRes.rows.length; i++) {
                    var len = cd.cnRelNames.length != null ? cd.cnRelNames.length : 0;
                    cd.cnRelNames[len] = cd.dataRes.rows[i][cd.dataRes.columns[1].name];
                    cd.cnRelValues[len] = cd.dataRes.rows[i][cd.dataRes.columns[0].name];
                    cd.cnDataRow[len] = cd.dataRes.rows[i];
                    if (cd.cnRelBackColor != null) cd.cnRelColors[len] = cd.dataRes.rows[i][cd.cnRelBackColor];
                    if (cd.cnRelShowInPD != null) cd.cnShowInPD[len] = cd.dataRes.rows[i][cd.cnRelShowInPD];
                }
            }
            cd.cnDataLoaded = true;
            self.SetPDData(cd);
        }

        self.SetPDData = function(coldef) {
            if (self.insRow) self.SetPullDownCell(self.insRow.cells[coldef.cnIndex], coldef);
            if (coldef.cnEditFormInput) 
            {
                self.SetPullDownOptions(coldef, coldef.cnEditFormInput, null);
                if(self.insRowDef)coldef.cnEditFormInput.value = self.insRowDef[coldef.cnDBName];
            }
            for (var i = 0; i < self.TableRows.length; i++)
                self.SetPullDownCell(self.TableRows[i].cells[coldef.cnIndex], coldef);
        }

        self.SetPullDownOptions = function (coldef, theEditor, row) {
            if (coldef.cnRelNames != null && coldef.cnRelNames.length != null && coldef.cnRelValues != null && coldef.cnRelValues.length == coldef.cnRelNames.length)
                theEditor.innerHTML = "";
            for (var i = 0; i < coldef.cnRelNames.length; i++) {
                if (coldef.cnShowInPD != null && coldef.cnShowInPD.length == coldef.cnRelNames.length && !coldef.cnShowInPD[i] && (row == null || coldef.cnValues[row.dbid] != coldef.cnRelValues[i]))
                    continue;
                if (coldef.cnFilterBy != null && (coldef.cnDataRow[i][coldef.cnFilterBy] != null && coldef.cnDataRow[i][coldef.cnFilterBy] != row.dbfilterval))
                    continue;
                if (row != null && !self.CheckKeys(row.dbid, i, coldef)) 
                    continue;
                var el = new Option(coldef.cnRelNames[i], coldef.cnRelValues[i]);
                theEditor.options.add(el);
                if (coldef.cnRelColors != null && coldef.cnRelColors.length == coldef.cnRelNames.length)
                    el.style.backgroundColor = coldef.cnRelColors[i];
            }
        }

        self.CheckKeys = function(rowId, datarownum, coldef) {
            if(coldef.cnKeys != null && coldef.cnKeys[rowId] != null)
                for (var prop in coldef.cnKeys[rowId])
                    if(coldef.cnDataRow[datarownum][prop] != null && coldef.cnDataRow[datarownum][prop] != coldef.cnKeys[rowId][prop])
                        return false;
            return true;
        }
        
        self.LoadDepends = function(row, coldef)
        {
            if(coldef.cnDepend != null && coldef.cnDepend != "")
                for (var i = 0; i < self.colDefs.length; i++)
                {
                    var cd = self.colDefs[i];
                    if(cd.cnDBName != coldef.cnDepend) continue;
                    if(cd.cnKeys[row.dbid] == null) cd.cnKeys[row.dbid] = new Object();
                    cd.cnKeys[row.dbid][coldef.cnDBName] = coldef.cnValues[row.dbid];
                    self.SetPullDownCell(row.cells[cd.cnIndex], cd);
                }
        }

        self.SetPullDownCell = function(cell, coldef) {
            var val = coldef.cnValues[cell.parentNode.dbid];
            if (coldef.cnRelNames != null && coldef.cnRelNames.length != null && coldef.cnRelValues != null && coldef.cnRelValues.length == coldef.cnRelNames.length)
                for (var i = 0; i < coldef.cnRelNames.length; i++)
                {
                    if (coldef.cnRelValues[i] == val || (coldef.cnRelValues[i] == null && val == "null") || (coldef.cnRelValues[i] == coldef.cnPdNullValue && val == null)) 
                    {
                        if(!self.CheckKeys(cell.parentNode.dbid, i, coldef)) continue;
                        cell.innerHTML = "<div class='def_valuediv_css " + (eq(cell.getAttribute("pat_readonlyObj"),"True") ? self.ReadonlyCss : "") + "' onselectstart='return false;'>" + self.FormatValue(coldef.cnRelNames[i], coldef) + "</div>";
                        if(self.theEditor) self.theEditor = null;
                        if (coldef.cnRelColors != null) {
                            cell.style.backgroundColor = coldef.cnRelColors[i];
                            cell.style.color = self.CalcTxtColor(coldef.cnRelColors[i]);
                        }
                        if(cell.firstChild && cell.firstChild.tagName && eq(cell.firstChild.tagName, "DIV"))
                        {
                            cell.firstChild.style.width = self.headRow.cells[coldef.cnIndex].style.width;
                            cell.firstChild.style.textAlign = self.headRow.cells[coldef.cnIndex].style.textAlign;
                            cell.firstChild.style.verticalAlign = "middle";
                        }
                        return;
                    }
                }
            cell.innerHTML = "<div></div>"
            if(cell.firstChild && cell.firstChild.tagName && eq(cell.firstChild.tagName, "DIV"))
                cell.firstChild.style.width = self.headRow.cells[coldef.cnIndex].style.width;
        }

        self.SizeTable = function(datarows) {
            var row, cell;
            while (self.TableRows.length > datarows)  //TODO
            {
                var r = self.TableRows.pop();
                var p = r.parentNode;
                p.removeChild(r);
                if (p.childNodes.length == (self.insRow == null ? 1 : 2)) p.parentNode.parentNode.removeChild(p.parentNode);
            }

            var insrowRendered = false;
            while (self.TableRows.length < datarows || datarows == 0) //TODO
            {
                var body;
                if ((self.PageSize == null && self.TableRows.length == 0) || self.TableRows.length % self.PageSize == 0) {
                    var page = (self.PageSize == null ? 0 : Math.round((self.TableRows.length - 1) / self.PageSize));
                    var t = self.tabPattern.cloneNode(false);
                    t.id = t.id + "_" + page;
                    t.style.display = "";
                    body = document.createElement("TBODY");
                    t.appendChild(body);
                    var head = self.headRow.cloneNode(true);
                    head.id = head.id + "_" + page;
                    body.appendChild(head); 
                    
                    for (var k = 0; k < head.cells.length; k++)
                    {
                        if(self.colDefs[k].cnEditType == fType.Delete || self.colDefs[k].cnButtons)
                        {
                            var btnstable = document.createElement("table");
                            btnstable.className = "def_sorttable_css";
                            var btnrow = btnstable.insertRow(0);
                            head.cells[k].appendChild(btnstable);
                            if(self.HideRowsBtn)
                            {
                                var btncell = btnrow.insertCell(0);
                                
                                self.hrBtn = document.createElement("img");
                                self.hrBtn.setAttribute("src", self.HideRowsImg);
                                self.hrBtn.setAttribute("alt", self.HideRowsCpt);
                                self.hrBtn.setAttribute("title", self.HideRowsCpt);
                                btncell.appendChild(self.hrBtn);
                                self.hrBtn.style.cursor = "pointer";
                                AddHandler(self.hrBtn, "click", self.HideRows_);

                                self.srBtn = document.createElement("img");
                                self.srBtn.setAttribute("src", self.ShowRowsImg);
                                self.srBtn.setAttribute("alt", self.ShowRowsCpt);
                                self.srBtn.setAttribute("title", self.ShowRowsCpt);
                                self.srBtn.style.display = "none";
                                btncell.appendChild(self.srBtn);
                                self.srBtn.style.cursor = "pointer";
                                AddHandler(self.srBtn, "click", self.ShowRows_);
                            }
                            if(self.RefreshBtn)
                            {
                                var img = document.createElement("img");
                                img.setAttribute("src", self.RefreshImg);
                                img.setAttribute("alt", self.RefreshCaption);
                                img.setAttribute("title", self.RefreshCaption);
                                btnrow.insertCell(0).appendChild(img);
                                img.style.cursor = "pointer";
                                AddHandler(img, "click", self.Refresh_);
                            }
                            if(self.PopUpInsert && self.PopUpInsertBtn)
                            {
                                var img = document.createElement("img");
                                img.setAttribute("src", self.InsertImg);
                                img.setAttribute("alt", self.PopUpInsertCaption);
                                img.setAttribute("title", self.PopUpInsertCaption);
                                btnrow.insertCell(0).appendChild(img);
                                img.style.cursor = "pointer";
                                AddHandler(img, "click", self.ShowEF);
                            }
                        }
                        if (self.colDefs[k].cnSorting) 
                        {
                            AddHandler(head.cells[k], "click", self.SortOnHeadClick);
                            head.cells[k].innerHTML = "<table class='def_sorttable_css'><tr><td>" + head.cells[k].innerHTML + "</td><td><img src='" + patImg_UpDn + "' alt='sort' /></td></tr></table>";
                        }
                        self.colDefs[k].headCell = head.cells[k];
                    }

                    if (self.insRow != null && self.TableRows.length == 0 && !insrowRendered) 
                    {
                        insrowRendered = true;
                        body.appendChild(self.insRow);

                        if(self.insRowHolder)
                        {
                            self.insRowHolder.innerHTML = "";
                            self.insRowHolder.appendChild(t);
                            continue;
                         }
                    }

                    self.tableHolders[page].innerHTML = "";
                    self.tableHolders[page].appendChild(t);
                }
                else
                    body = self.TableRows[self.TableRows.length - 1].parentNode;

                if (datarows == 0) break;
                var row = self.rowPattern.cloneNode(true);
                body.appendChild(row);
                self.TableRows.push(row);
            }

            //  Add pager control
            if (self.topPager || self.botPager) {
                var pagerCode = self.RenderPager(1);
                if (self.topPager && self.TopPagerCss) self.topPager.className = self.TopPagerCss;
                if (self.botPager && self.BotPagerCss) self.botPager.className = self.BotPagerCss;
                if (self.topPager) { self.topPager.innerHTML = pagerCode; self.topPager.style.display = (pagerCode == "" ? "none" : ""); }
                if (self.botPager) { self.botPager.innerHTML = pagerCode; self.botPager.style.display = (pagerCode == "" ? "none" : ""); }
            }

            if (self.rowsPP) {
                var rpp_sbscr = (self.rowsPP.getAttribute("sbscr") ? self.rowsPP.getAttribute("sbscr") : "rows/pp: ");
                self.rowsPP.innerHTML = rpp_sbscr + "<input style=\"width:30px;height:13px;font-size:11px;\" type=\"text\" value=\"" + self.PageSize + "\" onblur=\"javascript:TableSetRowsPerPage('" + self.name + "', this.value);\" onkeypress=\"javascript:if((window.event || event).keyCode == ENTER_KEY_CODE){TableSetRowsPerPage('" + self.name + "', this.value); return false;}\" />";
                self.rowsPP.className = (self.topDiv.getAttribute("RowsPPCss") ? self.topDiv.getAttribute("RowsPPCss") : "def_rowspp_css");
            }

            if (self.gotoPage) {
                var gtp_sbscr = (self.gotoPage.getAttribute("sbscr") ? self.gotoPage.getAttribute("sbscr") : "go to: ");
                self.gotoPage.innerHTML = (self.pagesCount > 1 ? gtp_sbscr + "<input style=\"width:30px;height:13px;font-size:11px;\" type=\"text\" value=\"" + self.currentPage + "\" onkeypress=\"javascript:if((window.event || event).keyCode == ENTER_KEY_CODE){TableSetCurPage('" + self.name + "', this.value); return false;}\" onblur=\"javascript:TableSetCurPage('" + self.name + "', this.value);\" />" : "");
                self.gotoPage.className = (self.topDiv.getAttribute("GoToPageCss") ? self.topDiv.getAttribute("GoToPageCss") : "def_gotopage_css");
            }

            if (self.rowsCnt) {
                var cnt_sbscr = (self.rowsCnt.getAttribute("sbscr") ? self.rowsCnt.getAttribute("sbscr") : "total: ");
                self.rowsCnt.innerHTML = cnt_sbscr + self.rowsTotal;
                self.rowsCnt.className = (self.topDiv.getAttribute("RowsCntCss") ? self.topDiv.getAttribute("RowsCntCss") : "def_rowscnt_css");
            }
        }

    self.addEditEvent = function (row, coldef, add)
        {
            var cell = row.cells[coldef.cnIndex];
            if (!cell) return;
            RmvHandler(cell, "click", self.DoEdit);
            cell.style.cursor = "";
            if(add && coldef.cnEditType == fType.Check)
                disableCtrl(cell.firstChild, eq(cell.getAttribute("pat_readonlyObj"),"True"));
            if(add && (row == self.curRow || self.OneClickEdit) && coldef.cnEditType != fType.Check && coldef.cnEditType != fType.Delete && coldef.cnEditType != fType.Custom && coldef.cnEditType != fType.Multsel && coldef.cnEditType != fType.None && (row.dbid == "-1" && !self.LockedInsR || row.dbid != "-1" && !self.LockedBody) && !eq(cell.getAttribute("pat_readonlyObj"),"True"))
            {
                AddHandler(cell, "click", self.DoEdit);
                cell.style.cursor = "text";
            }
        }

        self.setCell = function (row, coldef, val, DataRow) {
            if (row == null) return;

            var cell = row.cells[coldef.cnIndex];
            var isIns = (row.dbid == "-1");
            coldef.cnValues[row.dbid] = val;
            if (val == null && (coldef.cnEditType == fType.Select || coldef.cnEditType == fType.DDEx)) coldef.cnValues[row.dbid] = coldef.cnPdNullValue;
            if (coldef.cnEditType == fType.Select && coldef.cnFilterBy != null) row.dbfilterval = DataRow[coldef.cnFilterBy];

            self.LoadDepends(row, coldef);

            AddHandler(cell, "mouseover", self.cellMouseOver);
            AddHandler(cell, "mouseout", self.cellMouseOut);

            cell.className = "";

            var ReadonlyByVal = (row.dbid != "-1" && self.ReadOnly) || coldef.cnReadonly || (self.ReadonlyBy && self.ReadonlyBy != "" && DataRow && DataRow[self.ReadonlyBy] == 1) || (coldef.cnReadonlyBy && coldef.cnReadonlyBy != "" && DataRow && DataRow[coldef.cnReadonlyBy] == 1);
            cell.setAttribute("pat_readonlyObj", ReadonlyByVal ? "True" : "False");

            if (val == null) val = (coldef.cnEditType == fType.Select ? "null" : "");

            if (self.RowTip && DataRow && DataRow[self.RowTip]) row.setAttribute("title", DataRow[self.RowTip]); else row.setAttribute("title", "");
            if (self.RowBackColor && DataRow && DataRow[self.RowBackColor]) row.style.backgroundColor = DataRow[self.RowBackColor]; else row.style.backgroundColor = "";
            if (coldef.cnTip && DataRow && DataRow[coldef.cnTip]) cell.setAttribute("title", DataRow[coldef.cnTip]);
            else if (coldef.cnTip) cell.setAttribute("title", coldef.cnTip); else cell.setAttribute("title", "");

            switch (coldef.cnEditType) {
                case fType.Check:
                    var chk = cell.firstChild;
                    if (chk == null) {
                        chk = document.createElement("INPUT");
                        chk.type = "checkbox";
                        cell.appendChild(chk);
                        AddHandler(chk, "click", self.DoEditChk);
                        AddHandler(cell.childNodes[0], "keydown", self.TextKeyEvent);
                    }
                    chk.checked = val;
                    disableCtrl(chk, eq(cell.getAttribute("pat_readonlyObj"), "True"));
                    break;
                case fType.Datetime:
                case fType.Text:
                case fType.None:
                case fType.Textarea:
                    cell.innerHTML = "<div class='def_valuediv_css' onselectstart='return false;'>" + self.FormatValue(val, coldef, DataRow) + "</div>";
                    if (coldef.cnEditType == fType.Textarea) cell.style.whiteSpace = "normal";
                    if (coldef.cnSpellCheck && (typeof (PNWSpeller) != "undefined") && (PNWSpeller != null) && (row.dbid != "-1" || row.dbid == "-1" && self.SpellCheckInsRow))
                        SpellDecorate(cell, "en", self.simulateEndEdit, coldef.cnNoGrammarCheck);
                    break;
                case fType.Money:
                    cell.innerHTML = "<div class='def_valuediv_css' onselectstart='return false;'> $ " + FormatMoney(val) + "</div>";
                    break;
                case fType.Html:
                    cell.innerHTML = "<div class='def_valuediv_css' onselectstart='return false;'>"
                        + (coldef.cnMakeLinks ? MakeLinks(val, coldef.cnMakeLinksAttrs) : val)
                        + "</div>";
                    break;
                case fType.Delete:
                    if (!isIns && self.DeleteMode)
                        if (ReadonlyByVal)
                            cell.innerHTML = "<img title='Delete Row' alt='Delete Row' src='" + self.DeleteGrayImg + "' style='border: 0' />";
                        else
                            cell.innerHTML = "<img title='Delete Row' alt='Delete Row' src='" + self.DeleteImg + "' style='border: 0' onclick=\"if(this.getAttribute('disabled') != 'disabled'){ TableDelRow('" + self.name + "', '" + row.dbid + "', '" + self.delConfirm + "'); event.cancelBubble = true;}\" />";
                    break;
                case fType.Custom:
                    if (coldef.cnCellTemplFnc)
                        ExecEx(coldef, "cnCellTemplFnc", { obj: self, row: row, coldef: coldef, cell: cell, val: val });
                    else
                        cell.innerHTML = coldef.cnCellTemplIH;
                    break;
                case fType.Multsel:
                    var chk = cell.firstChild;
                    if (chk == null) {
                        chk = document.createElement("INPUT");
                        chk.type = "checkbox";
                        cell.appendChild(chk);
                        AddHandler(chk, "click", self.SelectRowOnCheck);
                    }
                    break;
                case fType.Upload:
                    cell.innerHTML = "";
                    var upload = document.createElement("DIV");
                    upload.innerHTML += self.FormatValue(val, coldef, DataRow);
                    cell.appendChild(upload);

                    $(cell).find('a.zoom_img').fancybox({
                        'titleShow' : true,
                        'transitionIn' : 'fade',
                        'transitionOut' : 'fade',
                        'easingIn' : 'easeOutBack',
                        'easingOut' : 'easeInBack'
                    });
                    break;
            }

            var el = cell.firstChild && cell.firstChild.tagName && cell.firstChild.tagName.toUpperCase() == "DIV" ? cell.firstChild : cell;
            el.style.width = self.headRow.cells[coldef.cnIndex].style.width;
            el.style.textAlign = self.headRow.cells[coldef.cnIndex].style.textAlign;
            el.style.verticalAlign = "middle";


            if (cell.firstChild && cell.firstChild.tagName && eq(cell.firstChild.tagName, "DIV") && coldef.cnWordWrap)
                cell.firstChild.className = cell.firstChild.className + " def_valuedivWW_css";

            if (cell.firstChild && cell.firstChild.tagName && eq(cell.firstChild.tagName, "DIV") && eq(cell.getAttribute("pat_readonlyObj"), "True"))
                cell.firstChild.className = cell.firstChild.className + " " + self.ReadonlyCss;

            cell.style.backgroundColor = ExecEx(coldef, "cnGetSellColorFnct", { obj: self, val: val });
            cell.className = cell.className + (coldef.cnCellCss != null && coldef.cnCellCss != "" ? " " + coldef.cnCellCss : "");

            if (DataRow != null && coldef.cnBackColor != null && DataRow[coldef.cnBackColor] != null) {
                cell.style.backgroundColor = DataRow[coldef.cnBackColor];
                cell.style.color = self.CalcTxtColor(DataRow[coldef.cnBackColor]);
            }

            if (self.OneClickEdit) self.addEditEvent(row, coldef, true)
        }

    self.SelectRowOnCheck = function (chk, evt)
        {
            if(!eq(chk.tagName, "input")) return;
            self.SelectRow(chk.parentNode.parentNode.dbid,chk.checked);
            if(chk.parentNode.parentNode == self.curRow && !chk.checked) self.UnselectCurRow();
            CancelBubbling(evt);
        }

    self.CalcTxtColor = function (backColor) 
        {            
            if(!backColor || backColor.length != 7) return "#000000";
            var res = "#";
            for(var i=1; i<7; i++)
            {
                var l = backColor.substring(i,i+1);
                res += (isNaN(l) || l>8 ? "0" : "F");
            }
            return res;
        }

    self.DelRow = function (row, confirm) 
        {             
            if (confirm == "" || confirm.toLowerCase() == "false" || window.confirm(confirm)) ServerCall("DelRow", self, {rowId: row, args: self.args});
        }

    self.OnDelRowRes = function (res)
        {
            if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});
            self.LoadData(); 
            DataChangeNotify(self.DataChangeNotifyList, {act: "table_delrow", rowId: res["args"]["rowId"]});
        }

    self.EndEdit_ = function (inp, evt)
        {
            self.EndEdit(inp, null, null);
        }

	self.simulateEndEdit = function(ctrl, val)
	{
		self.EndEdit(ctrl, val, true);
	}

    self.EndEdit = function (oc, newValue, skipSpell)
        {
            while(oc != null && !eq(oc.tagName,"TD")) oc = oc.parentNode;
            if (oc == null || !self.theEditor) return;

            var row = oc.parentNode;
            var val;
            var coldef = self.colDefs[oc.getAttribute("columnIndex")];
            var datarow = self.GetDataRow(row.dbid);

            switch (coldef.cnEditType)
            {
                case fType.Money:
					if (newValue != null)
						val = newValue;
					else
						val = self.theEditor.value;

                    if (!self.isValid(coldef, val)) return self.cancelEdit();
                    oc.firstChild.innerHTML = " $ " + FormatMoney(val);
                    break;
                case fType.Text:
                case fType.Textarea:
                case fType.Html:
					if (newValue != null)
						val = newValue;
					else
						val = self.theEditor.value;

                    if (!self.isValid(coldef, val)) return self.cancelEdit();
                    oc.firstChild.innerHTML = self.FormatValue(val, coldef, datarow);
                    break;
                case fType.Datetime:
                    val = self.theEditor.firstChild.value;
                    if (!self.isValid(coldef, val)) return self.cancelEdit();
                    oc.firstChild.innerHTML = self.FormatValue(val, coldef, datarow);
                    break;
                case fType.Select:
                    val = self.theEditor.value;
                    if(val == "null") val = null;
                    if (!self.isValid(coldef, val) || coldef.cnRelValues == null || coldef.cnRelValues.length == 0) return self.cancelEdit();
                    if(self.theEditor.selectedIndex > -1) oc.style.backgroundColor = self.theEditor.options[self.theEditor.selectedIndex].style.backgroundColor;
                    break;
                case fType.DDEx:
                    val = DDExValue("TableEditDDEx_" + row.dbid + "_" + coldef.cnIndex);
                    if(val == "null") val = null;
                    if (!self.isValid(coldef, val)) return self.cancelEdit();
                    break;
                case fType.Check:
                    break;
                case fType.Upload:
                    break;
            }
            var oldVal = "" + coldef.cnValues[row.dbid];
            val = "" + val;
            var changed = true;
            if(coldef.cnEditType != fType.Datetime)
                changed = (oldVal.replace(/\r\n/g,"<BR>").replace(/\n/g,"<BR>").replace(/\r/g,"<BR>") != val.replace(/\r\n/g,"<BR>").replace(/\n/g,"<BR>").replace(/\r/g,"<BR>"));
            else
            {
                var oD = DeserializeDate(oldVal), nD = new Date(val);
                changed = (oD == null || oD == "" || typeof(oD.getMonth) == "undefined" || oD.getMonth() != nD.getMonth() || oD.getDate() != nD.getDate() || oD.getYear() != nD.getYear());
            }
            var oldval = coldef.cnValues[row.dbid];
            coldef.cnValues[row.dbid] = val;
            if (row.dbid != "-1" && changed)
            {
                ServerCall("SetData", self, {rowId: row.dbid, field: coldef.cnDBName, value: val, oldval: oldval, args: self.args});
                // !!! Moved here from onSetDataRes
                DataChangeNotify(self.DataChangeNotifyList, {act: "table_setdata", rowId: row.dbid});
            }
            
            self.SwitchTableEditToView();
            
            self.LoadDepends(row, coldef);

            if(datarow) datarow[coldef.cnDBName] = val;
            
	        if (!skipSpell && coldef.cnSpellCheck && (typeof(PNWSpeller) != "undefined") && (PNWSpeller != null) && (row.dbid != "-1" || row.dbid == "-1" && self.SpellCheckInsRow))
				SpellDecorate(oc, "en", self.simulateEndEdit, coldef.cnNoGrammarCheck);
        }
        
        self.SwitchTableEditToView = function()
        {
            if(self.theEditor == null) return; 
            
            var cell = self.theEditor;
            while(cell != null && !eq(cell.tagName,"TD")) cell = cell.parentNode;
            if(cell == null) return OnErr({obj: self, msg: "Wrong table html"});

            var coldef = self.colDefs[cell.getAttribute("columnIndex")];
            if(coldef && (coldef.cnEditType == fType.Select || coldef.cnEditType == fType.DDEx)) { self.theEditor = null; return self.SetPullDownCell(cell,coldef); }
            
            AutoCompleteDel(self.theEditor.id); // if it was AutoComplete all attached events will be deleted
            var edt = self.theEditor;
            while(edt.parentNode != null && !eq(edt.parentNode.tagName.toUpperCase(),"TD")) edt = edt.parentNode;
            if(edt.parentNode == null || edt.parentNode.firstChild == null || !eq(edt.parentNode.firstChild.tagName,"DIV")) return OnErr({obj: self, msg: "Wrong table html"});
            edt.style.display = "none";
            edt.parentNode.firstChild.style.display = "";
            window.setTimeout(function(){if(edt && edt.parentNode) edt.parentNode.removeChild(edt);}, 1); 
            self.theEditor = null;
        }

        self.OnSetDataRes = function(res) {
            var rowId = res["args"]["rowId"];
            var field = res["args"]["field"];
            var value = res["args"]["value"];
            var oldval = res["args"]["oldval"];
            var scalarRes = res["scalarRes"];

            for (var j = 0; j < self.colDefs.length && self.colDefs[j].cnDBName != field; j++);
            if (res["errCode"] != null) {
                var row = self.Row(rowId);

                self.setCell(row, self.colDefs[j], oldval, null);
                if (self.colDefs[j].cnEditType == fType.Select || self.colDefs[j].cnEditType == fType.DDEx) self.SetPullDownCell(row.cells[j], self.colDefs[j]);
                return OnErr({obj: self, msg: res["errCode"]});
            } else {
                if (self.colDefs[j].RefreshTableOnChange) self.LoadData();
            }

            if (self.RefreshRowOnChange || self.colDefs[j].RefreshFieldOnChange) self.RefreshRow(rowId, self.colDefs[j].RefreshFieldOnChange);
        }

    self.DoEditChk = function (chk, evt)
        {
            if(!eq(chk.tagName, "input")) return;
            self.DoEdit(chk.parentNode,  evt);
        }

        self.DoEdit = function(cell, evt) {
            if ((evt != null && evt.ctrlKey) || !eq(cell.tagName, "td") || cell.firstChild == null || cell.firstChild.style == null || eq(cell.firstChild.style.display, "NONE")) return;
            var row = cell.parentNode;
            if(self.theEditor) self.EndEdit(self.theEditor, null, null);
            var coldef = self.colDefs[cell.getAttribute("columnIndex")];
	        switch(coldef.cnEditType)
            {
                case fType.Text:
                    var EditSpan = document.createElement("span");
                    EditSpan.id = "TableEditObj";

                    cell.firstChild.style.display = "none";
                    cell.appendChild(EditSpan);

                    self.theEditor = document.createElement("INPUT");
                    self.theEditor.type = "text";
                    self.theEditor.style.width = (cell.offsetWidth - 17).toString() + "px";
                    self.theEditor.style.height = (cell.offsetHeight - 10).toString() + "px";
                    self.theEditor.id = "TableEdit";
                    if(coldef.cnMaxLength) self.theEditor.setAttribute("maxlength", coldef.cnMaxLength);
                    EditSpan.appendChild(self.theEditor);

                    self.theEditor.value = coldef.cnValues[row.dbid] == null ? "" : coldef.cnValues[row.dbid];
                    
                    if(coldef.cnRelTableName) 
                    {
                        self.theEditor.setAttribute("autocomplete","off");

                        self.theEditor.setAttribute("cnRelTableName", coldef.cnRelTableName);
                        if(coldef.cnRelColors)    self.theEditor.setAttribute("cnRelColors",    coldef.cnRelColors);
                        if(coldef.cnRelNames)     self.theEditor.setAttribute("cnRelNames",     coldef.cnRelNames);
                        if(coldef.cnRelBackColor) self.theEditor.setAttribute("cnRelBackColor", coldef.cnRelBackColor);
                        if(coldef.cnRelSplitter)  self.theEditor.setAttribute("cnRelSplitter",  coldef.cnRelSplitter);
                        if(coldef.cnMaxOpts)      self.theEditor.setAttribute("cnMaxOpts",      coldef.cnMaxOpts);
                        if(coldef.cnColumn)       self.theEditor.setAttribute("cnColumn",       coldef.cnColumn);
                        if(coldef.cnVColumn)      self.theEditor.setAttribute("cnVColumn",      coldef.cnVColumn);
                        if(coldef.cnFilter)       self.theEditor.setAttribute("cnFilter",       coldef.cnFilter);
                        if(coldef.divW)           self.theEditor.setAttribute("divW",           coldef.divW);
                        if(coldef.divH)           self.theEditor.setAttribute("divH",           coldef.divH);

                        self.theEditor.setAttribute("onHide", "TableEndEdit('" + self.name + "')");
                        //AddHandler(self.theEditor, "blur", self.EndEdit_);
                        
                        window.setTimeout(function(){if(self.theEditor)AutoCompleteInit(self.theEditor.id);}, 5);
                    }
                    else
                    {
                        AddHandler(self.theEditor, "blur", self.EndEdit_);
                    }
                    self.theEditor.focus();
                    AddHandler(self.theEditor, "keydown", self.TextKeyEvent);
                    break;
                case fType.Money:
                    self.theEditor = document.createElement("INPUT");
                    self.theEditor.type = "text";
                    self.theEditor.style.width = (cell.offsetWidth - 17).toString() + "px";
                    self.theEditor.style.height = (cell.offsetHeight - 10).toString() + "px";
                    self.theEditor.id = "TableEdit";

                    AddHandler(self.theEditor, "keydown", self.TextKeyEvent);
                    AddHandler(self.theEditor, "blur", self.EndEdit_);
                    self.theEditor.onkeydown = function(evt){return ImpEventMoneyFormatCheck(this, (evt ? evt : window.event)); };

                    cell.firstChild.style.display = "none";
                    cell.appendChild(self.theEditor);

                    self.theEditor.value = FormatMoney(coldef.cnValues[row.dbid]);
                    self.theEditor.focus();
                    break;
                case fType.Textarea:
                case fType.Html:
                    self.theEditor = document.createElement("textarea");
                    self.theEditor.style.width = (cell.offsetWidth - 17).toString() + "px";
                    self.theEditor.style.height = (cell.offsetHeight - 9).toString() + "px";
                    self.theEditor.className = self.TextAreaCss;
                    self.theEditor.id = "TableTextArea";

                    AddHandler(self.theEditor, "keydown", self.TextKeyEvent);
                    AddHandler(self.theEditor, "blur", self.EndEdit_);

                    self.theEditor.value = coldef.cnValues[row.dbid];
                    cell.firstChild.style.display = "none";
                    cell.appendChild(self.theEditor);
                    self.theEditor.focus();
                    break;
                case fType.Datetime:
                    self.theEditor = document.createElement("span");
                    self.theEditor.id = "ClndObj_" + coldef.cnIndex + "_" + row.dbid;

                    AddHandler(self.theEditor, "keydown", self.TextKeyEvent);

                    cell.firstChild.style.display = "none";
                    cell.appendChild(self.theEditor);
                    jCalendar({base:self.theEditor, val:DeserializeDate(coldef.cnValues[row.dbid]), onClose:self.EndEdit, showbtn:false, opened:true, showdate: true, showtime: coldef.showTime });
                    AddHandler(self.theEditor, "blur", self.EndEdit_);
                    self.theEditor.focus();
                    break;
                case fType.DDEx:
                    var EditDiv = document.createElement("div");
                    EditDiv.id = "TableEditDDEx_" + row.dbid + "_" + coldef.cnIndex;

                    cell.firstChild.style.display = "none";
                    cell.appendChild(EditDiv);

                    if (coldef.cnRelTableName) EditDiv.setAttribute("cnRelTableName", coldef.cnRelTableName);
                    EditDiv.setAttribute("opened", "True");
                    if (coldef.cnRelColors) EditDiv.setAttribute("cnRelColors", coldef.cnRelColors);
                    if (coldef.cnRelNamesStr) EditDiv.setAttribute("cnRelNames", coldef.cnRelNamesStr);
                    if (coldef.cnRelValuesStr) EditDiv.setAttribute("cnRelValues", coldef.cnRelValuesStr);
                    if (coldef.cnRelBackColor) EditDiv.setAttribute("cnRelBackColor", coldef.cnRelBackColor);
                    if (coldef.cnRelSplitter) EditDiv.setAttribute("cnRelSplitter", coldef.cnRelSplitter);
                    if (coldef.cnMaxOpts) EditDiv.setAttribute("cnMaxOpts", coldef.cnMaxOpts);
                    if (coldef.cnColumn) EditDiv.setAttribute("cnColumn", coldef.cnColumn);
                    if (coldef.cnVColumn) EditDiv.setAttribute("cnVColumn", coldef.cnVColumn);
                    if (coldef.cnRelShowInPD) EditDiv.setAttribute("cnRelShowInPD", coldef.cnRelShowInPD);
                    if (coldef.divW) EditDiv.setAttribute("divW", coldef.divW);
                    if (coldef.divH) EditDiv.setAttribute("divH", coldef.divH);

                    EditDiv.setAttribute("impW", coldef.impW ? coldef.impW : (cell.offsetWidth - 20) + "px");
                    EditDiv.setAttribute("onHide", "TableEndEdit('" + self.name + "'); DDExDel('" + EditDiv.id + "');");

                    self.theEditor = cell;
    		        self.theEditorObj = DDExInit(EditDiv.id, self.args); 
    		        DDExSetValue(EditDiv.id, coldef.cnValues[row.dbid] == null ? "" : coldef.cnValues[row.dbid]);
                    if (objList[EditDiv.id]) objList[EditDiv.id].onValueChanged = function() { TableEndEdit(self.name); DDExDel(EditDiv.id); }

                    break;
                case fType.Select:
                    self.theEditor = document.createElement("SELECT");
                    self.theEditor.style.width = (cell.offsetWidth - 12).toString() + "px";
                    self.theEditor.style.height = (cell.offsetHeight - 2).toString() + "px";

                    self.theEditor.id = "TablePullDown";

                    self.SetPullDownOptions(coldef, self.theEditor, row);

                    AddHandler(self.theEditor, "keydown", self.TextKeyEvent);
                    AddHandler(self.theEditor, "change", self.EndEdit_);
                    AddHandler(self.theEditor, "blur", self.EndEdit_);

                    cell.firstChild.style.display = "none";
                    cell.appendChild(self.theEditor);
                    if(self.theEditor.value != coldef.cnValues[row.dbid]) self.theEditor.value = coldef.cnValues[row.dbid];
                    self.theEditor.focus();
                    break;
                case fType.Check:
                    coldef.cnValues[row.dbid] = cell.childNodes[0].checked;
                    if (row.dbid != "-1") {
                        ServerCall("SetData", self, { rowId: row.dbid, field: coldef.cnDBName, value: cell.childNodes[0].checked, oldval: !cell.childNodes[0].checked, args: self.args });
                        DataChangeNotify(self.DataChangeNotifyList, { act: "table_setdata", rowId: row.dbid });
                    }
                    break;
                case fType.Upload:
                    self.theEditor = document.createElement("DIV");
                    self.theEditor.id = "TableEdit_" + coldef.cnIndex + "_" + row.dbid;
                    if (qq != null) {
	                    new qq.FileUploader({
		                    element: self.theEditor,
		                    action: Project.uploadPath,
		                    allowedExtensions: coldef.cnFileUploadExtensions,
		                    onSubmit: function () {
			                    this.params = {
				                    objName: self.name,
                                    uploadPath: coldef.cnFileUploadPath,
				                    rowId: row.dbid,
                                    field: coldef.cnDBName
			                    };
		                    },
		                    onComplete: function (id, fileName, response) {
			                    if (response.success) {
                                    coldef.cnValues[row.dbid] = response.fileName;
                                    cell.innerHTML = self.FormatValue(response.fileName, coldef);

                                    $(cell).find('a.zoom_img').fancybox({
                                        'titleShow' : true,
                                        'transitionIn' : 'fade',
                                        'transitionOut' : 'fade',
                                        'easingIn' : 'easeOutBack',
                                        'easingOut' : 'easeInBack'
                                    });
                                    if (row.dbid != "-1") {
                                        ServerCall("SetData", self, { rowId: row.dbid, field: coldef.cnDBName, value: response.fileName, oldval: "", args: self.args });
                                        DataChangeNotify(self.DataChangeNotifyList, { act: "table_setdata", rowId: row.dbid });
                                    }
				                    $('ul.qq-upload-list', self.theEditor).find('li').remove();
			                    }
		                    },
                            onCancel: function() {
                                cell.innerHTML = self.FormatValue(coldef.cnValues[row.dbid], coldef);
                                self.EndEdit_();
                            }
	                    });
                    }
                    AddHandler(self.theEditor, "keydown", self.EndEdit_);
                    cell.firstChild.style.display = "none";
                    cell.appendChild(self.theEditor);
                break;
            }
        }

    self.SelectRowByRow = function (row, evt)
        {
            if(!row) {self.UnselectCurRow(); return;}
            if(!eq(row.tagName, "tr")) return;
            
            if(evt && evt.ctrlKey && self.multSel)
            {
                if(row == self.curRow)
                {
                    self.UnselectCurRow();
                    ExecEx(self, "onSelRow", {obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "")});
                    RowChangeNotify(self.RowChangeNotifyList, {rowId: -1, rowidname: self.DbRowId});
                    ExecEx(self, "onRowSelected", {obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "")});
                }
                else
                    self.SelectRow(row.dbid,self.selRowsId[row.dbid] != true);

                return;
            }
            else if(evt && evt.shiftKey && self.multSel && self.curRow)
            {
                var go = false;
                for (var i = 0; i < self.TableRows.length; i++)
                {
		            if(self.TableRows[i] == self.curRow || self.TableRows[i] == row) go = !go;
		            if(go || self.TableRows[i] == self.curRow || self.TableRows[i] == row)
                        self.SelectRow(self.TableRows[i].dbid,true);
		        }
                return;
            }
            
            ExecEx(self, "onNewRow", {obj: self, rowId: (row ? row.dbid : "''")});
            if(row == self.curRow) return;

            self.ClearAllSelection();
            self.SelectCurRow(row);
            
            if(self.HideRowsOnSelect) self.HideRows_();
            
            ExecEx(self, "onSelRow", {obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "")});
            RowChangeNotify(self.RowChangeNotifyList, self.curRow ? {rowId: self.curRow.dbid, rowidname: self.DbRowId} : null);
            ExecEx(self, "onRowSelected", { obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "") });
        }

    self.Row = function (id)
    {
        for (var i = 0; i < self.TableRows.length; i++)
	        if(self.TableRows[i].dbid == id)
	            return self.TableRows[i];
	    if(id == "-1") return self.insRow;
        return null;
	}

    self.SelectAllRows = function (sel)
    {
        for (var i = 0; i < self.TableRows.length; i++)
            self.SelectRow(self.TableRows[i].dbid, sel);
    }

    self.SelectRow = function (id, sel)
    {
        var row = self.Row(id);
        
        if(sel)
            if(self.multSel || self.curRow != null && self.curRow.dbid == id) self.selRowsId[id] = true;
        else
            delete self.selRowsId[id];

        if(row)row.className = (sel ? self.SelRowCss : self.RowCss);
        for (var j = 0; j < self.colDefs.length; j++)
            if(row && self.colDefs[j].cnEditType == fType.Multsel && row.cells[j].firstChild) row.cells[j].firstChild.checked = sel;
        ExecEx(self, "onSelRowsChanged", {obj: self, selRowsId: self.selRowsId, rowId: id, selected: sel});
    }

    self.SelectCurRow = function (row)
    {
        self.curRow = row;
        self.SelectRow(row.dbid,true);
        if (!self.OneClickEdit && self.curRow) 
            for (var j = 0; j < self.colDefs.length; j++) self.addEditEvent(self.curRow, self.colDefs[j], true);
    }

    self.UnselectCurRow = function(LeaveSelected) {
        if (!self.curRow) return;
        if (!LeaveSelected) self.SelectRow(self.curRow.dbid, false);
        for (var j = 0; j < self.colDefs.length; j++) {
            if (!self.OneClickEdit) self.addEditEvent(self.curRow, self.colDefs[j], false);
            if (self.colDefs[j].cnSpellCheck && (typeof (PNWSpeller) != "undefined") && (PNWSpeller != null)) {
                var td = self.curRow.cells[self.colDefs[j].cnIndex];
                if (((typeof (td.decorator) == "undefined") || (td.decorator == null) || !td.decorator.stuffed) && (self.curRow.dbid != "-1" || self.curRow.dbid == "-1" && self.SpellCheckInsRow))
                    SpellDecorate(td, "en", self.simulateEndEdit, self.colDefs[j].cnNoGrammarCheck);
            }
        }
        self.curRow = null;
        ExecEx(self, "onRowSelected", {obj: self, curRowId: (self.curRow ? "" + self.curRow.dbid : "")});
    }

    self.ClearAllSelection = function ()
    {
        for (var id in self.selRowsId) 
        {
            self.SelectRow(id, false);
            delete self.selRowsId[id];
        }
        self.UnselectCurRow();
    }

    self.LockTable = function(disable, disableIns) {
        if (disable == null) disable = true;
        if (disableIns == null) disableIns = disable;
        self.LockedBody = disable;
        self.LockedInsR = disableIns;
        
        self.LockRow(self.insRow, disableIns);

        if (!self.TableRows) return;

        for (var i = 0; i < self.TableRows.length; i++)
            self.LockRow(self.TableRows[i], disable);
    }
    
    self.Disable = function(disable) {
        self.LockTable(disable, disable);
    }    

    self.LockRow = function(row, lock) 
    {
        if (!row) return;
        for (var j = 0; j < self.colDefs.length; j++) {
            var coldef = self.colDefs[j];

            disableCtrl(row.cells[coldef.cnIndex], lock);
            self.addEditEvent(row, coldef, !lock);
        }
    }
    
    self.InsertRow = function() 
    {
        var n = new Array();
        var o = new Array();
        var ln = 0;
        for (var i = 0; i < self.colDefs.length; i++) {
            var coldef = self.colDefs[i];
            if (coldef.cnDBName == null || coldef.cnDBName == "") continue;
            switch (coldef.cnEditType) {
                case fType.Text: case fType.Textarea: case fType.Check: case fType.Money: case fType.Html: case fType.Select: case fType.Datetime: case fType.Upload:
                    if (self.insRow && eq(self.insRow.cells[coldef.cnIndex].getAttribute("pat_readonlyObj"),"True")) break;
                    o[ln] = (self.insRow == null ? coldef.cnDefVal : coldef.cnValues["-1"]);
                    n[ln++] = coldef.cnDBName;
                    break;
                case fType.Custom:
                    if (coldef.cnCustomValue == "" || !self.insRow || eq(self.insRow.cells[coldef.cnIndex].getAttribute("pat_readonlyObj"),"True")) break;
                    o[ln] = self.insRow[coldef.cnCustomValue];
                    n[ln++] = coldef.cnDBName;
                    break;
            }
            if (!self.isValid(coldef, o[ln - 1])) return;
        }

        ServerCall("InsRow", self, {insNames: n, insArgs: o, args: self.args});
    }
    
    self.InsertRowEF = function() 
    {
        var n = new Array(self.colDefs.lenght);
        var o = new Array(self.colDefs.lenght);
        var ln = 0;
        for (var i = 0; i < self.colDefs.length; i++) {
            var coldef = self.colDefs[i];
            var imp = coldef.cnEditFormInput;
            if (coldef.cnDBName == null || coldef.cnDBName == "") continue;
            if (coldef.cnReadonly) continue;

            switch (coldef.cnEditType) {
                case fType.Text: case fType.Textarea: case fType.Money: case fType.Html: case fType.Select:
                    o[ln] = (imp == null ? coldef.cnDefVal : imp.value);
                    n[ln++] = coldef.cnDBName;
                    break;
                case fType.Check:
                    o[ln] = (imp == null ? coldef.cnDefVal : imp.checked);
                    n[ln++] = coldef.cnDBName;
                    break;
                case fType.Datetime:
                    o[ln] = (imp == null ? coldef.cnDefVal : imp.value);// (imp.firstChild.firstChild.firstChild.firstChild.firstChild ? imp.firstChild.firstChild.firstChild.firstChild.firstChild.value : imp.firstChild.firstChild.firstChild.firstChild.value));
                    n[ln++] = coldef.cnDBName;
                    break;
                case fType.Custom:
                    break;
            }
            if (!self.isValid(coldef, o[ln - 1])) return;
        }

        ServerCall("InsRow", self, {insNames: n, insArgs: o, args: self.args, popUp : self.name + "_Inner"});
    }

    self.OnInsRowRes = function (res)
        {
            if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});
            if(res.args.popUp) 
            {
                PopUpShow(res.args.popUp, false);
                delete res.args.popUp;
            }
            self.LoadData();
            DataChangeNotify(self.DataChangeNotifyList, {act: "table_insrow"});
        }

    self.TextKeyEvent = function (inp, evt)
        {
            var cell = inp.parentNode;
            while(cell.parentNode != null && !eq(cell.tagName, "TD")) cell = cell.parentNode;
            var type = self.colDefs.length > cell.getAttribute("columnIndex") ? self.colDefs[cell.getAttribute("columnIndex")].cnEditType : fType.None;
            if(!eq(cell.tagName, "td")) return;            
            switch(evt.keyCode)
            {
                case ESC_KEY_CODE:
                    window.setTimeout(function(){self.cancelEdit();}, 1);
                    break;
                case ENTER_KEY_CODE:
                    if(type != fType.Textarea) window.setTimeout(function(){self.EndEdit(cell);}, 1); 
                    break;
                case TAB_KEY_CODE:
                    var cd = null, cl = null, row = self.curRow;

                    if(type != fType.Check && type != fType.Datetime) self.EndEdit(cell);
                    
                    cl = self.getNextEditCellInRow(row, cell.getAttribute("columnIndex"));
                    if(cl ==null && row == self.insRow && self.InsertRowOnTab)
                    {
                        self.args["SelectFirstInsCell"] = 1;
                        return self.InsertRow();
                    }
                    while(!cl)
                    {
                        row = self.getNextRow(row);
		                if(!row) break;
		                cl = self.getNextEditCellInRow(row, 0);
                    }

                    self.selectCell(cl, row);
                    break;
            }
        }

    self.selectCell = function (cl, row)
    {
        if(!cl) return;
        if(row != self.curRow) self.SetCurRow(row.dbid);

        if(self.colDefs[cl.getAttribute("columnIndex")].cnEditType != fType.Check)
            return window.setTimeout(function(){self.DoEdit(cl, null);}, 300); // let finish onEdit reaction before (important!!!)
        else
            return window.setTimeout(function(){cl.firstChild.focus();}, 300);
    }

    self.getNextRow = function (row)
    {
        if(row == self.insRow)
        {
            if(self.TableRows.length > 0)
                return self.TableRows[0];
            else 
                return null;
        }
        for (var i = 0; i < self.TableRows.length; i++)
            if(self.TableRows[i] == row && i + 1 < self.TableRows.length)
                return self.TableRows[i + 1];
        return null;
    }

    self.getNextEditCellInRow = function (row, cellnum)
    {
        if(!row) return null;
        for(var i=parseInt(cellnum) + 1; i<self.colDefs.length; i++)
            if(!eq(row.cells[self.colDefs[i].cnIndex].getAttribute("pat_readonlyObj"),"True") && self.colDefs[i].cnEditType != fType.Delete && self.colDefs[i].cnEditType != fType.Custom && self.colDefs[i].cnEditType != fType.None)
                return row.cells[self.colDefs[i].cnIndex];
        return null;
    }

    self.cancelEdit = function ()
        {
            self.SwitchTableEditToView();
        }

    self.isValid = function (coldef, val)
        {
            if(coldef == null || coldef.cnValidate == null || coldef.cnValidate == "") return true;
            var result = ExecEx(coldef, "cnValidate", {obj: self, val: val});
            if (!result) OnErr({obj: self, msg: "Incorrect " + coldef.cnTitle, val: val});
            else if (typeof(result) == "string") 
                OnErr({msg: result });
            return result && typeof(result) != "string";
        }
    
    self.rowMouseOver = function (row, evt)
        {
            if(!eq(row.tagName, "tr")) return;
            if (row != self.curRow && self.selRowsId[row.dbid] != true) row.className = self.MouseOverRowCss;
            if(self.CustomRowTip && self.theEditor == null)
            {
                if(!eq(self.tipdiv.parentNode.tagName,"BODY")) document.body.appendChild(self.tipdiv);
                ExecEx(self, "CustomRowTip", {obj: self, row: row, tip: self.tipdiv});

	            if(self.tiptimer) window.clearTimeout(self.tiptimer);
                var doc = (document.compatMode && document.compatMode=="CSS1Compat") ? document.documentElement : document.body;
	            var X = evt.clientX + doc.scrollLeft + 3, Y = evt.clientY + doc.scrollTop + 3;
	            if(trim(self.tipdiv.innerHTML) != "") self.tiptimer = window.setTimeout(function() { ShowPopUpByCords(self.tipdiv, X, Y); }, 300);
            }
            ExecEx(self, "onRowMouseOver", {obj: self, row: row, evt: evt});
        }
    self.rowMouseOut = function (row, evt)
        {
            if(!eq(row.tagName, "tr")) return;
            if (row != self.curRow && self.selRowsId[row.dbid] != true) row.className = self.RowCss;
            if(self.tiptimer) window.clearTimeout(self.tiptimer);
            if(self.CustomRowTip) HidePopUp(self.tipdiv);
            ExecEx(self, "onRowMouseOut", {obj: self, row: row, evt: evt});
        }
    
    self.cellMouseOver = function (td, evt)
        {
            if(!eq(td.tagName, "td")) return;
            var coldef = self.colDefs[td.getAttribute("columnIndex")];
            if(coldef.cnCstmTip && self.theEditor == null)
            {
                if(!eq(self.tipdiv.parentNode.tagName,"BODY")) document.body.appendChild(self.tipdiv);
                ExecEx(coldef, "cnCstmTip", {obj: self, id: td.parentNode.dbid, tip: self.tipdiv, cd: coldef});

	            if(self.tiptimer) window.clearTimeout(self.tiptimer);
                var doc = (document.compatMode && document.compatMode=="CSS1Compat") ? document.documentElement : document.body;
	            var X = evt.clientX + doc.scrollLeft + 3, Y = evt.clientY + doc.scrollTop + 3;
	            if(trim(self.tipdiv.innerHTML) != "") self.tiptimer = window.setTimeout(function() { ShowPopUpByCords(self.tipdiv, X, Y); }, 300);
            }
        }
    self.cellMouseOut = function (td, evt)
        {
            if(!eq(td.tagName, "td")) return;
            var coldef = self.colDefs[td.getAttribute("columnIndex")];
            if(self.tiptimer) window.clearTimeout(self.tiptimer);
            if(coldef.cnCstmTip) HidePopUp(self.tipdiv);
        }

        self.FormatValue = function (val, coldef, row) {
            //we should formate Date now, and have only one attribut to format value by: cnMaxLen, but it is possible to have more format attributs
            var res = "" + PrsDate(val);

            if (coldef.cnFormat) res = ExecEx(coldef, "cnFormat", { obj: self, coldef: coldef, val: val, row: row });
            else if (coldef.cnFormatHTML) res = ExecEx(coldef, "cnFormatHTML", { obj: self, coldef: coldef, val: res, row: row  });

            res = ((coldef.cnMaxLen && coldef.cnMaxLen > 0 && coldef.cnMaxLen < res.length) ? subval = res.substring(0, coldef.cnMaxLen) + "..." : res);

            if (coldef.cnEditType == fType.Text || coldef.cnEditType == fType.None || coldef.cnEditType == fType.Textarea) {
                var d = document.createElement("div");
                if (coldef.cnFormatHTML) d.innerHTML = res;
                else SetText(d, res);
                res = d.innerHTML;
            }
            if (coldef.cnEditType == fType.Textarea) {
                res = res.replace(new RegExp("\\cM\\cJ", "gm"), "<br>");
                res = res.replace(new RegExp("\\cJ", "gm"), "<br>");
            }

            if (coldef.cnEditType == fType.Upload) {
                var reg = /[\\/]([^\\/]*)$/.exec(res);
                if((res && (reg && reg[1] != "undefined")) || (res && !reg)) {
                    var file_path = reg ? res.replace(/\\+/, '/').replace(/~\//, '') : ('/' + coldef.cnFileUploadPath + '/' + coldef.cnFileThumbFolder + '/' + res),
                        file_name = reg ? reg[1] : res;

                    if (/.\.(jpe?g||gif||png||bmp)$/i.test(file_path)) {
                        res = '<a class="zoom_img" title="' + file_name + '" href="' + file_path + '"><img src="' + file_path + '" alt="' + file_name + '" align="middle" height="50" /></a>' ;
                    } else {
                        res = '<a target="_blank" href="' + file_path + '">' + file_path + '</a>' ;
                    }
                }
                res = (res.indexOf("undefined") == -1 ? res : "") + '<a href="#" style="margin-left:10px;">Edit</a>';
            }

            return res;
        }

    self.RenderPager = function (Step)
        {
	        var result = "";
	        if (self.pagesCount > 1)
	        {
	            if(Step < 1) Step = 1; // Step - num of additional page links before and after current page

	            var start = self.currentPage - Step;
	            if (start < 2) start = 2;

	            var finish = start + Step*2 + 1;
	            if (finish > self.pagesCount)
	            {
	                start -= (finish - self.pagesCount);
		            if (start < 2) start = 2;
	                finish = self.pagesCount;
	            }

                result += self.PagerCode(1);
                if(start > 2)result += "<div class=\"" + self.PagerSepCss + "\">...</div>";

	            for (var i = start; i < finish; i++)
	                result += self.PagerCode(i);

                if(finish < self.pagesCount)result += "<div class=\"" + self.PagerSepCss + "\">...</div>";
                result += self.PagerCode(self.pagesCount);
	        }
	        return result;
        }

    self.PagerCode = function (Page)
        {
            var Label = Page;
            if(self.pagerText) Label = ExecEx(self, "pagerText", {obj: self, page: Page});
            if (self.currentPage == Page) return "<div class=\"" + self.SelPagerCss + "\"><b>" + Label + "</b></div>";
            var funct = (self.pagerFunction == null ? "" : self.pagerFunction + "(" + Page + ");");
            return "<div class=\"" + self.PagerCss + "\" onclick=\"javascript:TableSetCurPage('" + self.name + "', " + Page + ");" + funct + "\">" + Label + "</div>";
        }

        self.ClearPDCache = function () {
            for (var i = 0; i < self.colDefs.length; i++) {
                var cd = self.colDefs[i];
                if (cd.cnEditType == fType.Select || cd.cnEditType == fType.DDEx)
                    cd.cnDataLoaded = false;
            }
        }
    if(self.HtmBlocks == null)self.SizeTable(0);
    self.LoadData();
}

//------------------------------------------------- Row Editor -------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function REInit(name, args, prms)
{
    return PatObj_Init(PatObjType.SRE, name, args, prms);
}

function RERefresh(name, args, rowId)
{
    if(objList[name]) {
        objList[name].Refresh(args, rowId);
    }
}

function RERowID(name) {
    if(objList[name]) return objList[name].args.rowId;
    return -1;
}

function REUpdateArgs(name,args){
    if(objList[name])
    {
        if(args) AddHash(objList[name].args, args);        
    }
}

function REDisable(name, dis)
{
    if(objList[name]) objList[name].Disable(dis);
}

function REClear(name)
{
    if(objList[name] && objList[name].ClearControls) objList[name].ClearControls();
}

function IsRELinked(name)
{
    var res = false;
    if (objList[name]) 
        res = objList[name].IsLinked();
    
    return res;
}

function REMakeInsert(name,args){
    if(args)AddHash(objList[name].args,args);
    objList[name].REInsertRow(true);
}

function RESave(name, inp)
{
    if(objList[name]) objList[name].REEndEdit(inp);
}

function RowEditor(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
    self.args = args;

    if(prms["rowId"]) self.args.rowId = prms["rowId"];

    self.alwaysEnabled = prms["alwaysEnabled"];

	self.controls = null;
    self.table = null;

    self.OnDataChangeNotification = function(params) 
        { 
            self.LoadData();
        }

    self.OnRowChangeNotification = function (params) 
        { 
            if(params != null && params["rowId"] != null)
                if(params["rowidname"] != null && params["rowidname"] != "rowId" && params["rowidname"] != "id")
                    self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
                else
                    self.args.rowId = params["rowId"];
			self.LoadData();
        }

    self.OnServiceRes = function (res)
    {
        switch(res["cmdCode"])
        {
            case "GetSingleRow": self.OnGetSingleRowRes(res); break;
            case "SetData":      self.OnSetDataRes(res); break;
            case "InsRow":       self.OnInsRowRes(res); break;
        }
    }

    self.Refresh = function(args, rowId) {
        if (args) AddHash(self.args, args);
        if (rowId) self.args.rowId = rowId;
        self.LoadData();
    }

    self.LoadData = function ()
    {
        self.LoadControls();
        ServerCall("GetSingleRow", self, {rowId: self.args.rowId, args: self.args});
    }

    self.SetUpButtons = function ()
    {
        if(self.UpdateButton)
        {
            self.onValidateUpdate = self.UpdateButton.getAttribute("onValidate");
            disableCtrl(self.UpdateButton, self.args.rowId == "-1");
            AddHandler(self.UpdateButton, "click", self.REUpdateRow_);
        }
        if(self.InsertButton)
        {
            self.onValidateInsert = self.InsertButton.getAttribute("onValidate");
            disableCtrl(self.InsertButton, self.args.rowId != "-1" && !self.UpdateButton);
            AddHandler(self.InsertButton, "click", self.REInsertRow_);
        }
        if(self.ClearButton)
        {
            disableCtrl(self.ClearButton, self.args.rowId != "-1" && !self.UpdateButton);
            AddHandler(self.ClearButton, "click", self.REClear);
        }
        if(self.DeselectButton)
        {
            disableCtrl(self.DeselectButton, self.args.rowId == "-1");
            AddHandler(self.DeselectButton, "click", self.REDeselect);
        }
    }

	self.LoadControls = function()
	{
	    if(self.controls != null) return;
		self.controls = new Array();
		var rex = new RegExp("^(.+)_" + self.name + "$", "i");
		var docall = document.getElementsByTagName('*');
		for (var i = 0; i < docall.length; i++)
		{
			var el = rex.exec(docall[i].id);
            if (el == null) continue;
            if (el[1] == "UpdateButton")  { self.UpdateButton = docall[i]; continue; }
            if (el[1] == "InsertButton")  { self.InsertButton = docall[i]; continue; }
            if (el[1] == "ClearButton")   { self.ClearButton = docall[i]; continue; }
            if (el[1] == "DeselectButton"){ self.DeselectButton = docall[i]; continue; }

            var control = { Input:docall[i], Type:fieldType(docall[i]), Related:new Array() };
	        self.controls.push(control);
		}

		for (var i = 0; i < self.controls.length; i++)
		{
            var control = self.controls[i];
		    if(control.Type == fType.Select) 
		    {
		        control.Related = self.FindRelated(control.Input);
		        DDInit(control.Input.id, self.args); 
		    }
		    else if(control.Type == fType.DDEx) 
		    {
		        control.Related = self.FindRelated(control.Input);
		        DDExInit(control.Input.id, self.args); 
		    }
		    else if(control.Type == fType.Auto) 
		    {
		        AutoCompleteInit(control.Input.id, self.args); 
		    }
		    else if(control.Type == fType.tinyMCE) 
		    {
		        TinyMceInit(control.Input.id);
		    }
		    else if(control.Type == fType.Money)
		    {
                var pr = control.Input.parentNode;
                var t = document.createElement("table");  
                t.className = (control.Input.getAttribute("MoneyTableCss") ? control.Input.getAttribute("MoneyTableCss") : "def_emptytable_css");
                var r = t.insertRow(0);
                r.insertCell(0).appendChild(control.Input);
                r.insertCell(1).innerHTML = "&nbsp;$";

                pr.appendChild(t);
                control.Input.onkeydown = function(evt){return ImpEventMoneyFormatCheck(this, (evt ? evt : window.event)); };
		    }
		    else if(control.Type == fType.Datetime)
		    {
		        if (!this.disabled) jCalendar({ base: control.Input, showdate: true, showtime: control.showTime, showbtn: control.Input.getAttribute("cnClndBtn") != "False", openOnFocus: control.Input.getAttribute("cnOpenOnFocus") != "False" }) // base, val, onClose, showbtn, opened
		    }
		}
	}

	self.FindRelated = function(inp)
	{
	    var List = inp.getAttribute("RowChangeNotifyList");
        var names  = (List != null && List != "" ? List.replace(/\s/g, "").split(",") : new Array());
        var res = new Array();
        for(var i=0; i<names.length; i++)
        {
            var parts = names[i].split("/");
    		var rex = new RegExp("^(.+)_" + self.name + "$", "i");
			if(parts.length > 1 && rex.exec(parts[0])) res.push({obj:parts[0], arg:parts[1]});
		}
        return res;
	}

	self.OnGetSingleRowRes = function(res)
	{
	    if (res["errCode"] != null) return OnErr({ obj: self, msg: res["errCode"] });
	    var tabData = res["tableRes"];

	    self.SetUpButtons();

	    self.table = tabData;

	    if (self.args.rowId == "-1" || self.table == null || self.table.rows == null || self.table.columns == null || self.table.rows.length < 1)
	    {
	        self.ClearControls();
	        if (!self.alwaysEnabled) self.Disable(true);

	        for (var i = 0; i < self.controls.length; i++)
	        {
	            var input = self.controls[i].Input;

	            if (self.controls[i].Type == fType.Text || self.controls[i].Type == fType.Textarea) RmvHandler(input, "blur", self.REEndEdit);
	            else if (self.controls[i].Type == fType.Check) RmvHandler(input, "click", self.REEndEdit);
	            else if (self.controls[i].Type == fType.Select) RmvHandler(input, "change", self.REEndEdit);
	            else if (self.controls[i].Type == fType.Datetime) $(input).datepicker("option", { onClose: null });
	            else if (self.controls[i].Type == fType.DDEx && objList[self.controls[i].id]) objList[self.controls[i].id].onValueChanged = null;
	            input.setAttribute("rowId", self.args.rowId);
	            input.setAttribute("dbname", input.id.replace("_" + self.name, ""));

	            if (eq(input.getAttribute("spellcheck"), "True") && (typeof (PNWSpeller) != "undefined") && (PNWSpeller != null))
	                SpellDecorate(input, "en", null, eq(input.getAttribute("nogrammarcheck"), "True"));
	        }
	        return;
	    }

	    for (var i = 0; i < self.controls.length; i++)
	    {
	        var input = self.controls[i].Input;
	        var field = input.id.replace("_" + self.name, "");
	        var value = self.table.rows[0][field];

	        disableCtrl(input, false);
	        var opts = (input ? input.options : null);

	        if (input.id.indexOf("Button_") > 0)
	            continue;

	        value = (value == null ? "" : PrsDate(value));
	        switch (self.controls[i].Type)
	        {
	            case fType.Text: case fType.Textarea: case fType.Auto:
	                if (input.getAttribute("cnFormat")) value = ExecEx(input, "cnFormat", { obj: self, input: input, val: value });
	                input.value = value;
	                if (!self.UpdateButton) AddHandler(input, "blur", self.REEndEdit);
	                if (self.controls[i].Type == fType.Auto && !self.UpdateButton && objList[input.id]) objList[input.id].onSelected = function(params) { self.REEndEdit(params.obj.base); }
	                if (eq(input.getAttribute("spellcheck"), "True") && (typeof (PNWSpeller) != "undefined") && (PNWSpeller != null))
	                    SpellDecorate(input, "en", self.REEndEdit_noSpell, eq(input.getAttribute("nogrammarcheck"), "true"));
	                break;
                case fType.Img:
                    input.src = value;
                break;
	            case fType.Html:
	                input.innerHTML = value; break;
	            case fType.tinyMCE:
	                var mce = typeof (tinyMCE) != "undefined" ? tinyMCE.get(input.id) : null;
	                if (!mce) break;
	                mce.setContent(value);
	                if (!self.UpdateButton) AddHandler(mce.getWin(), "blur", self.REEndEdit);
	                break;
	            case fType.Datetime:
	                $(input).datepicker("setDate", value);
	                if (self.UpdateButton) break;
	                AddHandler(input, "blur", self.REEndEdit);
	                $(input).datepicker("option", { onClose: function(dateText, inst) { self.REEndEdit(inst.input[0], null, true); } });
	                break;
	            case fType.DDEx:
	                DDExRefresh(input.id, AddHash(self.args, "rowId", self.args.rowId), value);
	                if (!self.UpdateButton && objList[input.id]) objList[input.id].onValueChanged = function(params) { self.REEndEdit(params.obj.base); }
	                break;
	            case fType.Money:
	                input.value = FormatMoney(value);
	                if (!self.UpdateButton) AddHandler(input, "blur", self.REEndEdit);
	                break;
	            case fType.Select:
	                if (objList[input.id]) objList[input.id].initval = value;
	                for (var k = 0; k < self.controls[i].Related.length; k++) SetArgs(self.controls[i].Related[k].obj, Hash(self.controls[i].Related[k].arg, value));
	                if (!self.UpdateButton) AddHandler(input, "change", self.REEndEdit);
	                break;
	            case fType.Check:
                    var bool = false;
                    if(value == "" || value == "0" || value == "false" || value == "False" || value == false) {
                        bool = false;
                    }
                    if(value == "1" || value == "true" || value == "True" || value == true) {
                        bool = true;
                    }
	                input.checked = bool;
	                if (!self.UpdateButton) AddHandler(input, "click", self.REEndEdit);
	                break;
	            default:
	                if (input.getAttribute("cnFormat")) value = ExecEx(input, "cnFormat", { obj: self, input: input, val: value });
	                try { input.value = value; SetText(input, value); } catch (ex) { }
	                break;
	        }
	        input.setAttribute("rowId", self.args.rowId);
	        input.setAttribute("dbname", field);
	        if (self.controls[i].Type == fType.Text || self.controls[i].Type == fType.Textarea)
	            input.setAttribute("oldval", text2inputvalue(input, value));
	        else
	            input.setAttribute("oldval", value);
	    }
	    for (var i = 0; i < self.controls.length; i++)
	        if (self.controls[i].Type == fType.Select) DDRefresh(self.controls[i].Input.id, { rowId: self.args.rowId });
	    if (self.ReadOnly)
	    {
	        self.Disable(true);
	    }
	}

    self.Disable = function (dis)
        {
            for (var i = 0; i < self.controls.length; i++)
                disableCtrl(self.controls[i].Input, dis);

            if (self.InsertButton)   disableCtrl(self.InsertButton, dis);
            if (self.UpdateButton)   disableCtrl(self.UpdateButton, dis);
            if (self.ClearButton)    disableCtrl(self.ClearButton, dis);
            if (self.DeselectButton) disableCtrl(self.DeselectButton, dis);
        }

    self.ClearControls = function ()
        {
            for (var i = 0; i < self.controls.length; i++)
                ClearInput(self.controls[i].Input);                
        }

    self.REEndEdit_noSpell = function(inp) 
    {
		self.REEndEdit(inp, null, true);
    }

    self.REEndEdit = function (inp, evt, skipSpell) {
        if (inp == null) return;
        if (typeof (tinyMCE) != "undefined" && tinyMCE.getByWnd != null && tinyMCE.getByWnd(inp)) inp = $get(tinyMCE.getByWnd(inp).id); // tinyMCE hack

        var val = inp.value;
        var type = fieldType(inp);
        if (type == fType.Check) val = (inp.checked ? 1 : 0);
        if (type == fType.DDEx) val = DDExValue(inp.id);
        if (type == fType.Auto) val = AutoCompleteSelValue(inp.id);
        if (type == fType.tinyMCE) val = tinyMCE.get(inp.id).getContent();
        if (inp.value == "null") val = null;

        if (inp.getAttribute("oldval") != val) {
            if (inp.getAttribute("cnValidate") && inp.getAttribute("cnValidate") != "") {
                var result = ExecEx(inp, "cnValidate", { obj: self, val: txt2js(val) });
                if (result != null && (result.length == null && result == false || result.length && result.length > 0)) {
                    if (inp.getAttribute("cnValidateMess") && inp.getAttribute("cnValidateMess") != "") {
                        var mess = inp.getAttribute("cnValidateMess");
                        OnErr({ msg: mess });
                    } else {
                        if (result.length == null)
                            OnErr({ obj: self, msg: "Incorrect " + inp.id.substr(0, inp.id.indexOf("_")), val: val });
                        else OnErr({ obj: self, msg: result });
                    }
                    return inp.value = inp.getAttribute("oldval");
                }
            }
            var rid = inp.getAttribute("rowId");
            ServerCall("SetData", self, { rowId: rid, field: inp.getAttribute("dbname"), value: val, args: self.args, inputid: inp.id });
            // !!! Moved here from OnSetDataRes
            DataChangeNotify(self.DataChangeNotifyList, { act: "sre_setdata", rowId: rid });
        }

        if (!skipSpell && eq(inp.getAttribute("spellcheck"), "True") && (typeof (PNWSpeller) != "undefined") && (PNWSpeller != null))
            SpellDecorate(inp, "en", self.REEndEdit_noSpell, eq(inp.getAttribute("nogrammarcheck"), "True"));
    }

    self.OnSetDataRes = function (res)
        {
            var input = $get(res["args"]["inputid"]);
            var rowId = res["args"]["rowId"];
            var value = res["args"]["value"];
            if (res["errCode"] != null)
            {
				if (input.type == "checkbox")
                  input.checked = input.getAttribute("oldval");
                else
                  input.value = input.getAttribute("oldval");
              return OnErr({obj: self, msg: res["errCode"]});
            }

            input.setAttribute("oldval", text2inputvalue(input, value));

            ExecEx(input, "onSet", {obj: self, res: res});
        }

    self.REUpdateRow_ = function (btn, evt)
        {
            if(self.onValidateUpdate != null && !ExecEx(self, "onValidateUpdate", {obj: self})) return;
            self.REInsertRow(true);
        }

    self.REInsertRow_ = function (btn, evt)
        {
            if(self.onValidateInsert != null && !ExecEx(self, "onValidateInsert", {obj: self})) return;
            self.REInsertRow(false);
        }

        self.REInsertRow = function(updt) {
            var n = new Array();
            var o = new Array();

            for (var i = 0; i < self.controls.length; i++) {
                var input = self.controls[i].Input;

                if (input.id.indexOf("Button_") > 0)
                    continue;

                var val = "";
                var type = fieldType(input);
                if (type == fType.None)
                    continue;
                if (type == fType.Check) val = input.checked;
                else if (type == fType.DDEx) val = DDExValue(input.id);
                else if (type == fType.tinyMCE && typeof(tinyMCE) != "undefined" && tinyMCE.get(input.id)){ val = tinyMCE.get(input.id).getContent(); }
                else if (type == fType.Auto) val = AutoCompleteSelValue(input.id);
                else if (input.value) val = input.value;

                if ((updt && input.getAttribute("oldval") != val) || !updt) { 
                    if (input.getAttribute("cnValidate") && input.getAttribute("cnValidate") != "") {
                        var result = ExecEx(input, "cnValidate", { obj: self, val: txt2js(val) });
                        if (result != null && (result.length == null && result == false || result.length && result.length > 0)) {
                            if (input.getAttribute("cnValidateMess") && input.getAttribute("cnValidateMess") != "") {
                                var mess = input.getAttribute("cnValidateMess");
                                OnErr({ msg: mess });
                            } else {
                                if (result.length == null)
                                    OnErr({ obj: self, msg: "Incorrect " + input.id.substr(0, input.id.indexOf("_")), val: val });
                                else OnErr({ obj: self, msg: result });
                            }
                            return input.focus();
                        }
                    }

                    o.push(val);
                    n.push(input.id.replace("_" + self.name, ""))
                }
            }

            if (n.length > 0)
            {
                ServerCall("InsRow", self, { insNames: n, insArgs: o, rowId: updt ? self.args.rowId : null, args: self.args });
                // !!! Moved here from OnInsRowRes
                DataChangeNotify(self.DataChangeNotifyList, {act: "sre_insertrow"});
            }
			else
				ExecEx(self, "onUpdateClickRes", new Object());
        }

    self.OnInsRowRes = function (res)
    {
        if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});

        if(!self.UpdateButton || self.args.rowId == "-1")
            if(!self.InsertButton || self.InsertButton && !eq(self.InsertButton.getAttribute("notClear"), "True"))     
                self.ClearControls();
				
		ExecEx(self, "onUpdateClickRes", AddHash(res, "obj", self));
    }

    self.REClear = function ()
    {
        for (var i = 0; i < self.controls.length; i++)
        {
            var input = self.controls[i].Input;
            RmvHandler(input, "blur", self.REEndEdit);
            RmvHandler(input, "click", self.REEndEdit);
            ClearInput(input);
			if (eq(input.getAttribute("spellcheck"), "True") && (typeof(PNWSpeller) != "undefined") && (PNWSpeller != null))
				SpellDecorate(input, "en", self.REEndEdit_noSpell, eq(input.getAttribute("nogrammarcheck"), "True"));
        }
    }

    self.REDeselect = function ()
    {
        self.args.rowId = "-1";
        self.OnGetSingleRowRes(new Object());
    }
    
    self.IsLinked = function()
    {
        return self.args.rowId != "-1";
    }
    
    self.LoadData();
}





//-------------------------------------------------- DropDown --------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function DDInit(name, args, rowId)
{
    return PatObj_Init(PatObjType.DD, name, args, {rowId: rowId});
}

function DDRefresh(name, args)
{
    if(objList[name]) objList[name].LoadData(args);
}

function SetArgs(name, args)
{
    if(objList[name]) objList[name].args = AddHash(objList[name].args, args);
}

function DDSetValue(name, val)
{
    if(objList[name]) objList[name].SetValue(val);
}

function DDDisable(name, dis)
{
    if(objList[name]) objList[name].Disable(dis);
}

function DropDown(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

	self.initval = (prms.rowId != null ? prms.rowId : null);
    self.name = name;
    self.args = args;
    self.table = null;

	self.base =  $get(self.name);
	if (self.base == null) return;
	
    if(self.base.getAttribute("onServerCall")) self.onServerCall = self.base.getAttribute("onServerCall");
    if(self.base.getAttribute("onServerRes")) self.onServerRes = self.base.getAttribute("onServerRes");
    if (self.base.getAttribute("onDataLoaded")) self.onDataLoaded = self.base.getAttribute("onDataLoaded");
    if (self.base.getAttribute("isDisabledColName")) self.isDisabledColName = self.base.getAttribute("isDisabledColName");

    if (self.base.getAttribute("RowChangeNotifyList")) self.RowChangeNotifyList = self.base.getAttribute("RowChangeNotifyList");

    self.OnDataChangeNotification = function (params) 
        { 
            self.LoadData();
        }

    self.OnRowChangeNotification = function (params) 
        { 
            if (params != null)
            {
				if (params["rowId"] != null && params["rowidname"] != null)
					self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
    				self.LoadData();
        	}
        }

    self.OnServiceRes = function (res)
    {
        switch(res["cmdCode"])
        {
            case "GetPDTable":   self.OnGetPDTableRes(res); break;
        }
    }

    self.LoadData = function (args)
        {
            if(args) AddHash(self.args, args);
            if (self.base.getAttribute("cnRelTableName")) 
                ServerCall("GetPDTable", self, {pdName: self.base.getAttribute("cnRelTableName"), args: self.args});
            else 
                self.OnGetPDTableRes(new Object());
        }


        self.OnGetPDTableRes = function(res) {
            if (res["errCode"] != null) return OnErr({ obj: self, msg: res["errCode"] });
            self.table = res["tableRes"];

            var value = (self.initval != null ? self.initval : (self.base.value == "" ? self.base.getAttribute("cnDefVal") : self.base.value));
            self.initval = null;

            if (self.table) self.DbRowId = self.table.columns[0].name;
            AddHandler(self.base, "change", self.RowChangeNotify_);

            var cnRelSplitter = (self.base.getAttribute("cnRelSplitter") ? self.base.getAttribute("cnRelSplitter") : ",");
            var cnRelNames = (self.base.getAttribute("cnRelNames") != null ? self.base.getAttribute("cnRelNames").split(cnRelSplitter) : new Array());
            var cnRelValues = (self.base.getAttribute("cnRelValues") != null ? self.base.getAttribute("cnRelValues").split(cnRelSplitter) : new Array());
            var cnRelColors = (self.base.getAttribute("cnRelColors") != null ? self.base.getAttribute("cnRelColors").split(cnRelSplitter) : new Array());
            var cnRelIsDisabled = new Array();
            var cnRelBackColor = self.base.getAttribute("cnRelBackColor");

            if (self.table && self.table.rows)
                for (var i = 0; i < self.table.rows.length; i++) {
                var len = cnRelNames.length != null ? cnRelNames.length : 0;
                cnRelNames[len] = self.table.rows[i][self.table.columns[1].name];
                cnRelValues[len] = self.table.rows[i][self.table.columns[0].name];
                if (self.isDisabledColName != null)
                    cnRelIsDisabled[len] = self.table.rows[i][self.isDisabledColName];
                if (cnRelBackColor != null) cnRelColors[len] = self.table.rows[i][cnRelBackColor];
            }

            self.base.innerHTML = "";
            for (var i = 0; i < cnRelNames.length; i++) {
                var el = new Option(cnRelNames[i], cnRelValues[i]);
                if (cnRelIsDisabled[i]) {
                    el.disabled = "true";
                }
                self.base.options.add(el);
                if (cnRelColors != null && cnRelColors.length == cnRelNames.length) {
                    el.style.backgroundColor = cnRelColors[i];
                    if (cnRelColors[i] == 'Black' || cnRelColors[i] == '#000000') {
                        el.style.color = "White";
                    }
                }
            }

            var display = self.base.style.display;
            self.base.style.display = "none"; self.base.style.display = display; // tabs hack

          if (value != null && value.toString() != "") self.base.value = value;
          if (self.base.value != value) self.base.selectedIndex = 0;
        }

    self.RowChangeNotify_ = function (dd, evt)
        {
            RowChangeNotify(self.RowChangeNotifyList, {rowId: self.base.value != "" ? self.base.value : self.initval, rowidname: self.DbRowId});
        }

    self.SetValue = function (val)
        {
            self.initval = val;
            self.base.value = val;
            self.RowChangeNotify_();
        }

    self.Disable = function (dis)
        {
            self.base.disabled = dis;
        }
        
    self.LoadData();
}

//------------------------------------------------- DropDownEx -------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function DDExInit(name, args, defval)
{
    return PatObj_Init(PatObjType.DDEx, name, args, {defval: defval});
}


function DDExDel(name)
{
    if(!objList[name]) return;
    
    var self = objList[name];
    
    HidePopUp(self.div);
    RmvHandler(window, "resize", self.changePos);
    RmvHandler(document, "click", self.Hide);    
    
    delete objList[name];
}

function DDExRefresh(name, args, val)
{
    if(objList[name]) objList[name].Refresh(args, val);
}

function DDExDisable(name, dis)
{
    if(objList[name]) objList[name].Disable(dis);
}

function DDExValue(name)
{
    if(objList[name]) return objList[name].value;
}

function DDExVisual(name)
{
    if(objList[name]) return objList[name].input.innerHTML;
}

function DDExSetValue(name, val) // No notification raised on this. We should care to refresh related objects
{
    if(objList[name]) return objList[name].SetValue(val);
}

function DDExSelectAll(name, select)
{
    if(objList[name]) return objList[name].SelectAll(select);
}

function DDExHide(name, fireEvn)
{
    if (objList[name]) objList[name].Hide(fireEvn);
}

function DDExClear(name)
{
    if(objList[name]) objList[name].Clear();
}

function DDExClearOpts(name)
{
    if(objList[name]) objList[name].ClearOpts();
}

//DO NOT USE. Should be replaced with PatObj_GetArgs
function DDExGetArgs(name)
{
    if(objList[name]) return objList[name].args;
}

function DropDownEx(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
    self.args = args;
    self.table = null;

    self.base = $get(self.name);
    if(self.base == null) return;

    self.selOpts  = new Object();
    self.addOpts  = "";
    self.multSel  = eq(self.base.getAttribute("multSel"), "True");
    self.showVal  = eq(self.base.getAttribute("showVal"), "True");
    self.editable = eq(self.base.getAttribute("editable"), "True");
    self.opened   = eq(self.base.getAttribute("opened"), "True");
    self.selAll = self.multSel && !eq(self.base.getAttribute("selAll"), "False");
    self.closeOpts = eq(self.base.getAttribute("continue"), "True");
    self.onlyDiv  = eq(self.base.getAttribute("onlyDiv"), "True");
    self.leaveOpen = eq(self.base.getAttribute("leaveOpen"), "True");

	self.defVl = (prms.defval != null ? prms.defval : self.base.getAttribute("cnDefVal"));
	self.selectFirst = eq(self.base.getAttribute("selectFirst"), "True");
	self.selectAll = eq(self.base.getAttribute("selectAll"), "True");
	self.selectedAllValue = (self.base.getAttribute("selectedAllValue") ? self.base.getAttribute("selectedAllValue") : "");
    self.value = null;
    self.dataLoaded = false;

    if(self.base.getAttribute("onServerCall")) self.onServerCall = self.base.getAttribute("onServerCall");
    if(self.base.getAttribute("onServerRes")) self.onServerRes = self.base.getAttribute("onServerRes");
    if(self.base.getAttribute("onDataLoaded")) self.onDataLoaded = self.base.getAttribute("onDataLoaded");
        
    self.divCss = (self.base.getAttribute("divCss") ? self.base.getAttribute("divCss") : self.onlyDiv ? "def_ddex_od_css" : "def_ddex_div_css");
    self.optCss = (self.base.getAttribute("optCss") ? self.base.getAttribute("optCss") : "def_ddex_opt_css");
    self.spnCss = (self.base.getAttribute("spnCss") ? self.base.getAttribute("spnCss") : "def_ddex_spn_css");
    self.hovCss = (self.base.getAttribute("hovCss") ? self.base.getAttribute("hovCss") : "def_ddex_hov_css");
    self.selCss = (self.base.getAttribute("selCss") ? self.base.getAttribute("selCss") : "def_ddex_sel_css");
    self.ocnCss = (self.base.getAttribute("ocnCss") ? self.base.getAttribute("ocnCss") : "def_ddex_optcnt_css");
    self.saCss  = (self.base.getAttribute("saCss")  ? self.base.getAttribute("saCss")  : "def_ddex_sa_css");
    
    self.colID  = (self.base.getAttribute("colID")  ? self.base.getAttribute("colID")  : null);
    self.colName = (self.base.getAttribute("colName") ? self.base.getAttribute("colName") : null);

    self.divW = (self.base.getAttribute("divW")  ? self.base.getAttribute("divW")  : "150px");
    self.divH = self.base.getAttribute("divH");

    self.divWe = self.base.getAttribute("divWe");
    self.divHe = self.base.getAttribute("divHe");

    self.onValueChanged = self.base.getAttribute("onValueChanged");
    if (self.base.getAttribute("RowChangeNotifyList")) self.RowChangeNotifyList = self.base.getAttribute("RowChangeNotifyList");
    
    self.defSubscr = (self.base.getAttribute("defSubscr")? self.base.getAttribute("defSubscr") : "Please Select");
    self.input = document.createElement("div");
    self.input.className = (self.base.getAttribute("impCss")? self.base.getAttribute("impCss") : "def_ddex_imp_css");    
    self.input.innerHTML = self.defSubscr;
    if(self.base.getAttribute("impW")) self.input.style.width = self.base.getAttribute("impW");
    
    self.button = document.createElement("div");    
    self.button.className = (self.base.getAttribute("btnCss") ? self.base.getAttribute("btnCss") : "def_ddex_btn_css");    

    self.CntTableCss = (self.base.getAttribute("cntTableCss") ? self.base.getAttribute("cntTableCss") : "def_ddex_cnttable_css");    
    self.base.innerHTML = "<table class='" + self.CntTableCss + "'><tr><td></td><td></td></tr></table>";
    self.base.childNodes[0].childNodes[0].childNodes[0].childNodes[0].appendChild(self.input);
    self.base.childNodes[0].childNodes[0].childNodes[0].childNodes[1].appendChild(self.button);
    if(self.onlyDiv) self.base.childNodes[0].style.display = "none";

    self.div = document.createElement("div");
    self.div.className = self.divCss;
    self.base.appendChild(self.div);
    if(!self.onlyDiv) self.div.style.display = "none";

    self.sadiv = document.createElement("div");
    self.sadiv.className = self.saCss;
    if (self.selAll || self.closeOpts) self.div.appendChild(self.sadiv);

    self.ocndiv = document.createElement("div");
    self.ocndiv.style.minHeight = "10px";
    if(self.divW) self.ocndiv.style.minWidth = self.divW.toString();
    if(self.divH) self.ocndiv.style.maxHeight = self.divH.toString();

    if(self.divWe) self.ocndiv.style.width = self.divWe.toString();
    if(self.divHe) self.ocndiv.style.height = self.divHe.toString();

    self.ocndiv.className = self.ocnCss;
    self.div.appendChild(self.ocndiv);

    self.OnDataChangeNotification = function (params) { 
        self.LoadData();
    };

    self.OnRowChangeNotification = function (params) { 
            if(params != null && params["rowId"] != null && params["rowidname"] != null)
                self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
	            self.LoadData();
    };

    self.OnServiceRes = function (res) {
        switch(res["cmdCode"])
        {
            case "GetPDTable":   self.OnGetPDTableRes(res); break;
        }
    };

    self.LoadData = function () {
        var s = $get(self.name);
        if (s && s.getAttribute("cnRelTableName"))
            ServerCall("GetPDTable", self, { pdName: s.getAttribute("cnRelTableName"), args: self.args });
        else
            self.OnGetPDTableRes(new Object());
    };

    self.OnGetPDTableRes = function(res) {
            if (res["errCode"] != null) return OnErr({ obj: self, msg: res["errCode"] });
            self.table = res["tableRes"];

            if (self.table) self.DbRowId = self.table.columns[0].name;

            var cnRelSplitter = (self.base.getAttribute("cnRelSplitter") ? self.base.getAttribute("cnRelSplitter") : ",");
            var cnRelNames = (self.base.getAttribute("cnRelNames") != null ? self.base.getAttribute("cnRelNames").split(cnRelSplitter) : new Array());
            var cnRelValues = (self.base.getAttribute("cnRelValues") != null ? self.base.getAttribute("cnRelValues").split(cnRelSplitter) : new Array());
            var cnRelColors = (self.base.getAttribute("cnRelColors") != null ? self.base.getAttribute("cnRelColors").split(cnRelSplitter) : new Array());
            var cnRelBackColor = self.base.getAttribute("cnRelBackColor");
            var cnRelShowInPD = self.base.getAttribute("cnRelShowInPD");
            var cnShowInPD = new Array();

            if (self.table && self.table.rows)
                for (var i = 0; i < self.table.rows.length; i++) {
                var len = cnRelNames.length != null ? cnRelNames.length : 0;
                cnRelNames[len] = self.table.rows[i][self.colName ? self.colName : self.table.columns[1].name];
                cnRelValues[len] = self.table.rows[i][self.colID ? self.colID : self.table.columns[0].name];
                if (cnRelBackColor != null) cnRelColors[len] = self.table.rows[i][cnRelBackColor];
                if (cnRelShowInPD != null) cnShowInPD[len] = self.table.rows[i][cnRelShowInPD];
            }
            var sadivval = "";
            if (self.selAll) sadivval = "<a href='javascript:DDExSelectAll(\"" + self.name + "\", true)'>Select All</a>&nbsp;<a href='javascript:DDExSelectAll(\"" + self.name + "\", false)'>Unselect All</a>";
            if (self.closeOpts) {
                if (sadivval != "") sadivval += "&nbsp;";

                sadivval += "<a href='javascript:DDExHide(\"" + self.name + "\", true)'>Continue</a>";
            }
            self.sadiv.innerHTML = sadivval;
            self.ocndiv.innerHTML = "";

            for (var i = 0; i < cnRelNames.length; i++) {
                self.addOpt(cnRelNames[i], cnRelValues[i], cnRelColors[i], null, !(cnShowInPD.length > i ? cnShowInPD[i] : true));
            }

            self.dataLoaded = true;


            if (self.multSel && self.selectAll) {
                self.value = cnRelValues.join();
                self.SetValue(self.value);
            }
            else {
                var contain = false;
                var vals = (self.value != null && self.value != "" ? self.value.toString().replace(/\s/g, "").split(",") : new Array());

                for (var i = vals.length - 1; i >= 0; i--) {
                    var numVal = parseFloat(vals[i]);
                    var val = isNaN(numVal) ? vals[i] : numVal;
                    if ($.inArray(numVal, cnRelValues) != -1) {
                        contain = true;
                    }
                    else {
                        vals.splice(i, 1);
                    }
                }
                self.value = vals.join();

                if (self.newval || self.newval == '') {
                    self.value = self.newval;
                    self.newval = null;
                    self.SetValue(self.value);
                }
                else if (self.value && contain)
                    self.SetValue(self.value);
                else if (!self.multSel && self.defVl == null && cnRelValues.length > 0 && self.selectFirst) {
                    self.value = cnRelValues[0];
                    self.SetValue(self.value);
                }
                else {
                    self.value = self.defVl;
                    self.SetValue(self.value);
                }
            }
            RowChangeNotify(self.RowChangeNotifyList, { rowId: self.value, rowidname: self.DbRowId });
            if (self.opened) self.onInpMouseClick();

            //            if (iPad) // iPad scroll
            //            {
            //                if (!self.onlyDiv) self.Show();
            //                var h = $(self.ocndiv).height() == 0 ? self.ocndiv.style.height : $(self.ocndiv).height();
            //                self.ocndiv.style.maxHeight = "";
            //                self.ocndiv.style.height = "";
            //                $(self.ocndiv).iScroll({ height: h });
            //                if (!self.onlyDiv) self.Hide();
            //            }
        }

        self.addOpt = function(name, val, bColor, Color, hidden) {
            var opt = document.createElement("div");
            opt.className = self.optCss;
            if (name) opt.innerHTML = "<table><tr>" + (self.multSel ? "<td><input type='checkbox'/></td>" : "") + "<td><div class='" + self.spnCss + "'>" + Quote(name) + "</div></td></tr></table>";
            opt.setAttribute("value", val);
            opt.setAttribute("name", name);
            if (hidden) opt.style.display = "none";
            if (bColor) { opt.style.backgroundColor = bColor; }
            if (Color) { opt.style.color = Color; }
            AddHandler(opt, "mouseover", self.onOptMouseOver);
            AddHandler(opt, "mouseout", self.onOptMouseOut);
            AddHandler(opt, "click", self.onOptMouseClick);
            self.ocndiv.appendChild(opt);
        }
    
    self.Refresh = function (args, val)
    {
        if(args) AddHash(self.args, args);
        if(val || val == '') self.newval = val;
        objList[name].LoadData();
    }

    self.onInpMouseClick = function(obj, evt) 
    {
        if (self.div.style.display == "") return;
        if (self.div.parentNode.tagName.toUpperCase() != "BODY") document.body.appendChild(self.div);
        //ShowPopUp(self.div, self.input);
        window.setTimeout(self.Show, 1);
        AddHandler(window, "resize", self.changePos);
        AddHandler(self.div, "click", self.onAllBtnMouseClick);            
        //if(evt) CancelBubbling(evt);
    }
    
    self.changePos = function (obj, evt)
        {
    	    SetDropDownPos(self.input, self.div);
        }

    self.onOptMouseClick = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            if(self.multSel)
            {
                var optText = opt.childNodes[0].childNodes[0].childNodes[0].childNodes[1].childNodes[0].innerHTML;
                if(opt.className == self.selCss) 
                {
                    opt.className = self.optCss; 
                    opt.innerHTML = "<table><tr><td><input type='checkbox' /></td><td><div class='" + self.spnCss + "'>" + optText + "</div></td></tr></table>"; // checked = true/false work bad with CancelBubbling
                    delete self.selOpts[opt.getAttribute("value")]; 
                }
                else
                {
                    self.selOpts[opt.getAttribute("value")] = opt;
                    opt.innerHTML = "<table><tr><td><input type='checkbox' checked/></td><td><div class='" + self.spnCss + "'>" + optText + "</div></td></tr></table>"; // checked = true/false work bad with CancelBubbling
                    opt.className = self.selCss;
                }
                CancelBubbling(evt);
            }
            else
            {
                 if (!self.leaveOpen) self.Hide();
                if(opt.className == self.selCss) return;
                self.ClearVals();
                self.selOpts[opt.getAttribute("value")] = opt;
                opt.className = self.selCss;
            }
            self.CalcValues();
            RowChangeNotify(self.RowChangeNotifyList, {rowId: self.value, rowidname: self.DbRowId});
            ExecEx(self, "onValueChanged", {obj: self, val: self.value, opt: opt});
        }

    self.onOptMouseOver = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            if(opt.className == self.optCss) opt.className = self.hovCss;
        }

    self.onOptMouseOut = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            if(opt.className == self.hovCss) opt.className = self.optCss;
        }

    self.Disable = function (dis)
        {
            if(dis) RmvHandler(self.input,  "click", self.onInpMouseClick); else AddHandler(self.input,  "click", self.onInpMouseClick);
            if(dis) RmvHandler(self.button, "click", self.onInpMouseClick); else AddHandler(self.button, "click", self.onInpMouseClick);
        }
        
    self.ClearVals = function ()
        {
            self.addOpts = "";
            for (var id in self.selOpts) 
            { 
                self.selOpts[id].className = self.optCss; 
                if(self.multSel) $(self.selOpts[id]).find(":input").attr("checked", false);
                delete self.selOpts[id]; 
            }
        }

        self.CalcValues = function() {
            var v = "", n = ""; selCount = 0;
            for (var id in self.selOpts) {
                v += id + ",";
                n += self.selOpts[id].getAttribute("name") + ", ";
                selCount++;
            }

            if (self.selectedAllValue != "" && selCount > 0 && selCount == self.ocndiv.childNodes.length && n != ", ") {
                n = self.selectedAllValue;
            }

            v = (v.length > 1 ? v.substring(0, v.length - 1) : "");
            self.value = v + (v != "" && self.addOpts != "" ? "," : "") + self.addOpts;
            if (self.selectedAllValue == "" || (self.selectedAllValue != "" && n != self.selectedAllValue)) {
                n = Quote(n.length > 2 ? n.substring(0, n.length - 2) : "");
                n = n + (n != "" && self.addOpts != "" ? ", " : "") + self.addOpts;
            }
            if (n == "") {
                n = self.defSubscr;
            }
            if (self.showVal) {
                self.input.innerHTML = self.ClearValue(self.value);
            }
            else {
                self.input.innerHTML = self.ClearValue(n);
            }
            for (var id in self.selOpts) {
                self.input.style.backgroundColor = self.selOpts[id].style.backgroundColor;
                return;
            }
        }

    self.Clear = function ()
        {
            self.ClearVals();
            self.CalcValues();
        }

    self.ClearOpts = function ()
    {
        self.Clear();
        self.ocndiv.innerHTML = "";
    }

    self.SetValue = function(val) {
        self.ClearVals();
        self.value = val;
        if (!self.dataLoaded) {
            self.newval = val;
            return;
        }
        var vals = (val != null && val != "" ? val.toString().replace(/(^\s*)|(\s*$)/g, "").split(",") : new Array());
        var op = null;

        for (var i = 0; i < vals.length; i++) {
            for (var j = 0; j < self.ocndiv.childNodes.length; j++) {
                op = self.ocndiv.childNodes[j];
                if (op.getAttribute("value") == vals[i]) {
                    self.selOpts[vals[i]] = op;
                    if (self.multSel) $(op).find(":input").attr("checked", true);
                    op.className = self.selCss;
                }
            }
            if (self.selOpts[vals[i]] == null) self.addOpts = self.addOpts + (self.addOpts != "" ? ", " : "") + vals[i];
        }
        if (self.editable) self.input.innerHTML = self.ClearValue(val);
        self.CalcValues();
    }
        
    self.ClearValue = function (val)    
        {
            if(!val) return "";
            return val.replace(/<[^>^<]*>/g, "");
        }

    self.LoadValue = function ()    
        {
            self.SetValue(self.ClearValue(self.input.innerHTML));        
            RowChangeNotify(self.RowChangeNotifyList, {rowId: self.value, rowidname: self.DbRowId});
            ExecEx(self, "onValueChanged", {obj: self, val: self.value, opt: null});
        }

        self.SelectAll = function(select) {
            self.ClearVals();

            for (var j = 0; select && j < self.ocndiv.childNodes.length; j++) {
                var op = self.ocndiv.childNodes[j];
                self.selOpts[op.getAttribute("value")] = op;
                if (self.multSel) $(op).find(":input").attr("checked", select);
                op.className = self.selCss;
            }
            self.CalcValues();
            RowChangeNotify(self.RowChangeNotifyList, { rowId: self.value, rowidname: self.DbRowId });
            ExecEx(self, "onValueChanged", { obj: self, val: self.value, opt: null });
        }

    self.Hide = function (fierEvt)
        {
            if(self.div.style.display == "none") return;
            self.div.style.display = "none";
            if(fierEvt != false) ExecEx(self.base, "onHide", {obj: self, val: self.base.value});
        }

        self.onAllBtnMouseClick = function(opt, evt) {
            CancelBubbling(evt);
        }

    self.Show = function ()
        {
            //OnShowPopUp(self.div);
        
            if(self.div.parentNode.tagName.toUpperCase() != "BODY")  document.body.appendChild(self.div);
	        self.div.style.display = "";
	        SetDropDownPos(self.input, self.div);
        }

    if(!self.onlyDiv)
    {
        AddHandler(document, "click", self.Hide);        
        if(self.editable) AddHandler(self.input, "blur", self.LoadValue);
        AddHandler(self.input,  "click", self.onInpMouseClick);
        AddHandler(self.button, "click", self.onInpMouseClick);
    }
    self.LoadData();
}


//------------------------------------------------- AutoComplete -------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function AutoCompleteInit(name, args, prms)
{
    return PatObj_Init(PatObjType.ACmplt, name, args, prms);
}

function AutoCompleteDel(name)
{
    if(!objList[name]) return;
    
    var self = objList[name];

    self.Hide(false);
    RmvHandler(document, "click", self.Hide);
    RmvHandler(self.base, "keydown", self.onKeyPress);
    RmvHandler(self.base, "focus", self.onFocus);
    
    //objList[name].div.parentNode.removeChild(objList[name].div);
    
    delete objList[name];
}

function AutoCompleteValue(name)
{
    if(objList[name]) return objList[name].base.value;
}

function AutoCompleteSelValue(name)
{
    if(objList[name]) return objList[name].selValue;
}

function AutoCompleteSetActRelValue(name, rval)
{
    if(objList[name]) return objList[name].SetActRelValue(rval);
}

function AutoCompleteClear(name)
{
    var obj = objList[name];
    
    if (obj)
    {
        var input = objList[name].base;
        var val  = input.getAttribute("cnDefVal") ? input.getAttribute("cnDefVal") : "";
        input.value = val;
        objList[name].selValue = val;
    }
}


function AutoCompleteHide(name, fierEvt)
{
    if(objList[name]) return objList[name].Hide(fierEvt);
}

function AutoCompleteDisable(name, dis)
{
    if(objList[name]) objList[name].Disable(dis);
}

function AutoComplete(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
    self.args = args;
    self.table = null;

    self.base = $get(self.name);
    if(self.base == null) return;

    if(self.base.getAttribute("onServerCall")) self.onServerCall = self.base.getAttribute("onServerCall");
    if(self.base.getAttribute("onServerRes")) self.onServerRes = self.base.getAttribute("onServerRes");
    if(self.base.getAttribute("onDataLoaded")) self.onDataLoaded = self.base.getAttribute("onDataLoaded");

    self.divCss = (self.base.getAttribute("divCss") ? self.base.getAttribute("divCss") : "def_ddex_div_css");
    self.optCss = (self.base.getAttribute("optCss") ? self.base.getAttribute("optCss") : "def_ddex_opt_css");
    self.spnCss = (self.base.getAttribute("spnCss") ? self.base.getAttribute("spnCss") : "def_ddex_spn_css");
    self.hovCss = (self.base.getAttribute("hovCss") ? self.base.getAttribute("hovCss") : "def_ddex_hov_css");
    self.ocnCss = (self.base.getAttribute("ocnCss") ? self.base.getAttribute("ocnCss") : "def_ddex_optcnt_css");
    self.saCss  = (self.base.getAttribute("saCss")  ? self.base.getAttribute("saCss")  : "def_ddex_sa_css");
            
    self.cnMaxOpts = (self.base.getAttribute("cnMaxOpts") ? self.base.getAttribute("cnMaxOpts") : null);        

    self.cnDelay = (self.base.getAttribute("cnDelay") ? self.base.getAttribute("cnDelay") : 300);        
    
    self.divW = (self.base.getAttribute("divW") ? self.base.getAttribute("divW") : "150px");
    self.divH = self.base.getAttribute("divH");

    self.base.setAttribute("autocomplete","off");
    
    self.div = document.createElement("div");
    self.div.className = self.divCss;
    self.div.style.display = "none";
    self.base.parentNode.appendChild(self.div);

    self.sadiv = document.createElement("div");
    self.sadiv.className = self.saCss;
    self.div.appendChild(self.sadiv);

    self.ocndiv = document.createElement("div");
    self.ocndiv.style.minHeight = "10px";
    if(self.divW) self.ocndiv.style.minWidth = self.divW.toString();
    if(self.divH) self.ocndiv.style.maxHeight = self.divH.toString();
    self.ocndiv.className = self.ocnCss;
    self.div.appendChild(self.ocndiv);

    if (self.base.getAttribute("RowChangeNotifyList")) self.RowChangeNotifyList = self.base.getAttribute("RowChangeNotifyList"); //only for selected values from list now

    self.cnRelTableName = self.base.getAttribute("cnRelTableName");
    self.cnFilter = self.base.getAttribute("cnFilter") ? self.base.getAttribute("cnFilter") : "filter";
    self.cnColumn = self.base.getAttribute("cnColumn");
    self.cnVColumn = self.base.getAttribute("cnVColumn");
    
    self.onSelected = self.base.getAttribute("onSelected");
    
    self.selValue = ""; // have a sence only if cnVColumn is setted
    
    self.timer = null;
    self.opts = new Array();
    self.aOptInsex = -1;

    self.OnServiceRes = function (res)
    {
        switch(res["cmdCode"])
        {
            case "GetPDTable":   self.OnGetPDTableRes(res); break;
        }
    }

    self.Disable = function (dis)
    {
        self.base.disabled = dis;
    }

    self.LoadData = function (opts)
        {
            self.args[self.cnFilter] = self.base.value;
             
            if (self.cnRelTableName) 
                ServerCall("GetPDTable", self, {
                pdName: self.cnRelTableName,
                pagesize: self.cnMaxOpts,
                args: typeof (opts) == "undefined" || !opts || typeof (opts.preselectId) == "undefined" ? self.args : AddHash(CloneHash(self.args), "preselectId", opts.preselectId)
            });
            else 
                self.OnGetPDTableRes(new Object());
        }
    self.OnRowChangeNotification = function(params) {
        if (params != null && params["rowId"] != null && params["rowidname"] != null)
            self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
    }

    self.OnGetPDTableRes = function (res)
        {
            if (res["errCode"] != null) return OnErr({obj: self, msg: res["errCode"]});
            self.table = res["tableRes"];
            
            self.selValue = "";
            
            var cnRelSplitter  = (self.base.getAttribute("cnRelSplitter") ? self.base.getAttribute("cnRelSplitter") : ",");
            var cnRelNames  = (self.base.getAttribute("cnRelNames")  != null ? self.base.getAttribute("cnRelNames").split(cnRelSplitter) : new Array());
            var cnRelValues = (self.base.getAttribute("cnRelValues") != null ? self.base.getAttribute("cnRelValues").split(cnRelSplitter) : new Array());
            var cnRelColors = (self.base.getAttribute("cnRelColors") != null ? self.base.getAttribute("cnRelColors").split(cnRelSplitter) : new Array());
            var cnRelBackColor = self.base.getAttribute("cnRelBackColor");

            if(self.table && self.table.rows)
                for (var i=0; i < self.table.rows.length; i++)
                {
                    var len = cnRelNames.length != null ? cnRelNames.length : 0;
                    var cname = (self.cnColumn ? self.cnColumn : self.table.columns[0].name);
                    cnRelNames[len]  = self.table.rows[i][cname];
                    if(self.cnVColumn) cnRelValues[len]  = self.table.rows[i][self.cnVColumn];
                    if (cnRelBackColor != null) cnRelColors[len] = self.table.rows[i][cnRelBackColor];
                }

            self.ocndiv.innerHTML = "";
            self.opts.length = 0;
            self.aOptInsex = -1;
            var preselectId = null;
            if (typeof (res.args.args.preselectId) != "undefined" && res.args.args.preselectId != -1)
	            preselectId = res.args.args.preselectId;
            for (var i = 0, j = 0; i < cnRelNames.length; i++)
            {
                if(!cnRelNames[i] || cnRelNames[i] == "") continue;
                var opt = document.createElement("div");
                opt.className = self.optCss;
                opt.setAttribute("index", j++);
                opt.innerHTML = "<span class='" + self.spnCss + "' value='" + (cnRelValues[i] ? Quote(cnRelValues[i]) : "") + "'>" + Quote(cnRelNames[i]) + "</span>";
                if(Quote(cnRelNames[i]) == self.base.value)
                    self.selValue = Quote(cnRelValues[i]);
                if (preselectId && preselectId == cnRelValues[i])
                {
                    self.selValue = Quote(cnRelValues[i]);
                    self.base.value = cnRelNames[i];
                }
                opt.setAttribute("name", cnRelNames[i]);
                if(cnRelColors){opt.style.backgroundColor = cnRelColors[i];}
                AddHandler(opt, "mouseover", self.onOptMouseOver);
                AddHandler(opt, "mouseout", self.onOptMouseOut);
                AddHandler(opt, "click", self.onOptMouseClick);
                self.ocndiv.appendChild(opt);
                self.opts.push(opt);
            }
            
            if(self.selValue == null || self.selValue == "") self.selValue = self.base.value;
            if (preselectId)
                self.onClose();
            else 
            {
                if(self.opts.length > 0)
                {
                    self.Show();
                    AddHandler(window, "resize", self.changePos);
                }
                else
                    self.Hide(false);
            }
        }

    self.onKeyPress = function (obj, evt)
        {
            switch(evt.keyCode)
            {
                case BOTTOM_KEY_CODE:
                    if(self.div.style.display == "none") break;
                    self.SetActiveOpt(self.aOptInsex + 1);
                    return;
                case TOP_KEY_CODE:
                    self.SetActiveOpt(self.aOptInsex - 1);
                    return;
                case ENTER_KEY_CODE: case TAB_KEY_CODE:
                    self.Close();
                    return;
                case ESC_KEY_CODE:
                    self.Hide(false);
                    return;
                case CTRL_KEY_CODE:
                case SHIFT_KEY_CODE:
                case ALT_KEY_CODE:
                    return;
            }
            window.setTimeout(self.onKeyup, 1);
	        if(self.timer) window.clearTimeout(self.timer);
	        self.timer = window.setTimeout(self.LoadData, self.cnDelay);
        }
    
    self.onKeyup = function (obj, evt)
        {
            self.selValue = self.base.value;
        }
        
    self.onFocus = function (obj, evt)
        {
	        if(self.timer) window.clearTimeout(self.timer);
	        self.timer = window.setTimeout(self.LoadData, self.cnDelay);
        }
    
    self.onBaseClick = function (obj, evt)
        {
            CancelBubbling(evt);
        }
    
    self.changePos = function (obj, evt)
        {
    	    SetDropDownPos(self.base, self.div);
        }

    self.onOptMouseClick = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            self.Close();
        }

    self.Close = function ()
        {
            if(self.aOptInsex >= 0 && self.aOptInsex < self.opts.length)
            {
                self.base.value = UnQuote(self.opts[self.aOptInsex].firstChild.innerHTML);
                self.selValue = self.opts[self.aOptInsex].firstChild.getAttribute("value");
                if(self.selValue == null || self.selValue == "") self.selValue = self.base.value;
                self.onClose();
            }
            self.Hide();
        }

    self.onClose = function()
        {
            ExecEx(self, "onSelected", { obj: self, val: self.base.value, selval: self.selValue });
            RowChangeNotify(self.RowChangeNotifyList, { rowId: self.selValue, rowidname: null });
        }

    self.onOptMouseOver = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            self.SetActiveOpt(opt.getAttribute("index"));
        }

    self.onOptMouseOut = function (opt, evt)
        {
            if(!eq(opt.tagName, "div")) return;
            self.SetActiveOpt();
        }

    self.SetActiveOpt = function (index)
        {
            if(self.opts.length == 0) return;
            if(self.aOptInsex >= 0 && self.aOptInsex < self.opts.length)self.opts[self.aOptInsex].className = self.optCss;
            self.aOptInsex = -1;

            if(index == null) return;
            if(index < 0) index = self.opts.length - 1;
            if(index >= self.opts.length) index = 0;
            
            self.aOptInsex = index;
            self.opts[index].className = self.hovCss;
        }

	self.SetActRelValue = function (rval)
		{
			for (var i = 0; i < self.table.rows.length; i++)
			{
				if (self.table.rows[i].id == rval)
				{
                    self.selValue = Quote(rval);
                    self.base.value = self.table.rows[i].ttl;
				}
			}
		}

    self.Hide = function (fierEvt)
        {
            if(self.div.style.display == "none") return;
            self.div.style.display = "none";
            if(fierEvt != false) ExecEx(self.base, "onHide", {obj: self, val: self.base.value});
        }

    self.Show = function ()
        {
            //OnShowPopUp(self.div);

            if(self.div.parentNode.tagName.toUpperCase() != "BODY")  document.body.appendChild(self.div);
	        self.div.style.display = "";
	        SetDropDownPos(self.base, self.div);
        }

    AddHandler(document, "click", self.Hide);
    AddHandler(self.base, "keydown", self.onKeyPress);
    AddHandler(self.base, "focus", self.onFocus);
    AddHandler(self.base, "click", self.onBaseClick);

    if (typeof (prms.preselectId) != "undefined")
        self.LoadData({ preselectId: prms.preselectId });
}

//--------------------------------------------------- TabsObj --------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function TabsInit(name, selected, loader)
{
    return PatObj_Init(PatObjType.Tabs, name, null, {selected: selected, loader: loader});
}

function TabsSetSelected(name, tabname)
{
    if(objList[name]) objList[name].SetSelected(tabname);
}

function TabsDisable(name, tabname)
{
    if(objList[name]) objList[name].Disable(tabname);
}

function TabsHide(name, hide)
{
    if(objList[name]) objList[name].Hide(hide);
}

function TabsHideTab(name, tabname, hide)
{
    if(objList[name]) objList[name].HideTab(tabname, hide);
}

function TabsSetTabCaption(name, tabname, caption)
{
    if(objList[name]) objList[name].SetTabCaption(name, tabname, caption);
}

function TabsGetSelected(name)
{
    if(objList[name]) return objList[name].selected;
    return null; 
}

function TabsObj(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
    self.selected = prms.selected;
	self.tabsContainer = $get(name + "_TabsContainer");
	self.tabsMenu = $get(name + "_TabsMenu");	
	
	if(self.tabsContainer == null || self.tabsMenu == null || self.tabsContainer.childNodes.length == 0) return;

	self.tabCss = self.tabsMenu.getAttribute("tabCss");
	self.atabCss = self.tabsMenu.getAttribute("atabCss");
	self.dtabCss = self.tabsMenu.getAttribute("dtabCss");
	self.htabCss = self.tabsMenu.getAttribute("htabCss");
	
	self.outSpCss = self.tabsMenu.getAttribute("outSpCss");
	self.aoutSpCss = self.tabsMenu.getAttribute("aoutSpCss");
	self.doutSpCss = self.tabsMenu.getAttribute("doutSpCss");
	self.houtSpCss = self.tabsMenu.getAttribute("houtSpCss");

	self.inSpCss = self.tabsMenu.getAttribute("inSpCss");
	self.ainSpCss = self.tabsMenu.getAttribute("ainSpCss");
	self.dinSpCss = self.tabsMenu.getAttribute("dinSpCss");
	self.hinSpCss = self.tabsMenu.getAttribute("hinSpCss");

    self.tabs = new Object();
    self.itms = new Object();

    self.LoadObj = function () 
        { 
	        for(var i=0; i<self.tabsContainer.childNodes.length; i++)
	        {
	            if(self.tabsContainer.childNodes[i].getAttribute == null || self.tabsContainer.childNodes[i].getAttribute("name") == null || self.tabsContainer.childNodes[i].style.display == "none") continue;
	            var name = self.tabsContainer.childNodes[i].getAttribute("name");
	            if(self.selected == null) self.selected = name;
	            self.tabs[name] = self.tabsContainer.childNodes[i];
                if(name != self.selected) self.tabs[name].style.display = "none";

                var ul = document.createElement("ul");
                var lnk = document.createElement("li");
                var outSp = document.createElement("span");
                var inSp = document.createElement("span");
                AddHandler(lnk, "click", self.OnItemClick);
                AddHandler(lnk, "mouseover", self.OnItemMOver);
                AddHandler(lnk, "mouseout", self.OnItemMOut);
                lnk.setAttribute("name", name);
                inSp.innerHTML = name;
                lnk.className = (name != self.selected ? self.tabCss : self.atabCss);
                outSp.className = (name != self.selected ? self.outSpCss : self.aoutSpCss);
                inSp.className = (name != self.selected ? self.inSpCss : self.ainSpCss);
                ul.appendChild(lnk);
                lnk.appendChild(outSp);
                outSp.appendChild(inSp);
                self.tabsMenu.appendChild(ul);
                self.itms[name] = lnk;
	        }
	    }

    self.SetSelected = function (tabname) 
        { 
            if(self.selected == tabname || self.itms[tabname].className == self.dtabCss) return;
            self.tabs[self.selected].style.display = "none";
            self.itms[self.selected].className = self.tabCss;
            self.itms[self.selected].firstChild.className = self.outSpCss;
            self.itms[self.selected].firstChild.firstChild.className = self.inSpCss;
            self.selected = tabname;
            self.tabs[self.selected].style.display = "";
            self.itms[self.selected].className = self.atabCss;
            self.itms[self.selected].firstChild.className = self.aoutSpCss;
            self.itms[self.selected].firstChild.firstChild.className = self.ainSpCss;
            if(self.tabs[self.selected].getAttribute("onSelect"))
                ExecEx(self.tabs[self.selected], "onSelect", {obj: self, tabname: tabname});
        }

    self.SetTabCaption = function (name, tabname, caption)
        {
            self.itms[tabname].firstChild.firstChild.innerHTML = caption;
        }

    self.Disable = function (tabname) 
        { 
            self.itms[tabname].className = self.dtabCss;
            self.itms[tabname].firstChild.className = self.doutSpCss;
            self.itms[tabname].firstChild.firstChild.className = self.dinSpCss;
        }

    self.Hide = function (hide) 
        { 
            self.tabsMenu.style.display = (hide ? "none" : "");
        }
        
    self.HideTab = function (tabname, hide)
        {
            self.itms[tabname].style.display = (hide ? "none" : "");
            if(hide && self.itms[tabname].className == self.atabCss)
                for (var tname in self.itms)
                    if(self.itms[tname].style.display != "none" && self.itms[tname].className != self.dtabCss)
                        return self.SetSelected(tname);
        }
        
    self.OnItemClick = function (lnk, evt) 
        {
            if(!eq(lnk.tagName, "li")) return;
            self.SetSelected(lnk.getAttribute("name"));
        }
        
    self.OnItemMOver = function (lnk, evt) 
        {
            if(!eq(lnk.tagName, "li")) return;
            if(lnk.className != self.atabCss && lnk.className != self.dtabCss)
            {
                lnk.className = self.htabCss;
                lnk.firstChild.className = self.houtSpCss;
                lnk.firstChild.firstChild.className = self.hinSpCss;
            }
        }
        
    self.OnItemMOut = function (lnk, evt) 
        {
            if(!eq(lnk.tagName, "li")) return;
            if(lnk.className != self.atabCss && lnk.className != self.dtabCss)
            {
                lnk.className = self.tabCss;
                lnk.firstChild.className = self.outSpCss;
                lnk.firstChild.firstChild.className = self.inSpCss;
            }
        }

    self.LoadObj();
}


//--------------------------------------------------- PopUpObj --------------------------------------------------
//--------------------------------------------------------------------------------------------------------------

function PopUpInit(name, args, prms)
{
    return PatObj_Init(PatObjType.PopUp, name, args, prms);
}

function PopUpShow(name, show, title, args)
{
    if(objList[name]) objList[name].Show(show,title);
}

function PopUpShowWithFrame(name, url)
{
    if(objList[name]) objList[name].ShowWithFrame(url);
}

function PopUpObj(name, args, prms)
{
    var self = objList[name];
    if(self == null) return OnErr({msg: "Can't init object", val: name});

    self.name = name;
	self.body = $get(name + "_PopUp");
	if(!self.body) { OnErr({obj: self, msg: "Pop-up init Error"}); return; }

    self.modaldiv = document.createElement("div");
    self.body.parentNode.appendChild(self.modaldiv);
    self.modaldiv.className = (self.body.getAttribute("ModalDivCss") ? self.body.getAttribute("ModalDivCss") : "def_popup_modaldiv_css");
	self.modaldiv.style.display = "none";
    
    self.bodybasediv = document.createElement("div");
    self.body.parentNode.appendChild(self.bodybasediv);

    self.bodybasediv.innerHTML = "<table style='width:100%; height:100%'><tr><td style='vertical-align:middle; text-align:center;' id='" + self.name + "_PopUp_baseTd'></td></tr></table>";  
    self.bodybasediv.className = (self.body.getAttribute("ModalBasDivCss") ? self.body.getAttribute("ModalBasDivCss") : "def_popup_basediv_css");
	self.bodybasediv.style.display = "none";
	
	self.onShow = (self.body.getAttribute("OnShow") ? self.body.getAttribute("OnShow") : null);
	self.onHide = (self.body.getAttribute("OnHide") ? self.body.getAttribute("OnHide") : null);

	self.disEsc = (self.body.getAttribute("DisEsc") ? eq(self.body.getAttribute("DisEsc"), "TRUE") : false);
	
	if(eq(self.body.getAttribute("Style3d"), "True"))
	{
	    var Caption = (self.body.getAttribute("Caption3d") ? "<span style='float:left; margin-left:5px;'>" + self.body.getAttribute("Caption3d") + "</span>" : "")
	    var Close = (self.body.getAttribute("Close3d") != "False" ? "<span style='float:right; cursor:pointer; margin-right:5px;' onclick=\"PopUpShow('" + self.name + "', false);\">x</span>" : "")
	    $get(name + "_PopUp_baseTd").innerHTML = "<center><table class='dialog-3d'><tr><th class='caption-left' /><th class='caption-center'><span id='"+self.name+"_Title'>" + Caption + "</span>" + Close + "</th><th class='caption-right' /></tr>" +
                              "<tr class='body-row'><td class='body-left' /><td class='body-placeholder' id='" + name + "_PopUp_base3dTd'>" + 
                              "</td><td class='body-right' /></tr><tr><td class='bottom-left' /><td class='bottom-center' /><td class='bottom-right' /></tr></table></center>";
        self.content = $get(name + "_PopUp_base3dTd");
	}
	else
        self.content = $get(name + "_PopUp_baseTd");
        
    self.content.appendChild(self.body);
    
    self.Show = function (show,title) 
    {
        if (title) {
            $get(name+"_Title").innerHTML = title;
        }
        if(show) patPopUpList.push(self); else patPopUpList.pop();
        self.modaldiv.style.display = (show ? "inline" : "none");
        self.bodybasediv.style.display = (show ? "inline" : "none"); 
        if (show) 
        {
            ExecEx(self, "onShow", {obj: self});
        }
        else
        {
            ExecEx(self, "onHide", {obj: self});
            if(self.iframe) self.content.innerHTML = "";
        }
	}

    self.ShowWithFrame = function (url) 
    { 
        self.content.innerHTML = "<iframe id='" + name + "_Frame' src='' style='" + self.body.getAttribute("style") + "' frameborder='0' scrolling='no'></iframe>";
        self.iframe = $get(name + "_Frame");
        self.iframe.src = url;
        self.Show(true);
    }
}

// to provide dissapear top pop-up on Esc button
var patPopUpList = new Array();
function hideTopPopUp()
{
    if(patPopUpList.length > 0 && !patPopUpList[patPopUpList.length-1].disEsc) patPopUpList[patPopUpList.length-1].Show(false);
}
function hideTopPopUpOnEsc(inp, evt)
{
    if(evt.keyCode == ESC_KEY_CODE) hideTopPopUp();
}
window.setTimeout(function(){AddHandler(document, "keydown", hideTopPopUpOnEsc);}, 100);


//----------------------------------------------- Load Indicator ----------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------

function ProgressInit(name, prms)
{
   Progress = new PATProgress(name, prms);   
}

function PATProgress(name, prms)
{
    if(prms == null) prms = new Object();
    var self = this;
   
   self.PrgrCss      = (prms.PrgrCss == null ? "def_progress_css" : prms.PrgrCss);
   self.PrgrOnShow   = prms.PrgrOnShow;
   self.PrgrCstShow  = prms.PrgrCstShow;
   self.PrgrObj      = prms.obj;
   self.PrgrDisable  = prms.PrgrDisable;
   self.PrgrCoverDiv = prms.PrgrCoverDiv;
   self.PrgrCoverDivCss    = (prms.PrgrCoverDivCss == null ? "def_coverdiv_css" : prms.PrgrCoverDivCss);
   self.PrgrCoverDivImgCss = (prms.PrgrCoverDivImgCss == null ? "def_coverdivimg_css" : prms.PrgrCoverDivImgCss);
   self.PrgrCoverDivImg    = (prms.PrgrCoverDivImg == null ? patImg_Prgr : prms.PrgrCoverDivImg);
   self.PrgrCoverDivTxt    = (prms.PrgrCoverDivTxt == null ? "please wait..." : prms.PrgrCoverDivTxt);
   
   if(null != name)   
   {
      self.Prgr = document.getElementById(name);
      if(self.Prgr) self.Prgr.className = self.PrgrCss;
        
      self.Show = function(show) 
      {
          ExecEx(self, "PrgrOnShow", {obj: self, show: show, prgObj: self.PrgrObj});
          if(self.PrgrCstShow) ExecEx(self, "PrgrCstShow", {obj: self, show: show, prgObj: self.PrgrObj});
          else if (self.Prgr) self.Prgr.style.visibility = (show ? "visible" : "hidden");
          else if (self.PrgrDisable && self.PrgrObj && self.PrgrObj.Disable) self.PrgrObj.Disable(show);
          else if (self.PrgrCoverDiv) self.ShowCover(show);
      }
      
      self.CreateCover = function()
      {
        if(!docLoaded) return;
        self.DivToCover = document.getElementById(self.PrgrCoverDiv);
        if(self.DivToCover == null) return;

        self.DivToShow = document.createElement("div");
        self.DivToShow.className = self.PrgrCoverDivCss;
        self.DivToShow.style.display = "none";
        //self.DivToShow.style.position = "absolute";
        document.body.appendChild(self.DivToShow);

        self.ImgToShow = document.createElement("div");
        self.ImgToShow.className = self.PrgrCoverDivImgCss;
        self.ImgToShow.style.display = "none";
        self.ImgToShow.style.position = "absolute";
        self.ImgToShow.innerHTML = "<table><tr><td valign='middle'><img src='" + self.PrgrCoverDivImg + "'></td><td valign='middle' style='padding-left:10px;'>" + self.PrgrCoverDivTxt + "</td></tr></table>";
        document.body.appendChild(self.ImgToShow);
      }
      
      self.ShowCover = function(show)
      {
        if(self.DivToShow == null) self.CreateCover();
        if(self.DivToCover == null || self.DivToShow == null) return;
        
        var rect = GetRect(self.DivToCover);
        self.DivToShow.style.height = rect.Height + "px"; 
        self.DivToShow.style.width  = rect.Width + "px";

        self.DivToShow.style.left = rect.x + "px"; 
        self.DivToShow.style.top  = rect.y + "px";
        
        
        self.ImgToShow.style.left = rect.x + rect.Width/2 - 40 + "px"; 
        self.ImgToShow.style.top  = rect.y + rect.Height/2 - 10 + "px";
        
        self.DivToShow.style.display = (show ? "" : "none");
        self.ImgToShow.style.display = (show ? "" : "none");
      }
   }
   self.Show(false);
}

//----------------------------------------------- Export ------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------

function ExportTo(objName, format, map, css, useSort)
{
    if (useSort == null)
        useSort = false;
	var sParams = "export="+format+"&obj=" + objName;
	var objArgs = PatObj_GetArgs(objName);
	if (typeof(objArgs) != "undefined")
		sParams += "&args=" + escape(Quote(JSON2.stringify(objArgs)));
	if (typeof(map) != "undefined")
		sParams += "&map=" + map;
	if (typeof(css) != "undefined" && css != null)
		sParams += "&css=" + css;
		
	if (useSort)
	{
	    var sort = objList[objName].SortNames;
	    if (typeof(sort) != "undefined" && sort != "")
	        sParams += "&sort=" + sort;
	}
	window.open(Project.rootFolder + 'PAT/Export/Export.aspx?' + sParams);
}


//---------------------------------------------- Pivot Table -------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------

function PVTableEditor(name, args, xsl)
{
    var self = this;

    self.name = name;
    self.args = args;
    self.xsl = "" + xsl;
    self.OnRowSelect = function() {};
    
    var tblResult = null;
    var tblFiltered = null;
    var tblFilterBy = null;
 
    var Init = function()
    {
        if(self.args == null)
            self.args = new Object();
        
        if(self.args["pivotTable"] == null)
            self.args["pivotTable"] = self.name;
        
        tblFiltered = $get(self.name + "Filtered");
        if(tblFiltered == null)
            throw "Pivot table:\r\nFiltered table should be declared!";
        
        tblFilterBy = $get(self.name + "FilterBy");
        if(tblFilterBy == null)
            throw "Pivot table:\r\nFilterBy table should be declared!";
        
        tblResult = $get(self.name + "Result")
        if(tblResult == null)
            throw "Pivot table:\r\nResult table should be declared!";
            
        TableInit(tblFilterBy.id, self.args);
        TableInit(tblResult.id, self.args);
        TableInit(tblFiltered.id, self.args);
    }
  
    var activeField = "";
    self.OnFilterByRowChanged = function(p)
    {
        var name = p.obj.name;
        var dbid = p.curRowId;
        if(arguments.length > 0 && activeField != dbid)
        {
            var params = CloneHash(self.args);
            
            AddHash(params, "filterby", dbid);
            TableRefresh( self.name + "Result", params);
            TableRefresh( self.name + "FilterBy", params);
            TableRefresh( self.name + "Filtered", params);
            activeField = dbid;
        }
    }
    self.SelectedRender = function(p)
    {
        var chk = null;
        if(p.cell.firstChild != null)
            chk = p.cell.firstChild;
        else
        {    
            chk = document.createElement("INPUT");
            chk.type = "checkbox";
            p.cell.appendChild(chk);
        }

        chk.checked = p.val;
        chk.setAttribute("table", p.obj.name);
        var rowId = -1;
        try{rowId = p.obj.table.rows[p.row.rowIndex-1][p.obj.DbRowId];}catch(e){;}
        
        chk.style.display = (rowId == null || rowId == -1)? "none": "inline";
        chk.setAttribute("rowId", rowId);
        
        AddHandler(chk, "click", DoChk);
    }
    self.OnFilterByEnabled = function(p)
    {
        if(p.row.getAttribute("arranged")) return;
        if( p.val == "0")
            p.row.style.color = "gray";
        else
            p.row.style.color = "";
    }
    self.OnDeleteFilter = function(p)
    {
        if(p.cmdCode == "DelRow") {
            TableRefresh( self.name + "Result", PatObj_GetArgs(self.name + "Result"));        
            TableRefresh( self.name + "FilterBy", PatObj_GetArgs(self.name + "FilterBy"));
        }
    }
    self.OnArrangedByRender = function(p)
    {
        var div = p.row.cells[0].firstChild;
        if (p.row.getAttribute("arranged") != null && div != null)
        {
            if (p.val == "")
            {
                p.row.removeAttribute("disabled");
                p.row.style.color = "";
            }
            else
            {
            	if (div.innerHTML.indexOf(p.val) == 0)
                {
                    AddHandler(p.row, "click", p.obj.SelectRowByRow);
                    AddHandler(p.row, "mouseover", p.obj.rowMouseOver);
                    AddHandler(p.row, "mouseout", p.obj.rowMouseOut);
                    p.row.style.color = "green";
                }
                else
                {
                    RmvHandler(p.row, "click", p.obj.SelectRowByRow);
                    RmvHandler(p.row, "mouseover", p.obj.rowMouseOver);
                    RmvHandler(p.row, "mouseout", p.obj.rowMouseOut);
                    p.row.style.color = "red";
                }
            }
        }
    }
    
    var DoChk = function(obj, evt)
    {
        var name = obj.getAttribute("table");
        var rowId = obj.getAttribute("rowId");

        var params = CloneHash(self.args);
        AddHash(params, "rowId", rowId);
        AddHash(params, "value", obj.checked);

        CustServerCall("Select", self.name, params, OnSelectRes)
    }
    var OnRes = function()
    {
        TableRefresh( self.name + "Result", self.args);
        TableRefresh( self.name + "FilterBy", self.args);
        TableRefresh( self.name + "Filtered", self.args);
    }
    var OnSelectRes = function()
    {
        TableRefresh( self.name + "FilterBy", self.args);
    }
    self.PivotReport = function()
    {
        var name = self.name +  "Result";
        var params = "type=pivot&obj=" + name;
        params += "&xsl=" + self.xsl;
        params += "&args=" + escape(JSON2.stringify(self.args));
        
        window.open(Project.rootFolder + 'PAT/Export/PVReport.aspx?' + params);
    }
    self.ExportTo = function(format)
    {
        var name = self.name +  "Result";
        var params = "export="+format+"&obj=" + name;
        params += "&args=" + escape(JSON2.stringify(self.args));
        
        window.open(Project.rootFolder + 'PAT/Export/Export.aspx?' + params);
    }
    self.Refresh = function()
    {
        CustServerCall("Refresh", self.name, self.args, OnRes);
    }
    
    Init();    
}



//---------------------------------------------- New Pivot Table -------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------


(function($) {
    var uniqueid = 0;
    $.pivot_uniqueid = function() { return uniqueid++; }
    $.fn.pivot_xsignal = function(key, sig) {
        var holder = $("<span class='xsignal' key='" + key + "'/>");
        holder.data("xsignal", sig);
        holder.appendTo(this);
        return this;
    }
    $.pivot_xsignal = function(key) {
        return function() {
            var args = arguments;
            $(".xsignal[key='" + key + "']").each(function() { $(this).data("xsignal").apply(null, args); });
        }
    }
    $.fn.pivot_xdata = function(key, obj) {
        if (obj) {
            var holder = $("<span class='xdata' key='" + key + "'/>");
            holder.data("xdata", obj);
            holder.appendTo(this);
            return this;
        } else return this.closest(":has(span.xdata[key='" + key + "'])")
            .children("span.xdata[key='" + key + "']")
            .data("xdata");
    }
    //Pivot Object
    function PivotSelectorDefaultBehaviour(pivot) {
        var self = this;
        self.pivot = pivot;
        self.selectors = $(".pivot-table-selectors", self.pivot.options.pivot);
        $(".pivot-table-selector", self.selectors).click(function(e) {
            self.pivot.setSelector($(this).attr("selector"));
        });
        self.selectors.pivot_xsignal("pivot-table-" + self.pivot.options.id + "/selector/changed", function() {
            $(".pivot-table-selector", self.selectors).hide();
            $(".pivot-table-selector", self.selectors).removeClass("def_selrow_css");
            $(".pivot-table-selector[selector='" + self.pivot.selector + "']", self.selectors).addClass("def_selrow_css");
            $(".pivot-table-selector[selector='" + self.pivot.getParent() + "']", self.selectors).addClass("def_selrow_css");
            $(".pivot-table-selector[selector*='" + self.pivot.getParent() + "']", self.selectors).show();
            $(".pivot-table-selector[show-always = 'true']", self.selectors).show();
        });
    }
    function PivotTable(options) {
        var self = this;
        var defaults = {
            id: $.pivot_uniqueid(),
            pivot: null,
            classname: null,
            params: null
        };
        self.selector = "";
        self.options = $.extend({}, defaults, options);
        self.results = $(".pivot-table-results", self.options.pivot);
        self.mapSelectorID = [];
        self.filter = $(".pivot-table-filter", self.options.pivot);
        self.getParent = function() {
            var pos = self.selector.indexOf('/', 1);
            if (pos == -1) return self.selector;
            else return self.selector.substr(0, pos);
        }
        self.refresh = function() {
            self.filter.data("table").Refresh(); //refresh filter
            //refresh or init result
            var div = $("#" + self.mapSelectorID[self.selector], self.results);
            if (div.length == 0) throw "pivot-table-result[selector='" + self.selector + "'] is not defined";
            var table = div.data("table");
            if (!table) {
                var params = $.extend({}, self.options.params, { id: self.options.id, classname: self.options.classname });
                table = TableInit(div.attr("id"), params, { onServerCall: function(p) { return onServerCallResult(div, p); } });
                div.data("table", table);
            } else table.Refresh();

            //show result
            $(".pivot-table-result", self.results).hide();
            div.show();
        }
        self.setSelector = function(selector) {
            self.selector = selector;
            var args = { id: self.options.id, classname: self.options.classname, selector: selector };
            CustServerCall("set-selector", "pivot", args, function() { });
            self.refresh();
            $.pivot_xsignal("pivot-table-" + self.options.id + "/selector/changed")(self, selector);
        }
        self.setOptions = function(options) {
            var args = { id: self.options.id, classname: self.options.classname, options: options };
            CustServerCall("set-option", "pivot", args, function() { });
        }
        self.ExportTo = function(format) {
            var objName = self.mapSelectorID[self.selector];

            var sParams = "export=" + format + "&obj=" + objName;
            var objArgs = $.extend({}, self.options.params, { id: self.options.id, classname: self.options.classname });
            if (typeof (objArgs) != "undefined")
                sParams += "&args=" + escape(Quote(JSON2.stringify(objArgs)));
            window.open('/PivotExport.aspx?' + sParams);

        }
        self.PivotReport = function(format) {
            var objName = self.mapSelectorID[self.selector];
            var cat = self.getParent();
            var sParams = "obj=" + objName +
                          "&xsl=" + (self.options.classname.replace("Table", "Report")) +
                          "&cat=" + cat.substr(1) +
                          "&filter=" + Number(cat != self.selector);
            var objArgs = $.extend({}, self.options.params, { id: self.options.id, classname: self.options.classname });
            if (typeof (objArgs) != "undefined")
                sParams += "&args=" + escape(Quote(JSON2.stringify(objArgs)));
            window.open('/PivotReport.aspx?' + sParams);
        }
        var onServerCallResultPAT = function(obj, res) {
            if (ExecEx(obj, "onServerRes", AddHash(res, "obj", obj)) != false) {
                try { obj.OnServiceRes(res); } catch (err) { OnErr({ msg: err.message }); }
                ExecEx(obj, "onDataLoaded", AddHash(res, "obj", obj)); // perform after objects process results
                if (obj && obj.Progress) obj.Progress.Show(false);
            }
        }
        var onServerCallResult = function(div, p) {
            var onres = function(res) { onServerCallResultPAT(div.data("table"), res["CustRes"]); }
            var args = $.extend({}, p.args, { id: self.options.id, classname: self.options.classname });
            if (p.cmdCode == "GetPage") {
                CustServerCall("get-result", "pivot", args, onres);
                return false;
            } else if (p.cmdCode == "SetData" && p.args.field == "marked") {
                CustServerCall("set-marked", "pivot", args, onres);
                return false;
            }
        }
        var onServerCallFilter = function(p) {
            var onres = function(res) { onServerCallResultPAT(self.filter.data("table"), res["CustRes"]); }
            var args = $.extend({}, p.args, { id: self.options.id, classname: self.options.classname });
            if (p.cmdCode == "GetPage") {
                CustServerCall("get-filter", "pivot", args, onres);
                return false;
            } else if (p.cmdCode == "DelRow") {
                CustServerCall("remove-filter", "pivot", args, onres);
                self.refresh();
                return false;
            }
        }
        var init = function() {
            self.options.pivot.pivot_xdata("pivot", self); //now any inner element can find pivot by call $(this).pivot_xdata("pivot")
            CustServerCall("init", "pivot", { id: self.options.id, classname: self.options.classname }, function() { }); //init
            //generate appropriate id for result tables
            $(".pivot-table-result", self.results).hide();
            var pagesize = self.results.attr("pagesize");
            $(".pivot-table-result", self.results).each(function(i, elem) {
                var div = $(elem);
                div.attr("id", "pivot-table-" + self.options.id + "-result-" + $.pivot_uniqueid());
                var selectors = div.attr("selectors").split(" ");
                for (var i = 0; i < selectors.length; ++i) self.mapSelectorID[selectors[i]] = div.attr("id");
                var table = $("table", div).attr("id", div.attr("id") + "Table");
                var tr = $("tr", table).attr("id", div.attr("id") + "Header");
                if (pagesize && !div.attr("pagesize")) {
                    div.attr("pagesize", pagesize);
                    var pager = $(".pivot-table-result-pager", div);
                    if (pager.length == 0)
                        pager = $("<div class='pivot-table-result-pager'></div>").appendTo(div);
                    pager.attr("id", div.attr("id") + "BottomPager");
                }
            });
            //set map selector-name
            var selectors = $(".pivot-table-selectors", self.options.pivot);
            $(".pivot-table-selector", selectors).each(function(i, elem) {
                var selector = $(elem).attr("selector");
                var name = $(elem).attr("name"); if (!name) name = '';
                var args = $.extend({}, self.options.params, {
                    id: self.options.id, classname: self.options.classname,
                    selector: selector, name: name
                });
                CustServerCall("set-map-selector-name", "pivot", args, function() { });
            });
            //generate id for filter table and init
            self.filter.attr("id", "pivot-table-" + self.options.id + "-filter");
            var table = $("table", self.filter).attr("id", self.filter.attr("id") + "Table");
            var tr = $("tr", table).attr("id", self.filter.attr("id") + "Header");
            var params = $.extend({}, self.options.params, { id: self.options.id, classname: self.options.classname });
            self.filter.data("table", TableInit(self.filter.attr("id"), params, { onServerCall: onServerCallFilter }));
            //default behaviour
            if ($(".pivot-table-categories", self.options.pivot).hasClass("pivot-table-default-behaviour"))
                new PivotCategoryDefaultBehaviour(self);
            if ($(".pivot-table-selectors", self.options.pivot).hasClass("pivot-table-default-behaviour"))
                new PivotSelectorDefaultBehaviour(self);
            //start
            self.setSelector(self.options.pivot.attr("default-selector"));
        }
        init();
    }
    $.fn.pivot = function(options) {
        var defaults = { pivot: this, classname: this.attr("classname") };
        options = $.extend({}, defaults, options);
        var pivot = new PivotTable(options);
        return this;
    }
})(jQuery);


function PatUpload(name, opts) {
	if (qq != null && qq.FileUploader != null) {
		if(!objList[name] && $('#' + name).length > 0) {
            opts.text = opts.text || 'Upload a file';
			var defaults = {
				element: $('#' + name).get(0),
				action: Project.uploadPath,
                template: '<div class="qq-uploader">' + 
                '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' +
                '<div class="qq-upload-button">' + opts.text +'</div>' +
                '<ul class="qq-upload-list"></ul>' + 
             '</div>'
			};
			var onSubmit = null;
			if(opts && opts.onSubmit) {
				onSubmit = opts.onSubmit;	
			}
			$.extend(defaults, opts);
			defaults.onSubmit = function() {
				this.params = {
					objName: opts.objName || name,
                    uploadPath: opts.uploadPath || ""
				};
				if(onSubmit) {
					onSubmit.call(this);
				}
			}
			objList[name] = new qq.FileUploader(defaults);
		}
	}
	return objList[name];
}

var PatGalleryType = {
    img: 1,
    video: 2,
    youtube: 3,
    file: 3,
    foundType: function(name) {
        if(/\.(?:jpg|png|gif)$/.test(name)){
            return PatGalleryType.img;
        }
        if(/\b(?:youtube\.|youtu\.be)\b/.test(name)){
            return PatGalleryType.youtube;
        }
    }
};

function PatGallery(name, args, prms) {
    PatGallery.callbacks = PatGallery.callbacks || {};
    var self = objList[name];
    if(self == null) {
        return OnErr({msg: "Can't init object", val: name});
    }
    
    self.name = name;
    self.args = args;
    self.curItemId = null;
    self.table = null;
    self.holder = $('#' + self.name).addClass('def_gallery ui-widget ui-widget-content ui-corner-all');

    if(self.holder.attr("onServerCall")) {
        self.onServerCall = self.holder.attr("onServerCall");
    }
    if(self.holder.attr("onServerRes")) {
        self.onServerRes = self.holder.attr("onServerRes");
    }
    if(self.holder.attr("onDataLoaded")) {
        self.onDataLoaded = self.holder.attr("onDataLoaded");
    }

    if(prms["rowId"]) {
        self.args.rowId = prms["rowId"];
    }
    self.zoom = $('<a>').addClass('def_zoom').append($('<img>').attr('src', Project.imgFolder + 'zoom.png'));

    self.alwaysEnabled = prms["alwaysEnabled"];
    self.onSelRow = self.onSelRow != null ? self.onSelRow : self.holder.attr("onSelRow");

    self.DbRowId = (self.holder.attr("DbRowId") ? self.holder.attr("DbRowId") : "id");
    self.DbRowName = (self.holder.attr("DbRowName") ? self.holder.attr("DbRowName") : "name");
    self.DbRowValue = (self.holder.attr("DbRowValue") ? self.holder.attr("DbRowValue") : "file");

    if (self.holder.attr("RowChangeNotifyList")) {
        self.RowChangeNotifyList = self.holder.attr("RowChangeNotifyList");
    }


    self.OnDataChangeNotification = function(params)
    {
        self.LoadData();
    }

    self.OnRowChangeNotification = function (params)
    {
        if(params != null && params["rowId"] != null) {
            if(params["rowidname"] != null && params["rowidname"] != "rowId" && params["rowidname"] != "id") {
                self.args = AddHash(self.args, params["rowidname"], params["rowId"]);
            }
            else {
                self.args.rowId = params["rowId"];
            }
        }
        self.LoadData();
    }

    self.OnServiceRes = function (res)
    {
        switch(res["cmdCode"])
        {
            case "GetPage": self.OnGetPageRes(res); break;
        }
    }

    self.Refresh = function(args, rowId) {
        if (args) {
            AddHash(self.args, args);
        }
        if (rowId) {
            self.args.rowId = rowId;
        }
        self.LoadData();
    }

    self.LoadData = function ()
    {
        ServerCall("GetPage", self, {rowId: self.args.rowId, args: self.args});
    }

    self.FindRelated = function(inp)
	{
	    var List = inp.getAttribute("RowChangeNotifyList");
        var names  = (List != null && List != "" ? List.replace(/\s/g, "").split(",") : new Array());
        var res = new Array();
        for(var i = 0; i < names.length; i++)
        {
            var parts = names[i].split("/");
    		var rex = new RegExp("^(.+)_" + self.name + "$", "i");
			if(parts.length > 1 && rex.exec(parts[0])) {
                res.push({obj : parts[0], arg : parts[1]});
            }
		}
        return res;
	}

    self.OnGetPageRes = function(res)
	{
	    if (res["errCode"] != null) {
            return OnErr({ obj: self, msg: res["errCode"] });
        }
	    self.table = res["tableRes"];
        self.rowsTotal = res["scalarRes"];

        self.ClearControls();

        var len = (self.table != null && self.table.rows != null ? self.table.rows.length : 0);
	    for (var i = 0; i < len; i++)
	    {
	        var id = self.table.rows[i][self.DbRowId];
            var name = self.table.rows[i][self.DbRowName];
            var value = self.table.rows[i][self.DbRowValue];
            var item = self.BuildItem(id, name, value, self.table.rows[i]);
            self.holder.append(item);
	    }
        self.holder.lazyload({
            placeholder: Project.imgFolder + 'ajax-loader.gif',
            elements: 'img.def_gallery_image'
        });

        self.holder.find("a.def_zoom").fancybox({
            'titleShow' : true,
            'transitionIn' : 'elastic',
            'transitionOut' : 'elastic',
            'easingIn' : 'easeOutBack',
            'easingOut' : 'easeInBack'
        }); 
	}

    self.BuildItem = function(id, name, val, row, i) {
        var a = $('<a>').addClass('def_gallery_item');
        var img = $('<img>').attr({
                    'item-id': id
                }).addClass('def_gallery_image');
        a.click(function(e) {
            self.onSelectItem(id, val, row, e);
        });
        var zoom = self.zoom.clone(false);
        zoom.attr('rel', self.name);
        switch(PatGalleryType.foundType(val)) {
            case PatGalleryType.img:
                if(!name) {
                    name = /[^/\\]+$/.exec(val)[0];
                }
                img.attr({
                    'data-src': row.hasOwnProperty("thumb") ? row.thumb : val
                });
                zoom.attr('href', val);
                break;
            case PatGalleryType.youtube:
                var regex = new RegExp('(?:youtube\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})', "i");
                val = regex.exec(val)[1];
                a.addClass('def_video');
                zoom.attr('href', 'http://www.youtube.com/embed/' + val +'?autoplay=1');
                zoom.addClass('iframe')
                if(PatGallery.cache[val]) {
                    var data = PatGallery.cache[val];
                    if(!name) {
                        name = data.title;
                    }
                    img.attr({
                        'src': data.thumbnail.hqDefault
                    });
                } else {
                    var callback = 'y' + val.replace(/-/g, "_");
                    PatGallery.callbacks[callback] = function(o) {
                        PatGallery.OnYouTube(val, o);
                    }
                    $(document).append('<script type="text/javascript" src="http://gdata.youtube.com/feeds/api/videos/' + val + '?v=2&alt=jsonc&format=5&callback=PatGallery.callbacks.' + callback + '"></script>');
                    img.addClass(val);
                    img.data('id', id);
                    img.data('name', self.name);
                    img.data('fill', false);
                    name = "";
                }
                break;
        }
        zoom.attr('title', name);
        a.append(img).append(zoom).append($('<span>').addClass('def_gallery_text').text(name));
        return a;
    }

    PatGallery.cache = {};
    PatGallery.OnYouTube = function(id, o) {
        var img = $('.' + id);
        if(o && o.data && o.data.id) {
            PatGallery.cache[o.data.id] = o.data;
            if(img.length > 0) {
                img.each(function() {
                    var el = $(this);
                    if(el.data('fill') == false) {
                        var obj = objList[el.data('name')];
                        var id = el.data('id');
                        var name = o.data.title;

                        for(var i = 0; i < obj.table.rows.length; i++) {
                            if(obj.table.rows[i][obj.DbRowId] == id) {
                                if(obj.table.rows[i][obj.DbRowName]) {
                                    name = obj.table.rows[i][obj.DbRowName];
                                }
                                break;
                            }
                        }
                        el.attr('src', o.data.thumbnail.hqDefault);
                        el.parent().find('.def_gallery_text').text(name);
                        el.parent().find('.def_zoom').attr('title', name);
                        el.data('fill', true);
                    }
                });
            }
        } else if(o.error) {
            img.each(function() {
                var el = $(this);
                if(el.data('fill') == false) {
                    var obj = objList[el.data('name')];
                    var id = el.data('id');
                    var name = "Error: " + o.error.message;

                    el.attr('src', Project.imgFolder + '/not-available.jpg');
                    el.parent().find('.def_gallery_text').text(name);
                    el.data('fill', true);
                }
            });
        }
    }

    self.onSelectItem = function(id, val, row, e) {
        self.curItemId = id;
        $(e.currentTarget).addClass('def_selected').siblings('a').removeClass('def_selected');
        ExecEx(self, "onSelRow", { obj: self, row: row, curRowId: self.curRowId });
        RowChangeNotify(self.RowChangeNotifyList, { rowId: self.curItemId, rowidname: self.DbRowId });
    }

    self.ClearControls = function ()
    {
        self.holder.empty();
    }

    self.Deselect = function ()
    {
        self.args.rowId = "-1";
        self.OnGetSingleRowRes(new Object());
    }

    self.IsLinked = function()
    {
        return self.args.rowId != "-1";
    }

    self.LoadData();
}
