duplicateObj = function (o,inArr,notInArr) {
	n = {};
	for (var i in o) {
		if ((!inArr || inArr.indexOf(i)!==-1) && (!notInArr || notInArr.indexOf(i)===-1))
			n[i]=o[i];
	}
	return n;
}

Array.prototype.extract=function(p,ra){
	a=[];
	for(var i=0;i<this.length;i++){
		eval('a.push(this[i].'+p+');');
	};
	if (ra) return a;
	return a.join(',');
}
/*globals Ext, CSRFKiller */

Array.prototype.distinct = function () {
	var r = new Array();
	o:for(var i = 0, n = this.length; i < n; i++){
		for(var x = 0, y = r.length; x < y; x++){
			if(r[x]==this[i]){
				continue o;
			}
		}
		r[r.length] = this[i];
	}
	return r;
}

Ext.namespace("Ext.ux", "Ext.ux.data", "Ext.ux.tree"); //Used by extensions

// Required by data mirroring
Ext.override(Ext.util.Observable, {
  resetEvents: function() {
    var e, es = this.events;
    this.events = {};
    for(e in es) {
      this.events.e = true;
    }
  }
});

Ext.onReady(function() {
 Ext.Ajax.on('beforerequest', function(conn, options) {
    if(typeof options.params == 'object') {
      //Make the two possibilities easier
      options.params = Ext.urlEncode(options.params);
    } else if(options.params && typeof options.params == "string") {
      //options.params = options.params+'&'+CSRFKiller.field+'='+CSRFKiller.token;
    } else if(typeof options.params == 'undefined' && options.form) {
      //options.params = CSRFKiller.field+'='+CSRFKiller.token;
    }
 });
});

Ext.override(Ext.form.Field, {
  setFieldLabel: function(text) {
    var ct = this.el.findParent('div.x-form-item', 3, true);
    var label = ct.first('label.x-form-item-label');
    label.update(text);
  }
});

Ext.override(Ext.data.Store, {
  // Their load records function isn't very extensible,
  // so I had to copy it in here
  loadRecords : function(o, options, success){
    if(!o || success === false){
        if(success !== false){
            this.fireEvent("load", this, [], options);
        }
        if(options.callback){
            options.callback.call(options.scope || this, [], options, false);
        }
        return;
    }
    var r = o.records, t = o.totalRecords || r.length;
    if(!options || options.add !== true){
        if(this.pruneModifiedRecords){
            this.modified = [];
        }
        for(var i = 0, len = r.length; i < len; i++){
            r[i].join(this);
        }
        if(this.snapshot){
            this.data = this.snapshot;
            delete this.snapshot;
        }
        this.data.clear();
        this.data.addAll(r);
        this.totalLength = t;

        this.onDataChanged(); //This line added

        this.fireEvent("datachanged", this);
    }else{
        this.totalLength = Math.max(t, this.data.length+r.length);
        this.add(r);
    }

    this.fireEvent("load", this, r, options);
    if(options.callback){
        options.callback.call(options.scope || this, r, options, true);
    }
  },
  onDataChanged: function() {
    this.applySort();
  }
});


Ext.override(Ext.tree.TreeNodeUI, {
  onDblClick : function(e){
    e.preventDefault();
    if(this.disabled){
      return;
    }
    if(this.checkbox){
      this.toggleCheck();
    }
    // Removed expanding the node, put a dblclick
    // listener on if you want it
    this.fireEvent("dblclick", this.node, e);
  }
});

Ext.override(Ext.Window, {
  showMask: function() {
    if(this.modal){
      Ext.getBody().addClass("x-body-masked");
      this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
      this.mask.show();
    }
  },
  hideMask: function() {
    if(this.modal){
      this.mask.hide();
      Ext.getBody().removeClass("x-body-masked");
    }
  }
});

Ext.override(Ext.tree.TreePanel, {
	findNode: function(prop, value) {
		return this.findNodeBy(function(node) {
		  return (node.attributes[prop] == value);
		});
	},
	findNodeBy: function(fn, scope) {
		for (var id in this.nodeHash) {
		  var node = this.nodeHash[id];
		
		  // It's up to the user to specify unique
		  // constraints
		  if(fn.call((scope || this),  node)) {
		    return node;
		  }
		}
	}
});

