// --------------------------------------------------------------------------------------------------------------------------
// Support script for Extended API demonstration
// --------------------------------------------------------------------------------------------------------------------------

// --------------------------------------------------------------------------------------------------------------------------
var IDisable, IStyle, DLog; // Pointers to html controls
var FRow;                   // Actual focused row for update
var Styles = [              // Style settings
   "Style='G' Grid = 'Grid.gif' Toolbar='Toolbar.gif' Height = '17' Line = '21' Tree = '26' Panel = '13' Sort = '14' Filter = '17' Row = '17'",
	"Style='GL' Grid = 'GridLight.gif' Toolbar='ToolbarLight.gif' Height = '17' Line = '21' Tree = '26' Panel = '13' Sort = '14' Filter = '17' Row = '17'",
	"Style='GB' Grid = 'GridBigger.gif' Toolbar='Toolbar.gif' Height = '21' Line = '21' Tree = '26' Panel = '18' Sort = '14' Filter = '17' Row = '17'",
	"Style='GG' Grid = 'GridGame.gif' Toolbar='Toolbar.gif' Height = '17' Line = '21' Tree = '26' Panel = '13' Sort = '14' Filter = '17' Row = '17'"
	];
// --------------------------------------------------------------------------------------------------------------------------



// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                                                  Main and starting functions
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// --------------------------------------------------------------------------------------------------------------------------
// Creates grid from JavaScript
function Start(){

// --- Prepares and clears controls on pages ---
DLog = GetElem("LOG");
IStyle = GetElem("IStyle");
IDisable = GetElem("IDisable");
DLog.value = "";
IDisable.checked = false;
IStyle.checked = false;
GetElem("IColor").value = "002";
GetElem("SData").selectedIndex = 0;
GetElem("SStyle").selectedIndex = 0;

// --- creates grid ---
var D = new TDataIO();   // new object for communication
D.Layout.Url = "EAPIDef.xml";
D.Data.Url = "EAPIData.xml"; 

if(location.protocol=="http:"){ // Export only on server, uses ASP.NET
   D.Export.Url = "../AspNetCS/Export.aspx";
   D.Export.Data = "TGData";
   }
Resize();                // User function to resize main tag to resize it to whole window
TreeGrid(D,"GRID");      // Creates new grid, this grid will now be accessed from Grids[0] property 
}
// --------------------------------------------------------------------------------------------------------------------------
// Loads data chosen from the first combo
function LoadData(){
var idx = GetElem("SData").selectedIndex; // The first combo, contains selected data
var Def = [   // Def contain layout definitions, for examples only, for tutorials is used only data file
   "EAPIDef.xml","AjaxDef.xml","TableDef.xml","BooksDef.xml","AddPageDef.xml",
   "../ServerDLL/Data1Def.xml","../ServerDLL/Data2Def.xml","../ServerDLL/Data3Def.xml","../ServerDLL/Data4Def.xml",
   "UnknownDef.xml",""
   ];
var Data = [ // Data contain data xml, for examples there are also used layouts from Def, for tutorials this is the only data
   "EAPIData.xml","AjaxData.xml","TableData.xml","BooksData.xml","AddPageData.xml",
   "../ServerDLL/Data1S.xml","../ServerDLL/Data2S.xml","../ServerDLL/Data3S.xml","../ServerDLL/Data4S.xml",
   "UnknownData.xml",
   "Basic1 Empty grid.xml","Basic2 Rows.xml","Basic3 Fixed rows.xml","Basic4 Columns.xml","Basic5 Fixed columns.xml","Basic6 Tree.xml","Basic7 Settings.xml",
   "Advanced1 Cells.xml","Advanced2 Formats.xml","Advanced3 Permissions.xml","Advanced4 Sorting.xml","Advanced5 Filters.xml","Advanced6 Defaults.xml","Advanced7 Defaults children.xml",
   "Calc1 Columns.xml","Calc2 Rows.xml","Calc3 Order.xml","Calc4 Tree.xml",
   "Editing1 Basics.xml","Editing2 Multiline.xml","Editing3 Mask.xml","Editing4 Advanced types.xml",
   "Id1 Basic id.xml","Id2 Characters.xml","Id3 Column.xml","Id4 Tree.xml","Id5 More columns.xml","Id6 Server.xml",
   "Expert1 Spanning.xml","Expert2 User rows.xml","Expert3 Header.xml","Expert4 Body.xml","Expert6 Resizing.xml"];
   
var G = Grids[0];
var D = G.Data;
if(idx>=10){ // Tutorials
   D.Layout.Url = null;
   D.Data.Url = "../../Tutorials/"+Data[idx];
   }
else { // Examples
   D.Layout.Url = Def[idx];
   D.Data.Url = Data[idx];
   }   
if(idx==41) D.Page.Url="../../Tutorials/Expert4 Page.xml"; // special case, this tutorial uses server paging
else if(idx==4){ D.Page.Method="Get"; D.Page.Url="AddPagePage*Pos.xml"; }          // special case, this example uses server paging
else D.Page.Url = null;                                    // standard case
if(idx==31) D.Layout.Bonus="<Grid><Cfg BaseUrl='../'/></Grid>"; // special case, this tutorial uses Img expected to be from another location
else D.Layout.Bonus=null;                                       // standard case
D.Upload.Data = null; D.Upload.Url = null;                                    // standard case
if(idx>=32 && idx<=36) D.Upload.Data = "<Grid><IO Result='0' Message='Uploaded successfully\nSee ids if and how changed'/></Grid>";
if(idx==37) D.Upload.Url="../../Tutorials/Id6 Server Sample Response.xml"; // special case, this tutorial uses server response

if(location.protocol=="http:"){ // Export only on server, uses ASP.NET
   D.Export.Url = "../AspNetCS/Export.aspx";
   D.Export.Data = "TGData";
   }

var sidx = GetElem("SStyle").selectedIndex;
D.Data.Bonus="<Grid><Cfg MaxWidth='0' MaxHeight='0'/><Img "+Styles[sidx]+"/></Grid>";  // Suppresses MaxHeigth and MaxWidth for any data that has them set, because main tag is positioned in <TABLE> and sets selected style

G.Reload();  // Reloads new data to grid 
FRow = null; // Nulls FRow - no focused row in grid
}
// --------------------------------------------------------------------------------------------------------------------------
// Called when window is resized to set extents of the grid to maximize its area
function Resize(){
var D = GetElem("GRID");
var R = GetElem("RIGHT");
var S = GetWindowSize();
D.style.width = (S[0] - 240)+"px";
var h = S[1] - 270 - D.parentNode.offsetTop;
if(h<R.offsetHeight) h = R.offsetHeight;
D.style.height = h+"px";
}
// --------------------------------------------------------------------------------------------------------------------------
// Helper function, logs string to LOG DIV
function Log(str,ln,color,size){
if(IDisable.checked) return;
var D = document.createElement(ln?"DIV":"SPAN");
D.innerHTML = str+(ln?"":"; ");
if(color) D.style.color = color;
if(size) D.style.fontSize = size;
DLog.appendChild(D);
DLog.scrollTop = 10000;
}
// --------------------------------------------------------------------------------------------------------------------------
// Helper function, escapes the string for using in XML/HTML (to display in log)
function Esc(str){
if(str.length>50) str = str.slice(0,50)+" ... ";
return str.replace(/&/g,"&amp;").replace(/</g,"&lt;");
}
// --------------------------------------------------------------------------------------------------------------------------














// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                                                  Event handlers
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// --------------------------------------------------------------------------------------------------------------------------
//                                               Style event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnGetColor = function(G,row,col,r,g,b,type){ 
   if(IStyle.checked) Log("OnGetColor("+row.id+","+col+","+r+","+g+","+b+","+type+")",0,"#CAC");
   if(row[col+"Marked"] && !type) return "rgb("+r+","+(g-64)+","+b+")"; // Checks custom attribute set by function Color
   if(G.id=="Books" && !Get(row,"Spanned")) return "rgb("+r+","+g+","+(b-20)+")";// Support function for particular example

}
Grids.OnGetClass = function(G,row,col,cls){ 
   if(IStyle.checked) Log("OnGetClass("+row.id+","+col+","+cls+")",0,"#ACC");
   if(G.id.slice(0,4)=="List") return ListGetClass(G,row,col,cls);
   return cls;
}
Grids.OnGetType = function(G,row,col,type){ 
   if(IStyle.checked) Log("OnGetType("+row.id+","+col+","+type+")",0,"#CCA");
   return type;
}
Grids.OnGetFormat = function(G,row,col,format,edit){ 
   if(IStyle.checked) Log("OnGetFormat("+row.id+","+col+","+format+","+edit+")",0,"#CAA");
   return format;
}
Grids.OnGetEnum = function(G,row,col,enuma){ 
   if(IStyle.checked) Log("OnGetEnum("+row.id+","+col+","+enuma.join("|")+")",0,"#ACA");
   if(G.id=="List3") return List3GetEnum(G,row,col,enuma); // Support function for particular example
   return enuma;
}
Grids.OnCanEdit = function(G,row,col,can){ 
   if(IStyle.checked) Log("OnCanEdit("+row.id+","+col+","+can+")",0,"#AAC");
   if(G.id=="List3") return List3CanEdit(G,row,col,can); // Support function for particular example
   if(G.id=="Books" && row.id=='B' && col=='B') return 1;// Support function for particular example
   return can;
}

