// TreeList

ASPxClientTreeList = _aspxCreateClass(ASPxClientControl, {

    constructor: function(name) {
        this.constructor.prototype.constructor.call(this, name);
        
        this.elementCache = { };        
        this.rowIdPrefix = this.name + "_" + this.RowIDSuffix;
        this.focusedKey = null;
        this.dragHelper = new ASPxClientTreeListDragHelper(this);
        this.lpTimer = -1;
        this.selectionDiff = { };
        this.syncLock = false;
        this.callbackHandlersPool = [ ];
        
        // set from outside
        this.enableFocusedNode = false;
        this.lpDelay = 300;        
        this.lpDisable = false;
        this.focusSendsCallback = false;
        this.selectionSendCallback = false;
        this.columns = [ ];
        this.expandCollapseAction = this.ExpandCollapseAction.Button;
        this.focusOnExpandCollapse = true;
        this.confirmDeleteMsg = null;
        this.allowStylizeEditingNode = false;
        
        // updatable on callbacks
        this.maxVisibleLevel = -1;
        this.visibleColumnCount = 0;
        this.rowCount = 0;
        this.editingKey = null;
        this.isNewNodeEditing = false;
                        
        this.FocusedNodeChanged = new ASPxClientEvent();
        this.SelectionChanged = new ASPxClientEvent();
        this.CustomizationWindowCloseUp = new ASPxClientEvent();
        this.CustomDataCallback = new ASPxClientEvent();		
        this.NodeClick = new ASPxClientEvent();
        this.NodeDblClick = new ASPxClientEvent();
        this.ContextMenu = new ASPxClientEvent();
		this.StartDragNode = new ASPxClientEvent();
		this.EndDragNode = new ASPxClientEvent();        
		this.CustomButtonClick = new ASPxClientEvent();
		this.NodeFocusing = new ASPxClientEvent();
        this.NodeExpanding = new ASPxClientEvent();
        this.NodeCollapsing = new ASPxClientEvent();
    },
    
    // UI
    OnNodeSelecting: function(check) {    
		this.OnNodeSelectingCore(check);		
		if(this.RaiseSelectionChanged())
			this.SendDummyCommand();		
    },
    OnNodeSelectingCore: function(check) {
		var row = this.FindDataRow(check);		
		if(!_aspxIsExistsElement(row)) return;
		var nodeKey = this.GetNodeKeyByRow(row);	        
		if(_aspxIsExists(this.selectionDiff[nodeKey]))
			delete this.selectionDiff[nodeKey];
		else
			this.selectionDiff[nodeKey] = 1;			
		this.PersistSelectionDiff();			 
		this.UpdateRowStyle(nodeKey);		
    },
    OnSelectingAll: function(state) {
		var input = this.GetSelectAllMarkInput();
		if(!_aspxIsExistsElement(input))
			return;
		input.value = state ? "A" : "N";		
		this.SendDummyCommand();
		this.RaiseSelectionChanged();
    },
    OnFocusingNode: function(key, htmlEvent) {		
		var prevKey = this.GetFocusedNodeKey();
		if(prevKey != key && this.RaiseNodeFocusing(key, htmlEvent)) {
			this.SetFocusedNodeKey(key);
			return this.RaiseFocusedNodeChanged();
		}
		return false;
    },
    OnDataTableMouseDown: function(e) {    
		if(this.syncLock) return;
		if(!_aspxGetIsLeftButtonPressed(e)) return;
		e = _aspxGetEvent(e);
		var row = this.FindDataRow(_aspxGetEventSource(e));
		if(!_aspxIsExistsElement(row))
			return;
		var helper = this.dragHelper;		
		var canDrag = !this.IsEditing();		
		if(canDrag) {
		    var nodeKey = this.GetNodeKeyByRow(row);
		    var targets = this.GetAllDataRows();
		    var header = this.GetHeaderRow();
		    if(_aspxIsExistsElement(header))
				targets.push(header);
		    canDrag = this.RaiseStartDragNode(nodeKey, e, targets);
		    if(canDrag)
			    helper.CreateNodeTargets(targets, row);
		}
		var drag = helper.CreateNodeDrag(row, e, canDrag);
    },
    OnDataTableClick: function(e) {
		if(this.syncLock) return;
		e = _aspxGetEvent(e);
		var source = _aspxGetEventSource(e);
		var sourceIsIndent = this.FindIndentCell(source) != null;
		var sourceIsCommandCell = this.FindCommandCell(source) != null;
		var row = this.FindDataRow(source);
		if(!_aspxIsExistsElement(row))
			return;
		var nodeKey = this.GetNodeKeyByRow(row);
		if(!sourceIsIndent && !sourceIsCommandCell) {
			if(!this.RaiseNodeClick(nodeKey, e))		
				return;
		}
		var tag = source.tagName;
		var className = source.className;

		if(tag == "IMG") {
			if(className == this.ExpandButtonClassName) {
				if(this.enableFocusedNode && this.focusOnExpandCollapse)
					this.OnFocusingNode(nodeKey, e);
                if(this.RaiseNodeExpanding(nodeKey, e))
				    this.ExpandNode(nodeKey);				
				return;
			}
			if(className == this.CollapseButtonClassName) {
				if(this.enableFocusedNode && this.focusOnExpandCollapse)
					this.OnFocusingNode(nodeKey, e);
                if(this.RaiseNodeCollapsing(nodeKey, e))
				    this.CollapseNode(nodeKey);
				return;			
			}
		}
		if(tag == "INPUT" && className == this.SelectionCheckClassName && !this.GetDataTable().onmousedown) {
			this.OnFocusingNode(nodeKey, e);
			this.OnNodeSelecting(source);
			return;
		}				
		if(!sourceIsIndent) {					
			var processOnServer = this.enableFocusedNode ? this.OnFocusingNode(nodeKey, e) : false;
			if(this.expandCollapseAction == this.ExpandCollapseAction.NodeClick && this.TryExpandCollapse(nodeKey, row))
				return;				
			if(processOnServer)
				this.SendDummyCommand();
		}
    },
    OnDataTableDblClick: function(e) {
		if(this.syncLock) return;
		e = _aspxGetEvent(e);		
		var source = _aspxGetEventSource(e);
		if(this.FindIndentCell(source) != null)
			return;		
		var row = this.FindDataRow(source);
		if(!_aspxIsExistsElement(row))
			return;
		var nodeKey = this.GetNodeKeyByRow(row);
		if(!this.IsEditing())
			_aspxClearSelection();				
		if(!this.RaiseNodeDblClick(nodeKey, e))
			return;
		if(this.expandCollapseAction == this.ExpandCollapseAction.NodeDblClick)
			this.TryExpandCollapse(nodeKey, row);
    },
    TryExpandCollapse: function(nodeKey, row) {
		var state = this.GetNodeState(nodeKey, row);
		if(state == "Expanded") {
			this.CollapseNode(nodeKey);
			return true;
		} else if(state == "Collapsed") {
			this.ExpandNode(nodeKey);
			return true;
		}
		return false;
    },  
    OnHeaderMouseDown: function(element, e) {    
		if(this.syncLock) return;
		if(!_aspxGetIsLeftButtonPressed(e)) return;
		var canDrag = element.id.indexOf(this.DragAndDropTargetMark) > -1;
		var drag = this.dragHelper.CreateHeaderDrag(element, e, canDrag);
		this.dragHelper.CreateHeaderTargets(drag, e);		
    },
    OnHeaderClick: function(element, shiftKey, ctrlKey) {
		if(this.syncLock) return;
		var index = this.GetLastNumberOfId(element);
		var column = this.GetColumnByIndex(index);
		if(column != null && column.canSort)
			this.SendSortCommand(index, ctrlKey ? "none" : "", !shiftKey && !ctrlKey);
    },
    OnColumnMoving: function(sourceIndex, targetIndex, before) {    			
		this.SendCommand("MoveColumn", [sourceIndex, targetIndex, before]);
    },
    OnNodeDeleting: function(nodeKey) {
		if(this.confirmDeleteMsg != null && !confirm(this.confirmDeleteMsg))
			return;
		this.DeleteNode(nodeKey);
    },
    OnContextMenu: function(objectType, objectKey, htmlEvent) {
		var e = new ASPxClientTreeListContextMenuEventArgs(objectType, objectKey, htmlEvent);
		this.RaiseContextMenu(e);
		return e.cancel;
    },    
    
    // Callbacks
    OnCallback: function(result) {
	    this.ProcessCallbackResult(result);
    },
    OnCallbackError: function(result, data) {
		if(result != ""){
		    var cell = this.GetErrorCell();
		    if(_aspxIsExistsElement(cell)) {
			    _aspxSetElementDisplay(cell.parentNode, true);
			    cell.innerHTML = result;
		    } 
		    else 
			    alert(result);
		}
		this.syncLock = false;
    },
    SendCommand: function(command, args) {
		if(this.syncLock)
			return;		    
		this.SerializeEditorValues();
		var id = this.CommandId[command];
		this.WakeLoadingPanel();		
		var monoArg = _aspxIsExists(args) ? ([id].concat(args)).join(this.SeparatorToken) : id;
		if(_aspxIsExists(this.callBack) && this.CanCreateCallback()) {		    
		    this.PurgeCaches();
		    this.syncLock = true;
			this.CreateCallback(monoArg, command);
		} else 
			this.SendPostBack(monoArg);		
    },
    SendDummyCommand: function() {
		this.SendCommand("Dummy");
    },
    SendSortCommand: function(columnIndex, order, reset) {
		this.SendCommand("Sort", [columnIndex, order, reset]);
    },    
    SendPagerCommand: function(arg) {
		this.SendCommand("Pager", [arg]);
    },
    SendAsyncCommand: function(command, args) {
		if(!_aspxIsExists(this.callBack)) return;
		var monoArg = [this.CommandId[command], args].join(this.SeparatorToken);
		this.CreateCallback(monoArg, command);
    },
	SendGetNodeValuesCommand: function(handler, mode, nodeKey, fieldNames) {
		if(fieldNames === null) fieldNames = "";
		var index = this.GetCallbackHandlerIndex(handler);
		var monoArg = ([index, mode, nodeKey].concat(fieldNames)).join(this.SeparatorToken);
		this.SendAsyncCommand("GetNodeValues", monoArg);		
	},          
    WakeLoadingPanel: function() {
		if(this.lpDisable || this.lpTimer > -1)
			return;
		this.CreateLoadingDiv(this.GetUpdatableCell());			
		if(this.lpDelay > 10) {
			var js = "aspxTLShowLoadingPanel('" + this.name + "')";
			this.lpTimer = _aspxSetTimeout(js, this.lpDelay);
		} else {
			this.ShowLoadingPanel();
		}				
    },
    ShowLoadingPanel: function() {
		this.ClearLoadingPanelTimer();
		this.GetLoadingDiv().style.cursor = "wait";
		this.CreateLoadingPanelWithAbsolutePosition(this.GetUpdatableCell());
    },
    ClearLoadingPanelTimer: function() {
		this.lpTimer = _aspxClearTimer(this.lpTimer);		
    },
    HideLoadingPanel: function() {
		this.ClearLoadingPanelTimer();
		this.constructor.prototype.HideLoadingPanel.call(this);
    },
    ProcessCallbackResult: function(resultObj) {
		if(resultObj.customData) {			
			if(_aspxIsExists(resultObj.handler)) {
				var index = parseInt(resultObj.handler);
				var handler = this.callbackHandlersPool[index];
				this.callbackHandlersPool[index] = null;
				handler(resultObj.data);
			} else {
				this.RaiseCustomDataCallback(resultObj.arg, resultObj.data);
			}
			return;
		}		  		
		// state
		this.GetCallbackStateInput().value = resultObj.state;
		// focused node
		if(_aspxIsExists(resultObj.fkey))
			this.SetFocusedNodeKeyInternal(resultObj.fkey);
		// selection inputs
		var selectionInput = this.GetSelectionInput();
		if(_aspxIsExistsElement(selectionInput))
			selectionInput.value = "";
		var selectAllMarkInput = this.GetSelectAllMarkInput();
		if(_aspxIsExistsElement(selectAllMarkInput))
			selectAllMarkInput.value = "";
		// custom properties
		if(resultObj.cp) {			
			for(var name in resultObj.cp)
				this[name] = resultObj.cp[name];
		}
			
		if(_aspxIsExists(resultObj.partial))
			this.ProcessPartialCallbackResult(resultObj);
		else
			this.ProcessFullCallbackResult(resultObj);			
				
		this.maxVisibleLevel = resultObj.level;
		this.visibleColumnCount = resultObj.visColCount;
		this.rowCount = resultObj.rows;
		this.editingKey = _aspxIsExists(resultObj.editingKey) ? resultObj.editingKey : null;
		this.isNewNodeEditing = _aspxIsExists(resultObj.newNode);
		this.syncLock = false;
    },    
    ProcessPartialCallbackResult: function(resultObj) {
		this.UpdateFirstDataCellSpans(resultObj.level);

		var row = this.GetRowByNodeKey(resultObj.partial);
		if(!_aspxIsExistsElement(row))
			return;
		if(resultObj.remove)
			this.KillNextRows(row, resultObj.remove);
			
        var uid = "";
        var cell = this.GetUpdatableCell();
        var html = cell.innerHTML;
        do {
            uid = this.GenerateUID();
        } while(html.indexOf(uid) > -1);
        
        var placeholder = document.createTextNode(uid);        
        row.parentNode.insertBefore(placeholder, row);
        this.DestroyHtmlRow(row);        
        
        html = cell.innerHTML;                   
        html = html.replace(uid, resultObj.data);
        cell.innerHTML = html;
    },
    ProcessFullCallbackResult: function(resultObj) {
		this.GetUpdatableCell().innerHTML = resultObj.data;
    },
    
    // Elements
    GetCallbackStateInput:			function() { return this.GetCachedElementById("STATE"); },
    GetFocusedKeyInput:				function() { return this.GetCachedElementById("FKey"); },
    GetSelectionInput:				function() { return this.GetCachedElementById("Sel"); },
    GetSelectAllMarkInput:			function() { return this.GetCachedElementById("SAM"); },
    GetEditorValuesInput:			function() { return this.GetCachedElementById("EV"); },
    GetDataTable:					function() { return this.GetCachedElementById("D"); },
    GetUpdatableCell:				function() { return this.GetCachedElementById("U"); },        	
	GetStyleTable:					function() { return this.GetCachedElementById("ST"); },
    GetDragAndDropArrowDownImage:	function() { return this.GetCachedElementById("DAD"); },
    GetDragAndDropArrowUpImage:		function() { return this.GetCachedElementById("DAU"); },
    GetDragAndDropHideImage:		function() { return this.GetCachedElementById("DH"); },
    GetDragAndDropNodeImage:		function() { return this.GetCachedElementById("DN"); },
    GetErrorCell:					function() { return this.GetCachedElementById("Error"); },
    GetHeaderRow:					function() { return this.GetCachedElementById("HDR"); },
	
    GetCustomizationWindow: function() { 
		return aspxGetControlCollection().Get(this.name + this.GetCustomizationWindowSuffix()); 
	},
	GetCustomizationWindowElement: function() {
		var win = this.GetCustomizationWindow();
		return win ? win.GetWindowElement(-1) : null;		
	},
	GetHeaderSuffix: function() { return this.DragAndDropTargetMark + "H-"; },
	GetCustomizationWindowSuffix: function() { return this.DragAndDropTargetMark + "CW"; },
	GetEmptyHeaderSuffix: function() { return this.DragAndDropTargetMark + "EH"; },	
       
    // Columns
    CreateColumn: function(index, name, fieldName, canSort, showInCw) {
		var column = new ASPxClientTreeListColumn(index, name, fieldName);		
		column.canSort = canSort;
		column.showInCw = showInCw;
		this.columns.push(column);
    },
    FindColumn: function(id) {
		if(!_aspxIsExists(id)) return null;
		if(id.__dxColumnObject)
			return id;
		if(typeof id == "number")
			return this.GetColumnByIndex(id);
		var result = this.GetColumnByName(id);
		if(result == null)
			result = this.GetColumnByFieldName(id);
		return result;
    },
    
    // Utils
    UpdateRowStyle: function(nodeKey) {
		if(!this.allowStylizeEditingNode && nodeKey == this.editingKey)
			return;
		var row = this.GetRowByNodeKey(nodeKey);
		if(!_aspxIsExistsElement(row))
			return;
			
		var isFocused = this.focusedKey == nodeKey;
		var isSelected = this.IsRowSelected(row);
		
		if(isFocused) {
			this.ApplyRowStyle(row, 0);
		} else if(isSelected) {
			this.ApplyRowStyle(row, 1);
		} else {		
			var index = row.rowIndex;
			this.ApplyRowStyle(row, 2 + index);
		}
    },    
    ApplyRowStyle: function(row, index) {
		var styledCells = this.GetStyleTable().rows[0].cells;
		var max = styledCells.length - 1;
		if(index > max)
			index = max; // the case of simple style-table
		row.className = styledCells[index].className;
		row.style.cssText = styledCells[index].innerHTML;
    },
    GetNodeKeyByRow: function(row) {
		return row.id.substr(this.rowIdPrefix.length);
    },
    GetRowByNodeKey: function(nodeKey) { 		
		return this.GetCachedElementById(this.RowIDSuffix + nodeKey);		
    },
    GetAllDataRows: function() {
        var result = [ ];
		var rows = this.GetDataTable().rows;
		for(var i = 0; i < rows.length; i++) {
			var row = rows[i];			
			if(!this.IsElementDataRow(row)) continue;
			result.push(row);
		}
		return result;
    },        
    IsElementEmptyHeader: function(element) {
		return element.id == this.name + this.GetEmptyHeaderSuffix();
    },
    IsElementDataRow: function(element) {
		return element.id.indexOf(this.rowIdPrefix) == 0;		
    },    
    IsElementIndentCell: function(element) {
		return element.className.indexOf("dxtl__I") > -1;
    },    
    IsElementCommandCell: function(element) {
		return element.className.indexOf("dxtl__cc") > -1;
    },    
    FindDataRow:		function(element) { return this.FindElementUpwardsCore(element, this.IsElementDataRow); },
    FindIndentCell:		function(element) { return this.FindElementUpwardsCore(element, this.IsElementIndentCell); },
    FindCommandCell:	function(element) { return this.FindElementUpwardsCore(element, this.IsElementCommandCell); },
    FindElementUpwardsCore: function(startElement, matchEvaluator) {
		var dataTable = this.GetDataTable();
		var element = startElement;
		while(_aspxIsExistsElement(element) && element != dataTable) {
			if(matchEvaluator.call(this, element))
				return element;
			element = element.parentNode;
		}
		return null;        
    },
    IsRowSelected: function(row) {
		var check = this.FindSelectionCheck(row);
		if(!_aspxIsExistsElement(check))
			return null;
		return check.checked;
    },
    FindSelectionCheck: function(row) {
		var elements = row.getElementsByTagName("INPUT");
		for(var i = 0; i < elements.length; i++) {
			var element = elements[i];
			if(element.type == "checkbox" && element.className == this.SelectionCheckClassName)
				return element;
		}
		return null;
    },    
    PersistSelectionDiff: function() {
		var input = this.GetSelectionInput();
		if(!_aspxIsExistsElement(input))
			return;
		var list = [ ];
		for(var key in this.selectionDiff)
			list.push(key);
		input.value = list.join(this.SeparatorToken);
    },
    UpdateFirstDataCellSpans: function(newMaxVisibleLevel) {
		if(this.maxVisibleLevel == newMaxVisibleLevel) 
			return;
			        
		var rows = this.GetDataTable().rows;
		var spanDiff = newMaxVisibleLevel - this.maxVisibleLevel;
		for(var i = 0; i < rows.length; i++) {
			var row = rows[i];
			var tempCell;
			var firstDataCell;
			var tempCell = row.cells[row.cells.length - 1];
			if(tempCell.colSpan > 1)
				firstDataCell = tempCell; // only preview-row can have spanned last cell
			else 
				firstDataCell = row.cells[row.cells.length - this.GetColumnCellCount()];
			var newSpan = firstDataCell.colSpan + spanDiff;
			if(newSpan > 0)
				firstDataCell.colSpan = newSpan;
		}    	
    },
    KillNextRows: function(row, count) {		
		while(count-- > 0) {
			var rowToKill = row.nextSibling;
			if(!_aspxIsExistsElement(rowToKill))
				break;			
			if(rowToKill.nodeType != 1) {	// Gecko
				count++;
				_aspxRemoveElement(rowToKill);
			} else {
				this.DestroyHtmlRow(rowToKill);
			}			
		}
    },
    DestroyHtmlRow: function(row) {
		for(var i = row.cells.length - 1; i >= 0; i--)
			row.cells[i].innerHTML = "";
		_aspxRemoveElement(row);		
    },
    SetFocusedNodeKeyInternal: function(key) {
		this.focusedKey = key;
		this.GetFocusedKeyInput().value = key;
    },    
    GetLastNumberOfId: function(element) {		
		var matches = element.id.match(/\d+/g);
		if(matches != null) {
			var count = matches.length;
			if(count > 0)
				return parseInt(matches[count - 1], 10);
		}
		return -1;
    },
    GetCachedElementById: function(id) {
		id = this.name + "_" + id;
		if(!_aspxIsExistsElement(this.elementCache[id]))
			this.elementCache[id] = _aspxGetElementById(id);
		return this.elementCache[id];
    },
    GenerateUID: function() {
        var result = "";
        for(var i = 0; i < 16; i++) {
            var num = Math.floor(10000 * (1 + Math.random()));
            result += num.toString(36);
        }
        return result;
    },
    PurgeCaches: function() {		
		this.elementCache = { };		
		this.focusedKey = null;
		this.selectionDiff = { };
    },
    GetColumnCellCount: function() {
		var count = this.visibleColumnCount;
		if(count < 1) count = 1; // when no columns visible
		return count;
    },
    GetCallbackHandlerIndex: function(handler) {
		for(var i = 0; i < this.callbackHandlersPool.length; i++) {
			if(this.callbackHandlersPool[i] == null) {
				this.callbackHandlersPool[i] = handler;
				return i;
			}
		}
		this.callbackHandlersPool.push(handler);
		return this.callbackHandlersPool.length - 1;
    },
    
    // Editing
    GetEditorObjects: function() {
		var list = [ ];
		var clientObjects = aspxGetControlCollection().elements;		
		for(var name in clientObjects) {
			if(name.indexOf(this.name) != 0)
				continue;
			var pos = name.indexOf(this.EditorSuffix);
			if(pos < 0 || name.indexOf("_", pos) > -1)
				continue;
			var obj = clientObjects[name];
			if(!_aspxIsExists(obj.GetMainElement) || !_aspxIsExistsElement(obj.GetMainElement()))
				continue;			
			list.push(obj);
		}
		return list;
    },
    SerializeEditorValues: function() {
		var editors = this.GetEditorObjects();
		var count = editors.length;
		var result = [ count ];						
		for(var i = 0; i < count; i++) {
			var columnIndex = this.GetEditorColumnIndex(editors[i]);
			var value = editors[i].GetValueString();
			var length = -1;
			if(!_aspxIsExists(value)) {
				value = "";
			} else {
				value = value.toString();
				length = value.length;
			}
			result.push(columnIndex);
			result.push(length);
			result.push(value);
		}
		this.GetEditorValuesInput().value = result.join(this.SeparatorToken);		
    },
    GetEditorColumnIndex: function(editorObject) {
		var name = editorObject.name;
		return name.substr(name.lastIndexOf(this.EditorSuffix) + this.EditorSuffix.length);
    },

	// Consts
	SeparatorToken: " ",
	RowIDSuffix: "R-",
	EditorSuffix: "DXEDITOR",
	DragAndDropTargetMark: "_DX-DnD-",		
    ExpandButtonClassName: "dxtl__Expand",
    CollapseButtonClassName: "dxtl__Collapse",
    SelectionCheckClassName: "dxtl__Sel",
    CommandId: {
		Expand:				1,
		Collapse:			2,
		Pager:				3,
		CustomDataCallback:	4,
		MoveColumn:			5,
		Sort:				6,
		Dummy:				8,
		ExpandAll:			9,
		CollapseAll:		10,
		CustomCallback:		11,
		StartEdit:			12,
		UpdateEdit:			14,
		CancelEdit:			15,
		MoveNode:			16,
		DeleteNode:			17,
		StartEditNewNode:	18,
		GetNodeValues:		20
    },    
	ExpandCollapseAction: {
		Button: 0,
		NodeClick: 1,
		NodeDblClick: 2
	},
	GetNodeValuesCommandMode: {	
		ByKey:				0,
		Visible:			1,
		SelectedAll:		2,
		SelectedVisible:	3
	},
	// base
	RaiseContextMenu: function(e) {
		if(!this.ContextMenu.IsEmpty())
			this.ContextMenu.FireEvent(this, e);
	},
	RaiseCustomButtonClick: function(nodeKey, buttonIndex, buttonID) {
		var handler = this.CustomButtonClick;
		if(handler.IsEmpty()) return false;
		handler.FireEvent(this, new ASPxClientTreeListCustomButtonEventArgs(nodeKey, buttonIndex, buttonID));
	},
    
    /**************************************** FOCUSED NODE **************************************/
    GetFocusedNodeKey: function() {
		if(!this.enableFocusedNode)
			return "";
		if(this.focusedKey === null)
			this.focusedKey = this.GetFocusedKeyInput().value;
		return this.focusedKey;
    },
    SetFocusedNodeKey: function(key) {
		if(!this.enableFocusedNode)
			return;
							
		var prevKey = this.GetFocusedNodeKey();		
		this.SetFocusedNodeKeyInternal(key);
		this.UpdateRowStyle(prevKey);	
		this.UpdateRowStyle(key);
    },
    RaiseNodeFocusing: function(nodeKey, htmlEvent) {
		if(this.NodeFocusing.IsEmpty()) return true;
		var args = new ASPxClientTreeListNodeEventArgs(nodeKey, htmlEvent);
		this.NodeFocusing.FireEvent(this, args);
		return !args.cancel;
    },    
    RaiseFocusedNodeChanged: function() {
		var processOnServer = false;
		if(!this.FocusedNodeChanged.IsEmpty()) {
			var args = new ASPxClientProcessingModeEventArgs(processOnServer);
			this.FocusedNodeChanged.FireEvent(this, args);
			processOnServer = args.processOnServer;			
		}
		return this.focusSendsCallback || processOnServer;
    },
    
    /*************************************** SELECTION *********************************************/    
    RaiseSelectionChanged: function() {
		var processOnServer = false;
		if(!this.SelectionChanged.IsEmpty()) {
			var args = new ASPxClientProcessingModeEventArgs(processOnServer);
			this.SelectionChanged.FireEvent(this, args);
			processOnServer = args.processOnServer;
		}
		return this.selectionSendsCallback || processOnServer;
    },
    IsNodeSelected: function(nodeKey) {
		var row = this.GetRowByNodeKey(nodeKey);
		if(!_aspxIsExistsElement(row))
			return null;
		return this.IsRowSelected(row);
    },
    SelectNode: function(nodeKey, state) {    
		var row = this.GetRowByNodeKey(nodeKey);
		if(!_aspxIsExistsElement(row))
			return;			
		var check = this.FindSelectionCheck(row);
		if(!_aspxIsExistsElement(check) || check.checked == state)
			return;
		if(!_aspxIsExists(state))
			state = true;			
		check.checked = state;
		this.OnNodeSelectingCore(check);
    },
	GetVisibleSelectedNodeKeys: function() {
		var rows = this.GetAllDataRows();
		var result = [ ];
		for(var i = 0; i < rows.length; i++) {
			var key = this.GetNodeKeyByRow(rows[i]);
			if(this.IsNodeSelected(key))
				result.push(key);
		}
		return result;
	},    
                
    /********************************* CUSTOMIZATION WINDOW ****************************************/
    IsCustomizationWindowVisible: function() {
		var win = this.GetCustomizationWindow();
		return win != null && win.IsVisible();
    },
    ShowCustomizationWindow: function(htmlElement) {
		var win = this.GetCustomizationWindow();
		if(win == null)
			return;
		if(!_aspxIsExistsElement(htmlElement))
			htmlElement = this.GetMainElement();
		win.ShowAtElement(htmlElement);
    },
    HideCustomizationWindow: function() {
		var win = this.GetCustomizationWindow();
		if(win != null)
			win.Hide();
    },
    RaiseCustomizationWindowCloseUp: function() {
		if(!this.CustomizationWindowCloseUp.IsEmpty())
			this.CustomizationWindowCloseUp.FireEvent(this, { } );
	},
	
	/************************************* CUSTOM CALLBACKS ****************************************/
	PerformCustomCallback: function(arg) {
		this.SendCommand("CustomCallback", [arg]);
	},
	PerformCustomDataCallback: function(arg) {
		this.SendAsyncCommand("CustomDataCallback", arg);
	},
	RaiseCustomDataCallback: function(arg, result) {
		if(!this.CustomDataCallback.IsEmpty()) {
			var e = new ASPxClientTreeListCustomDataCallbackEventArgs(arg, result);
			this.CustomDataCallback.FireEvent(this, e);
		}		
	},
	GetNodeValues: function(nodeKey, fieldNames, onCallback) {
		this.SendGetNodeValuesCommand(onCallback, this.GetNodeValuesCommandMode.ByKey, nodeKey, fieldNames);
	},
	GetVisibleNodeValues: function(fieldNames, onCallback) {
		this.SendGetNodeValuesCommand(onCallback, this.GetNodeValuesCommandMode.Visible, "", fieldNames);
	},
	GetSelectedNodeValues: function(fieldNames, onCallback, visibleOnly) {
		var mode = visibleOnly 
			? this.GetNodeValuesCommandMode.SelectedVisible 
			: this.GetNodeValuesCommandMode.SelectedAll; 
		this.SendGetNodeValuesCommand(onCallback, mode, "", fieldNames);
	},		
	
	/************************************* PAGING *************************************************/
	GoToPage: function(index) {
		if(index < -1)
			return;
		this.SendPagerCommand("PN" + index);
	},
	PrevPage: function() {
		this.SendPagerCommand("PBP");
	},
	NextPage: function() {
		this.SendPagerCommand("PBN");
	},
	
	/*****************************************  NODES  ******************************************/
	GetNodeState: function(nodeKey, row) {		
		if(!_aspxIsExists(row))
			row = this.GetRowByNodeKey(nodeKey);
		if(!_aspxIsExistsElement(row))
			return "NotFound";
		var children = row.getElementsByTagName("IMG");
		for(var i = 0; i < children.length; i++) {
			var name = children[i].className;
			if(name == this.ExpandButtonClassName)
				return "Collapsed";
			if(name == this.CollapseButtonClassName)
				return "Expanded";
		}
		return "Child";
	},
	ExpandAll: function() {
		this.SendCommand("ExpandAll");
	},
	CollapseAll: function() {
		this.SendCommand("CollapseAll");
	},
	ExpandNode: function(key) {
		this.SendCommand("Expand", [key]);
	},
	CollapseNode: function(key) {
		this.SendCommand("Collapse", [key]);
	},
	GetVisibleNodeKeys: function() {
	    var rows = this.GetAllDataRows();
	    var result = [ ];
	    for(var i = 0; i < rows.length; i++)
	        result.push(this.GetNodeKeyByRow(rows[i]));
	    return result;
	},
	GetNodeHtmlElement: function(nodeKey) {
	    return this.GetRowByNodeKey(nodeKey);
	},
	RaiseNodeClick: function(nodeKey, htmlEvent) {
		if(this.NodeClick.IsEmpty()) return true;
		var e = new ASPxClientTreeListNodeEventArgs(nodeKey, htmlEvent);
		this.NodeClick.FireEvent(this, e);
		return !e.cancel;		
	},
	RaiseNodeDblClick: function(nodeKey, htmlEvent) {
		if(this.NodeDblClick.IsEmpty()) return true;
		var e = new ASPxClientTreeListNodeEventArgs(nodeKey, htmlEvent);
		this.NodeDblClick.FireEvent(this, e);
		return !e.cancel;
	},
	RaiseNodeExpanding: function(nodeKey, htmlEvent) {
		if(!this.NodeExpanding.IsEmpty()) {
			var e = new ASPxClientTreeListNodeEventArgs(nodeKey, htmlEvent);
			this.NodeExpanding.FireEvent(this, e);
			return !e.cancel;
		}
		return true;
	},
	RaiseNodeCollapsing: function(nodeKey, htmlEvent) {
		if(!this.NodeCollapsing.IsEmpty()) {
			var e = new ASPxClientTreeListNodeEventArgs(nodeKey, htmlEvent);
			this.NodeCollapsing.FireEvent(this, e);
			return !e.cancel;
		}
		return true;
	},
	RaiseStartDragNode: function(nodeKey, htmlEvent, targets) {
	    if(this.StartDragNode.IsEmpty()) return true;
	    var e = new ASPxClientTreeListStartDragNodeEventArgs(nodeKey, htmlEvent, targets);
	    this.StartDragNode.FireEvent(this, e);
	    return !e.cancel;
	},
	RaiseEndDragNode: function(nodeKey, htmlEvent, targetElement) {
	    if(this.EndDragNode.IsEmpty()) return true;
	    var e = new ASPxClientTreeListEndDragNodeEventArgs(nodeKey, htmlEvent, targetElement);
	    this.EndDragNode.FireEvent(this, e);
	    return !e.cancel;
	},
		
	/******************************************** COLUMNS ****************************************/
	GetVisibleColumnCount: function() {
		return this.visibleColumnCount;
	},
	GetColumnCount: function() {
		return this.columns.length;
	},
    GetColumnByIndex: function(index) {
		for(var i = 0; i < this.columns.length; i++) {
			if(this.columns[i].index == index)
				return this.columns[i];
		}
		return null;
    },
    GetColumnByName: function(name) {
		if(name == "")
			return null;
		for(var i = 0; i < this.columns.length; i++) {
			if(this.columns[i].name == name)
				return this.columns[i];
		}
		return null;
    },
    GetColumnByFieldName: function(fieldName) {
		if(fieldName == "")
			return null;
		for(var i = 0; i < this.columns.length; i++) {
			if(this.columns[i].fieldName == fieldName)
				return this.columns[i];
		}
		return null;
    },
    SortBy: function(columnId, order, reset) {
		var column = this.FindColumn(columnId);
		if(column == null)
			return;
    
		if(!_aspxIsExists(order)) order = "";
		if(!_aspxIsExists(reset)) reset = true;
		this.SendSortCommand(column.index, order, reset);
    },
    
    /******************************************** EDITING ****************************************/
    StartEdit: function(nodeKey) {
		this.SendCommand("StartEdit", [nodeKey]);
    },
    UpdateEdit: function() {
		if(!this.IsEditing()) return;
		var editors = this.GetEditorObjects();		
		for(var i = 0; i < editors.length; i++) {
			var editor = editors[i];
			editor.Validate();
			if(!editor.GetIsValid()) {
				if(editor.setFocusOnError)
					editor.Focus();
				return;
			}
		}
		this.SendCommand("UpdateEdit");
    },
    CancelEdit: function() {
		if(!this.IsEditing()) return;
		this.SendCommand("CancelEdit");
    },
    IsEditing: function() { 
		return this.editingKey != null || this.isNewNodeEditing;
	},
    GetEditingNodeKey: function() { 
		return this.editingKey; 
	},
	MoveNode: function(nodeKey, parentNodeKey) {
		this.SendCommand("MoveNode", [nodeKey, parentNodeKey]);
	},
	DeleteNode: function(nodeKey) {
		this.SendCommand("DeleteNode", [nodeKey]);
	},
	StartEditNewNode: function(parentNodeKey) {
		if(!_aspxIsExists(parentNodeKey))
			parentNodeKey = "";
		this.SendCommand("StartEditNewNode", [parentNodeKey]);
	},
	GetEditor: function(columnId) {
		var column = this.FindColumn(columnId);
		if(column == null) return null;
		var editors = this.GetEditorObjects();
		for(var i = 0; i < editors.length; i++) {
			if(column.index == this.GetEditorColumnIndex(editors[i]))
				return editors[i];
		}
		return null;
	},	
	GetEditValue: function(columnId) {
		var editor = this.GetEditor(columnId);
		if(editor == null) return null;
		return editor.GetValue();
	},		
	SetEditValue: function(columnId, value) {
		var editor = this.GetEditor(columnId);
		if(editor != null)
			editor.SetValue(value);
	},	
	FocusEditor: function(columnId) {
		var editor = this.GetEditor(columnId);
		if(editor != null && _aspxIsExists(editor.SetFocus))
			editor.SetFocus();
	}
});