// Override show / hide Container
Ext.override(Ext.form.Field, {
	showContainer: function() {
		this.enable();
		this.show();
		this.getEl().up('.x-form-item').setDisplayed(true); // show entire container and children (including label if applicable)
	},
	hideContainer: function() {
		this.disable(); // for validation
		this.hide();
		this.getEl().up('.x-form-item').setDisplayed(false); // hide container and children (including label if applicable)
	},
	setContainerVisible: function(visible) {
		if (visible) {
			this.showContainer();
		} else {
			this.hideContainer();
		}
		return this;
		}
});

// Override reBuild grid
Ext.override(Ext.grid.GridPanel,{
	reBuild: function (){
    	this.reconfigure(this.store,this.getColumnModel());
    }
});

// Override getSelectedIds

Ext.override(Ext.grid.GridPanel,{
	getSelectedIds: function (){
		var ids='';
    	var m = this.getSelections();
		for(var i=0; i<m.length; i++){
			var ss = m[i].get("id");
			if(i===0){
				ids = ss;
			} else {
			    ids += "," + ss;
			}
		}
		return ids.toString();
    }
});

Ext.override(Ext.tree.TreePanel,{
	getSelectedIds: function () {
		if (!this.idName) this.idName = 'id';
		
		var ids = ''; var ids_array = [];
		var selModel = this.getSelectionModel();
		
		if (selModel.selNode) {		/* Single Selection Model */
			ids_array = [selModel.selNode];
		} else if (selModel.selNodes) {
			ids_array = selModel.selNodes;
		}
		
		return ids_array.extract('attributes.'+this.idName);
		
//		for(var i=0; i<ids_array.length; i++){
//			var ss = ids_array[i].attributes[this.idName];
//			if(i===0) ids = ss;
//			else ids += "," + ss;
//		}
//		return ids;
    },
    getNodeByAttribute: function(attrib, value, node) {
    	if (!node) node = this.getRootNode();
		var nodeFound = null;
		node.cascade(function fnCheckAttrib() {
			if(this.attributes[attrib] && this.attributes[attrib] == value)
			{
				nodeFound = this;
				return false;
			}
		});
		return nodeFound;
	},
	getNodesByAttributes: function(atts, node, single) {
    	if (!node) node = this.getRootNode();
		var nodesFound = [];
		node.cascade(function fnCheckAttrib() {
			
			var found = true;
			
			for (var i in atts) {
				if(!this.attributes[i]) {
					found = false;
				} else {
					if (typeof atts[i].inArray == 'function') {
						if (!atts[i].inArray(this.attributes[i])) {
							found = false;
						}
					} else {
						if (this.attributes[i] != atts[i]) {
							found = false;
						}
					}
				}
			}
			
			if (found) {
				if (single) {
					nodesFound = this;
					return false;
				} else {
					nodesFound.push(this);
				}
			}

		});
		return nodesFound;
	}
});



/*	Overrides default load functionality of the PagingToolbar
	Making it retain all previous store's filters	*/

Ext.PagingToolbar.override({
    doLoad : function(start){
    	if (this.store.lastOptions && this.store.lastOptions.params)
        	var o = this.store.lastOptions.params;
        else var o = {};
        var pn = this.paramNames;
        o[pn.start] = start;
        o[pn.limit] = this.pageSize;
        if(this.fireEvent('beforechange', this, o) !== false){
            this.store.load({params:o});
        }
    }
});

Ext.override(Ext.Container, {
    removeAll: function() {
        this.items.each(function(childItem){ this.remove(childItem);}, this);
    }
});