// --------------------------------------------------------------------------------------------------------------------------
//                                                 State event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnValueChanged = function(G,row,col,val){
   Log("OnValueChanged: User <b>set value</b> in cell ["+row.id+","+col+"], from value '"+Esc(G.GetString(row,col))+"' to '"+Esc(G.ValueToString(val,G.GetType(row,col),G.GetFormat(row,col)))+"'.",1);
   if(G.id.slice(0,4)=="List") return List3ValueChanged(G,row,col,val); // Support function for particular example
   if(G.id=="Books" && row.id=='B' && col=='B') EditChange(val);// Support function for particular example
   return val;
}
Grids.OnAfterValueChanged = function(G,row,col){
   Log("OnAfterValueChanged: User <b>CHANGED value</b> in cell ["+row.id+","+col+"] to '"+G.GetString(row,col)+"'.",1);
}
Grids.OnResultMask = function(G,row,col,val){
   Log("OnResultMask: Value '"+Esc(val)+"' tried to save to cell ["+row.id+","+col+"] collides with mask.",1,"red");
   return 0;
}
Grids.OnRowMoveToGrid = function(G,row,togrid,torow,copy){
// not used in this example
}
Grids.OnRowMove = function(G,row,oldparent,oldnext){
   var log = "OnRowMove: User <b>MOVED row</b> '"+row.id+"' ";
   if(oldparent==row.parentNode) log+="inside "+(oldparent.tagName=='I'?"parent row '"+oldparent.id+"'":"root");
   else log+="from "+(oldparent.tagName=='I'?"parent row '"+oldparent.id+"'":"root")+" to "+(row.parentNode.tagName=='I'?"parent row '"+row.parentNode.id+"'":"root");
   log+=" "+(row.nextSibling?"before row '"+row.nextSibling.id+"'":"as the last row");
   Log(log,1);
}
Grids.OnCanDrag = function(G,row,togrid,torow,type){
// log is not used due too many statements
return type;
}
Grids.OnRowDelete = function(G,row,type){
   Log("OnRowDelete: User <b>"+(type==1?"DELETED":"UNDELETED")+" row</b> '"+row.id+"'.",1);
}
Grids.OnCanRowDelete = function(G,row,type){
   Log("OnCanRowDelete: User tries to <b>"+(type!=3?"DELETE":"UNDELETE")+" row</b> '"+row.id+"'.",1);
return type;
}
Grids.OnCanRowAdd = function(G,parent,next){
   Log("OnCanRowAdd: User tries to <b>ADD new row</b> to "+(parent&&parent.tagName=='I'?"parent row '"+parent.id+"'":"root") + " " + (next?"before row '"+next.id+"'":"as the last row"),1);
return true;
}
Grids.OnRowAdd = function(G,row){
   Log("OnRowAdd: User <b>ADDED new row</b> '"+row.id+"' to "+(row.parentNode.tagName=='I'?"parent row '"+row.parentNode.id+"'":"root") + " " + (row.nextSibling?"before row '"+row.nextSibling.id+"'":"as the last row"),1);
   if(G.id=="CalcAPI") row.X = row.id;  // Support code for particular example
   FillIds();
   return true;
}
Grids.OnExpand = function(G,row){
   Log("OnExpand: User <b>"+(row.Expanded?"COLLAPSES":"EXPANDS")+" row</b> '"+row.id+"'",1);
}
Grids.OnPrint = function(G){
   Log("OnPrint: TreeGrid is printed",1);
   return ["",""];
}

Grids.OnSetRowId = function(G,row,newid){
   Log("OnSetRowId: The id attribute of the row '"+row.id+"' was set to '"+newid+"'",1);
   return newid;
}

Grids.OnGenerateId = function(G,row,newid){
   Log("OnGenerateId: The identity attribute ("+G.IdNames[G.IdNames.length-1]+") of the row '"+row.id+"' was set to '"+newid+"'",1);
   return newid;
}