ASPxClientTreeListDragHelper = _aspxCreateClass(null, {
	constructor: function(treeList)  {
		this.treeList = treeList;		
	},
	  
	// Header dragging
    CreateHeaderDrag: function(element, e, canDrag) {		
		e = _aspxGetEvent(e);
	    var drag = new ASPxClientDragHelper(e, element, true);	    
	    drag.canDrag = canDrag;
	    drag.ctrl = e.ctrlKey;
	    drag.shift = e.shiftKey;
	    drag.treeListHelper = this;	    
		
        drag.onDoClick = this.OnHeaderClick;
        drag.onCloneCreating = this.OnHeaderCloneCreating;
        drag.onEndDrag = this.OnHeaderEndDrag;
        drag.onCancelDrag = this.OnHeaderCancelDrag;
        
        return drag;		
    },
	CreateHeaderTargets: function(drag, e) {
	    if(!drag.canDrag)
			return;
			
        var targets = new ASPxClientCursorTargets();                
        targets.obj = drag.obj;
        targets.treeListHelper = this;
        targets.onTargetChanging = this.OnHeaderTargetChanging; 
        targets.onTargetChanged = this.OnHeaderTargetChanged;
        var tree = this.treeList;        
        
        targets.RegisterTargets(tree.GetDataTable().rows[0], 
			[ tree.GetEmptyHeaderSuffix(), tree.GetHeaderSuffix() ]);

		var cw = tree.GetCustomizationWindowElement();
		if(cw != null) {
			var index = this.treeList.GetLastNumberOfId(drag.obj);
			var columnObj = this.treeList.FindColumn(index);
			if(columnObj && columnObj.showInCw)
			    targets.RegisterTargets(cw, [ tree.GetCustomizationWindowSuffix() ]);
		}
	},    
    
	OnHeaderClick: function(drag) {
		var treeList = drag.treeListHelper.treeList;	
		treeList.OnHeaderClick(drag.obj, drag.shift, drag.ctrl);
	},
    OnHeaderCloneCreating: function(clone) {    
		clone.colSpan = 1;	// when we drag the first header		
		ASPxClientTreeListDragHelper.RestoreElementBorder(clone);		
		var table = document.createElement("table");
		table.cellSpacing = 0;
		var row = table.insertRow(-1);
		row.appendChild(clone);
		table.style.width = this.obj.offsetWidth + "px";
		table.style.opacity = 0.80;
		table.style.filter = "alpha(opacity=80)";    
		return table;
    },		
    OnHeaderEndDrag: function(drag) {
        if(drag.targetElement == null) 
			return;		
		var treeList = drag.treeListHelper.treeList;
		var sourceIndex = treeList.GetLastNumberOfId(drag.obj);
		var targetIndex;
		var cwElement = treeList.GetCustomizationWindowElement();
		if(_aspxIsExists(cwElement) && drag.targetElement.id == cwElement.id) {
			targetIndex = -1;
		} else if(treeList.IsElementEmptyHeader(drag.targetElement)) {
			targetIndex = 0;
		} else {
			targetIndex = treeList.GetLastNumberOfId(drag.targetElement);
			if(sourceIndex == targetIndex)
				return;
		}		
		treeList.OnColumnMoving(sourceIndex, targetIndex, drag.targetTag);
    },    	
    OnHeaderCancelDrag: function(drag) {
		if(drag.canDrag)
			drag.treeListHelper.HideHeaderDragImages();
    },
    
    OnHeaderTargetChanging: function(targets) {
		targets.targetTag = targets.isLeftPartOfElement();
    },        
    OnHeaderTargetChanged: function(targets) {    
		if(__aspxDragHelper == null) 
			return;
							
		var helper = targets.treeListHelper;
		helper.HideHeaderDragImages();
		if(targets.targetElement != null && targets.targetElement != __aspxDragHelper.obj) {			
			__aspxDragHelper.targetElement = targets.targetElement;
			__aspxDragHelper.targetTag = targets.targetTag;			
			var left = _aspxGetAbsoluteX(targets.targetElement);
			if(!targets.targetTag)
				left += targets.targetElement.offsetWidth;
				
			if(targets.targetElement == helper.treeList.GetCustomizationWindowElement()) {
				var hideImage = helper.treeList.GetDragAndDropHideImage();
				hideImage.style.top = "";
				__aspxDragHelper.addElementToDragDiv(hideImage);
			} else {
				helper.SetHeaderDragImagesPosition(targets.targetElement, left);
			}
			
		} else {
			__aspxDragHelper.targetElement = null;
		}
    },
    
    SetHeaderDragImagesPosition: function(element, left) {
        this.ShowHeaderDragImages();
        var arrowDown = this.treeList.GetDragAndDropArrowDownImage();
        var arrowUp = this.treeList.GetDragAndDropArrowUpImage();
        
        _aspxSetAbsoluteX(arrowDown, Math.ceil(left - arrowDown.offsetWidth / 2));
        _aspxSetAbsoluteX(arrowUp, Math.ceil(left - arrowUp.offsetWidth / 2));
        
        var top = _aspxGetAbsoluteY(element);
        _aspxSetAbsoluteY(arrowDown, top - arrowDown.offsetHeight);
        _aspxSetAbsoluteY(arrowUp, top + element.offsetHeight);
    },
    HideHeaderDragImages: function() {
		this.SetHeaderDragImagesVisibility("hidden");
        if(__aspxDragHelper != null)
            __aspxDragHelper.removeElementFromDragDiv();		
    },
    ShowHeaderDragImages: function() {
		this.SetHeaderDragImagesVisibility("visible");
    },
    SetHeaderDragImagesVisibility: function(value) {
		this.treeList.GetDragAndDropArrowDownImage().style.visibility = value;
		this.treeList.GetDragAndDropArrowUpImage().style.visibility = value;
    },
        
    // Node dragging
    CreateNodeDrag: function(row, e, canDrag) {
		e = _aspxGetEvent(e);
		var drag = new ASPxClientDragHelper(e, row, true);
		drag.__treeList = this.treeList;
		drag.__savedHtmlEvent = e;		
		drag.canDrag = canDrag;
		drag.onCloneCreating = this.OnNodeCloneCreating;
		drag.onCancelDrag = this.OnNodeCancelDrag;
		drag.onEndDrag = this.OnNodeEndDrag;
		drag.onDoClick = this.OnNodeClick;
		return drag;
    },
    CreateNodeTargets: function(targetElements, sourceElement) {
		var targets = new ASPxClientCursorTargets();
		targets.__treeList = this.treeList;
		for(var i = 0; i < targetElements.length; i++) {
			if(targetElements[i] == sourceElement) continue;
		    targets.list.push(new ASPxClientCursorTarget(targetElements[i]));
		}
		targets.onTargetChanged = this.OnNodeTargetChanged;
    },
    OnNodeCloneCreating: function(row) {
		var treeList = __aspxDragHelper.__treeList;
		var table = document.createElement("table");
		table.cellSpacing = 0;		
		var tbody = document.createElement("tbody");
		table.appendChild(tbody);
		tbody.appendChild(row);
		var list = [ ];
		var thr = row.cells.length - treeList.GetColumnCellCount();
		var originalRow = __aspxDragHelper.obj;
		var removedWidth = 0;
		for(var i = 0; i < row.cells.length; i++) {
			var cell = row.cells[i];
			if(i < thr) {
				list.push(cell);
				removedWidth += originalRow.cells[i].offsetWidth;
			} else {
				ASPxClientTreeListDragHelper.RestoreElementBorder(cell, "top");
				ASPxClientTreeListDragHelper.RestoreElementBorder(cell, "bottom");
				if(i == thr)
					ASPxClientTreeListDragHelper.RestoreElementBorder(cell, "left");
				if(i == row.cells.length - 1)
					ASPxClientTreeListDragHelper.RestoreElementBorder(cell, "right");
			}				
		}
		for(var i = 0; i < list.length; i++)
			row.removeChild(list[i]);
		table.width = (originalRow.offsetWidth - removedWidth).toString() + "px";
		table.style.marginLeft = removedWidth + "px";
		table.style.opacity = 0.80;
		table.style.filter = "alpha(opacity=80)";
		return table;
    },
    OnNodeTargetChanged: function(targets) {
		if(__aspxDragHelper == null) return;
		var element = targets.targetElement;
		var hasTarget = _aspxIsExistsElement(element);
		targets.__treeList.dragHelper.SetNodeImageVisibility(hasTarget, element);
		__aspxDragHelper.targetElement = hasTarget ? targets.targetElement : null;
    },
    OnNodeCancelDrag: function(drag) {
		drag.__treeList.dragHelper.SetNodeImageVisibility(false);
    },
    OnNodeEndDrag: function(drag) {		
        if(drag.targetElement == null) 
			return;
		var sourceRow = drag.obj;
		var targetElement = drag.targetElement;
		if(sourceRow == targetElement)
			return;
		var treeList = drag.__treeList;
		var nodeKey = treeList.GetNodeKeyByRow(sourceRow);		
		if(!treeList.RaiseEndDragNode(nodeKey, drag.__savedHtmlEvent, targetElement))
		    return;				
		if(treeList.IsElementDataRow(targetElement)) {
		    var parentKey = treeList.GetNodeKeyByRow(targetElement);
		    treeList.MoveNode(nodeKey, parentKey);
		} else if(targetElement == treeList.GetHeaderRow()) {
			treeList.MoveNode(nodeKey, "");
		} else {
		    alert("Unprocessed custom target id=" + targetElement.id);
		}
    },
	OnNodeClick: function(drag) {
		drag.__treeList.OnDataTableClick(drag.__savedHtmlEvent);
	},    
    SetNodeImageVisibility: function(visible, element) {
        if(element == document.body)
            visible = false;
		var img = this.treeList.GetDragAndDropNodeImage();
		img.style.visibility = visible ? "visible" : "hidden";
		if(!visible) return;
		if(element.cells && element.cells.length > 0) {            
			for(var i = element.cells.length - 1; i >= 0; i--) {
				if(element.cells[i].className.indexOf("dxtl__I") > -1) {          
					element = element.cells[1 + i];
					break;
				}
			}
		}
		_aspxSetAbsoluteX(img, _aspxGetAbsoluteX(element) - img.offsetWidth + 4);
		_aspxSetAbsoluteY(img, _aspxGetAbsoluteY(element) + Math.floor(0.5 * (element.clientHeight - img.clientHeight)));
    }
});
ASPxClientTreeListDragHelper.RestoreElementBorder = function(element, borderPart) {
	var ruleName = _aspxIsExists(borderPart) ? "border-" + borderPart + "-style" : "border-style";
	element.style.cssText += ";" + ruleName + ": solid!important;";
};