Ext.override(Ext.tree.TreeNode, {
	addChild: function(node,unique,before) {
		var tree = this.getOwnerTree();
		if (tree.idName && !unique) 
			unique = tree.idName;
			
		if (!unique) 
			unique = 'id';
		
		if (this.isExpanded()) {
//			this.beginUpdate();
			var appendedNode = this.insertBefore(node,before);
//			this.endUpdate();
			tree.fireEvent('afterNodeAdd',tree,this,appendedNode,before);
		} else {
			this.addListener('expand',function() {
				var foundNode = this.findChild(unique,node.attributes[unique]);
				if (!foundNode) {
//					this.beginUpdate();
					var appendedNode = this.insertBefore(node,before);
//					this.endUpdate();
					tree.fireEvent('afterNodeAdd',tree,this,appendedNode,before);
				} else {
					tree.fireEvent('afterNodeAdd',tree,this,foundNode,before);
				}
			},null,{single: true});
			this.expand();
		}
	},
	addChildren: function(nodes,unique) {
		if (!unique) unique = 'id';
		
		if (this.isExpanded()) {
			for (var i=0; i<nodes.length;i++) {
				this.appendChild(nodes[i]);
			}
		} else {
			this.addListener('expand',function() {
				for (var i=0; i<nodes.length;i++) {
					
					if (!this.findChild(unique,nodes[i].attributes[unique])) {
						this.appendChild(nodes[i]);
					}
					
				}
			},null,{single: true});
			this.expand();
		}
	},
	copy: function(customAtts) {
		var atts = this.attributes;
		if (customAtts)
			for (var i in customAtts) 
				atts[i] = customAtts[i];
		return new Ext.tree.TreeNode(atts);
	},
	isLoaded: function() {
		if (this.childNodes.length>0) {
			return true;
		}
		return false;
	},
	reload: function(callback) {
		if (this.getOwnerTree())
			this.getOwnerTree().loader.load(this,callback);
	},
	getParentBy : function(property, value) {
		if (!this.parentNode) return false;
		if (this.parentNode.attributes[property]==value) {
			return this.parentNode;
		} else {
			return this.getParentBy(property, value);
		}
	}
});

Ext.override(Ext.BoxComponent,{
	setInnerHtml: function(v) {
		this.el.dom.innerHTML=v;
	},
	getInnerHtml: function(v) {
		return this.el.dom.innerHTML;
	}
});

// destroys form fields, including their label
Ext.override(Ext.layout.FormLayout, {
    renderItem : function(c, position, target){
        if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
            var args = [
                   c.id, c.fieldLabel,
                   c.labelStyle||this.labelStyle||'',
                   this.elementStyle||'',
                   typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                   (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                   c.clearCls || 'x-form-clear-left' 
            ];
            if(typeof position == 'number'){
                position = target.dom.childNodes[position] || null;
            }
            if(position){
                c.formItem = this.fieldTpl.insertBefore(position, args, true);
            }else{
                c.formItem = this.fieldTpl.append(target, args, true);
            }

//          Remove the form layout wrapper on Field destroy.
            c.on('destroy', c.formItem.remove, c.formItem, {single: true});
            c.render('x-form-el-'+c.id);
        }else {
            Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
        }
    }
});

Ext.DomQuery.matchers[2].re = /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?(?:"(.*?)"|'(.*?)'|(.*?)))?[\]\}])/;

Ext.override(Ext.form.Radio, {
	getGroupValue : function(){
		var c = this.getParent().child('input[name="'+this.el.dom.name+'"]:checked', true);
		return c ? c.value : null;
	},
	toggleValue : function() {
		if(!this.checked){
			var els = this.getParent().select('input[name="'+this.el.dom.name+'"]');
			els.each(function(el){
				if(el.dom.id == this.id){
					this.setValue(true);
				}else{
					Ext.getCmp(el.dom.id).setValue(false);
				}
			}, this);
		}
	},
	setValue : function(v){
		if(typeof v=='boolean') {
			Ext.form.Radio.superclass.setValue.call(this, v);
		}else{
			var r = this.getParent().child('input[name="'+this.el.dom.name+'"][value="'+v+'"]', true);
			if(r && !r.checked){
				Ext.getCmp(r.id).toggleValue();
			};
		}
	}
});

/**
 * Override Ext.FormPanel so that in case whe create a form without items from a json
 * it still has a item list.
 */
Ext.override(Ext.FormPanel, {
    // private
    initFields : function(){
        //BEGIN FIX It can happend that there is a form created without items (json)
        this.initItems();
        //END FIX
        var f = this.form;
        var formPanel = this;
        var fn = function(c){
            if(c.doLayout && c != formPanel){
                Ext.applyIf(c, {
                    labelAlign: c.ownerCt.labelAlign,
                    labelWidth: c.ownerCt.labelWidth,
                    itemCls: c.ownerCt.itemCls
                });
                if(c.items){
                    c.items.each(fn);
                }
            }else if(c.isFormField){
                f.add(c);
            }
        }
        this.items.each(fn);
    }
});