// --------------------------------------------------------------------------------------------------------------------------
//                                           Sort and filter event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnSort = function(G,col){
   Log("OnSort: User <b>SORTS</b> grid according to column '"+col+"'",1);
}
Grids.OnCanFilter = function(G,start){
   var F = G.GetRowById("Filter"), cls="";
   if(F){
      for(var c=G.GetFirstCol();c;c=G.GetNextCol(c)){
         if(Get(F,c+"Filter")) cls+=(cls?",":"")+c;
         }
      if(start){
         if(cls) Log("OnCanFilter: TreeGrid is <b>filtered</b> according to columns "+cls,1,"#aaa");
         else Log("OnCanFilter: TreeGrid is <b>not filtered</b>",1,"#aaa");
         }
      else {
         if(cls) Log("OnCanFilter: User <b>FILTERS</b> grid according to columns "+cls,1);
         else Log("OnCanFilter: User <b>CLEARS FILTERS</b> in grid",1);
         }
      }
   else {
      Log("OnCanFilter: TreeGrid has no filter",1,"#aaa");
      }
   return true;
}
Grids.OnRowFilter = function(G,row,show){
   //if(row.firstChild) show = true; // suppress filter for rows with children
   Log("OnRowFilter: row '"+row.id+"' is "+(show?"VISIBLE":"HIDDEN"),0,"#CCC");
   return show;
}

// --------------------------------------------------------------------------------------------------------------------------
//                                            Click and key event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnButtonClick = function(G,row,col){
   Log("OnButtonClick: User <b>clicked right button</b> in cell ["+row.id+","+col+"]",1);
   G.ShowDialog(row,col,"<DIV style='background:#ffffaa;border:2px inset #ddaadd; padding:10px;'> User dialog on<br>row '"
      + Esc(G.GetString(row,"A"))+"'<br>column '"+Esc(G.GetCaption(col))+"'<br>"
      +"<BR><DIV align=center><BUTTON style='width:60;' onclick='Grids[0].CloseDialog();'>OK</BUTTON></DIV></DIV>");
}

Grids.OnLinkClick = function(G,row,col,url,target){
   Log("OnLinkClick: User <b>clicked link</b> at cell ["+row.id+","+col+"]. Url is '"+Esc(url)+"', target is "+target+".",1);
}

Grids.OnClick = function(G,row,col,x,y,handled){
   Log("OnClick: User <b>clicked</b> cell ["+row.id+","+col+"] at ["+x+","+y+"]."+(col!="Panel"&&row!=G.XHeader?" Cell value is '"+Esc(G.GetString(row,col))+"'.":"")+" Event "+(handled?"WAS":"was NOT")+" handled yet.",1);
}
Grids.OnDblClick = function(G,row,col,x,y){
   Log("OnDblClick: User <b>double clicked</b> cell ["+row.id+","+col+"] at ["+x+","+y+"]."+(col!="Panel"&&row!=G.XHeader?" Cell value is '"+Esc(G.GetString(row,col))+"'.":""),1);
}
Grids.OnRightClick = function(G,row,col,x,y){
   Log("OnRightClick: User <b>right clicked</b> cell ["+row.id+","+col+"] at ["+x+","+y+"]."+(col!="Panel"&&row!=G.XHeader?" Cell value is '"+Esc(G.GetString(row,col))+"'.":""),1);
   ShowMenu(G,row,col,x,y);
   return true;
}

Grids.OnKeyDown = function(G,key){ Log("Keydown: "+key,0,"#CCC"); }
Grids.OnKeyPress = function(G,key){ Log("Keypress: "+key,0,"#CCC"); }

Grids.OnTabOutside = function(G,move){
   if(move>0){
      Log("User pressed tab key on the last cell and focus is moved to next control");
      GetElem("SData").focus();
      return true;
      }
   else {
      Log("User pressed tab key on the first cell and focus is moved to previous control");
      GetElem("IStyle").focus();
      return true;
      }   
}

Grids.OnFocus = function(G,row,col,orow,ocol,fpagepos){
   if(fpagepos!=null) var S = "OnFocus: User <b>focused</b> cell on page ["+(row.id?row.id:G.GetPageNum(row))+","+col+"] on position "+fpagepos+".";
   else var S = "OnFocus: User <b>focused</b> cell ["+row.id+","+col+"]"+(col?", with value '"+Esc(G.GetString(row,col))+"'":"")+".";
   
   Log(S+" Original focused cell was ["+(orow?orow.id:"none")+","+(ocol?ocol:"none")+"].",1);
   FRow = row;
   if(fpagepos==null) SetDRow(G,row); // Sets row values in DRow tag's inputs
}
Grids.OnSelect = function(G,row,type){
   var t = ["unselected","selected","changed selection of"];
   Log("OnSelect: User <b>"+t[type-0]+"</b> row "+row.id+".",1);
}
Grids.OnStartEdit = function(G,row,col){
   Log("OnStartEdit: User <b>STARTED editation</b> in cell ["+row.id+","+col+"], with value '"+Esc(G.GetString(row,col))+"'.",1);
}
Grids.OnEndEdit = function(G,row,col,save){
   Log("OnEndEdit: User <b>"+(save?"FINISHED":"CANCELED")+" editation</b> in cell ["+row.id+","+col+"], with original value '"+Esc(G.GetString(row,col))+"'.",1);
}