function aspxTLPager(name, arg) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList != null)
		treeList.SendPagerCommand(arg);
}

function aspxTLSelectAll(name, check) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList != null)
		treeList.OnSelectingAll(check.checked);
}

function aspxTLClick(name, e) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnDataTableClick(e);
}

function aspxTLDblClick(name, e) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnDataTableDblClick(e);
}

function aspxTLHeaderDown(name, element, e) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnHeaderMouseDown(element, e);
}

function aspxTLShowLoadingPanel(name) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.ShowLoadingPanel();
}

function aspxTLCWCloseUp(name) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.RaiseCustomizationWindowCloseUp();
}

function aspxTLMouseDown(name, e) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnDataTableMouseDown(e);
}

function aspxTLSelecting(name, check) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnNodeSelecting(check);	
}

function aspxTLStartEdit(name, key) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.StartEdit(key);	
}
function aspxTLStartEditNewNode(name, parentKey) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.StartEditNewNode(parentKey);	
}
function aspxTLDeleteNode(name, key) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.OnNodeDeleting(key);	
}
function aspxTLUpdateEdit(name) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.UpdateEdit();	
}
function aspxTLCancelEdit(name) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.CancelEdit();	
}
function aspxTLCustomButton(name, nodeKey, index, id) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.RaiseCustomButtonClick(nodeKey, index, id);
}

