
Ext.define("Ext.ux.folderorganizer.Tree", {
	extend: "Ext.tree.Panel",
	collapsible: false,
	useArrows: true,
	rootVisible: false,
	multiSelect: true,
	singleExpand: false,
	border: false,
	region: "center",
	/**
	 * @cfg expandToLevel
	 * Set what level to expand to initially
	 */
	expandToLevel: 0,

	/**
	 * @cfg levelSelectFirst
	 * Set what on what level to autoselect first node in
	 */
	levelSelectFirst: false,

	config: {
		searchValue: null,
		xpdEditorConfig: null
	},

	requires: [
		"Ext.ux.folderorganizer.Util"
	],

	initComponent: function(config) {

		var i_self = this;

		var o_config = Ext.apply({}, i_self.initialConfig);
		var i_store = i_self.getStore();
		var i_proxy = i_store.getProxy();
		var o_extraParams = i_proxy.getExtraParams();
		var i_model = i_store.model;
		var i_record = i_model.create({});
		var i_wrapper = i_self.wrapper;
		var o_viewConfig;

		Ext.apply(o_extraParams, {
			displayCheckbox: i_self.displayCheckbox
		});

		var n_expandToLevel = i_self.expandToLevel;

		i_store.on({
			load: {
				fn: function (i_store, a_nodes) {
					if (n_expandToLevel) {
						i_self.expandTree(i_self, a_nodes);
					}
				},
				single: true
			}
		});

		i_store.on({
			load: {
				fn: function (i_store, a_nodes) {
					Ext.Array.each(a_nodes, function(i_node) {
						if (i_node.get("ID") == -1) {
							i_node.set("allowDrop", false);
						}
						if (i_node.get("iconCls").indexOf("parent") != -1) {
							i_node.set("leaf", false);
							i_node.commit(true);
						}
						else {
							i_node.set("appendOnly", true);
						}
					});
				}
			}
		});

		// is association specified, then setup
		i_self.hasAssociation = (i_self.wrapper) ? true : false;
		if (i_self.hasAssociation) {
			i_self.setupAssociation();
		}

		// is writer model specified, then create writer store
		if (!i_self.writerStore && i_model.prototype.writerModel) {
			i_self.writerStore = Ext.create("Ext.data.Store", {
				model: i_model.prototype.writerModel,
				autoSync: false
			});

			var i_writeStore = i_self.writerStore;

			var i_writeProxy = i_writeStore.getProxy();
			var o_writerParams = i_writeProxy.getExtraParams();

			Ext.apply(o_writerParams, {
				uiName: "portfolio",
				context: "form"
			});
		}

		// create tree grid
		var a_columns = i_self.columns || [];
		var a_modelColumns = i_self.getModelColumns();
		var a_extra = i_self.extraColumns || [];

		a_columns = a_columns.concat(a_modelColumns.concat(a_extra));

		i_self.hideHeaders = a_columns.length == 1;

		if (i_self.hasAssociation) {
			// tree grid view

			var b_dragAndDrop = ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".update");

			o_viewConfig = {
				rootVisible: i_self.rootVisible,
				plugins: {
					ptype: "treeviewdragdrop",
					dragGroup: "organizerItems",
					dropGroup: "organizerItems",
					enableDrag: b_dragAndDrop,
					enableDrop: b_dragAndDrop,
					appendOnly: false,
					onViewRender: function(view) {
						var dnd = this;

						if (dnd.enableDrag) {
							dnd.dragZone = Ext.create("Ext.tree.ViewDragZone", {
								view: view,
								ddGroup: dnd.dragGroup || dnd.ddGroup,
								dragText: dnd.dragText,
								repairHighlightColor: dnd.nodeHighlightColor,
								repairHighlight: dnd.nodeHighlightOnRepair
							});
						}

						if (dnd.enableDrop) {
							dnd.dropZone = Ext.create("Ext.tree.ViewDropZone", {
								view: view,
								ddGroup: dnd.dropGroup || dnd.ddGroup,
								allowContainerDrops: dnd.allowContainerDrops,
								appendOnly: dnd.appendOnly,
								allowParentInserts: dnd.allowParentInserts,
								expandDelay: dnd.expandDelay,
								dropHighlightColor: dnd.nodeHighlightColor,
								dropHighlight: dnd.nodeHighlightOnDrop,
								isValidDropPoint: function(node, position, dragZone, e, data) {
									return i_self.validateDropPoint(this, node, data, position);
								},
								onNodeDrop: function(node, dragZone, e, data) {
									var dropZone = this;
									var dropped = false;
									var record = data.records[0];
									var processDrop = function () {
										dropZone.invalidateDrop();
										if (record.isNode) {
											dropZone.handleNodeDrop(data, dropZone.overRecord, dropZone.currentPosition);
											if (dropZone.currentPosition == "append") {
												dropZone.overRecord.set("leaf", false);
											}
										}
										dropped = true;
										dropZone.fireViewEvent("drop", node, data, dropZone.overRecord, dropZone.currentPosition);
									};
									performOperation = false;

									if (dropZone.valid) {
										performOperation = dropZone.fireViewEvent("beforedrop", node, data, dropZone.overRecord, dropZone.currentPosition, processDrop);
										if (performOperation !== false) {
											if (!dropped) {
												processDrop();
											}
										}
									}
									return performOperation;
								}
							});
						}
					}
				},
				listeners: {
					beforedrop: {
						fn: function(node, data, targetNode, dropPosition, dropFunction, eOpts) {
							var record = data.records[0];
							if (record.isNode && i_self.hasSequenceProperty(record)) {
								i_self.calculateNewSequence(this, node, data, targetNode, dropPosition);
							}
						}
					},
					drop: {
						fn: function(node, data, targetNode, dropPosition, eOpts) {

							var record = data.records[0];
							if (record.isNode && i_self.hasSequenceProperty(record)) {
								i_self.resequenceNode(this, node, data, targetNode, dropPosition);
							}
							else {
								i_self.addAssociation(targetNode, data.records, true);
							}
						}
					}
				}
			};
		}

		// XpdEditor window config
		i_self.xpdEditorConfig = {
			height: "90%",
			width: 700,
			collapsible: true,
			listeners: {
				show: {
					fn: function (i_window) {

						var i_form = i_window.down("form").getForm();
						var a_items = i_form.getFields();
						var i_xpdfield = a_items.items[0];

						var a_fields = i_xpdfield.query("field");

						Ext.Array.each(a_fields, function(i_field, n_index) {
							if (!i_field.isDisabled() && !i_field.readOnly) {
								i_field.focus(true, 10);
								return false;
							}
						});
					}
				}
			}
		};

		Ext.Object.merge(i_self, o_config, {
			idCount: 0,
			node: {},
			name: i_self.namePrefix + ".,tree",
			mode: i_self.mode || "model",
			viewConfig: o_viewConfig,
			listeners: {
				afterrender: {
					fn: function() {
						i_self.deselect();
						var i_root = i_self.getRootNode();
						if (i_self.view.rootVisible) {
							i_root.set("allowDrop", true);
						}
					}
				},
				beforeitemexpand: {
					fn: function(node) {
						i_self.beforeItemExpand(node);
					}
				},
				checkchange: {
					fn: function(node, checked) {
						i_self.nodeCheckChange(node, checked);
					}
				},
				select: {
					fn: function(view, node, index, e) {
						i_self.wrapper.fireEvent("nodeselection", node);
						Ext.ux.folderorganizer.Util.setCollectionColumnHeader(i_self, node);
					}
				},

				foldercreate: {
					fn: function(parentNode, properties) {
						i_self.folderCreate(parentNode, properties);
					}
				},
				rowselection: {
					fn: function(cid, records) {
						i_self.hasmanySelectionChange(records);
					}
				},
				propertychange: {
					fn: function(record, action, node) {
						i_self.save(record, action, node);
					}
				},
				itemcontextmenu: {
					fn: function(view, node, item, index, e) {
						e.stopEvent();

						i_self.getSelectionModel().select(node);
						var el = Ext.get(item);
						var coords = e.getXY();

						// row menu
						var items = [];
						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".create")) {

							var i_toolbar = i_self.up("panel").down("toolbar");
							var i_buttonAdd = i_toolbar.getComponent("add");

							if (i_buttonAdd && !i_buttonAdd.disabled) {
								if (!i_buttonAdd.menu) {
									var o_config = i_buttonAdd.initialConfig;
									o_config.icon = "";
									o_config.iconCls = "pf-add";
									var i_item  = Ext.widget("menuitem", o_config);
									items.push(i_item);
								}
								else {
									var i_menu = i_buttonAdd.menu;
									var a_items = i_menu.items.items;
									var a_menuitems = [];
									var parentCollection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(i_toolbar, node);
									if (parentCollection) {
										ip2.each(parentCollection.allowedchilds, function(n_cid) {
											ip2.each(a_items, function(i_button) {
												if (i_button.itemId == "add" + n_cid) {
													var o_config = i_button.initialConfig;
													var i_item = Ext.widget("menuitem", o_config);
													a_menuitems.push(i_item);
												}
											});
										});
									}
									else {
										ip2.each(a_items, function(i_button) {
											var o_config = i_button.initialConfig;
											var i_item = Ext.widget("menuitem", o_config);
											a_menuitems.push(i_item);
										});
									}
									var i_menuadd = Ext.widget("menuitem", {
										text: ip2.i18n({
											da: "Tilføj",
											en: "Add"
										}),
										iconCls: "pf-add",
										menu: {
											items: a_menuitems
										}
									});
									items.push(i_menuadd);
								}
							}
						}

						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".update")) {
							items.push({
								xtype: "menuitem",
								allowOtherMenus: false,
								text: ip2.i18n({
									da: "Rediger menupunkt",
									en: "Edit menuitem"
								}),
								itemId: "update",
								iconCls: "pf-edit",
								handler: function() {
									i_self.updateRow(i_self.node);
								}
							});
						}
						var o_properties = Ext.decode(node.get("PROPERTIES"), true) || {};
						if (o_properties.itemnumber || o_properties.pagetoken) {
							if (i_self.entity == "WSHMNUET00" || i_self.entity == "WSHFLDET00") {
								var s_type = ip2.i18n({
									da: i_self.entity == "WSHMNUET00" ? "side" : "produkt",
									en: i_self.entity == "WSHMNUET00" ? "page" : "product"
								});
								var b_get = true;
								switch (i_self.entity) {
									case "WSHMNUET00":
										b_get = (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data.WSHPAGET00.update") ||
												ip2.isAccessible("app." + i_wrapper.dmdProject + ".data.WSHPAGET00.read"));
										break;
									case "WSHFLDET00":
										b_get = (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data.WSHITMET00.update") ||
												ip2.isAccessible("app." + i_wrapper.dmdProject + ".data.WSHITMET00.read"));
										break;
								}
								if (b_get) {
									items.push({
										xtype: "menuitem",
										iconCls: "pf-edit",
										itemId: "edit",
										text: ip2.i18n({
											da: "Rediger " + s_type,
											en: "Edit " + s_type
										}),
										handler: function() {

											var s_entity = "";

											// TODO: Get this info from XPD.
											if (i_self.entity == "WSHMNUET00") {
												s_entity = "WSHPAGET00";
												s_tkn = o_properties.pagetoken;
											}

											if (i_self.entity == "WSHFLDET00") {
												s_entity = "WSHITMET00";
												s_tkn = o_properties.itemnumber;
											}

											var i_model = ip2.model({
												dmd: i_self.dmd,
												entityName: s_entity,
												context: "grid",
												uiName: "portfolio"
											});
											var o_config = {
												dmd: i_self.dmd,
												entityName: s_entity
											};
											var i_record = i_model.create();
											i_record.set(i_record.idProperty, s_tkn);

											var o_size = Ext.ux.file.util.Handler.getWindowSize();

											var wnd = Ext.widget("window", {
												modal: false,
												closable: true,
												collapsible: true,
												resizable: true,
												width: o_size.width * 0.9,
												height: o_size.height * 0.9,
												layout: "fit"
											});

											ip2.addChild(wnd, i_record, "update", o_config);

											var s_event = "entityupdate:" + s_entity;
											var o_event = {};

											o_event[s_event] = {
												fn: function(i_record) {
													var i_store = i_self.store;
													i_parentnode = node.parentNode;
													i_parentnode.removeAll(false);
													i_store.load({
														node: i_parentnode
													});
												},
												single: true
											};

											i_self.mon(ip2.App, o_event);
											wnd.show();
										}
									});
								}
							}
						}
						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".delete")) {
							items.push({
								xtype: "menuitem",
								itemId: "remove",
								text: i_self.getI18N("remove"),
								iconCls: "pf-delete",
								handler: function() {
									i_self.removeRow(i_self.node);
								}
							});
						}
						if (i_self.allowExpandChildren && !node.isLeaf()) {
							items.push({
								xtype: "menuitem",
								itemId: "expand",
								text: i_self.getI18N("expand"),
								iconCls: "pf-navigate_expand",
								handler: function() {
									i_self.expandChildren(i_self.node);
								}
							});
						}
						if (i_self.allowExport) {
							items.push({
								xtype: "menuitem",
								itemId: "export",
								text: i_self.getI18N("eksport"),
								iconCls: "pf-cloud_download",
								handler: function() {
									i_self.exportRow(i_self.node);
								}
							});
						}
						if (!node.get("leaf")) {
							items.push({
								xtype: "menuitem",
								iconCls: "pf-refresh",
								itemId: "icon-reload",
								text: ip2.i18n({
									da: "Genindlæs",
									en: "Reload"
								}),
								handler: function() {
									var i_store = i_self.store;
									node.removeAll(false);
									i_store.load({
										node: node
									});
								}
							});
						}

						var menu = Ext.widget("menu", {
							items: items
						});

						menu.showAt([coords[0], coords[1]]);

						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".update") ||
							ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".read")) {
							if (node.get("allowEdit") && i_self.checkAllowEdit(node)) {
								menu.down("#update").enable();
								i_self.node = node;
							}
							else {
								menu.down("#update").disable();
							}
						}

						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".delete")) {
							if (node && !node.isRoot()) {
								if (node.get("allowRemove")) {
									menu.down("#remove").enable();
									i_self.node = node;
								}
							}
							else {
								menu.down("#remove").disable();
							}
						}
					}
				},
				itemdblclick: {
					fn: function(view, node, item, index, e) {
						if (ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".update") ||
							ip2.isAccessible("app." + i_wrapper.dmdProject + ".data." + i_self.entity + ".read")) {
							if (node.get("allowEdit") && i_self.checkAllowEdit(node)) {
								i_self.updateRow(node);
							}
						}
					}
				}
			},
			displayCheckbox: Ext.isBoolean(i_self.displayCheckbox) ? i_self.displayCheckbox : true,
			allowEdit: (i_self.menus.indexOf("edit") == -1) ? false : true,
			allowRemove: (i_self.menus.indexOf("remove") == -1) ? false : true,
			allowExport: i_self.allowExport !== undefined ? i_self.allowExport : true,
			allowExpandChildren: (i_self.menus.indexOf("expandChildren") == -1) ? false : true,
			addToAllChildren: i_self.addToAllChildren || { allow: false },
			previousNodeCid: 0,
			searchValue: "",
			languageService: i_self.languageService || "",
			expandToLevel: o_config.expandToLevel || i_self.expandToLevel,
			levelSelectFirst: o_config.levelSelectFirst || i_self.levelSelectFirst,
			columns: a_columns,
			selMol: o_config.selMol !== undefined ? o_config.selMol : Ext.create("Ext.ux.folderorganizer.TreeSelModel")
		});

		i_self.callParent();

		if (i_self.addToAllChildren && i_self.addToAllChildren.multiChoiceDialog) {
			var mcd = i_self.addToAllChildren.multiChoiceDialog;

			i_self.questions = [];
			for (var i = 0; i < mcd.questions.length; i++) {
				i_self.questions.push({
					answer: i,
					question: ip2.i18n(mcd.questions[i])
				});

				switch (i) {
					case 0:
						i_self.questions[i].icon = "/system/images/icons/16x16/check2.png";
						break;
					case 1:
						i_self.questions[i].icon = "/system/images/icons/16x16/checks.png";
						break;
					case 2:
						i_self.questions[i].icon = "/system/images/icons/16x16/undo.png";
						break;
				}
			}
		}

		if (i_self.rootVisible) {
			var i_root = i_self.getRootNode();
			i_root.set("TITLE", "...");
			i_root.commit();
			i_root.expand();
		}

		return i_self;
	},

	/* ----------------------------------------------------------------------------------------------------
			Expand the treegrid
		 ---------------------------------------------------------------------------------------------------- */
	expandTree: function (i_self, a_node) {
		var me = this;

		if (me.el && me.el.dom) {
			me.expandTreeToLevel(a_node, 0, function (i_node, a_node) {
				// Callback pr. expand
			});
		}
		else {
			me.on({
				"afterrender": {
					fn: function () {
						me.expandTreeToLevel(a_node, 0, function (i_node, a_node) {
							// Callback pr. expand
						});
					},
					single: true
				}
			});
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Expand the treegrid to a certain level down, and also select first node at certain level
			if config options expandToLevel and levelSelectFirst are set
		 ---------------------------------------------------------------------------------------------------- */
	expandTreeToLevel: function (a_node, n_level, f_callback) {

		f_callback = f_callback || Ext.emptyFn;
		var me = this;

		if (a_node) {
			if (a_node.length === undefined) {
				a_node = [];
			}
		}

		var i_cmp = this;
		var i_tree = i_cmp;
		var i_store = i_cmp.store;
		var n_expandToLevel = i_cmp.expandToLevel;
		var n_selectLevel = i_cmp.levelSelectFirst;

		if (n_expandToLevel > n_level) {
			Ext.Array.each(a_node, function (i_node) {
				i_store.on({
					"load": {
						fn: function (i_self, a_node) {
							i_cmp.expandTreeToLevel(a_node, n_level, f_callback);

							if (n_selectLevel && n_selectLevel == n_level && a_node && a_node[0]) {
								i_tree.getSelectionModel().select(a_node[0]);
								me.wrapper.fireEvent("nodemouseup", a_node[0]);
							}

							f_callback(i_node, a_node);
						},
						single: true
					}
				});
				i_node.expand();
			});
			n_level++;
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Expand the treegrid to specified node
		 ---------------------------------------------------------------------------------------------------- */
	expandTreeToNode: function (array, selectedTreeNode, propertyField, propertyName) {
		var me = this;
		var node;
		var propertyValue = Ext.decode(selectedTreeNode.data[propertyField])[propertyName];

		me.expandTreeNodeInArray(0, array, node, function(parentNode) {
			me.getSelectionModel().select(me.findChildByProperty(parentNode, propertyField, propertyName, propertyValue));
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			Expand the treegrid to specified node
		 ---------------------------------------------------------------------------------------------------- */
	expandTreeNodeInArray: function (counter, array, node, callback) {

		callback = callback || Ext.emptyFn;

		var me = this;

		if (counter >= array.length) {
			callback(node);
		}
		else {
			var nodeId = parseInt(array[counter]);
			var node = me.store.getNodeById(nodeId);
			counter++;
			if (node.isLeaf()) {
				me.expandTreeNodeInArray(counter, array, node, callback);
			}
			else if (node.isExpanded()) {
				me.expandTreeNodeInArray(counter, array, node, callback);
			}
			else if (node.isLoaded()) {
				node.on({
					"expand": {
						fn: function (node) {
							me.expandTreeNodeInArray(counter, array, node, callback);
						},
						single: true
					}
				});
				node.expand();
			}
			else {
				me.store.on({
					"load": {
						fn: function (store, a_record, success, operation, node) {
							var node = operation.node;
							me.expandTreeNodeInArray(counter, array, node, callback);
						},
						single: true
					}
				});
				node.expand();
			}
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Check tree node allow edit according xpd collection
		 ---------------------------------------------------------------------------------------------------- */
	findChildByProperty: function(node, propertyField, propertyName, value) {
		var cs = node.childNodes;
		var len = cs.length;
		var i = 0;
		var n;

		for (; i < len; i++) {
			n = cs[i];
			if (Ext.decode(n.data[propertyField])[propertyName] == value) {
				return n;
			}
		}

		return null;
	},

	/* ----------------------------------------------------------------------------------------------------
			Check tree node allow edit according xpd collection
		 ---------------------------------------------------------------------------------------------------- */
	checkAllowEdit: function(node) {
		var me = this;

		var collection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, node);

		return collection !== null ? (collection.disallowedit) ? false : true : false;
	},

	/* ----------------------------------------------------------------------------------------------------
			Validate drop point
		 ---------------------------------------------------------------------------------------------------- */
	validateDropPoint: function(treegrid, node, data, position) {

		var me = this;
		var b_allowed = false;
		var dragNode = data.records[0];
		var i_store = dragNode.getTreeStore ? dragNode.getTreeStore() : dragNode.store;
		var targetNode = treegrid.view.getRecord(node);

		if (!targetNode.get("allowDrop")) {
			return false;
		}

		if (targetNode.parentNode) {
			if (ip2.isEqual(targetNode.parentNode.data, dragNode.data)) {
				return false;
			}
		}

		// drag from folder property grid
		if (i_store.model && i_store.model.owner && i_store.model.owner.xtype == "folderorganizerFolderPropertyGrid") {
			b_allowed = false;
		}

		if (dragNode.get("cid")) {
			// cid specified in model
			b_allowed = me.validateDropPointXpd(dragNode, targetNode, position);
		}
		if (me.isTreeNodeDrag(dragNode)) {
			// leaf node DnD in tree grid
			if (targetNode.id == dragNode.id) {
				b_allowed = false;
			}
			else {
				b_allowed = me.validateDropPointXpd(dragNode, targetNode, position);
				if (dragNode.get("PARENTID") == targetNode.get("ID")) {
					b_allowed = false;
				}
				else {
					if (dragNode.get("PARENTID") != targetNode.get("PARENTID")) {
						if (me.isLeaf(targetNode)) {
							b_allowed = false;
						}
						else {
							if (position != "append") {
								b_allowed = false;
							}
						}
					}
					else {
						if (position == "before") {
							if (dragNode.nextSibling) {
								if (dragNode.nextSibling.get("ID") == targetNode.get("ID")) {
									b_allowed = false;
								}
							}
						}
						else if (position == "after") {
							if (dragNode.previousSibling) {
								if (dragNode.previousSibling.get("ID") == targetNode.get("ID")) {
									b_allowed = false;
								}
							}
						}
					}
				}
			}
		}
		else {
			if (!ip2.isEqual(dragNode.data, targetNode.data)) {
				b_allowed = true;
			}
		}

		if (b_allowed && targetNode.childNodes && targetNode.childNodes.length > 0) {
			var a_children = targetNode.childNodes;
			ip2.each(a_children, function(i_node) {
				if (i_node.get("token")) {
					var n_token = i_node.get("token");
					if (n_token == dragNode.get(dragNode.idProperty)) {
						b_allowed = false;
					}
				}
				else {
					if (i_node.get("rowProps") || i_node.get("PROPERTIES")) {

						var o_prop;
						var n_token;
						var s_idProperty = dragNode.idProperty;

						if (i_node.get("rowProps")) {
							o_prop = Ext.decode(i_node.get("rowProps"));
							n_token = o_prop.token;
							if (n_token == dragNode.get(dragNode.idProperty)) {
								b_allowed = false;
							}
						}
						if (b_allowed && i_node.get("PROPERTIES")) {
							o_props = Ext.decode(i_node.get("PROPERTIES"));
							if (s_idProperty == "ITMITMTKN") {
								n_token = o_props.itemnumber;
							}
							else if (s_idProperty == "PAGPAGTKN") {
								n_token = o_props.pagetoken;
							}

							if (n_token == dragNode.get(dragNode.idProperty)) {
								b_allowed = false;
							}
						}
					}
				}
			});
		}
		else {
			if (b_allowed && targetNode.parentNode) {
				var a_children = targetNode.parentNode.childNodes;
				ip2.each(a_children, function(i_node) {
					if (i_node.get("token")) {
						var n_token = i_node.get("token");
						if (n_token == dragNode.get(dragNode.idProperty)) {
							b_allowed = false;
						}
					}
					else {
						if (i_node.get("rowProps") || i_node.get("PROPERTIES")) {

							var o_prop;
							var n_token;
							var s_idProperty = dragNode.idProperty;

							if (i_node.get("rowProps")) {
								o_prop = Ext.decode(i_node.get("rowProps"));
								n_token = o_prop.token;
								if (n_token == dragNode.get(dragNode.idProperty)) {
									b_allowed = false;
								}
							}
							if (b_allowed && i_node.get("PROPERTIES")) {
								o_props = Ext.decode(i_node.get("PROPERTIES"));
								if (s_idProperty == "ITMITMTKN") {
									n_token = o_props.itemnumber;
								}
								else if (s_idProperty == "PAGPAGTKN") {
									n_token = o_props.pagetoken;
								}

								if (n_token == dragNode.get(dragNode.idProperty)) {
									b_allowed = false;
								}
							}
						}
					}
				});
			}
		}

		return b_allowed;
	},

	/* ----------------------------------------------------------------------------------------------------
			Check tree node drop according xpd rules (allow parent & childs)
		 ---------------------------------------------------------------------------------------------------- */
	validateDropPointXpd: function(dragNode, targetNode, position) {

		var me = this;
		var b_allowed = false;
		var parentCid = Ext.ux.folderorganizer.Util.getXpdCollectionId(me, targetNode);
		var parentCollection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, targetNode);
		var childCollection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, dragNode);

		if (parentCollection && !Ext.isEmpty(parentCollection.childlimit) && targetNode.childNodes.length >= parentCollection.childlimit) {
			b_allowed = false;
		}
		else if (childCollection.id == parentCid) {
			b_allowed = true;
		}
		else if (childCollection.allowedparents && childCollection.allowedparents.length > 0) {
			b_allowed = (childCollection.allowedparents.indexOf(parentCid) == -1) ? false : true;
		}
		else if (!parentCollection.allowedchilds) {
			// if no allowedchild specified - then allow drop
			b_allowed = true;
		}
		else {
			var cid = Ext.ux.folderorganizer.Util.getXpdCollectionId(me, dragNode);
			b_allowed = (parentCollection.allowedchilds.indexOf(cid) == -1) ? false : true;
		}

		return b_allowed;
	},

	/* ----------------------------------------------------------------------------------------------------
			Is it a tree drag from one tree node to another
		 ---------------------------------------------------------------------------------------------------- */
	isTreeNodeDrag: function(i_node) {

		var i_self = this;
		var b_isNode = i_node.isNode;

		if (b_isNode) {
			b_isNode = i_node.get("PARENTID") ? true : false;
			if (b_isNode) {
				return b_isNode;
			}
		}

		var o_dragData = i_node.get("dragData");

		if (o_dragData) {

			var o_view = o_dragData.view;

			if (o_view) {
				b_isNode = o_dragData.view.xtype == "treeview";
			}
		}

		return b_isNode;
	},

	/* ----------------------------------------------------------------------------------------------------
			Check if node has sequence property in column PROPERTIES
		 ---------------------------------------------------------------------------------------------------- */
	hasSequenceProperty: function(node) {
		if (!node.data || !node.data.PROPERTIES) {
			return false;
		}

		var props = Ext.decode(node.data.PROPERTIES);
		if (props.__cid__) {
			var collection = this.wrapper.xpd.flatten.collections[props.__cid__];
			if (collection && collection.properties.seq) {
				return true;
			}
		}

		return false;
	},

	/* ----------------------------------------------------------------------------------------------------
			Resequence node
		 ---------------------------------------------------------------------------------------------------- */
	calculateNewSequence: function(treegrid, node, data, targetNode, position) {
		var me = this;

		if (!data.records || !data.records[0]) {
			return;
		}

		var dragNode = data.records[0];

		var afterSeq;
		var beforeSeq;

		switch (position) {
			case "append":
				afterSeq = 0;
				if (targetNode.childNodes && targetNode.childNodes[0]) {
					beforeSeq = me.getSequenceFromProperties(targetNode.childNodes[0]);
				}
				else {
					beforeSeq = 2.0;
				}
				break;
			case "after":
				afterSeq = me.getSequenceFromProperties(targetNode);
				if (targetNode.nextSibling) {
					beforeSeq = me.getSequenceFromProperties(targetNode.nextSibling);
				}
				else {
					beforeSeq = afterSeq + 2.0;
				}
				break;
			case "before":
				if (targetNode.previousSibling) {
					afterSeq = me.getSequenceFromProperties(targetNode.previousSibling);
				}
				else {
					afterSeq = 0;
				}
				beforeSeq = me.getSequenceFromProperties(targetNode);
				break;
		}

		var props = Ext.decode(dragNode.data.PROPERTIES);
		var seq = (afterSeq + beforeSeq) / 2;

		if (Ext.isNumeric(seq)) {
			if (seq === 0) {
				dragNode.data.reseqAllSiblings = true;
			}
			else {
				dragNode.data.reseqAllSiblings = false;
				props.seq = seq.toString();
				dragNode.data.PROPERTIES = Ext.encode(props);
			}
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Resequence node
		 ---------------------------------------------------------------------------------------------------- */
	resequenceNode: function(treegrid, node, data, targetNode, position) {
		var me = this;

		if (!data.records || !data.records[0]) {
			return;
		}

		if (data.records[0].data.reseqAllSiblings) {
			me.resequenceAllChildrenNodes(targetNode.parentNode);
		}
		else {
			me.getRecord({
				node: data.records[0],
				success: function(args) {
					me.updateRowProperties(args.record, args.node, args.node.data.PROPERTIES, false);
				}
			});
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Resequence all children nodes
		 ---------------------------------------------------------------------------------------------------- */
	resequenceAllChildrenNodes: function(parentNode) {
		var me = this;
		var nextSeq = 0;

		for (var i = 0; i < parentNode.childNodes.length; i++) {
			var node = parentNode.childNodes[i];
			nextSeq += 10;

			var props = Ext.decode(node.data.PROPERTIES);
			props.seq = nextSeq;
			node.data.PROPERTIES = Ext.encode(props);

			me.getRecord({
				node: node,
				success: function(args) {
					me.updateRowProperties(args.record, args.node, args.node.data.PROPERTIES, false);
				}
			});
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Get sequence from properties
		 ---------------------------------------------------------------------------------------------------- */
	getSequenceFromProperties: function(node) {
		props = Ext.decode(node.data.PROPERTIES);

		return parseFloat(props.seq) || 0;
	},

	/* ----------------------------------------------------------------------------------------------------
			Add new row
		 ---------------------------------------------------------------------------------------------------- */
	addRow: function(collection) {
		var me = this;

		if (!me.writerStore) {
			Ext.ux.folderorganizer.Alert({
				title: me.getI18N("invalidConfig"),
				msg: me.getI18N("missingConfigOptions")
			});
			return;
		}

		if (collection) {
			me.addRowProperties(collection);
		}
		else {
			var key = {};
			key[me.propertyNameMapping.ID] = 0;
			me.writerStore.proxy.extraParams.key = Ext.encode(key);

			var model = me.writerStore.proxy.getModel();
			var record = model.create();
			me.writerStore.insert(0, record);
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Add row properties using XPD
		 ---------------------------------------------------------------------------------------------------- */
	addRowProperties: function(collection) {

		var me = this;

		var parentNode = me.node;

		var i_tree = this;
		var i_root = i_tree.getRootNode();
		var i_store = i_tree.getStore();
		var i_model = i_store.getModel();
		var b_parent = !Ext.Object.getSize(parentNode) ? false : true;

		var record = { data: {} };
		record.data[me.propertyNameMapping.PROPERTIES] = "{\"__cid__\":" + collection.id + "}";
		record = i_model.create(record.data);

		var o_config = Ext.apply({
			title: me.getI18N("create") + " \"" + ip2.xpd.translate(me.wrapper.xpd, collection.title, ip2.session.getLanguage()) + "\"",
			xpd: me.wrapper.xpd,
			collection: collection,
			record: record,
			environment: me.environment,
			propertyNameMapping: me.propertyNameMapping,
			action: "create",
			entity: me.wrapper.entity,
			dmdFile: me.writerStore.model.dmdFile,
			languageService: collection.languageService || me.languageService || "",
			preSave: me.preSave || function(a_modifed, f_callback) {
				f_callback();
			},
			doSave: function(record, node, properties, f_callback) {

				// MODIFIED BY ISJ

				// Deal with mapping values
				var o_data = {};
				o_data[me.propertyNameMapping.ID] = 0;
				o_data[me.propertyNameMapping.PARENTID] = b_parent ? (parentNode.data.ID > 0 ? parentNode.data.ID : 0) : 0;

				if (b_parent) {
					var o_props = Ext.decode(properties, true) || {};
					if (Ext.isEmpty(o_props.seq)) {
						me.setSequenceProperty(parentNode, o_props);
						properties = Ext.encode(o_props); // TODO: Why do this when o_props has not been touched?
					}
				}
				o_data[me.propertyNameMapping.PROPERTIES] = properties;

				// Deal with the record
				var i_record = me.writerStore.model.create(o_data);
				var i_node;
				var o_nodeData;
				var i_child;

				var i_created = Ext.create(me.store.model.$className, {
					ID: o_data[me.propertyNameMapping.ID],
					PARENTID: o_data[me.propertyNameMapping.PARENTID],
					TITLE: "",
					PROPERTIES: o_data[me.propertyNameMapping.PROPERTIES],
					leaf: me.setLeafForCollection(properties),
					allowEdit: true,
					allowRemove: true,
					loaded: true
				});
				o_nodeData = me.titlePropertyRenderer(i_created);
				i_created.set("TITLE", o_nodeData.title);
				i_created.set("icon", o_nodeData.icon);
				if (!o_nodeData.icon) {
					i_created.set("iconCls", "webshop-parent-node");
				}

				var f_loadParent = function(i_node, f_callback) {

					if (i_node) {
						i_node.collapse();
						i_node.removeAll(false);
						me.store.load({
							node: i_node,
							callback: function() {
								i_node.expand();
								f_callback();
							}
						});
					}
				};

				// Create key for store and save record
				i_record.phantom = true;

				me.saveRow(i_record, function(b_created, i_record) {

					if (b_created) {
						me.wrapper.fireEvent("nodeadded", i_created);
						f_loadParent(parentNode, function() {
							f_callback(i_record);
						});
					}
					else {
						if (f_callback) {
							f_callback(false, o_error);
						}
					}
				});

				delete me.writerStore.proxy.extraParams.key;

				var o_createdProperties = Ext.decode(i_created.getData("PROPERTIES"), true) || {};
				var o_parentProperties = Ext.decode(parentNode.getData("PROPERTIES"), true) || {};

				var childCid = o_createdProperties.__cid__;
				var parentCid = o_parentProperties.__cid__;

				if (childCid == 1 && parentCid == 2) {
					Ext.Ajax.request({
						url: me.proxy,
						params: {
							action: "setFolderCid",
							token: parentNode.get("ID"),
							cid: 1
						}
					});
					o_parentProperties.__cid__ = 1;
					parentNode.set("PROPERTIES", Ext.encode(o_parentProperties));
				}
			}
		}, me.xpdEditorConfig);

		i_window = Ext.create("Ext.ux.folderorganizer.XpdEditor", o_config);

		i_window.show();
		i_window.expand();
	},

	/* ----------------------------------------------------------------------------------------------------
			Update existing row
		 ---------------------------------------------------------------------------------------------------- */
	updateRow: function(node) {
		var me = this;

		if (!me.writerStore) {
			Ext.ux.folderorganizer.Alert({
				title: me.getI18N("invalidConfig"),
				msg: me.getI18N("missingConfigOptions")
			});
			return;
		}

		if (!node) {
			var nodes = me.getSelectionModel().getSelection();
			node = nodes[0];
		}

		if (node.data.allowEdit) {
			me.getRecord({
				node: node,
				success: function(args) {
					if (me.mode == "folder") {
						var collection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, args.record);
						if (collection) {
							me.updateRowUsingXpdEditor(args.record, args.node);
						}
						else {
							me.wrapper.fireEvent("nodestartedit", args.record);
						}
					}
				}
			});
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Update row properties using XPD
		 ---------------------------------------------------------------------------------------------------- */
	updateRowUsingXpdEditor: function(record, node) {

		var i_cmp = this;

		var i_window;

		if (!i_window) {

			var o_idColumn = (function () {

				var i_store = i_cmp.writerStore;
				var i_model = i_store.getModel();
				var o_model = i_model.prototype.dmdModel;
				var a_entities = o_model.entities;

				var o_entity = Ext.Array.findBy(a_entities, function(o_value) {
					return o_value.name == i_cmp.wrapper.entity;
				});

				var s_idColumn = o_entity.idColumn;

				var a_columns = [];

				if (Ext.isArray(o_entity.columns)) {
					a_columns = Ext.Array.slice(o_entity.columns);
				}
				else {
					Ext.Object.each(o_entity.columns, function(s_key, o_value) {
						o_value.name = s_key;
						a_columns.push(o_value);
					});
				}

				var o_return;

				Ext.Array.each(a_columns, function(o_column) {
					if (o_column.name == s_idColumn) {
						o_return = o_column;
						return false;
					}
				});

				return o_return;
			})();

			var s_title = (function () {

				var s_title = node.data.TITLE;
				var	o_xpd = i_cmp.wrapper.xpd;
				var	s_language = ip2.session.getLanguage();
				var	s_update = i_cmp.getI18N("update");

				s_title = ip2.xpd.translate(o_xpd, s_title, s_language);

				return [s_update, s_title].join(" ");
			})();

			var o_properties = Ext.decode(record.get(i_cmp.propertyNameMapping.PROPERTIES), true) || {};
			var o_collection = {};

			if (i_cmp.xpd.flatten.collections[o_properties[i_cmp.xpd.idProperty]]) {
				o_collection = i_cmp.xpd.flatten.collections[o_properties[i_cmp.xpd.idProperty]];
			}

			var o_config = Ext.apply({
				title: s_title,
				xpd: i_cmp.wrapper.xpd,
				environment: i_cmp.environment,
				propertyNameMapping: i_cmp.propertyNameMapping,
				idColumn: o_idColumn,
				node: node,
				record: record,
				action: "update",
				entity: i_cmp.wrapper.entity,
				dmdFile: i_cmp.writerStore.model.dmdFile,
				languageService: o_collection.languageService || i_cmp.languageService || "",
				preSave: i_cmp.preSave || function(a_modifed, f_callback) {
					f_callback();
				},
				doSave: function(record, node, properties, f_callback) {
					i_cmp.updateRowProperties(record, node, properties, true, function(i_node, o_error) {
						f_callback(i_node, o_error);
					});
				}
			}, i_cmp.xpdEditorConfig);

			i_window = Ext.create("Ext.ux.folderorganizer.XpdEditor", o_config);
		}

		i_window.show();
		i_window.expand();
	},

	/* ----------------------------------------------------------------------------------------------------
			Get row title
		 ---------------------------------------------------------------------------------------------------- */
	updateRowProperties: function(i_record, i_node, o_properties, b_updateNode, f_callback) {

		var i_self = this;

		if ((i_record.get(i_self.propertyNameMapping.PARENTID) !== undefined) && (i_node.parentNode.data.ID !== undefined)) {
			i_record.set(i_self.propertyNameMapping.PARENTID, (i_node.parentNode.get("ID") > 0 ? i_node.parentNode.get("ID") : 0));
		}
		i_record.set(i_self.propertyNameMapping.PROPERTIES, o_properties);

		var a_batch = [];
		var o_keys = {};

		o_keys[i_record.idProperty] = i_record.get(i_record.idProperty);

		var o_batch = {
			type: "upsert",
			dmd: i_self.dmd,
			entity: i_self.entity,
			transactiontype: i_self.entity,
			key: o_keys,
			row: i_record.getData()
		};

		a_batch.push(o_batch);

		Ext.Ajax.request({
			url: i_self.writerStore.proxy.url,
			jsonData: {
				transaction: {
					batch: a_batch
				}
			},
			success: function(i_repsonse, i_operation) {

				var o_result = Ext.decode(i_repsonse.responseText, true) || [{}];
				var o_record = o_result.batch[0].row;

				i_record.set(i_record.idProperty, o_record[i_record.idProperty]);

				if (i_record.get(i_self.propertyNameMapping.PARENTID) === 0) {
					i_self.wrapper.fireEvent("noderemoved", i_node);
				}
				else {
					i_node.set("PARENTID", i_record.get(i_self.propertyNameMapping.PARENTID));
				}
				if (f_callback) {
					f_callback(i_node);
				}
			},
			failure: function(o_error) {
				if (f_callback) {
					f_callback(false, o_error);
				}
			},
			getRecords: function() {
				return [i_record];
			}
		});

		// update node
		if (i_node && b_updateNode) {
			i_node.set("PROPERTIES", o_properties);
			var nodeData = i_self.titlePropertyRenderer(i_node);
			if (nodeData.title && nodeData.title.length > 0) {
				i_node.set("TITLE", nodeData.title);
				i_node.set("icon", nodeData.icon);
			}

			i_self.wrapper.fireEvent("nodechanged", i_node);
		}
	},

	saveRow: function(i_record, f_callback) {

		var i_self = this;
		f_callback = f_callback || Ext.emptyFn;

		var o_keys = {};
		var a_batch = [];
		var o_data = i_record.getData();
		o_keys[i_self.propertyNameMapping.ID] = o_data[i_self.propertyNameMapping.ID] || null;

		var o_batch = {
			type: "upsert",
			dmd: i_self.dmd,
			entity: i_self.entity,
			transactiontype: i_self.entity,
			key: o_keys,
			row: o_data
		};

		a_batch.push(o_batch);

		Ext.Ajax.request({
			url: i_self.writerStore.proxy.getUrl(),
			jsonData: {
				transaction: {
					batch: a_batch
				}
			},
			success: function(i_repsonse, i_operation) {

				var o_result = Ext.decode(i_repsonse.responseText, true) || [{}];
				var o_record = o_result.batch[0].row;

				Ext.Object.each(o_record, function(s_key, value) {
					i_record.set(s_key, value);
				});

				f_callback(true, i_record);
			},
			failure: function(o_error) {
				if (f_callback) {
					f_callback(true, o_error);
				}
			}
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			Set leaf for collection
		 ---------------------------------------------------------------------------------------------------- */
	setLeafForCollection: function(properties) {
		var me = this;

		var props = Ext.decode(properties),
		collection = Ext.ux.folderorganizer.Util.getXpdCollection(me, props.__cid__);

		if (collection.allowedchilds && collection.allowedchilds.length > 0) {
			return false;
		}
		else {
			return true;
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Remove existing row
		 ---------------------------------------------------------------------------------------------------- */
	removeRow: function(node) {
		var me = this;

		node = node || this.node;

		var collection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, node);

		if (!node.isLeaf()) {
			if (collection && collection.organizer && collection.organizer.prompt && collection.organizer.prompt.removeChildren) {
				Ext.create("Ext.ux.folderorganizer.MultiChoice", {
					title: ip2.i18n(
						{ da: "Sletning af afdeling og personer." },
						{ en: "What to delete ?" }
					),
					questions: [{
						answer: 0,
						question: ip2.i18n(
							{ da: "Slet afdelingen samt alle personer i denne!" },
							{ en: "This row, and all children." }
						),
						icon: "/system/images/icons/16x16/row_delete.png"
					},{
						answer: 1,
						question: ip2.i18n(
							{ da: "Slet afdelingen på uden af slette personerne." },
							{ en: "Only this row." }
						),
						icon: "/system/images/icons/16x16/row_delete.png"
					},{
						answer: 2,
						question: ip2.i18n(
							{ da: "Fortryd." },
							{ en: "Cancel." }
						),
						icon: "/system/images/icons/16x16/undo.png"
					}],
					guideLine: ip2.i18n(
						{ da: "Vælg, hvad der skal slettes:" },
						{ en: "Choose what to delete." }
					),
					modal: true,
					node: node,
					handler: function(answer) {
						var msgbox = this;

						switch (answer) {
							case 0:
								me.doDelete(msgbox.node, "allchildren");
								break;
							case 1:
								me.doDelete(msgbox.node, "onlyparent");
								break;
							case 2:
								break;
						}
					}
				}).show();
			}
			else {
				Ext.MessageBox.show({
					title: me.getI18N("confirmDeleteTitle"),
					msg: me.getI18N("confirmDelete").toString(),
					buttons: Ext.MessageBox.YESNOCANCEL,
					fn: function(btn, text) {
						switch (btn) {
							case "yes":
								me.doDelete(node);

								break;
							case "no":
								break;
							default:
								break;
						}
					},icon: Ext.MessageBox.QUESTION
				});
			}
		}
		else {
			if (collection && collection.organizer && collection.organizer.prompt && collection.organizer.prompt.removeRow) {
				Ext.create("Ext.ux.folderorganizer.MultiChoice", {
					title: ip2.i18n(
						{ da: "Sletning af person." },
						{ en: "What to delete ?" }
					),
					questions: [{
						answer: 0,
						question: ip2.i18n(
							{ da: "Slet personen!" },
							{ en: "Reference to above row and this row." }
						),
						icon: "/system/images/icons/16x16/row_delete.png"
					},{
						answer: 1,
						question: ip2.i18n(
							{ da: "Slet tilknytning til afdelingen." },
							{ en: "Only reference to above row." }
						),
						icon: "/system/images/icons/16x16/row_delete.png"
					},{
						answer: 2,
						question: ip2.i18n(
							{ da: "Fortryd." },
							{ en: "Cancel." }
						),
						icon: "/system/images/icons/16x16/undo.png"
					}],
					guideLine: ip2.i18n(
						{ da: "Vælg, hvad der skal slettes:" },
						{ en: "Choose what to delete." }
					),
					modal: true,
					node: node,
					handler: function(answer) {
						var msgbox = this;

						switch (answer) {
							case 0:
								me.doDelete(msgbox.node, "");
								break;
							case 1:
								me.doDelete(msgbox.node, "onlyreference");
								break;
							case 2:
								break;
						}
					}
				}).show();
			}
			else {
				me.doDelete(node);
			}
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Expand all children
		 ---------------------------------------------------------------------------------------------------- */
	expandChildren: function(node) {

		var me = this;
		node = node || me.node;

		node.expand(true);
	},

	/* ----------------------------------------------------------------------------------------------------
			Confirm export
		 ---------------------------------------------------------------------------------------------------- */
	exportRow: function(node) {

		var me = this;
		node = node || me.node;

		Ext.MessageBox.show({
			title: me.getI18N("confirmExportTitle"),
			msg: me.getI18N("confirmExport") + " <b>" + node.data.TITLE + "</b>",
			buttons: Ext.MessageBox.YESNO,
			fn: function(btn, text) {
				switch (btn) {
					case "yes":
						me.doExport(node);
						break;
					case "no":
						break;
					default:
						break;
				}
			},
			icon: Ext.MessageBox.QUESTION
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			Export
		 ---------------------------------------------------------------------------------------------------- */
	doExport: function(node) {
		var me = this;

		var loadingMask = new Ext.LoadMask(
			Ext.getBody(),
			{
				msg: me.getI18N("pleaseWait")
			}
		);
		loadingMask.show();

		Ext.Ajax.request({
			url: "/wsh-services/wshExpFld.aspx?folderToken=" + node.data.ID,
			node: node,
			loadingMask: loadingMask,
			success: function(response, request) {
				request.loadingMask.hide();
				var result = Ext.decode(response.responseText);
				if (result.success) {
					var msg = me.getI18N("exportInformation");
					msg = msg.replace("&title", node.data.TITLE);
					msg = msg.replace("&folder", result.folder);
					Ext.MessageBox.show({
						title: me.getI18N("information"),
						msg: msg,
						buttons: Ext.MessageBox.OK,
						icon: Ext.MessageBox.INFORMATION
					});
				}
			}
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			DMD editor
		 ---------------------------------------------------------------------------------------------------- */
	dmdEditor: function(store, record, node, action) {
		var me = this;

		var win = Ext.create("Ext.ux.folderorganizer.DmdEditor", {
			wrapper: this,
			store: store,
			record: record,
			action: action,
			title: (action == "new") ? me.getI18N("create") : me.getI18N("update"),
			listeners: {
				datachanges: function(writeaction, record) {
					if (writeaction == "create") {
						var childNode = me.createNewNode(record, node);
						var parentNode = node || me.store.getRootNode();
						if (parentNode && parentNode.isLoaded()) {
							parentNode.insertChild(0, childNode);
						}
					}
					else if (writeaction == "update") {
						node.set("GROUP", record.data.NAME);
					}
					else if (writeaction == "destroy") {
						me.doDelete(node);
					}
				}
			}
		});
		win.show();
	},

	/* ----------------------------------------------------------------------------------------------------
			Create new node
		 ---------------------------------------------------------------------------------------------------- */
	createNewNode: function(record, node) {
	},

	/* ----------------------------------------------------------------------------------------------------
			Get the record for a tree node
		 ---------------------------------------------------------------------------------------------------- */
	getRecord: function(args) {
		var me = this;

		if (!me.writerStore) {
			return;
		}

		var i_node = args.node;

		// get record by ip2 model and use model writer to sync with server

		var i_store = me.writerStore;

		var i_proxy = me.writerStore.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		Ext.apply(o_extraParams, {
			action: "getRow",
			dmdFile: me.writerStore.model.dmdFile,
			uiName: "portfolio",
			context: "form",
			key: Ext.encode(me.getKey(args.node)),
			args: args
		});

		i_store.load(function(a_records, i_operation, success) {

			var i_request = i_operation.getRequest();
			var o_parmas = i_request.getParams();
			var args = o_parmas.args;

			if (Ext.isFunction(args.success) && !args.success.fired) {
				args.success.fired = true;
				args.record = a_records[0];
				args.success.call(this, args);
			}
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			get key values from node
		 ---------------------------------------------------------------------------------------------------- */
	getKey: function(node) {
		var me = this;

		key = {};
		if (node) {
			key[me.propertyNameMapping.ID] = node.data.ID;
		}
		return key;
	},

	/* ----------------------------------------------------------------------------------------------------
			Save
		 ---------------------------------------------------------------------------------------------------- */
	save: function(record, action, node) {
		var me = this;

		if (!action || Ext.isObject(action)) {
			action = "update";
		}

		// find node, if not passed
		if (action == "update" && !node) {
			node = me.store.getNodeById(record.data[me.propertyNameMapping.ID]);

			// if still no node, because off an error in extjs. nodeHash array not correct updated !
			// -then check if editing selected node
			var nodes = me.getSelectionModel().getSelection();
			if (nodes && nodes[0].data.ID == record.data[me.propertyNameMapping.ID]) {
				node = nodes[0];
			}
		}

		if (action == "update" && node) {

			for (var key in record.data) {
				if (node.data[me.propertyNameMappingReverse[key]]) {
					node.set(me.propertyNameMappingReverse[key], record.data[key]);
				}
			}

			if (me.mode == "folder" && record.data[me.propertyNameMapping.PROPERTIES]) {
				me.setNodeTitle(node, record.data[me.propertyNameMapping.PROPERTIES]);
			}
		}

		if (me.writerStore) {
			me.writerStore.proxy.extraParams.action = "updateRow";
			me.writerStore.proxy.extraParams.dmdFile = me.writerStore.model.dmdFile;
			record.save({
				success: function(record, operation) {
					if (operation.action == "add") {
						this.refresh();
					}
				},
				action: action,
				scope: me
			});
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Refresh the tree view
		 ---------------------------------------------------------------------------------------------------- */
	refresh: function(clear) {
		var me = this;

		var root = me.store.getRootNode();
		root.removeAll();

		if (Ext.isBoolean(clear)) {
			delete me.store.proxy.extraParams.search;
		}

		me.store.load({
			scope: this,
			callback: function(records, operation, success) {
				me.expandTree(this, records[0], records);
			}
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			Search
		 ---------------------------------------------------------------------------------------------------- */
	search: function(searchValue) {

		var i_self = this;
		var i_store = i_self.getStore();
		var i_root = i_self.getRootNode();
		var i_proxy = i_store.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		o_extraParams.search = searchValue;

		i_root.collapse();
		i_root.removeAll();

		i_store.load({
			scope: this,
			callback: function(a_records, operation, success) {
				i_root.expand();
				i_self.expandTree(i_self, a_records);
			}
		});
	},

	/* ----------------------------------------------------------------------------------------------------
			Refresh the tree view
		 ---------------------------------------------------------------------------------------------------- */
	deselect: function() {
		var me = this;

		me.getSelectionModel().deselectAll();

		if (!me.cid) {
			return;
		}

		var node = me.getRootNode();
		if (Ext.isEmpty(node.data.PROPERTIES)) {
			var props = {
				__cid__: me.cid
			};
			node.set("PROPERTIES", Ext.encode(props));
		}
		me.getSelectionModel().select(node);
	},

	/* ----------------------------------------------------------------------------------------------------
			Before expanding a tree node
		 ---------------------------------------------------------------------------------------------------- */
	beforeItemExpand: function(node) {
		var me = this;

		//me.getSelectionModel().select(node);

		if (node.hasChildNodes()) {
			var childNode = node.getChildAt(0);
			if (childNode.isLeaf()) {
				var getAssociation = (Ext.isBoolean(childNode.getAssociation)) ? childNode.getAssociation : true;
				if (getAssociation) {
					for (var c in node.childNodes) {
						me.getAssociationNode(node.childNodes[c]);
					}
				}
			}
		}
		else if (me.isLeaf(node)) {
			return false;
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Set tree node title depending on the property data
		 ---------------------------------------------------------------------------------------------------- */
	setNodeTitle: function(node, properties) {
		var data = (Ext.isString(properties)) ? Ext.decode(properties) : properties;
		if (data.title) {
			node.set("TITLE", data.title);
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			The checkbox for a node is changed
		 ---------------------------------------------------------------------------------------------------- */
	isForeignRelation: function(node) {
		var me = this;

		var collection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(me, node);
		if (collection && collection.entity) {
			return true;
		}
		else {
			return false;
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			The checkbox for a node is changed
		 ---------------------------------------------------------------------------------------------------- */
	nodeCheckChange: function(node, checked) {
		var me = this;

		if (me.hasAssociation) {
			// validate: "hasMany" (itemgrid) has an selection
			var selection = me.wrapper.hasMany.panel.getSelectionModel().getSelection();
			if (selection.length === 0) {
				Ext.ux.folderorganizer.Alert({
					title: me.getI18N("message"),
					msg: me.getI18N("hasManySelectionMissing")
				});
				node.set("checked", !checked); // swap check mark back
				return;
			}

			me.changeAssociation(node, selection, checked, false);
		}
	},

	/* ----------------------------------------------------------------------------------------------------
			Get the association nodes
		 ---------------------------------------------------------------------------------------------------- */
	getAssociationNode: function(node) {
		var me = this;

		node.set("allowEdit", true);

		if (me.hasAssociation && me.wrapper.association && me.wrapper.association.name && me.mode != "folder") {
			node[me.wrapper.association.name]().load({
				callback: function(records) {
					// change node from leaf to parent node
					if (records.length > 0) {
						node.set("leaf", false);
					}

					// get associated nodes
					for (var i = 0; i < records.length; i++) {
						var childNode = me.createAssociationNode(node, records[i], false, false);
					}
				}
			});
		}
	},

	addAssociation: function(node, selection, dropped) {
		this.changeAssociation(node, selection, true, dropped);
	},

	removeAssociation: function(node, selection) {
		this.changeAssociation(node, selection, false, false);
	},

	changeAssociation: function(node, selection, checked, dropped) {
		var me = this;

		if (!me.hasAssociation) {
			return;
		}

		if (Ext.isBoolean(node.data.allowDrop) && node.data.allowDrop) {
			me.changeAssociationSelection(node, selection, checked, dropped);
		}
		else if (node.hasChildNodes()) {
			var childNode = node.getChildAt(0);
			if (me.isLeaf(childNode)) {
				me.changeAssociationSelection(node, selection, checked, dropped);
			}
			else {
				me.traverseChildren(node, selection, checked, dropped);
			}
		}
		else {
			me.changeAssociationSelection(node, selection, checked, dropped);
		}
	},

	traverseChildren: function(node, selection, checked, dropped) {
		var me = this;

		var children = node.childNodes;
		for (var i = 0; i < children.length; i++) {
			if (children[i].hasChildNodes()) {
				me.traverseChildren(children[i], selection, checked, dropped);
			}
			else {
				me.changeAssociationSelection(children[i], selection, checked, dropped);
			}
		}
	},

	changeAssociationSelection: function(node, selection, checked, dropped) {
		if (checked) {
			this.appendAssociationSelection(node, selection, dropped);
		}
		else {
			this.removeAssociationSelection(node, selection);
		}
	},

	appendAssociationSelection: function(node, selection, dropped) {
		var me = this;

		if (!me.hasAssociation) {
			return;
		}

		var record = {};
		for (var i = 0; i < selection.length; i++) {
			var duplicate = false;
			if (me.many2many && !me.many2many.allowDuplicateRecords) {
				duplicate = me.checkDuplicateAssociation(node, selection[i]);
			}
			if (!duplicate) {
				if (me.addToAllChildren.allow && node.data.hasSubFolders) {

					// params for the YES button function
					var params = {
						node: node,
						selection: selection[i],
						dropped: dropped
					};

					// Traverse all node children thru and add selection
					var updateAllChildren = function(params) {
						var properties = me.buildProperties(params.selection);
						Ext.Ajax.request({
							url: me.proxy,
							params: {
								action: "addItemToAllChildren",
								folder: params.node.data.ID,
								item: properties.keyValue,
								cid: params.selection.data.cid,
								xpd: me.wrapper.xpdPath
							},
							success: function(response, request) {
								if (request.node) {
									// remove all children
									if (request.node.childNodes) {
										var children = request.node.childNodes;
										var len = children.length;
										for (var i = 0; i < len; i++) {
											children[0].remove(true);
										}
									}

									// refresh this node
									me.store.load({
										node: request.node
									});
								}
							},
							node: params.node
						});
					};

					if (me.addToAllChildren.warning && me.questions) {
						Ext.create("Ext.ux.folderorganizer.MultiChoice", {
							title: ip2.i18n(me.addToAllChildren.multiChoiceDialog.title),
							questions: me.questions,
							guideLine: ip2.i18n(me.addToAllChildren.multiChoiceDialog.guideLine),
							modal: true,
							params: params,
							handler: function(answer) {
								switch (answer) {
									case 0:
										me.doCreate(this.params.node, this.params.selection, true, this.params.dropped);
										break;
									case 1:
										updateAllChildren(this.params);
										break;
									case 2:
										break;
								}
							}
						}).show();
					}
					else {
						updateAllChildren(params);
					}
				}
				else {
					me.doCreate(node, selection[i], true, dropped);
				}
			}
		}

		// parent node not a leaf any more
		node.set("leaf", false);
	},

	removeAssociationSelection: function(node, selection) {
		var me = this;

		if (!me.hasAssociation) {
			return;
		}

		var children = node.childNodes;
		var removeCnt = 0;
		var noOfChildren = children.length;

		// finding all children for this node matching a selection
		for (var c = 0; c < noOfChildren; c++) {
			var child = children[c];
			for (var i = 0; i < selection.length; i++) {
				var sID = selection[i].data.ID;
				var keys = me.getKeysFromNode(child);
				if (sID == keys.hasMany.ID) {
					removeCnt++;

					// remove record in many - many store
					var record = me.findMany2ManyRecord(keys);
					if (record !== null) {
						me.doDeleteRelation(record);
					}

					// remove this tree node
					child.remove(true);
				}
			}
		}

		if (removeCnt > 0) {
			if (removeCnt == children.length) {
				node.set("leaf", true);
			}
		}
	},

	createMany2ManyRecord: function(parentNode, item) {
		var me = this;

		var data = me.buildMany2ManyData(parentNode, item);

		return me.many2many.store.model.create(data);
	},

	buildMany2ManyData: function(i_parent, i_node) {

		var i_self = this;

		var o_data = {};

		if (i_self.wrapper.xpd) {

			o_data[i_self.propertyNameMapping.PARENTID] = i_parent.get("ID");

			if (i_node.get("cid")) {

				var o_build = i_self.buildProperties(i_node);
				var o_properties = o_build.properties;

				if (Ext.isEmpty(o_properties.seq)) {

					i_self.setSequenceProperty(i_parent, o_properties);
				}

				o_data[i_self.propertyNameMapping.PROPERTIES] = Ext.encode(o_properties);

				// kopi data from original
				var o_collection = Ext.ux.folderorganizer.Util.getXpdCollection(i_self, i_node.get("cid"));

				if (o_collection.organizer && o_collection.organizer.copyOriginalWhenDropped) {

					o_data[i_self.propertyNameMapping.PROPERTIES] = i_node.get(i_self.propertyNameMapping.PROPERTIES);

					if (!o_data[i_self.propertyNameMapping.PROPERTIES]) {

						var o_key = {};
						o_key[i_self.propertyNameMapping.ID] = i_node.get(i_self.propertyNameMapping.ID);

						var rs = ip2.sajax.request({
							url: "/ip2-services/ip2dmdProxy.aspx?model=" + i_self.entity + "&action=getRow&dmdFile=" + i_self.writerStore.model.dmdFile + "&key=" + Ext.encode(o_key),
							method: "GET",
							asJson: true
						});

						o_data[i_self.propertyNameMapping.PROPERTIES] = rs.rows[0][i_self.propertyNameMapping.PROPERTIES];
					}
				}

				if (o_collection && o_collection.titleColumns) {

					var a_titles = o_collection.titleColumns.split(",");

					Ext.Array.each(a_titles, function(s_title, n_index) {
						o_data.TITLE = i_self.concatTitle(o_data.TITLE, i_node.get(s_title.trim()));
					});
				}
			}
		}
		else {
			var belongToKey;
			if (Ext.isFunction(i_parent.get("ID").substring) && i_parent.get("ID").substring(0, 11) == "association") {
				belongToKey = i_parent.get("parentId");
			}
			else {
				belongToKey = i_parent.get("ID");
			}
			o_data[i_self.wrapper.belongTo.foreignKey] = belongToKey;
			o_data[i_self.wrapper.hasMany.foreignKey] = i_node.get("ID");
		}

		return o_data;
	},

	isLeaf: function(i_node) {

		var b_leaf = i_node.isLeaf();

		if (Ext.isBoolean(i_node.get("isHasManyNode")) && i_node.get("isHasManyNode")) {
			b_leaf = true;
		}

		return b_leaf;
	},

	isNode: function(i_record) {
		return i_record.isNode ? true : false;
	},

	buildProperties: function(item) {

		var me = this;
		var cid = item.data.cid;
		var properties = {};
		var property;
		var props = {};
		var keyValue;

		if (item.data.PROPERTIES) {
			props = Ext.decode(item.data.PROPERTIES);
		}
		Ext.apply(props, item.data);

		if (cid === undefined) {
			cid = props.__cid__;
		}

		var collection = Ext.ux.folderorganizer.Util.getXpdCollection(me, cid);
		for (var key in collection.properties) {
			if (collection.properties[key].isKey) {
				property = collection.properties[key].databasebind;
				if (props[property] === undefined) {
					property = key;
				}
				properties[key] = props[property];
				keyValue = props[property];
			}
			else if (collection.properties[key].databasebind) {
				var dataexclude = Ext.isBoolean(collection.properties[key].dataexclude) ? collection.properties[key].dataexclude : false;
				if (!dataexclude) {
					if (Ext.isArray(collection.properties[key].databasebind)) {
						Ext.Array.each(collection.properties[key].databasebind, function(property) {
							if (props[property]) {
								properties[key] = props[property];
							}
							else {
								property = property;
								if (props[property]) {
									properties[key] = props[property];
								}
							}
						});
					}
					else {
						property = collection.properties[key].databasebind;
						property = property;
						properties[key] = props[property] || "";
					}
				}
			}
			else if (collection.properties[key].value) {
				properties[key] = collection.properties[key].value;
			}
			else {
				properties[key] = props[key] || "";
			}
		}
		properties.__cid__ = cid;

		if (!keyValue) {
			if (props[me.propertyNameMapping.ID]) {
				keyValue = props[me.propertyNameMapping.ID];
			}
			else if (item.data.ID) {
				keyValue = item.data.ID;
			}
		}

		return { keyValue: keyValue, properties: properties };
	},

	/* ----------------------------------------------------------------------------------------------------
			Get next sequence number for properties
		 ---------------------------------------------------------------------------------------------------- */
	setSequenceProperty: function(parentNode, properties) {
		var seq = 20;
		if (parentNode.hasChildNodes() && parentNode.firstChild) {
			var props = (parentNode.firstChild.data.PROPERTIES) ? Ext.decode(parentNode.firstChild.data.PROPERTIES) : { seq: 20 };
			if (!Ext.isNumber(props.seq)) {
				seq = parseFloat(props.seq);
				if (isNaN(seq)) {
					seq = 20;
					props.seq = seq;
				}
				else {
					props.seq = seq;
				}
			}
			else {
				seq = props.seq;
			}
		}

		seq = seq / 2;
		properties.seq = seq;
	},

	checkDuplicateAssociation: function(node, selection) {
		var me = this;
		var keys;

		// child node it self
		if (Ext.isFunction(node.data.ID.substring) && node.data.ID.substring(0, 11) == "association") {
			keys = me.getKeysFromNode(node);
			if (keys.hasMany.ID == selection.data.ID) {
				return true;
			}
		}

		// parent node without any children
		if (!node.hasChildNodes()) {
			return false;
		}

		// parent node with children, check all children
		var children = node.childNodes;
		for (var i = 0; i < children.length; i++) {
			keys = me.getKeysFromNode(children[i]);
			if (keys.hasMany.ID == selection.data.ID) {
				return true;
			}
		}
	},

	createAssociationNode: function(i_parent, i_record, setChecked, b_dropped) {

		var i_self = this;
		var i_store = i_self.getStore();
		var i_model = i_store.getModel();
		var s_title = "";
		var ID;

		if (!i_self.hasAssociation) {
			return;
		}

		if (b_dropped) {
			s_title = i_self.getAssociationNodeTitle(i_record);
			ID = i_parent.get("ID") + "_" + i_record.get(i_record.idProperty);
		}
		else {
			if (i_self.wrapper.hasMany.foreignKey) {
				var hasManyRecord;
				hasManyRecord = i_self.wrapper.hasMany.store.getById(i_record.data[i_self.wrapper.hasMany.foreignKey]);
				if (!hasManyRecord) {
					hasManyRecord = i_self.wrapper.hasMany.store.getById(i_record.get("ID"));
				}
				if (hasManyRecord) {
					s_title = i_self.getAssociationNodeTitle(hasManyRecord);
					ID = i_parent.get("ID") + "_" + hasManyRecord.get("ID");
				}
			}
		}

		var i_node;

		if (i_record.isNode) {
			i_node = i_record;
		}
		else {
			i_node = Ext.create(i_model.$className, {
				ID: "association_" + ID
			});
		}

		// node.set(i_model.treeColumnName, title);
		i_node.set("leaf", true);
		i_node.set("isHasManyNode", true);
		i_node.set("allowEdit", i_self.allowEdit);
		i_node.set("allowRemove", i_self.allowRemove);

		var o_data = i_self.buildMany2ManyData(i_parent, i_record);

		if (o_data) {

			Ext.Object.each(o_data, function(s_key, value) {
				i_node.set(i_self.propertyNameMappingReverse[s_key], value);
			});
		}

		i_node.getAssociation = false; // prevent this leaf node from trigger "getAssociation"

		if (i_self.displayCheckbox) {

			i_parent.set("checked", setChecked);
		}

		var o_render = i_self.titlePropertyRenderer(i_node);

		if (s_title && s_title.length > 0) {
			o_render.title = s_title;
		}
		i_node.set(i_model.treeColumnName, o_render.title);
		i_node.set("icon", o_render.icon);

		return i_node;
	},

	getAssociationNodeTitle: function(record) {
		var me = this;
		var title = "";

		// get title using the description in xpd
		if (me.wrapper.xpd) {
			if (record.data.cid) {
				var cid = record.data.cid;
				var collection = Ext.ux.folderorganizer.Util.getXpdCollection(me, cid);
				if (collection.titleColumns) {
					var titles = collection.titleColumns.split(",");
					for (var i = 0; i < titles.length; i++) {
						title = me.concatTitle(title, record.data[titles[i].trim()]);
					}
				}
				else if (collection.properties) {
					for (var name in collection.properties) {
						if (collection.properties[name].isTitle) {
							var props = record.data;
							if (record.data.PROPERTIES) {
								Ext.apply(props, Ext.decode(record.data.PROPERTIES));
							}
							if (Ext.isArray(collection.properties[name].databasebind)) {
								Ext.Array.each(collection.properties[name].databasebind, function(name) {
									if (props[name]) {
										title = me.concatTitle(title, props[name]);
										return false;
									}
								});
							}
							else {
								if (props[collection.properties[name]]) {
									title = me.concatTitle(title, props[collection.properties[name].databasebind]);
								}
							}
						}
					}
				}
			}
		}
		else {
			var fields = me.wrapper.hasMany.titleFields || record.store.model.prototype.fields.keys;
			for (var i = 0; i < fields.length; i++) {
				title += ((title.length === 0) ? "" : ", ") + record.data[fields[i]];
			}
		}

		return title;
	},

	concatTitle: function(title, value) {
		return (title) ? title + " " + ip2.i18n(value) : ip2.i18n(value);
	},

	getKeysFromNode: function(node) {
		var keys = {};
		keys.belongTo = {};
		keys.hasMany = {};

		if (Ext.isFunction(node.data.ID.indexOf)) {
			var temp = node.data.ID.substring(node.data.ID.indexOf("_") + 1);
			keys.belongTo.ID = Number(temp.substring(0, temp.indexOf("_")));
			keys.hasMany.ID = Number(temp.substring(temp.indexOf("_") + 1));
		}
		else {
			keys.belongTo.ID = node.data.ID;
			keys.hasMany.ID = node.data.ID;
		}

		return keys;
	},

	findMany2ManyRecord: function(keys) {
		var me = this;

		var record;
		var cnt = 0;

		record = me.many2many.store.findRecord(me.wrapper.hasMany.foreignKey, keys.hasMany.ID, 0);
		while (record) {
			if (record.data[me.wrapper.belongTo.foreignKey] == keys.belongTo.ID) {
				return record;
			}
			record = me.many2many.store.findRecord(me.wrapper.hasMany.foreignKey, keys.hasMany.ID, ((record.index) ? record.index + 1 : cnt++));
		}

		return null;
	},
	hasmanySelectionChange: function(selection) {
		var me = this;

		if (!me.displayCheckbox) {
			return;
		}

		me.setNodesUnchecked();

		if (selection && selection[0]) {
			me.setNodesChecked(selection);
		}
	},

	setNodesChecked: function(selection) {

		var i_self = this;

		var i_store = i_self.getStore();

		var i_many2manyStore = i_self.many2many.store;
		var i_model = i_many2manyStore.getModel();
		var i_proxy = i_model.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		var a_filter = [];


		for (var i = 0; i < selection.length; i++) {
			o_option.property = i_self.wrapper.hasMany.foreignKey;
			o_option.value = selection[i].data.ID;
			o_option.operator = "OR";
			filter.push(o_option);
			delete o_option;
		}

		var oldAction = o_extraParams.action;
		o_extraParams.action = "getRows";
		o_extraParams.filter = Ext.encode(a_filter);


		i_many2manyStore.on({
			load: {
				fn: function(i_manyStore, a_records, b_successful, i_operation) {

					o_extraParams.action = oldAction;
					delete o_extraParams.filter;

					Ext.Array.each(a_records, function(i_record) {

						var i_belongToRecord = i_store.getNodeById(i_record.get(i_self.wrapper.belongTo.foreignKey));

						if (i_self.displayCheckbox && i_belongToRecord) {
							i_belongToRecord.set("checked", true);
						}
					});
				}
			}
		});

		i_many2manyStore.load();
	},

	setNodesUnchecked: function() {

		var i_self = this;

		var i_store = i_self.getStore();

		var i_many2manyStore = i_self.many2many.store;

		var a_records = i_many2manyStore.getRange();

		Ext.Array.each(a_records, function(i_record) {

			var i_belongToRecord = i_store.getNodeById(i_record.get(i_self.wrapper.belongTo.foreignKey));

			if (i_self.displayCheckbox && i_belongToRecord) {
				i_belongToRecord.set("checked", false);

				if (i_belongToRecord.parentNode) {
					i_belongToRecord.parentNode.set("checked", false);
				}

			}
		});
	},

	getModelColumns: function() {

		var i_self = this;
		var a_columns = [];

		var i_store = i_self.getStore();
		var i_model = i_store.getModel();
		var a_fields = i_model.getFields();

		Ext.Array.each(a_fields, function(i_field) {

			var o_column;
			var s_columnTitel;

			s_columnTitel = ip2.i18n(i_field.title);

			if (!s_columnTitel) {
				s_columnTitel = ip2.i18n(i_field.name);
			}

			if (i_field.isTitle) {

				o_column = {
					title: s_columnTitel,
					text: s_columnTitel,
					dataIndex: i_field.name,
					flex: i_field.flex ? i_field.flex : 1,
					renderer: Ext.isFunction(i_field.renderer) ? i_field.renderer :
						function(value, meta, record) {

							var s_title = value;
							var nodeData = i_self.titlePropertyRenderer(record);
							var s_value = nodeData.title;

							var s_search = i_self.getSearchValue();

							if (s_value) {
								s_title = s_value;
							}
							else {
								s_title = ip2.i18n(value);
							}
							if (s_search) {
								var s_searchLower = s_search.toLowerCase();
								var s_titleLower = s_title.toLowerCase();
								if (s_titleLower.indexOf(s_searchLower) > -1) {
									var n_start = s_titleLower.indexOf(s_searchLower);
									var n_end = n_start + s_searchLower.length;
									s_title = s_title.substring(0, n_start) +
									"<span style=\"background-color:yellow;n_\">" +
									s_title.substring(n_start, n_end) +
									"</span>" + s_title.substring(n_end);
								}
							}
							return s_title;
						},
					scope: i_self
				};

				// first column
				if (Ext.isEmpty(a_columns)) {
					o_column.xtype = "treecolumn";
					o_column.flex = 2;
					delete o_column.width;
					i_model.treeColumnName = i_model.treeColumnName || o_column.dataIndex;
				}

				a_columns.push(o_column);
			}
			else if (i_field.isOrganizer) {

				o_column = {
					header: s_columnTitel,
					text: s_columnTitel,
					title: s_columnTitel,
					dataIndex: i_field.name,
					flex: i_field.flex ? i_field.flex : 1,
					renderer: function(value, i_meta, i_record, n_index) {
						return value;
					},
					scope: i_self
				};

				a_columns.push(o_column);
			}
		});

		// if columns still empty, use all fields
		if (Ext.isEmpty(a_columns)) {
			Ext.Array.each(a_fields, function(i_field) {
				var o_column = {
					text: Ext.util.Format.capitalize(Ext.util.Format.lowercase(i_field.name)),
					dataIndex: i_field.name
				};

				// first column
				if (Ext.isEmpty(a_columns)) {
					o_column.xtype = "treecolumn";
					i_model.treeColumnName = i_model.treeColumnName || o_column.dataIndex;
				}
				a_columns.push(o_column);
			});
		}

		if (i_self.wrapper.xpd) {
			a_columns =	Ext.ux.folderorganizer.Util.addCollectionColumns(i_self, a_columns);
		}

		return a_columns;
	},
	/* -------------------------------------------------------------------------------------------------------------------
			Add columns dynamic depending on the collection
		 ------------------------------------------------------------------------------------------------------------------- */
	titlePropertyRenderer: function(i_record) {

		var i_self = this;

		var o_data = {
			title: "",
			icon: ""
		};

		var s_icon = i_record.get("icon");

		var o_collection = Ext.ux.folderorganizer.Util.getXpdCollectionByNode(i_self, i_record);

		if (!o_collection) {
			return o_data;
		}

		if (o_collection.icon) {
			o_data.icon = (i_self.wrapper.xpd.iconPath ? i_self.wrapper.xpd.iconPath + "/" : "") + o_collection.icon;
		}

		if (o_collection) {
			if (o_collection.properties) {

				var s_properties = i_record.get("PROPERTIES");

				var o_properties = Ext.decode(s_properties, true) || {};

				// Apply values from row properties, if not already exists or is empty
				var s_rowProperties = i_record.get("rowProps");

				if (s_rowProperties) {
					var o_rowPropperties = Ext.decode(s_rowProperties, true) || {};
					if (o_rowPropperties.properties) {
						o_rowPropperties = o_rowPropperties.properties;
					}

					Ext.apply(o_properties, o_rowPropperties);
				}

				var o_collectionProperties = o_collection.properties;

				Ext.Object.each(o_collectionProperties, function(s_key, value) {
					if (o_properties.hasOwnProperty(s_key) && value.isTitle) {
						o_data.title = i_self.concatTitle(o_data.title, o_properties[s_key]);
					}
				});

				if (o_data.title.length === 0) {
					var o_title = o_collectionProperties.title;

					if (o_title && o_title.databasebind) {
						var a_titleFields = Ext.isArray(o_title.databasebind) ? o_title.databasebind : [o_title.databasebind];
						Ext.Array.each(a_titleFields, function(s_field) {
							if (o_properties[field]) {
								o_data.title = o_properties[field];
								return false;
							}
						});
					}
					else if (o_properties.title) {
						o_data.title = ip2.i18n(o_properties.title);
					}

					if (s_icon.indexOf(o_properties.icon) > 0) {
						i_record.set("icon", "");
						i_record.commit(true);
					}
				}
			}
		}

		if (!Ext.isString(o_data.title)) {
			o_data.title = o_data.title + " ";
		}

		return o_data;
	},

	expandSelectedNodes: function() {
		var me = this;

		var nodes = me.getSelectionModel().getSelection();
		for (var i = 0; i < nodes.length; i++) {
			nodes[i].expand();
		}
	},

	doCreate: function(parentNode, selection, setChecked, dropped) {
		var me = this;

		// Warning for copy from original
		if (selection.data.cid && dropped) {
			var collection = Ext.ux.folderorganizer.Util.getXpdCollection(me, selection.data.cid);

			if (collection.organizer &&
				collection.organizer.copyOriginalWhenDropped &&
				collection.organizer.prompt &&
				collection.organizer.prompt.copyOriginal) {

				if (selection.data[me.propertyNameMapping.PARENTID] > 0) {
					Ext.create("Ext.ux.folderorganizer.MultiChoice", {
						title: ip2.i18n(
							{ da: "Personen er tilknyttet en anden afdeling." },
							{ en: "Copy from original ?" }
						),
						questions: [{
							answer: 0,
							question: ip2.i18n(
								{ da: "Lav en kopi af personen og tilknyt denne i stedet." },
								{ en: "Continue to copy this row." }
							),
							icon: "/system/images/icons/16x16/copy.png"
						},{
							answer: 1,
							question: ip2.i18n(
								{ da: "Fortryd." },
								{ en: "Cancel." }
							),
							icon: "/system/images/icons/16x16/undo.png"
						}],
						guideLine: ip2.i18n(
							{ da: "Vælg, hvad der skal ske:" },
							{ en: "This row already belong to an other parent." }
						),
						modal: true,
						parentNode: parentNode,
						selection: selection,
						setChecked: setChecked,
						dropped: dropped,
						handler: function(answer) {
							var msgbox = this;

							switch (answer) {
								case 0:
									me.createNodeAndRecord(msgbox.parentNode, msgbox.selection, msgbox.setChecked, msgbox.dropped, false);
									break;
								case 1:
									break;
							}
						}
					}).show();
				}
				else {
					me.createNodeAndRecord(parentNode, selection, setChecked, dropped, true);
				}
			}
			else {
				me.createNodeAndRecord(parentNode, selection, setChecked, dropped, false);
			}
		}
		else {
			me.createNodeAndRecord(parentNode, selection, setChecked, dropped, false);
		}
	},

	createNodeAndRecord: function(i_parent, i_selection, setChecked, b_dropped, b_keepOriginal) {

		var i_self = this;
		var i_many2manyStore = i_self.many2many.store;

		// Sync many to many relation
		var i_record = i_self.createMany2ManyRecord(i_parent, i_selection);
		if (b_keepOriginal) {
			i_record.set(i_self.propertyNameMapping.ID, i_selection.get(i_self.propertyNameMapping.ID));
		}

		i_many2manyStore.insert(0, i_record);

		// create new child node
		var i_child;
		var o_childProp;
		var o_parentProp;

		if (i_parent.isLoaded()) {
			i_child = i_self.createAssociationNode(i_parent, i_selection, setChecked, b_dropped);
		}

		// If parent node do not have any folder as children, but has product as children. Set parent as cid 2 = product folder
		var s_childValue = i_record.get(i_self.propertyNameMapping.PROPERTIES);
		var s_parentValue = i_parent.get("PROPERTIES");
		var n_childCid = 0;
		var n_parentCid = 0;

		if (s_childValue) {

			o_childProp = Ext.decode(s_childValue);
			n_childCid = o_childProp.__cid__;
		}
		if (s_parentValue) {

			o_parentProp = Ext.decode(s_parentValue);
			n_parentCid = o_parentProp.__cid__;
		}

		if (n_childCid == 3 && n_parentCid == 1) {
			if (!i_self.hasChildnodesWithCid(i_parent, 1) && !i_self.hasChildnodesWithCid(i_parent, 2)) {
				Ext.Ajax.request({
					url: i_self.proxy,
					params: {
						action: "setFolderCid",
						token: i_parent.get("ID"),
						cid: 2
					}
				});
				var o_properties = Ext.decode(i_parent.get("PROPERTIES"));
				o_properties.__cid__ = 2;
				i_parent.set("PROPERTIES" ,Ext.encode(o_properties));
			}
		}
		// Sync with server
		i_self.doCreateRelation(i_record, i_child, i_parent, b_keepOriginal);
	},

	doCreateRelation: function(i_record, i_child, i_parent, b_keepOriginal) {

		var i_self = this;

		var i_store = i_self.getStore();

		var i_many2manyStore = i_self.many2many.store;
		var i_model = i_many2manyStore.getModel();
		var i_proxy = i_model.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		var s_oldAction = o_extraParams.action;

		var key = {};
		if (b_keepOriginal) {
			key[i_self.propertyNameMapping.ID] = i_record.get(i_self.propertyNameMapping.ID);
		}
		else {
			key[i_self.propertyNameMapping.ID] = 0;
		}

		o_extraParams.action = "updateRow";
		o_extraParams.key = Ext.encode(key);

		i_self.saveRow(i_record, function(b_created, i_record) {

			if (b_created) {

				var s_properties = i_record.get(i_self.propertyNameMapping.PROPERTIES);

				if (s_properties) {

					var data = Ext.decode(s_properties, true) || {};

					if (data.__cid__ == 3) {

						Ext.Ajax.request({
							url: i_self.proxy,
							params: {
								action: "checkProductCatalogLocation",
								folderToken: i_record.get(i_self.propertyNameMapping.ID)
							}
						});
					}
				}

				if (i_parent) {

					i_parent.removeAll(false);

					i_store.load({
						node: i_parent
					});
				}
			}
		});

		o_extraParams.action = s_oldAction;
		delete o_extraParams.key;
	},

	doUpdateRelation: function(i_parent) {

		var i_self = this;

		var i_many2manyStore = i_self.many2many.store;
		var i_model = i_many2manyStore.getModel();
		var i_proxy = i_model.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		var oldAction = o_extraParams.action;

		var o_key = {};
		var o_parentKey = i_parent.get("key");
		if (o_parentKey) {
			o_key = o_parentKey;
		}
		else {
			o_key[i_self.propertyNameMapping.ID] = i_parent.get("ID");
		}

		o_extraParams.action = "updateRow";
		o_extraParams.key = Ext.encode(o_key);
		i_many2manyStore.sync();
		o_extraParams.action = oldAction;
		delete o_extraParams.key;
	},

	doDelete: function(i_node, s_action) {

		var i_self = this;

		var i_parent = i_node.parentNode;

		if (i_node.get("allowRemove")) {
			Ext.Ajax.request({
				url: i_self.proxy,
				node: i_node,
				params: {
					action: "removefolder",
					subaction: s_action || "allchildren",
					ID: i_node.get("ID"),
					entity: i_self.entity,
					idField: i_self.propertyNameMapping.ID,
					parentidField: i_self.propertyNameMapping.PARENTID,
					propertiesField: i_self.propertyNameMapping.PROPERTIES,
					dataLibrary: (Ext.isFunction(i_self.wrapper.getDataLibrary)) ? i_self.wrapper.getDataLibrary() : ""
				},
				success: function(response, request) {
					i_self.wrapper.fireEvent("noderemoved", request.node);
				}
			});
		}

		var o_parentProp = Ext.decode(i_parent.get("PROPERTIES"), true) || {};

		var n_parentCid = o_parentProp.__cid__;

		var n_cid = -1;

		i_node.remove();


		if (n_parentCid == 2 && !i_self.hasChildnodesWithCid(i_parent, 3)) {

			n_cid = 1;
		}
		else if (n_parentCid == 1 &&
				(
					!i_self.hasChildnodesWithCid(i_parent, 1) ||
					!i_self.hasChildnodesWithCid(i_parent, 2)
				) && i_self.hasChildnodesWithCid(i_parent, 3)
			) {

			n_cid = 1;
		}

		if (n_cid > -1)  {

			o_parentProp.__cid__ = n_cid;

			Ext.Ajax.request({
				url: i_self.proxy,
				params: {
					action: "setFolderCid",
					token: i_parent.get("ID"),
					cid: n_cid
				}
			});
		}

		i_parent.set("PROPERTIES", Ext.encode(o_parentProp));

		if (i_parent && i_parent.hasChildNodes && Ext.isFunction(i_parent.hasChildNodes)) {
		}
		else {
			i_parent.set("leaf", true);
		}
	},

	doDeleteRelation: function(i_node) {

		var i_self = this;

		var i_many2manyStore = i_self.many2many.store;
		var i_model = i_many2manyStore.getModel();
		var i_proxy = i_model.getProxy();
		var o_extraParams = i_proxy.getExtraParams();

		var oldAction = o_extraParams.action;

		var o_key = {};
		o_key[i_self.propertyNameMapping.ID] = i_node.get("ID");

		i_many2manyStore.remove(i_node);

		o_extraParams.action = "deleteRow";
		o_extraParams.key = Ext.encode(o_key);
		i_many2manyStore.sync();
		o_extraParams.action = oldAction;
		delete o_extraParams.key;
	},

	setFocus: function(component) {
		var me = this;

		if (component == "tree") {
			me.getSelectionModel().select(0);
			me.getView().focus();
		}
		else {
			if (!me[component]) return false; // component does not exists
			me[component].focus();
		}

		return true; // set focus successed
	},
	setupAssociation: function() {
		var me = this;

		// add listeners to many - many store. Set id
		if (me.many2many && me.many2many.store) {
			me.many2many.store.on("write", function(store, operation) {
				var response;
				var newId;
				var o_resp = operation.getResponse();
				if (o_resp.responseText) {
					response = Ext.decode(o_resp.responseText);
					if (response.success) {
						if (response.rows) {
							// new records, set id and commit
							for (var i = 0; i < operation.records.length; i++) {
								if (Ext.isObject(response.rows)) {
									newId = response.rows.ID;
								}
								else {
									newId = (response.rows[i].ID) ? response.rows[i].ID : 0;
								}
								operation.records[i].set("ID", newId);
								operation.records[i].commit();

								// update many - many store
								var record = me.many2many.store.getById(newId);
								record.set(me.wrapper.belongTo.foreignKey, operation.records[i].data[me.wrapper.belongTo.foreignKey]);
								record.set(me.wrapper.hasMany.foreignKey, operation.records[i].data[me.wrapper.hasMany.foreignKey]);
							}
						}
						else {
							for (var i = 0; i < operation.records.length; i++) {
								operation.records[i].commit();
							}
						}
					}
				}
			});
		}
	},
	getI18N: function(key) {
		var value = ip2.folderorganizer.getTextValue(key);
		if (Ext.isObject(value)) {
			value = value[ip2.folderorganizer.getLanguage()];
		}
		return value;
	},

	/* ----------------------------------------------------------------------------------------------------
			Has node children with specified cid
		 ---------------------------------------------------------------------------------------------------- */
	hasChildnodesWithCid: function(node, cid) {
		var me = this;

		for (var i = 0; i < node.childNodes.length; i++) {
			if (Ext.decode(node.childNodes[i].data.PROPERTIES).__cid__ == cid) {
				return true;
			}
		}

		return false;
	}
});