// --------------------------------------------------------------------------------------------------------------------------
//                                                 Data event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnDataSend = function(G,D,Data){ 
if(!Data) Data="";
if(Data.length>50) Data = Data.slice(0,50)+" ...";
Log("OnDataSend: TreeGrid sends data '"+Esc(Data)+"' to '"+D.Url+"'",1,"#CCC"); 
}		
Grids.OnDataError = function(G,code,mess,dataio,row){ Log("OnDataError: TreeGrid failed in data communication with code["+code+"] and message '"+mess+"'. Url was "+dataio.Url+".",1,"red"); }
Grids.OnDataReceive = function(G,row){ Log("OnDataReceive: TreeGrid receives data "+(row?" for row "+row.id:""),1,"#CCC"); }
Grids.OnDownloadPage = function(G,row,func){ 
   Log("OnDownloadPage: TreeGrid requests data for page "+(row.id?row.id:G.GetPageNum(row)),1); 
   if(G.id=="AddPage" && G.XB.lastChild == row && row.Pos<9) G.AddPage(); // Specific code for AddPage example
   }
Grids.OnReadData = function(G,D){ Log("OnReadData: TreeGrid reads data from '"+(D.Url?D.Url:D.Tag)+"'",1,"#AAA"); }		
Grids.OnSave = function (G,row,autoupdate){ Log("OnSave: TreeGrid saves changes"+(row?" in row "+row.id:"")+" to server."+(autoupdate?" Event was fired from auto update.":""),1); }
Grids.OnReload = function(G){ Log("OnReload: TreeGrid starts reloading",1,"blue"); }		
Grids.OnLoaded = function(G){ Log("OnLoaded: TreeGrid loaded its data",1,"#CCC"); }		
Grids.OnLoadError = function(G) { Log("OnLoadError: TreeGrid failed to load",1,"red"); }		
Grids.OnRenderStart = function(G){ Log("OnRenderStart: TreeGrid started rendering",1,"#CCC"); }		
Grids.OnRenderFinish = function(G){ 
   FillIds(); 
   Log("OnRenderFinish: TreeGrid finished rendering",1,"blue"); 
   Log("&nbsp;",1); Log("&nbsp;",1); Log("&nbsp;",1);
   Log("--------------------------",1);
   Log("<b>TreeGrid Extended API example</b>",1,"red","16px");
   Log("<i>In </i><b>this window</b><i> are displayed all events fired by TreeGrid and logged from user event handlers by </i><b>Log</b><i> function. You can </i><b>clear</b><i>, </i><b>disable</b><i> event log or </i><b>enable style events</b><i>.</i>",1,"#0AA","14px");
   Log("<i>In the </i><b>right window</b><i> you can run functions to demonstrate TreeGrid Extended API. In the first combo you can select data example to load in grid. "
   +"In second combo you can change TreeGrid's style. In next controls you can focus any cell. Color/Select/Filter any cell that contains given value. And also change any value of focused row.</i>",1,"#0AA","14px");
   Log("<i>By </i><b>right click</b><i> on any cell you can display </i><b>pop-up menu</b><i> and select presented options. For various cells and rows are different menu options, for </i><b>header</b><i> is there also another set of options.</i>",1,"#0AA","14px");
   Log("<i>Remember, some functions can be very slow, especially with logging enabled and large data. Also remember, this example is universal and some functions can be unsuitable for some data examples.</i>",1,"#066","14px");
   Log("--------------------------",1);
   }		
Grids.OnRenderPageStart = function(G,row){ 
   Log("OnRenderPageStart: TreeGrid started rendering "+(row.id?"row '"+row.id+"' children":"page "+G.GetPageNum(row)),1,"#CCC"); 
   if(G.id=="AddPage" && G.XB.lastChild == row && row.Pos>=9){  // Specific code for AddPage example
      G.AddPage("Generated "+(row.Pos+2),"<I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/><I A='0'/>");
      }
   }		
Grids.OnRenderPageFinish = function(G,row){ Log("OnRenderPageFinish: TreeGrid finished rendering page "+(row.id?"row '"+row.id+"' children":"page "+G.GetPageNum(row)),1,"#CCC"); }		
Grids.OnLoadCfg = function(G){ Log("OnLoadCfg: TreeGrid loads configuration from cookies",1,"#CCC"); }		
Grids.OnCfgLoaded = function(G){ Log("OnCfgLoaded: TreeGrid loaded configuration from cookies",1,"#CCC"); }		
Grids.OnSaveCfg = function(G){ Log("OnSaveCfg: TreeGrid saves configuration to cookies",1,"#CCC"); }		