function aspxTLMenu(name, objectType, objectKey, htmlEvent) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		return treeList.OnContextMenu(objectType, objectKey, htmlEvent);
	return true;
}

function aspxTLSort(name, columnIndex) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList)
		treeList.SortBy(columnIndex);
}

function aspxTLExpand(name, nodeKey) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList) {
		if(treeList.enableFocusedNode && treeList.focusOnExpandCollapse)
			treeList.OnFocusingNode(nodeKey, null);
		treeList.ExpandNode(nodeKey);
	}
}

function aspxTLCollapse(name, nodeKey) {
	var treeList = aspxGetControlCollection().Get(name);
	if(treeList) {
		if(treeList.enableFocusedNode && treeList.focusOnExpandCollapse)
			treeList.OnFocusingNode(nodeKey, null);	
		treeList.CollapseNode(nodeKey);
	}
}


// API classes
ASPxClientTreeListColumn = _aspxCreateClass(null, {	
	constructor: function(index, name, fieldName) {
		this.index = index;
		this.name = name;
		this.fieldName = fieldName;
						
		this.canSort = false;	// internal
		this.showInCw = true;   // internal
	},
	__dxColumnObject: true
});
ASPxClientTreeListCustomDataCallbackEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(arg, result) {
	    this.constructor.prototype.constructor.call(this);
        this.arg = arg;
        this.result = result;
    }
});
ASPxClientTreeListNodeEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(nodeKey, htmlEvent) {
		this.constructor.prototype.constructor.call(this);
		this.nodeKey = nodeKey;
		this.htmlEvent = htmlEvent;
		this.cancel = false;		
	}
});
ASPxClientTreeListContextMenuEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(objectType, objectKey, htmlEvent) {
		this.constructor.prototype.constructor.call(this);
		this.objectType = objectType;
		this.objectKey = objectKey;
		this.htmlEvent = htmlEvent;
		this.cancel = false;			
	}
});
ASPxClientTreeListStartDragNodeEventArgs = _aspxCreateClass(ASPxClientTreeListNodeEventArgs, {
    constructor: function(nodeKey, htmlEvent, targets) {
        this.constructor.prototype.constructor.call(this, nodeKey, htmlEvent);
        this.targets = targets;        
    }
});
ASPxClientTreeListEndDragNodeEventArgs = _aspxCreateClass(ASPxClientTreeListNodeEventArgs, {
    constructor: function(nodeKey, htmlEvent, targetElement) {
        this.constructor.prototype.constructor.call(this, nodeKey, htmlEvent);
        this.targetElement = targetElement;
    }    
});
ASPxClientTreeListCustomButtonEventArgs = _aspxCreateClass(ASPxClientEventArgs, {
	constructor: function(nodeKey, buttonIndex, buttonID) {
		this.constructor.prototype.constructor.call(this);
		this.nodeKey = nodeKey;
		this.buttonIndex = buttonIndex;
		this.buttonID = buttonID;
	}	
});