function $returnFalse() { return false; }

Function.implement({
	callOnce:function () {
		if (!this.$alreadyCalled) this();
		this.$alreadyCalled=true;
	},
	resetCall:function () {
		this.$alreadyCalled=null;
	}
});

Array.implement({
	removeDuplicates:function () {
		// TODO to implement
		return this;
	},
	pluck:function (property) {
		var a=[];
		this.each(function (o) { a.push(o?o[property]:null); });
		return a;
	},
	invoke:function (method,args) {
		var a=[];
		this.each(function (o) { a.push(o[method].apply(o,args || [])); });
		return a;
	}
});

Element.implement({
	setHTML:function() {
		var html=Array.join(arguments,"");
		this.innerHTML=html.trim().stripScripts(true);
		return this;
	},
	show:function () {
		return this.set("display",true);
	},
	hide:function () {
		return this.set("display",false);
	},
	setVisibility:function (visible) {
		console.log("setVisibility",this);
		return this.set("visibility",visible);
	},
	visible:function () {
		console.log("visible",this);
		return this.get("visibility");
	},

	toggle:function () {
		this.set("display","toggle");
	},

	hover:function (over,out) {
		return this.addEvent("mouseenter",over).addEvent("mouseleave",out);
	},
	setHoverClass:function (className) {
		if (!className) className="over";
		// only on ie6 since :hover is not working
		return Browser.Engine.trident4 ?
			this.hover(
				function () { this.addClass(className); },
				function () { this.removeClass(className); }
			) : this;
	},

	setSelection:function (b) {
		if (window.ie || window.opera) this[(b?"remove":"add")+"Event"]("selectstart",$returnFalse);
		if (window.gecko) this.style.MozUserSelect=b?"":"none";
		if (window.webkit) this.style.KhtmlUserSelect=b?"":"none";
		return this;
	},
	
	removeTextNodes:function () {
		if (this._removedTextNodes) return;
		$A(this.childNodes).each(function (o) {
			if (o.nodeType===3) {
				if (/^\s+$/.test(o.data)) o.parentNode.removeChild(o);
			}
			else $(o).removeTextNodes();
		});
		this._removedTextNodes=true;
		return this;
	},

	removeChildren:function () {
		while (this.hasChildNodes()) this.removeChild(this.lastChild);
		return this;
	},

	findParent:function (predicate) {
		var el=this;
		while (!predicate($(el)) && el && el!=document.documentElement) el=el.parentNode;
		return el===document.documentElement ? null : el;
	},

	replaceClass:function (oldClass,newClass) {
		this.removeClass(oldClass).addClass(newClass);
	},

	retrieveOrStore:function (key,getValueFunction) {
		if (!this.retrieve(key)) this.store(key,getValueFunction.call(this));
		return this.retrieve(key);
	},

	addReplacingEvent:function (type,fn) {
		return this.addEvent(type,function (e) {
			e.stop();
			fn.call(this,e);
		});
	}
});

Element.Properties.extend({
	visibility:{
		set:function (visible) {
			if (visible==="toggle") {
				this.set("visibility",!this.get("visibility"));
				return;
			}
			if (visible) this.removeClass("visibility-hidden").setStyle("visibility","");
			else this.addClass("visibility-hidden").setStyle("visibility","hidden");
		},
		get:function () {
			// check all parents for one that's not visible
			var p=this;
			while (p && p.get("tag")!="body" && p.getStyle("display")!="none" && p.getStyle("visibility")!="hidden") {
				p=p.getParent();
			}
			return p && p.get("tag")=="body";
		}
	},
	display:{
		set:function (visible) {
			if (visible==="toggle") {
				this.set("display",!this.get("display"));
				return;
			}
			if (visible) this.removeClass("display-none").setStyle("display","");
			else this.addClass("display-none").setStyle("display","none");
		},
		get:function () {
			return this.get("visibility");
		}
	}
});

Element.fromMarkup=function (markup,multipleElements) {
	var div=new Element("span").setHTML(markup.trim());
	return multipleElements ? div.getChildren() : div.getFirst();
};

String.implement({
	escapeHtml:function (isHtml) { 
		var str=this;
		if (!isHtml) str=str.replace(/>/g,"&gt;").replace(/</g,"&lt;");
		else str=str.replace(/\r?\n/g,"<br/>");
		str=str.replace(/"/g,"&quot;").replace(/'/g,"&#39;");
		return str;
	}
});

Events.makeObjectEventable=function (obj) {
	$extend(obj,Events.prototype);
};
Options.makeObjectOptionable=function (obj) {
	$extend(obj,Options.prototype);
};
Options.makeClassOptionable=function (cls) {
	cls.setOptions=function () {
		Options.prototype.setOptions.apply(cls.prototype,arguments);
	};
};