// --------------------------------------------------------------------------------------------------------------------------
//                                              Panel event handlers
// --------------------------------------------------------------------------------------------------------------------------

Grids.OnGetUserPanel = function (G){ 
   Log("OnGetUserPanel: User defined panel returned",1,"#ccc");
   return "<td><button class=GButton onclick='alert(\"User toolbar button clicked!\");'>User</button></td>"; 
   }
Grids.OnPanelClick = function(G,idx){ Log("OnPanelClick: User clicked to toolbar button with idx "+idx,1); }		

Grids.OnCanShowPanelItem = function(G,idx,show){
Log("OnCanShowPanelItem: Panel button ["+idx+"] is <b>"+(show?"visible":"hidden")+"</b>",0,"#ccc");
return show;
}
Grids.OnCanShowCfgItem = function(G,idx,show){
Log("OnCanShowCfgItem: Configuration menu item ["+idx+"] is <b>"+(show?"visible":"hidden")+"</b>",0,"#ccc"); 
return show;
}
Grids.OnCfgChanged = function(G){
Log("OnCfgChanged: Configuration was changed by user",1); 
}
Grids.OnCfgChanged = function(G){
Log("OnCfgChanged: Configuration was changed by user",1); 
}
Grids.OnColumnsChanged = function(G,cols,count){
Log("OnColumnsChanged: Column visibility was changed",1);
for(var c in cols) Log("Column '"+c+"' is now "+(cols[c]?"visible":"hidden"),1);
}
// --------------------------------------------------------------------------------------------------------------------------










// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//                                                  Controlling functions
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// --------------------------------------------------------------------------------------------------------------------------
// Function called after click on any item of user right click menu
// Runs appropriate command
// idx is option index, Items is array of option names
function MenuClick(idx,G, row,col,Items){
Log("User menu item '<b>"+Items[idx]+"</b>' selected.",1,"red");
switch(Items[idx]){
   case 'Unselect': 
   case 'Select' :
      G.SelectRow(row); break;          // Changes selection of the row
   case 'Select all':
   case 'Unselect all':
      G.SelectAllRows(); break;         // Changes selection of all rows
   case 'Delete' :
      G.DeleteRow(row,2); break;        // Deletes the row
   case 'Undelete' :
      G.DeleteRow(row,3); break;        // Undeletes the row
   case 'Delete selected':
   case 'Undelete selected':
      G.DeleteSelectedRows();           // Deletes or undeletes all selected rows
      break;
   case 'Collapse' :
      G.Collapse(row); break;           // Collapses the row
   case 'Expand' :
      G.Expand(row); break;             // Expands the row
   case 'Change value' :
      var val = prompt('Change cell value',G.GetString(row,col)); // Lets user to change the value as string
      if(val!=null) {
         G.SetString(row,col,val);     // Sets the changed value and recalculates the grid
         G.RefreshCell(row,col);       // Refreshes the cell to display new value
         G.ColorRow(row);              // Colors the row to change color according to changes
         }
      break;
   case 'Add child' :
      var r = G.AddRow(row,null,true); // Adds new row to the children of the row, to the end
      G.ScrollIntoView(r);             // Scrolls grid to display new row
      break;
   case 'Add next' :
      var r = G.AddRow(row.parentNode,G.GetNext(row),true); // Adds new row after the row, to the same parent
      G.ScrollIntoView(r);             // Scrolls grid to display new row
      break;
   case 'Sort descending':
      G.SortClick(col,0);              // Sorts grid descending according to the col
      break;
   case 'Sort ascending':
      G.SortClick(col,1);              // Sorts grid ascending according to the col
      break;
   case 'Hide column':
      G.HideCol(col);                  // Hides the column (column can be displayed from menu columns, it can be run from toolbar)
      G.SaveCfg();                     // Updates configuration in cookies
      break;
   }
}
// --------------------------------------------------------------------------------------------------------------------------
// Function shows popup menu on cell
// Called from OnRightClick event
function ShowMenu(G,row,col,x,y){
Log("User displayed popup menu",1);
var Items=new Array(), p = 0;
if(row==G.XHeader){                   // row is header row
   var R = G.GetSelRows();            // returns all selected rows as array
   if(R.length){                      // If there is at least one selected row
      Items[p++] = "Unselect all";
      for(var i=0,del=0;i<R.length;i++) if(Is(R[i],"Deleted")) { del=1; break; }
      if(del) Items[p++] = "Undelete selected";  // If there is at least one delete row in grid
      else Items[p++] = "Delete selected";       // No deleted row in grid
      }
   else Items[p++] = "Select all";   // No selected row in grid
   if(col!="Panel"){                 // Column is not left panel
      Items[p++] = "Sort descending";
      Items[p++] = "Sort ascending";
      Items[p++] = "Hide column";
      }
   }
else {                                 // row is not header row
   Items[p++] = Is(row,"Selected") ? "Unselect" : "Select";
   Items[p++] = Is(row,"Deleted") ? "Undelete" : "Delete";
   if(G.HasChildren(row)){            // If row has at least one visible child
      if(Is(row,"Expanded")) Items[p++] = "Collapse"; // For expanded row
      else Items[p++] = "Expand";                     // For collapsed row
      }
   if(G.CanEdit(row,col)) Items[p++] = "Change value";// If value can be edited by user
   if(!row.Fixed && G.GetCDef(row)) Items[p++] = "Add child"; // If this is body row (no foot or head) and has set CDef attribute
   if(!row.Fixed) Items[p++] = "Add next";           // If this is body row (no foot or head)
   }
G.ShowMenu(row,col,Items,null,MenuClick,null,"User&nbsp;menu",x,y,Items); // Shows user menu with given items and caption on given position
}
// --------------------------------------------------------------------------------------------------------------------------
// Function called from button Focus to focus chosen row
// Also called when SIds or SCols combo changed
function Focus(e){

// --- gets row and col from combos ---
var S = GetElem('SIds');
var id = S.options[S.selectedIndex].value;
var G = Grids[0];
var row = G.GetRowById(id);
var S = GetElem('SCols');
var col = S.options[S.selectedIndex].value;
Log("User run command <b>Focus</b> for cell ["+id+","+col+"].",1,"red");

if(!row){ // Row with given id is not present in the grid
   alert("Unknown id");
   return;
   }
if(!Is(row,"Visible")){  // Row is not visible (is hidden by filter or is deleted
   alert("Selected row is hidden and cannot be focused");
   return;
   }
setTimeout(function(){ // Delays the code, otherwise the grid will loose focus to the select box
   G.Focus(row,col,null,true);   // Focuses the row and also expands row's parent if it is collapsed (set by last parameter)
   },10);
}
// --------------------------------------------------------------------------------------------------------------------------
// Called to prepare controls for new data
// Called from OnRenderFinish and OnRowAdd event handlers
// Fills all ids of rows to SIds select box
// Fills all column names to SCols select box
// Generates inputs in DRow tag for every column, to let user to change values
function FillIds(){
var G = Grids[0]; // There is only one grid on page

// --- fills all rows' ids ---
var S = GetElem("SIds");
S.options.length = 0;
var A = new Array(), id=1;

// Variable rows
for(var r=G.GetFirst();r;r=G.GetNext(r)) { // Base cycle for iteration through all variable rows
   if(!r.id) r.id = "R"+(id<10?'0':"")+(id++); // Generate id, if row has not any
   A[A.length] = r.id;
   }
A.sort();                                  // sorts ids to better display in combo

// Fixed rows
var F = G.GetFixedRows(); id=1;
for(var i=0;i<F.length;i++){               
   if(!F[i].id) F[i].id = "F"+(id<10?'0':"")+(id++);
   A[A.length] = F[i].id;
   }
   
for(var i=0;i<A.length;i++){ // generates new options in select box
   S.options[S.options.length] = new Option(A[i],A[i]);
   }

// --- fills all columns' names ---
var S = GetElem("SCols");
S.options.length = 0;
var A = new Array();
//for(var c=G.GetFirstCol();c;c=G.GetNextCol(c)) A[A.length] = c; // Base cycle for iteration through all visible columns according to their position
for(var c in G.Cols) if(c!='Panel') A[A.length] = c; // Base cycle for iteration throught all columns
A.sort();                    // sorts column names to better display in combo
for(var i=0;i<A.length;i++){ // generates new options in select box
   S.options[S.options.length] = new Option(A[i],A[i]);
   }
   
// --- creates inputs for every column ---
var D = GetElem("DRow"), A = new Array(), p=0;
A[p++] = "<div id=ID_ID>Row</div>";
for(var c in G.Cols) if(c!='Panel'){ // Base cycle for iteration throught all columns
   A[p++] = G.GetCaption(c)+"&nbsp";
   A[p++] = "<input id=ID"+c+" type=text style='width:80px;font-size:10px;' onkeydown='if(event.keyCode==13 || event.charCode==13) Update(this);'><br>";
   }
D.innerHTML = A.join("");

var I = D.getElementsByTagName("input");
for(var i=0;i<I.length;i++){               // Sets with of all inputs to maximize it
   I[i].style.width = (D.offsetWidth-I[i].offsetLeft-15)+"px";
   }
Resize(); // Height of the tag was changed
}
// --------------------------------------------------------------------------------------------------------------------------
// Function for coloring/filtering/selecting found rows/cells
// Fro button=0 restores all rows
// For button=1 sets user attribute col+"Marked" if cell contains the searched value
// For button=2 hides row if any cell contains the searched value
// For button=3 selects rows if any cell contains the searched value
// Called when user presses the button or presses enter in input tag
function Color(button){
var G = Grids[0];
var V = GetElem("IColor").value;
Log("User run command <b>Color ("+button+")</b> with value '"+Esc(V)+"'.",1,"red");

// --- main function to change row's state or color ---
function color(r){
   var found = false;
   for(var c in G.Cols){
      if(c=="Panel") continue;
      var t = G.GetType(r,c), M = 0;
      if(t=="Int" || t=="Float") { // Number types compares as number
         M = V-0==G.GetValue(r,c);
         }
      else {                       
         var S = G.GetString(r,c);
         if(!S) M=!V;
         else M = V?S.search(V)>=0:0; // Other then number types searches for appearance
         }
      if(M) found = true;
      if(button!=1) M=0; // only button 'Color' colors the rows
      r[c+"Marked"]=M; // Color, sets custom attribute, this attribute will be used in event OnGetColor
      }
   if(button==2 && !found) G.HideRow(r); // Filter
   else G.ShowRow(r);
   if(!Is(r,"Selected") && button==3 && found || Is(r,"Selected") && (button!=3 || !found)) G.SelectRow(r); // Select
   if(found && button) G.ExpandParents(r); // Expands all parents of the row to make the row visible
   G.ColorRow(r);                          // Updates color of the row
   }
   
// --- iteration of all rows ---
G.Rendering = true;   // Suppresses updating grid layout after every change, speeds up the action. It is good especially for Filter (button==2), because it shows/hides rows
//for(var r=G.GetFirstVisible();r;r=G.GetNextVisible(r)) color(r); // Base cycle for iteration through all visible variable rows
for(var r=G.GetFirst();r;r=G.GetNext(r)) if(!Is(r,"Deleted")) color(r);  // Base cycle for iteration through all non deleted rows
var F = G.GetFixedRows(); for(var i=0;i<F.length;i++) color(F[i]); // Base cycle for iteration through fixed rows
G.Rendering = false;  // Restores original state
G.SetScrollBars();    // Updates grid layout
}
// --------------------------------------------------------------------------------------------------------------------------
// Sets focused row values in DRow tag's inputs
// Called from OnFocus event handler
function SetDRow(G,row){

// --- sets the first row - id, parents, page ---
var I = document.getElementById("ID_ID");
var S = "", r = row.parentNode;
while(r.tagName=="I"){ S += (S?",":"")+(r.id?r.id:"???"); r=r.parentNode; } // fills parents from tree
S = "Id="+(row.id?row.id:"???")+(S?"; parents="+S:"")+"; page="+(r.id?r.id:G.GetPageNum(r));
I.innerHTML = S;

// --- sets values ---
for(var col in G.Cols) if(col!='Panel'){ // Base cycle for iteration throught all columns
   var I = document.getElementById("ID"+col);
   if(I){
      var type = G.GetType(row,col);
      I.value =  G.ValueToString(G.GetValue(row,col),type,type=="Enum" ? G.GetEnum(row,col) : G.GetFormat(row,col,1)); // Gets string in editing format
      I.disabled = !G.CanEdit(row,col);  // Disables input if value cannot be edited
      }
   }
}
// --------------------------------------------------------------------------------------------------------------------------
// Updates inputs value after change
// Called when user presses enter in any input tag in DRow tag
// I is the tag input
function Update(I){
var G = Grids[0], row = FRow; // FRow is backup of focused row in the grid, because if grid user clicks on any control on page, the grid looses focus and clears Grids[0].FRow property
if(!FRow){ // (new) data was loaded and user did not clicked on any row yet
   alert("No row focused yet");
   return;
   }
var col = I.id.slice(2); // input's id is "ID"+column_name
var type = G.GetType(row,col);
var val = G.StringToValue(I.value,type,type=="Enum" ? G.GetEnum(row,col) : G.GetFormat(row,col,1)); // Converts string in editing format to value of given type
G.SetValue(row,col,val); // sets the value and recalculates grid
G.RefreshCell(row,col);  // Refreshes the cell to display new value
G.ColorRow(row);         // Colors the row to change color according to changes
}
// --------------------------------------------------------------------------------------------------------
// Changes style of TreeGrid, does not reload, just re-renders
function ChangeStyle(idx){
var G = Grids[0];
G.ChangeStyle("<Grid><Img "+Styles[idx]+"/></Grid>"); // Changes style by changing settings in <Img/>
}
// --------------------------------------------------------------------------------------------------------------------------
