<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Tim Wickstrom Blog</title>
	<atom:link href="http://www.timwickstrom.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.timwickstrom.com</link>
	<description>serving up clean code daily, since 2004</description>
	<lastBuildDate>Sun, 18 Jan 2015 19:53:50 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.7</generator>
	<item>
		<title>Beats Music Desktop App</title>
		<link>http://www.timwickstrom.com/client-side-code/beats-music-desktop-app/</link>
		<comments>http://www.timwickstrom.com/client-side-code/beats-music-desktop-app/#comments</comments>
		<pubDate>Sun, 18 Jan 2015 19:27:39 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[Client Side Code]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=295</guid>
		<description><![CDATA[I love Beats Music, I love their mobile app but as a Developer I wanted a Mac OS X desktop app so while I am developing I can enjoy my tunes without worrying about closing chrome. Unfortunately Dr. Dre and now Apple didn&#8217;t agree and have not launched an App. So I created it for them&#8230; Your welcome Apple. Download the app here: Beats Music App Unzip move to your applications folder and enjoy! Requires [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>I love Beats Music, I love their mobile app but as a Developer I wanted a Mac OS X desktop app so while I am developing I can enjoy my tunes without worrying about closing chrome.</p>
<p>Unfortunately Dr. Dre and now Apple didn&#8217;t agree and have not launched an App. So I created it for them&#8230; <em>Your welcome Apple</em>.</p>
<p>Download the app here: <a href="http://www.timwickstrom.com/wp-content/uploads/2015/01/beats-music-mac-app.zip">Beats Music App</a></p>
<p>Unzip move to your applications folder and enjoy! Requires 10.7+</p>
<blockquote><p>If this is the first time you have downloaded an app outside of the app store you will have to ctrl+click then click open the first time you launch the app.</p></blockquote>
<p>So if you have not checked out <a href="https://github.com/nwjs/nw.js/">Node Webkit</a> (<a href="https://github.com/nwjs/nw.js/">NW.js</a>) for creating desktop apps I would highly recommend taking a look.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/beats-music-desktop-app/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MooTools FullScreen API Wrapper</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/mootools-fullscreen-api-wrapper/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/mootools-fullscreen-api-wrapper/#comments</comments>
		<pubDate>Wed, 18 Apr 2012 20:45:22 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=271</guid>
		<description><![CDATA[The other day I released a quick method to extend the full screen API to any DOM element thats supports full screen. (view) Today I am pleased to release a fully functional API wrapper that supports events and maintains its state as well as merges the F11 key with the api all with an easy to use mootools class. Skip to the demo The Class onReady JS CSS The HTML View the demo]]></description>
				<content:encoded><![CDATA[<p>The other day I released a quick method to extend the full screen API to any DOM element thats supports full screen. (<a href="http://www.timwickstrom.com/client-side-code/mootools/cross-browser-full-screen-with-javascript-mootools/">view</a>)</p>
<p>Today I am pleased to release a fully functional API wrapper that supports events and maintains its state as well as merges the F11 key with the api all with an easy to use mootools class. <a href="http://www.timwickstrom.com/demo/mootools/fullscreen/">Skip to the demo</a></p>
<p><strong>The Class</strong></p>
<pre class="brush: jscript; title: ; notranslate">
/**
 * FullScreen
 * A Mootools wrapper of the FullScreen API.
 *
 * @copyright Copyright (c) 2011, Tim Wickstrom
 * @link http://www.timwickstrom.com Tim Wickstrom Blog
 * @version 0.2
 *
 * @License
 * This content is released under the (http://www.opensource.org/licenses/MIT) MIT License.
 *
 * @Attribution not required (but appreciated)
 * 
 * @Changelog
 * Version 0.2
 * 		- Fixed IE F11/Click Event Bug
 * Version 0.1
 *      - Initial Release
**/

var FullScreen = new Class({
	
	Implements: [Options, Events],	
	
	options: {
		/*	AVAILABLE EVENTS:
			onOpen: function(){},
			onClose: function(){},
			onBeforeStart: function(){},
			onDisabled: function(securityPrevention){}
		*/
		opener: '', // id (str) - required
		closer: '', // id (str) - optional
		target: '', // id (str) - optional default document.body
		ie: true, // true/false (boolean) - OLD IE support
		forceFull: false, // true/false (boolean) - forces the full screen target to be 100% width and 100% height default false
		classes: 'fullscreen' // adds classname to HTML element on full screen optional See CSS for more info
	},

	/* Initializes the Class */
	initialize: function(opener, closer, options) {
		// Set our options
		this.setOptions(typeOf(closer)=='object'?closer:options);
		
		// Lets try and rememeber our state
		this.state = 'normal';
		
		this.opener = document.id(opener) || false;
		
		this.closer = document.id(closer) || false;
		
		// Cache some variables
		this.target = document.id(this.options.target || document.body);
		this.html = document.id(document.html);
		
		// THIS IS BAD... and will need to be reworked but easiest way to know what we are dealing with
		this.types = {
			moz: {
				change: 'mozfullscreenchange',
				full: 'mozRequestFullScreen',
				cancel: 'mozCancelFullScreen',
				state: 'mozFullScreen'
			},
			webkit: {
				change: 'webkitfullscreenchange',
				full: 'webkitRequestFullScreen',
				cancel: 'webkitCancelFullScreen',
				state: 'webkitIsFullScreen'
			},
			w3c: {
				change: 'fullscreenchange',
				full: 'requestFullscreen',
				cancel: 'exitFullscreen',
				state: 'fullscreen'
			}
		}
		
		// If IE and OLD IE is supported lets test it out and target is IE
		if(this.options.ie &amp;&amp; this.target == document.body &amp;&amp; typeof window.ActiveXObject != &quot;undefined&quot;) {
			try {
				this.wscript = new ActiveXObject(&quot;WScript.Shell&quot;);
			} catch(e){
				this.wscript = false;
			}	
		}
		
		// OK Lets see what we are dealing with
		this.client = this.test();
		
		// If were are enabled lets GO!
		if(this.client.enabled) {
			if(this.opener) {
				this.addOpener(this.opener);
			}
			if(this.closer) {
				this.addCloser(this.closer);
			}
			// Only firefox will force full mode on elements (not sure if this is good, prob not for videos, but we can easily extend te everyone if we want to)
			if(this.options.forceFull) {
				this.target.store('prevStyle',this.target.getStyles('margin', 'padding', 'width', 'height', 'border'));
			}
		} else {
			// Add Class of disabled to our buttons if full screen is not supported
			if(this.opener) {
				this.opener.addClass('disabled');	
			}
			if(this.closer) {
				this.opener.addClass('disabled');	
			}
			
			// Nothing to do here but lets provide a method to return to the programmer
			this.state = 'disabled';
			this.fireEvent('disabled', !this.client.enabled &amp;&amp; this.client.oldIE ? true : false);
		}
		
		// Lets make sure to support native full screen functionality
		document.id(document).addEvent('keydown', function(e) {
			if(e.code==122 &amp;&amp; this.client.enabled) {
				if(this.options.ie &amp;&amp; Browser.ie) {
					this.state = 'fullscreen';
					this._set();
				} else {
					if(this.state == 'normal') {
						e.stop();
						this.opener.fireEvent('click');
					} else if(this.closer) {
						e.stop();
						this.closer.fireEvent('click');
					}
				}
			}
		}.bind(this));
		
		// browsers disable escape key detection when in fullscreen so lets!
		if(this.client.html5) { 
			var self = this;
			document.addEventListener(self.types[self.client.prefix].change, function (e) {
				if(!document[self.types[self.client.prefix].state] &amp;&amp; self.state=='fullscreen') {
					self.state = 'normal';
					self._remove();
				}
			}, false);
		}
	},
	_set: function() {
		if(this.options.ie &amp;&amp; Browser.ie &amp;&amp; !this.ieTimer) {
			this.ieTimer = this.ieMonitor.periodical(100,this);
		}
		this.html.addClass(this.options.classes);
		if(this.options.forceFull) {
			this.target.setStyles({
				border: 'none',
				margin: '0',
				padding: '0',
				width: '100%',
				height: '100%'
			});
		}
		this.fireEvent('open');
	},
	_remove: function() {
		this.html.removeClass(this.options.classes);
		if(this.options.forceFull) {
			this.target.setStyles(this.target.retrieve('prevStyles'));
		}
		this.fireEvent('close');	
	},
	open: function() {
		if(this.state != 'fullscreen') {
			this.state = 'fullscreen';
			if(this.client.html5) {
				this.fireEvent('beforeStart');
				this.target[this.types[this.client.prefix].full].call(this.target);
				this.state = 'fullscreen';
			} else if(this.options.ie &amp;&amp; Browser.ie) {
				this.fireEvent('beforeStart');
				this.wscript.SendKeys(&quot;{F11}&quot;);
			}
			if(this.state=='fullscreen') {
				this._set();
			}
		}
	},
	close: function() {
		if(this.state != 'normal') {
			if(this.client.html5) {
				document[this.types[this.client.prefix].cancel].call(document);
				this.state = 'normal';
			} else if(this.options.ie &amp;&amp; Browser.ie) {
				this.state = 'normal';
				this.wscript.SendKeys(&quot;{F11}&quot;);
				this.ieClick = true;
			}
			if(this.state=='normal') {
				this._remove();
			}
		}
	},
	addOpener: function(str) {
		return document.id(str) != null ? document.id(str).addEvent(&quot;click&quot;, this.open.bind(this)) : false;
	},
	addCloser: function(str) {
		return document.id(str) != null ? document.id(str).addEvent(&quot;click&quot;, this.close.bind(this)) : false;
	},
	test: function() {
		return {
			enabled: (this.target.requestFullscreen || this.target.mozRequestFullScreen || this.target.webkitRequestFullScreen || this.target.msRequestFullScreen) || (this.options.ie &amp;&amp; this.wscript !== false) ? true : false,
			html5: (this.target.requestFullscreen || this.target.mozRequestFullScreen || this.target.webkitRequestFullScreen || this.target.msRequestFullScreen) ? true : false,
			oldIE: Browser.ie ? true : false,
			prefix: this.target.requestFullscreen ? 'w3c' : this.target.mozRequestFullScreen ? 'moz' : this.target.webkitRequestFullScreen ? 'webkit' : ''
		}
		
	},
	ieMonitor: function() {
		var size = document.id(document.body).getSize();
		var scrollSize = document.id(document.body).getScrollSize();
		var statusSize = 23;
		if(this.state == 'fullscreen' &amp;&amp; ((size.y + statusSize + (size.x==scrollSize.x ? 0 : 15)) &lt; window.screen.availHeight)) {
			clearInterval(this.ieTimer);
			this.ieTimer = false;
			this.state = 'normal';
			this._remove();
		}
	}
});
</pre>
<p><strong>onReady JS</strong></p>
<pre class="brush: jscript; title: ; notranslate">
window.addEvent('domready' , function(){
	var full = new FullScreen(&quot;view-fullscreen&quot;,&quot;close-fullscreen&quot;,{
		onOpen: function() {
			// this refers to calling class instance
			console.log('callback open');
		},
		onClose: function() {
			// this refers to calling class instance
			console.log('callback close');
		}
	});
});
</pre>
<p><strong>CSS</strong></p>
<pre class="brush: css; title: ; notranslate">
/****
	The class will add a class of fill screen to the HTML tag for easy referencing of body atributes when in full screen mode (even in IE)
	However the fullscreen specification adds the following pseudo-classes:
	html:-moz-full-screen {
	
	}

	html:-webkit-full-screen {
		background: red;
	}

	html:fullscreen {
		background: red;
	}
****/
.fullscreen body {
	background: #a7a7a7;
}
</pre>
<p><strong>The HTML</strong></p>
<pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;view-fullscreen&quot;&gt;Open Full Screen&lt;/div&gt;
&lt;div id=&quot;close-fullscreen&quot;&gt;Exit Full Screen - optional&lt;/div&gt;
</pre>
<p><a href="http://www.timwickstrom.com/demo/mootools/fullscreen/">View the demo</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/mootools-fullscreen-api-wrapper/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Cross Browser Full Screen with JavaScript &#8211; mooTools</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/cross-browser-full-screen-with-javascript-mootools/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/cross-browser-full-screen-with-javascript-mootools/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 17:32:10 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=262</guid>
		<description><![CDATA[While visiting Facebook today and viewing a photo gallery I noticed they have included full screen viewing with a simple mouse click. First of all hats off to Facebook I firmly believe they have some of the best JavaScript engineers around. I absolutely love the current version of their site! So the full screen version was very intriguing to me as a couple months back I remembered needing something similar for a client. So I [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>While visiting Facebook today and viewing a photo gallery I noticed they have included full screen viewing with a simple mouse click. First of all hats off to Facebook I firmly believe they have some of the best JavaScript engineers around. I absolutely love the current version of their site! </p>
<p>So the full screen version was very intriguing to me as a couple months back I remembered needing something similar for a client. So I decided to dig deeper.</p>
<p>Imagine doing something as simple as:</p>
<pre class="brush: jscript; title: ; notranslate">
window.addEvent('domready' , function(){
	document.id(&quot;view-fullscreen&quot;).addEvent(&quot;click&quot;, function(){
		document.id(document.body).toFullScreen();
	});
});
</pre>
<p><strong>Break down:</strong><br />
The above code assumes you have an element with an ID of view-fullscreen. We are adding an onClick event handler to this element which is then requesting the document.body be presented in full screen.</p>
<p>NOTE: Any valid HTML element can be presented in full screen (except in IE < 10 which will only allow the document.body if it has active X enabled).

Simple add this mooTools code after your mootools library to support Element.toFullScreen():

<pre class="brush: jscript; title: ; notranslate">
Element.implement({
	toFullScreen: function(ie){
		fs 		= this.requestFullscreen ||
				  this.mozRequestFullScreen ||
				  this.webkitRequestFullScreen ||
				  this.msRequestFullScreen
		;
		if(fs) {
			fs.call(this);
		}
		else if(ie &amp;&amp; typeof window.ActiveXObject!=&quot;undefined&quot;){
			// for Internet Explorer
			try {
				var wscript = new ActiveXObject(&quot;WScript.Shell&quot;);
				wscript.SendKeys(&quot;{F11}&quot;);
			} catch(e){
				alert(&#8216;Your security settings are preventing full screen access please press F11 on your keyboard.&#8217;);
			}
		}
		return this;
	}
});
</pre>
<p><strong>Syntax:</strong><br />
myElement.toFullScreen(ieSupport);</p>
<p><strong>Arguments:</strong><br />
    ieSupport &#8211; (boolean true/false) Should the script be activated for IE with Active X. Default: false; </p>
<p><strong>Returns:</strong><br />
    (element) This Element.</p>
<p><strong>Still needed/being worked on</strong></p>
<ul>
<li>Exit Fullscreen</li>
<li>Implementing fullscreenchange event function support</li>
</ul>
<p><strong>Sources of inspiration:</strong><br />
<a href="http://hacks.mozilla.org/2012/01/using-the-fullscreen-api-in-web-browsers/">http://hacks.mozilla.org/2012/01/using-the-fullscreen-api-in-web-browsers/</a><br />
<a href="http://robnyman.github.com/fullscreen/">http://robnyman.github.com/fullscreen/</a></p>
<p>For IE6+ support:<br />
<a href="http://stackoverflow.com/users/815724/peter-o">Peter O.</a>&#8216;s Post at: <a href="http://stackoverflow.com/questions/1125084/how-to-make-in-javascript-full-screen-windows-stretching-all-over-the-screen">Stack Overflow</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/cross-browser-full-screen-with-javascript-mootools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dean Edwards Packer exposed.</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/dean-edwards-packer-exposed/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/dean-edwards-packer-exposed/#comments</comments>
		<pubDate>Tue, 10 Apr 2012 18:51:20 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=255</guid>
		<description><![CDATA[Have you ever packed code and misplaced the original? Sadly, so have I. Well after really looking at the packer for the first time the solution to decode it is painfully easy! The secret lies in the eval() function That is the function that allows the JavaScript engine to make sense of the code from Dean Edwards packer function. Replacing that function is what will allow us to make sense of the code. So using [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Have you ever <em>packed</em> code and <em>misplaced</em> the original? Sadly,<strong> so have I</strong>.</p>
<p>Well after really looking at the packer for the first time the solution to decode it is painfully easy!</p>
<p>The secret lies in the eval() function</p>
<p>That is the function that allows the JavaScript engine to make sense of the code from Dean Edwards packer function. Replacing that function is what will <strong>allow us to make sense of the code</strong>.</p>
<p>So using JavaScript (mootools in this example) we can do something super simple such as:</p>
<p>Prep Work</p>
<pre class="brush: jscript; title: ; notranslate">
/**
  * Open your packed code in your fav IDE.
  * Remove the opening eval(
  * than at the end of the string remove the );
  * copy what is left. (minus any comments, other JS functions etc...)
**/
</pre>
<p>Core HTML</p>
<pre class="brush: xml; title: ; notranslate">
&lt;textarea id=&quot;decoded&quot;&gt;&lt;/textarea&gt;
</pre>
<p>Core Javascript (mooTools)</p>
<pre class="brush: jscript; title: ; notranslate">
var decoder = function() {
    $('decoded').set('value', PASTE_YOUR_CODE_HERE);
}

window.addEvent('domready' , decoder);
</pre>
<p><strong>Run the code and behold your unPacked Javascript!</strong></p>
<p>Complete code example:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!doctype html&gt;
&lt;head&gt;
	&lt;title&gt;Javascript UnPacker&lt;/title&gt;
	&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed.js&quot;&gt;&lt;/script&gt;
	&lt;script&gt;
		var decoder = function() {
		    $('decoded').set('value', PASTE_YOUR_CODE_HERE);
		}
		window.addEvent('domready' , decoder);
	&lt;/script&gt;
	&lt;style&gt;
		body {
			margin:0;
			padding:0;
		}
		textarea {
			display: block;
			height: 500px;
			margin: 12px auto;
			width: 80%;
		}
		p {
			text-align: center;
		}
	&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;p&gt;Copy your code below and use it here: &lt;a href=&quot;http://jsbeautifier.org/&quot;&gt;Beautify JavaScript&lt;/a&gt;
	&lt;textarea id=&quot;decoded&quot;&gt;&lt;/textarea&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/dean-edwards-packer-exposed/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MooTools Element.allProperties</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/mootools-element-allproperties/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/mootools-element-allproperties/#comments</comments>
		<pubDate>Fri, 30 Mar 2012 16:50:33 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=246</guid>
		<description><![CDATA[So today I needed a way to get all of an Elements Attributes. I was limited to get() and getProperties() both require the name of the attribute or property you are after such as src or text or rel&#8230; you get the idea. So simply created my own implementation on the Element Class call allProperties. This will allow you to take any element and return all attributes as well as text and html if present [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>So today I needed a way to get all of an Elements Attributes.</p>
<p>I was limited to get() and getProperties() both require the name of the attribute or property you are after such as src or text or rel&#8230; you get the idea.</p>
<p>So simply created my own implementation on the Element Class call allProperties.</p>
<p>This will allow you to take any element and return all attributes as well as text and html if present as an object.</p>
<p>The Mootools Code:</p>
<pre class="brush: jscript; title: ; notranslate">
Element.implement({
    allProperties: function(){
        for (var attr, j = this.get([&quot;text&quot;,&quot;html&quot;]), i = 0, len = this.attributes.length; i &lt; len; ++i) {
            attr = this.attributes[i];
            j[attr['nodeName']] = attr['nodeValue'];
        };
        return j;
    }
});
</pre>
<p><strong>Syntax:</strong></p>
<pre class="brush: jscript; title: ; notranslate">var myProps = myElement.allProperties();</pre>
<p><strong>Arguments:</strong></p>
<p>    none</p>
<p><strong>Example:</strong><br />
HTML</p>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;me&quot; rel=&quot;you&quot; class=&quot;test&quot;&gt;&lt;span&gt;what?&lt;/span&gt;&lt;/div&gt;</pre>
<p>JS</p>
<pre class="brush: jscript; title: ; notranslate">$('me').allProperties()</pre>
<p>Returns</p>
<pre class="brush: jscript; title: ; notranslate">
{
	class:	&quot;test&quot;,	
	html:	&quot;&lt;span&gt;what?&lt;/span&gt;&quot;,
	id:	&quot;me&quot;,
	rel:	&quot;you&quot;,
	text:	&quot;what?&quot;
}
</pre>
<p>See it in action <a href="http://jsfiddle.net/KFS6t/3/" title="MooTool Element.allProperties" target="_blank">http://jsfiddle.net/KFS6t/3/</a></p>
<p>Update: 04/04/2012 <a href="http://jsfiddle.net/KFS6t/6/">http://jsfiddle.net/KFS6t/6/</a></p>
<p><strong>Optimized</strong> for speed by: <strong>Dimitar Christoff</strong> &#8211; <a href="http://fragged.org/">http://fragged.org/</a></p>
<p><a href="http://jsperf.com/allprops">http://jsperf.com/allprops</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/mootools-element-allproperties/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MooTools Roar Mac Style Notification System</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/mootools-roar-mac-style-notification-system/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/mootools-roar-mac-style-notification-system/#comments</comments>
		<pubDate>Fri, 09 Mar 2012 23:09:15 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=241</guid>
		<description><![CDATA[OK, so in the beginning&#8230; way back when&#8230; I was one of those programmers that thought JS was just a toy and nothing more&#8230; boy oh boy was I missing the boat! One of the craftsman that influenced me the most was Harald Kirschner of http://digitarald.de the guy is amazing. I have used many of his mooTools classes on numerous projects and more than that it gave me a point of reference when I started [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>OK, so in the beginning&#8230; way back when&#8230; I was one of those programmers that thought JS was just a toy and nothing more&#8230; boy oh boy was I missing the boat!</p>
<p>One of the craftsman that influenced me the most was Harald Kirschner of <a href="http://digitarald.de">http://digitarald.de</a> the guy is amazing. I have used many of his mooTools classes on numerous projects and more than that it gave me a point of reference when I started writing my own classes.</p>
<p>Today I needed Roar for a project, however I have moved on to mooTools 1.4+ so I am please to release the mooTools 1.4+ compatible code base (mooTools compatibility mode NOT required).</p>
<p>Oh and by the way, am I glad I jumped on that Boat, my life has not been the same over the last 5 years!</p>
<pre class="brush: jscript; title: ; notranslate">
/**
 * Roar - Notifications
 *
 * Inspired by Growl
 *
 * @version		1.0.2
 *
 * @license		MIT-style license
 * @author		Harald Kirschner &lt;mail [at] digitarald.de&gt;
 * @copyright	Author
 * @update		Tim Wickstrom
 * @date		03/19/2012
 */

var Roar = new Class({

	Implements: [Options, Events, Chain],

	options: {
		duration: 10000,
		position: 'lowerLeft',
		container: null,
		bodyFx: {},
		itemFx: {},
		margin: {x: 0, y: 46},
		offset: 10,
		className: 'roar',
		adjust: false
		/****, Available Events
			onShow: function(){},
			onHide:  function(){},
			onRender:  function(){}
		****/
	},

	initialize: function(options) {
		this.setOptions(options);
		this.items = [];
		this.container = document.id(this.options.container) || document;
	},

	alert: function(title, message, options) {
		var items = [new Element('h3', { 'html': title ? title.clean() : '' })];
		if (message) items.push(new Element('p', {'html': message.clean() }));
		return this.inject(items, typeOf(options)=='object'?options:{});
	},

	inject: function(elements, options) {
		if (!this.body) this.render();
		
		var offset = [-this.options.offset, 0];
		var last = this.items.getLast();
		if (last) {
			offset[0] = last.retrieve('roar:offset');
			offset[1] = offset[0] + last.offsetHeight + this.options.offset;
		}
		var to = {'opacity': 1};
		to[this.align.y] = offset;
		var item = new Element('div', {
			'class': options.className || this.options.className,
			styles: {
				'opacity': 0
			}
		}).adopt(
			new Element('div', {
				'class': 'roar-bg',
				'opacity': 0.7
			}),
			elements
		);

		item.setStyle(this.align.x, 0).store('roar:offset', offset[1]).set('morph', Object.merge({
			link: 'cancel',
			transition: Fx.Transitions.Back.easeOut
		}, this.options.itemFx));

		var remove = this.remove.create({
			bind: this,
			arguments: [item],
			delay: 10
		});
		
		this.items.push(item.addEvent('click', remove));

		if (this.options.duration) {
			var over = false;
			var trigger = (function() {
				trigger = null;
				if (!over) remove();
			}).delay(this.options.duration);
			item.addEvents({
				mouseover: function() {
					over = true;
				},
				mouseout: function() {
					over = false;
					if (!trigger) remove();
				}
			});
		}
		this.show(item,this.body,to)
		return this.fireEvent('show', [item, this.items.length]);
	},

	show: function(i,b,t) {
		i.inject(b).morph(t);
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		if (index == -1) return this;
		this.items.splice(index, 1);
		item.removeEvents();
		var to = {opacity: 0};
		to[this.align.y] = item.getStyle(this.align.y).toInt() - item.offsetHeight - this.options.offset;
		item.set('morph', Object.merge(
			{
				onComplete: function() {
					item.destroy();
				}
			},
			item.get('morph')
		)).morph(to);
		if(this.options.adjust) {
			this.adjust(index, item.offsetHeight)
		}
		return this.fireEvent('hide', [item, this.items.length]).callChain(item);
	},
	
	adjust: function(index, offset) {
		this.adjusting = true;
		Array.each(this.items,function(el, i) {
			if(i&gt;=index) {
				var to = {};
				to[this.align.y] = el.getStyle(this.align.y).toInt() - offset - this.options.offset;
				to.onComplete = function() {
					if(this.items.length == i){
						this.adjusting = false;
					}
				}
				el.store('roar:offset', to[this.align.y]).morph(to);
			}
		}.bind(this));
	},
	
	empty: function() {
		while (this.items.length) this.remove(this.items[0]);
		return this;
	},

	render: function() {
		this.adjusting = false;
		this.position = this.options.position;
		if (typeOf(this.position) == 'string') {
			var position = {x: 'center', y: 'center'};
			this.align = {x: 'left', y: 'top'};
			if ((/left|west/i).test(this.position)) position.x = 'left';
			else if ((/right|east/i).test(this.position)) this.align.x = position.x = 'right';
			if ((/upper|top|north/i).test(this.position)) position.y = 'top';
			else if ((/bottom|lower|south/i).test(this.position)) this.align.y = position.y = 'bottom';
			this.position = position;
		}
		this.body = new Element('div', {'class': 'roar-body'}).inject(document.body);
		
		this.moveTo = this.body.setStyles.bind(this.body);
		this.reposition();
		if (this.options.bodyFx) {
			var morph = new Fx.Morph(this.body, Object.merge({
				unit: 'px',
				chain: 'cancel',
				transition: Fx.Transitions.Circ.easeOut
			}, this.options.bodyFx));
			this.moveTo = morph.start.bind(morph);
		}
		var repos = this.reposition.bind(this);
		window.addEvents({
			scroll: repos,
			resize: repos
		});
		this.fireEvent('render', this.body);
	},

	reposition: function() {
		var max = document.getCoordinates(), scroll = document.getScroll(), margin = this.options.margin;
		max.left += scroll.x;
		max.right += scroll.x;
		max.top += scroll.y;
		max.bottom += scroll.y;
		var rel = (typeOf(this.container) == 'element') ? this.container.getCoordinates() : max;
		this.moveTo({
			left: (this.position.x == 'right')
				? (Math.min(rel.right, max.right) - margin.x)
				: (Math.max(rel.left, max.left) + margin.x),
			top: (this.position.y == 'bottom')
				? (Math.min(rel.bottom, max.bottom) - margin.y)
				: (Math.max(rel.top, max.top) + margin.y)
		});
	}

});
</pre>
<p>Documentation can still be found at <a href="http://digitarald.de/project/remooz/">http://digitarald.de/project/remooz/</a></p>
<p>I have added an experimental feature that is still in testing. A method called adjust. Its purpose is to slide all open roars back to place when one gets removed. Again this is still being tested but feel free to play with it and send any feedback.</p>
<p>Thanks</p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/mootools-roar-mac-style-notification-system/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MooTools String.isJSON</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/mootools-string-isjson/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/mootools-string-isjson/#comments</comments>
		<pubDate>Tue, 21 Feb 2012 21:30:47 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=237</guid>
		<description><![CDATA[Today I had a need to quickly test if a string was valid JSON and return a simple boolean Tested with mootools 1.4+]]></description>
				<content:encoded><![CDATA[<p>Today I had a need to quickly test if a string was valid JSON and return a simple boolean</p>
<p>Tested with mootools 1.4+</p>
<pre class="brush: jscript; title: ; notranslate">String.implement({
	isJSON: function() {
		return (!this || typeOf(this) != 'string' || !JSON.validate(this)) ? false : true;
	}
});
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/mootools-string-isjson/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Element to JSON</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/element-to-json/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/element-to-json/#comments</comments>
		<pubDate>Wed, 09 Nov 2011 23:29:38 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=235</guid>
		<description><![CDATA[Wouldn&#8217;t it be neat to simply take all fields from a form or any other element for that matter and create a JSON object? Well with this simple block of code below now you can do something like: And there you have it a nice neat JSON Object! The MooTools Code: Simply add that after you include mooTools core on your page, or better yet add it to your utilities file that you take with [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Wouldn&#8217;t it be neat to simply take all fields from a form or any other element for that matter and create a JSON object?</p>
<p>Well with this simple block of code below now you can do something like:</p>
<pre class="brush: jscript; title: ; notranslate">
console.log($('myElement').toJSON());
</pre>
<p>And there you have it a nice neat JSON Object!</p>
<p>The MooTools Code:</p>
<pre class="brush: jscript; title: ; notranslate">
Element.implement({
	toJSON: function(){
		var j = {};
		Array.each(this.toQueryString().split('&amp;'),function(i){
			var a = i.split('=')
			j[a[0]] = a[1]||'';
		});
		return JSON.encode(j);
	}
});
</pre>
<p>Simply add that after you include mooTools core on your page, or better yet add it to your utilities file that you take with you to every project!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/element-to-json/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mooFloor &#8211; FormCheck for mootools 1.4.x</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/moofloor-formcheck-for-mootools-1-4-x/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/moofloor-formcheck-for-mootools-1-4-x/#comments</comments>
		<pubDate>Fri, 04 Nov 2011 18:46:01 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=229</guid>
		<description><![CDATA[mooFloor FormCheck for mootools 1.4.x I love FormCheck by mooFloor.ch! However I have been using mootools in compatabilty mode for the last couple projects as it uses a few 1.2 classes and utilities from core. The script would also depend on Fx.Scroll. I finally took the time to correct these issues and present to you the updated Class. NOTE: if Fx.Scroll is present and scroll to first is activated it will still scroll with class! [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>mooFloor FormCheck for mootools 1.4.x</p>
<p>I love FormCheck by <a href="http://mootools.floor.ch/docs/formcheck" title="FormCheck by mooFloor">mooFloor.ch</a>!</p>
<p>However I have been using mootools in compatabilty mode for the last couple projects as it uses a few 1.2 classes and utilities from core.</p>
<p>The script would also depend on Fx.Scroll.</p>
<p>I finally took the time to correct these issues and present to you the updated Class.</p>
<p>NOTE: if Fx.Scroll is present and scroll to first is activated it will still scroll with class! If you find anything I missed or experience an error let me know and I will update the code.</p>
<p>Detailed documentation can be found at <a href="http://mootools.floor.ch/docs/formcheck" title="FormCheck by mooFloor">mooFloor.ch</a></p>
<pre class="brush: jscript; title: ; notranslate">
/*
	Class: FormCheck
		Performs different tests on forms and indicates errors.

	Usage:
		Works with these types of fields :
		- input (text, radio, checkbox)
		- textarea
		- select

		You just need to add a specific class to each fields you want to check.
		For example, if you add the class
			(code)
			validate['required','length[4, -1]','differs:email','digit']
			(end code)
		the value's field must be set (required) with a minimum length of four chars (4, -1),
		must differs of the input named email (differs:email), and must be digit.

		You can perform check during the datas entry or on the submit action, shows errors as tips or in a div before or after the field,
		show errors one by one or all together, show a list of all errors at the top of the form, localize error messages, add new regex check, ...

		The layout is design only with css. Now I added a hack to use transparent png with IE6, so you can use png images in formcheck.css (works only for theme, so the file must be named formcheck.css). It can also works with multiple forms on a single html page.
		The class supports now internationalization. To use it, simply specify a new &lt;script&gt; element in your html head, like this : &lt;script type=&quot;text/javascript&quot; src=&quot;formcheck/lang/fr.js&quot;&gt;&lt;/script&gt;.

		If you add the class
			(code)
			validate['submit']
			(end code)
		to an element like an anchor (or anything else), this element will act as a submit button.

		N.B. : you must load the language script before the formcheck and this method overpass the old way. You can create new languages following existing ones. You can otherwise still specifiy the alerts' strings when you initialize the Class, with options.
		If you don't use a language script, the alert will be displayed in english.

	Test type:
		You can perform various test on fields by adding them to the validate class. Be careful to *not use space chars*. Here is the list of them.

		required 					- The field becomes required. This is a regex, you can change it with class options.
		alpha 						- The value is restricted to alphabetic chars. This is a regex, you can change it with class options.
		alphanum 					- The value is restricted to alphanumeric characters only. This is a regex, you can change it with class options.
		nodigit 					- The field doesn't accept digit chars. This is a regex, you can change it with class options.
		digit 						- The value is restricted to digit (no floating point number) chars, you can pass two arguments (f.e. digit[21,65]) to limit the number between them. Use -1 as second argument to set no maximum.
		number 						- The value is restricted to number, including floating point number. This is a regex, you can change it with class options.
		email 						- The value is restricted to valid email. This is a regex, you can change it with class options.
		image						- The value is restricted to images (jpg, jpeg, png, gif, bmp). This is a regex, you can change it with class options.
		phone 						- The value is restricted to phone chars. This is a regex, you can change it with class options.
		phone_inter					- The value is restricted to international phone number. This is a regex, you can change it with class options.
		url: 						- The value is restricted to url. This is a regex, you can change it with class options.
		confirm 					- The value has to be the same as the specified. f.e. confirm:password.
		differs 					- The value has to be diferent as the one specifies. f.e. differs:user.
		length 						- The value length is restricted by argument (f.e. length[6,10]). Use -1 as second argument to set no maximum.
		group						- Use to validate several checkboxes as a group. Requires 2 arguments, the second one being optional (1 by default): the group id and the minimum amount of boxes to check. The second argument may be set on any or all items of the group. See example below.
		words						- The words number is limited by arguments. f.e. words[1,13]. Use -1 as second argument to don't have a max limit.
		target						- It's not really a validation test, but it allows you to attach the error message to an other element, usefull if the input you validate is hidden. You must specifiy target id, f.e. target:myDiv.

		You can also use a custom function to check a field. For example, if you have a field with class
			(code)
			validate['required','%customCheck']
			(end code)
		the function customCheck(el) will be called to validate the field. '%customcheck' works with other validate(s) together, and '~customcheck' works if the element pass the other validate(s).
		Here is an example of what customCheck could look :
			(code)
			function customCheck(el){
				if (!el.value.test(/^[A-Z]/)) {
					el.errors.push(&quot;Username should begin with an uppercase letter&quot;);
					return false;
				} else {
					return true;
				}
			}
			(end code)

		To validate checkoxes group, you could make something like :
			(code)
				&lt;input type=&quot;checkbox&quot; name=&quot;dog&quot; class=&quot;validate['group[1,2]']&quot;&gt;
				&lt;input type=&quot;checkbox&quot; name=&quot;cat&quot; class=&quot;validate['group[1]']&quot;&gt;
				&lt;input type=&quot;checkbox&quot; name=&quot;mouse&quot; class=&quot;validate['group[1]']&quot;&gt;
			(end code)
		For checkboxes from group 1, you will need to check at least 2 boxes.

		It is now possible to register new fields after a new FormCheck call by using &lt;FormCheck::register&gt; (see &lt;FormCheck::dispose&gt; too). You need first to add the validate class to the element you want to register ( $('myInput').addClass(&quot;validate['required']&quot;) ).

	Parameters:
		When you initialize the class with addEvent, you can set some options. If you want to modify regex, you must do it in a hash, like for display or alert. You can also add new regex check method by adding the regex and an alert with the same name.

		Required:

			form_id - The id of the formular. This is required.

		Optional:

			submit					- If you turn this option to false, the FormCheck will only perform a validation, without submitting the form, even on success. You can use validateSuccess event to execute some code.

			submitByAjax 			- you can set this to true if you want to submit your form with ajax. You should use provided events to handle the ajax request (see below). By default it is false.
			ajaxResponseDiv 		- id of element to inject ajax response into (can also use onAjaxSuccess). By default it is false.
			ajaxEvalScripts 		- use evalScripts in the Request response. Can be true or false, by default it is false.
			onAjaxRequest 			- Function to fire when the Request event starts.
			onAjaxComplete 			- Function to fire when the Request event completes regardless of and prior to Success or Failure.
			onAjaxSuccess 			- Function to fire when the Request receives .  Args: response [the request response] - see Mootools docs for Request.onSuccess.
			onAjaxFailure 			- Function to fire if the Request fails.

			onSubmit				- Function to fire when form is submited (so before validation)
			onValidateSuccess 		- Function to fire when validation pass (you should prevent form submission with option submit:false to use this)
			onValidateFailure		- Function to fire when validation fails

			tipsClass 				- The class to apply to tipboxes' errors. By default it is 'fc-tbx'.
			errorClass 				- The class to apply to alertbox (not tips). By default it is 'fc-error'.
			fieldErrorClass 		- The class to apply to fields with errors, except for radios. You should also turn on  options.addClassErrorToField. By default it is 'fc-field-error'

			trimValue				- If set to true, strip whitespace (or other characters) from the beginning and end of values. By default it is false.
			validateDisabled		- If set to true, disabled input will be validated too, otherwise not.

		Display:
			This is a hash of display settings. in here you can modify.

			showErrors 				- 0 : onSubmit, 1 : onSubmit &amp; onBlur, by default it is 0.
			titlesInsteadNames		- 0 : When you do a check using differs or confirm, it takes the field name for the alert. If it's set to 1, it will use the title instead of the name.
			errorsLocation 			- 1 : tips, 2 : before, 3 : after, by default it is 1.
			indicateErrors 			- 0 : none, 1 : one by one, 2 : all, by default it is 1.
			indicateErrorsInit		- 0 : determine if the form must be checked on initialize. Could be usefull to force the user to update fields that don't validate.
			keepFocusOnError 		- 0 : normal behaviour, 1 : the current field keep the focus as it remain errors. By default it is 0.
			checkValueIfEmpty 		- 0 : When you leave a field and you have set the showErrors option to 1, the value is tested only if a value has been set. 1 : The value is tested  in any case.  By default it is 1.
			addClassErrorToField 	- 0 : no class is added to the field, 1 : the options.fieldErrorClass is added to the field with an error (except for radio). By default it is 0.
			removeClassErrorOnTipClosure - 0 : Error class is kept when the tip is closed, 1 : Error class is removed when the tip is closed

			fixPngForIe 			- 0 : do nothing, 1 : fix png alpha for IE6 in formcheck.css. By default it is 1.
			replaceTipsEffect 		- 0 : No effect on tips replace when we resize the broswer, 1: tween transition on browser resize;
			closeTipsButton 		- 0 : the close button of the tipbox is hidden, 1 : the close button of the tipbox is visible. By default it is 1.
			flashTips 				- 0 : normal behaviour, 1 : the tipbox &quot;flash&quot; (disappear and reappear) if errors remain when the form is submitted. By default it is 0.
			tipsPosition 			- 'right' : the tips box is placed on the right part of the field, 'left' to place it on the left part. By default it is 'right'.
			tipsOffsetX 			- Horizontal position of the tips box (margin-left), , by default it is 100 (px).
			tipsOffsetY				- Vertical position of the tips box (margin-bottom), , by default it is -10 (px).

			listErrorsAtTop 		- List all errors at the top of the form, , by default it is false.
			scrollToFirst 			- Smooth scroll the page to first error and focus on it, by default it is true.
			fadeDuration 			- Transition duration (in ms), by default it is 300.

		Alerts:
			This is a hash of alerts settings. in here you can modify strings to localize or wathever else. %0 and %1 represent the argument.

			required 				- &quot;This field is required.&quot;
			alpha 					- &quot;This field accepts alphabetic characters only.&quot;
			alphanum 				- &quot;This field accepts alphanumeric characters only.&quot;
			nodigit 				- &quot;No digits are accepted.&quot;
			digit 					- &quot;Please enter a valid integer.&quot;
			digitmin 				- &quot;The number must be at least %0&quot;
			digitltd 				- &quot;The value must be between %0 and %1&quot;
			number 					- &quot;Please enter a valid number.&quot;
			email 					- &quot;Please enter a valid email: &lt;br /&gt;&lt;span&gt;E.g. yourname@domain.com&lt;/span&gt;&quot;
			phone 					- &quot;Please enter a valid phone.&quot;
			phone_inter 			- &quot;Please enter a valid international phone number.&quot;
			url 					- &quot;Please enter a valid url: &lt;br /&gt;&lt;span&gt;E.g. http://www.domain.com&lt;/span&gt;&quot;
			image					- &quot;This field should only contain image types&quot;
			confirm 				- &quot;This field is different from %0&quot;
			differs 				- &quot;This value must be different of %0&quot;
			length_str 				- &quot;The length is incorrect, it must be between %0 and %1&quot;
			length_fix 				- &quot;The length is incorrect, it must be exactly %0 characters&quot;
			lengthmax 				- &quot;The length is incorrect, it must be at max %0&quot;
			lengthmin 				- &quot;The length is incorrect, it must be at least %0&quot;
			words_min				- &quot;This field must concain at least %0 words, now it has %1 words&quot;
			words_range				- &quot;This field must contain between %0 and %1 words, now it has %2 words&quot;
			words_max				- &quot;This field must contain at max %0 words, now it has %1 words&quot;
			checkbox 				- &quot;Please check the box&quot;
			checkboxes_group		- &quot;Please check at least %0 box(es)&quot;
			radios 					- &quot;Please select a radio&quot;
			select 					- &quot;Please choose a value&quot;

	Example:
		You can initialize a formcheck (no scroll, custom classes and alert) by adding for example this in your html head this code :

		(code)
		&lt;script type=&quot;text/javascript&quot;&gt;
			window.addEvent('domready', function() {
				var myCheck = new FormCheck('form_id', {
					tipsClass : 'tips_box',
					display : {
						scrollToFirst : false
					},
					alerts : {
						required : 'This field is ablolutely required! Please enter a value'
					}
				})
			});
		&lt;/script&gt;
		(end code)

	About:
		formcheck.js v.1.6 for mootools v1.2 - 03 / 2010

		by Mootools.Floor (http://mootools.floor.ch) MIT-style license

		Created by Luca Pillonel (luca-at-nolocation.org),
		Last modified by Luca Pillonel

	Credits:
		This class was inspired by fValidator by Fabio Zendhi Nagao (http://zend.lojcomm.com.br)

		Thanks to all contributors from groups.google.com/group/moofloor (and others as well!) providing ideas, translations, fixes and motivation!
*/

var FormCheck = new Class({

	Implements: [Options, Events],

	options : {

		tipsClass : 'fc-tbx',				//tips error class
		errorClass : 'fc-error',			//div error class
		fieldErrorClass : 'fc-field-error',	//error class for elements

		submit : true,						//false : just validate the form and do nothing else. Use onValidateSuccess event to execute some code
		submitAction: false,				//Action page used to submit the form data to.
		submitMethod: false,				//Method used to submit the form, valid options : 'post' or 'get'

		trimValue : false,					//trim (remove whitespaces before and after) the value
		validateDisabled : false,			//skip validation on disabled input if set to false.

		submitByAjax : false,				//false : standard submit way, true : submit by ajax
		ajaxResponseDiv : false,			//element to inject ajax response into (can also use onAjaxSuccess) [cronix]
		ajaxEvalScripts : false,			//use evalScripts in the Request response [cronix]
		onAjaxRequest : function(){},				//Function to fire when the Request event starts
		onAjaxComplete : function(){},			//Function to fire when the Request is complete, before and regardless of Success or Failure
		onAjaxSuccess : function(){},				//Function to fire when the Request receives .  Args: response [the request response] - see Mootools docs for Request.onSuccess
		onAjaxFailure : function(){},				//Function to fire if the Request fails

		onSubmit		  : function(){},			//Function to fire when user submit the form
		onValidateSuccess : function(){},			//Function to fire when validation pass
		onValidateFailure : function(){},			//Function to fire when validation fails

		display : {
			showErrors : 0,
			titlesInsteadNames : 0,
			errorsLocation : 1,
			indicateErrors : 1,
			indicateErrorsInit : 0,
			keepFocusOnError : 0,
			checkValueIfEmpty : 1,
			addClassErrorToField : 0,
			removeClassErrorOnTipClosure : 0,
			fixPngForIe : 1,
			replaceTipsEffect : 1,
			flashTips : 0,
			closeTipsButton : 1,
			tipsPosition : &quot;right&quot;,
			tipsOffsetX : -45,
			tipsOffsetY : 0,
			listErrorsAtTop : false,
			scrollToFirst : true,
			fadeDuration : 300
		},

		alerts : {
			required : &quot;This field is required.&quot;,
			alpha : &quot;This field accepts alphabetic characters only.&quot;,
			alphanum : &quot;This field accepts alphanumeric characters only.&quot;,
			nodigit : &quot;No digits are accepted.&quot;,
			digit : &quot;Please enter a valid integer.&quot;,
			digitltd : &quot;The value must be between %0 and %1&quot;,
			number : &quot;Please enter a valid number.&quot;,
			email : &quot;Please enter a valid email.&quot;,
			image : 'This field should only contain image types',
			phone : &quot;Please enter a valid phone.&quot;,
			phone_inter : &quot;Please enter a valid international phone number.&quot;,
			url : &quot;Please enter a valid url.&quot;,

			confirm : &quot;This field is different from %0&quot;,
			differs : &quot;This value must be different of %0&quot;,
			length_str : &quot;The length is incorrect, it must be between %0 and %1&quot;,
			length_fix : &quot;The length is incorrect, it must be exactly %0 characters&quot;,
			lengthmax : &quot;The length is incorrect, it must be at max %0&quot;,
			lengthmin : &quot;The length is incorrect, it must be at least %0&quot;,
			words_min : &quot;This field must concain at least %0 words, currently: %1 words&quot;,
			words_range : &quot;This field must contain %0-%1 words, currently: %2 words&quot;,
			words_max : &quot;This field must contain at max %0 words, currently: %1 words&quot;,
			checkbox : &quot;Please check the box&quot;,
			checkboxes_group : 'Please check at least %0 box(es)',
			radios : &quot;Please select a radio&quot;,
			select : &quot;Please choose a value&quot;,
			select_multiple : &quot;Please choose at least one value&quot;
		},

		regexp : {
			required : /[^.*]/,
			alpha : /^[a-z ._-]+$/i,
			alphanum : /^[a-z0-9 ._-]+$/i,
			digit : /^[-+]?[0-9]+$/,
			nodigit : /^[^0-9]+$/,
			number : /^[-+]?\d*\.?\d+$/,
			email : /^([a-zA-Z0-9_\.\-\+%])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
			image : /.(jpg|jpeg|png|gif|bmp)$/i,
			phone : /^\+{0,1}[0-9 \(\)\.\-]+$/, // alternate regex : /^[\d\s ().-]+$/,/^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/
			phone_inter : /^\+{0,1}[0-9 \(\)\.\-]+$/,
			url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;amp;%\$#\=~])*$/i
		}
	},

	/*
	Constructor: initialize
		Constructor

		Add event on formular and perform some stuff, you now, like settings, ...
	*/
	initialize : function(form, options) {
		if (this.form = $(form)) {
			this.form.isValid = true;
			this.regex = ['length'];
			this.groups = {};

			//internalization
			if (typeof(formcheckLanguage) != 'undefined') this.options.alerts = Object.merge(this.options.alerts, formcheckLanguage);

			this.setOptions(options);

			this.form.setProperty('action',
				this.options.submitAction || this.form.getProperty('action') || 'post');

			this.form.setProperty('method',
				this.options.submitMethod || this.form.getProperty('method') || '');

			this.validations = [];
			this.alreadyIndicated = false;
			this.firstError = false;

			Object.each(this.options.regexp, function(el, key) {
				this.regex.push(key);
			}, this);

			this.form.getElements(&quot;*[class*=validate]&quot;).each(function(el) {
				this.register(el);
			}, this);

			this.form.addEvents({
				&quot;submit&quot;: this.onSubmit.bind(this)
			});

			if(this.options.display.fixPngForIe) this.fixIeStuffs();
			document.addEvent('mousewheel', function(){
				this.isScrolling = false;
			}.bind(this));

			if (this.options.display.indicateErrorsInit) {
				this.validations.each(function(el) {
					if(!this.manageError(el,'submit')) this.form.isValid = false;
				}, this);
			}
		}
	},

	/*
	Function: register
		Allows you to declare afterward new fields to the formcheck, to check dynamically loaded fields for example.
		By default it will be the last element to be validated as it's added after others inputs, but you can define a position with second parameter.

	Example:
		(code)
		&lt;script type=&quot;text/javascript&quot;&gt;
			window.addEvent('domready', function() {
				formcheck = new FormCheck('form_id');
			});

			// ...some code...

			var newField = new Element('input', {
				class	: &quot;validate['required']&quot;,
				name	: &quot;new-field&quot;
			}).inject('form_id');
			formcheck.register(newField, 3);

			new Element('input', {
				class	: &quot;validate['required']&quot;,
				name	: &quot;another-field&quot;,
				id		: &quot;another-field&quot;
			}).inject('form_id');
			formcheck.register($('another-field'));
		&lt;/script&gt;
		(end code)

	See also:
		&lt;FormCheck::dispose&gt;
	*/
	register : function(el, position) {
		el.validation = [];
		el.getProperty(&quot;class&quot;).split(' ').each(function(classX) {
			if (classX.match(/^validate(\[.+\])$/)) {
				var valid = true;

				var validators = eval(classX.match(/^validate(\[.+\])$/)[1]);
				for(var i = 0; i &lt; validators.length; i++) {
					el.validation.push(validators[i]);
					if (validators[i].match(/^confirm:/)) {
						var field = validators[i].match(/.+:(.+)$/)[1];
						if (this.form[field].validation.contains('required')) el.validation.push('required');
					}
					if (validators[i].match(/^target:.+/)) {
						el.target = validators[i].match(/^target:(.+)/)[1];
					}
				}

				//we check if group is already registered
				el.isChild = this.isChildType(el, validators);
				if (el.isChild &amp;&amp; el.type == 'radio') {
					this.validations.each(function(registeredEl){
						if (registeredEl.name == el.name) valid = false;
					}, this);
				}
				if (el.isChild &amp;&amp; el.type == 'checkbox') {
					this.validations.each(function(registeredEl){
						if (registeredEl.groupID == el.groupID) valid = false;
					}, this);
				}

				if (position &amp;&amp; position &lt;= this.validations.length) {
					var newValidations = [];
					this.validations.each(function(valider, i){
						if (position == i+1 &amp;&amp; valid) {
							newValidations.push(el);
							this.addListener(el);
						}
						newValidations.push(valider);
					}, this);
					this.validations = newValidations;
				} else if (valid) {
					this.validations.push(el);
					this.addListener(el);
				}
			}
		}, this);
	},

	/*
	Function: dispose
		Allows you to remove a declared field from formCheck

	Example:
		(code)
		&lt;script type=&quot;text/javascript&quot;&gt;
			window.addEvent('domready', function() {
				formcheck = new FormCheck('form_id');
			});

			// ...some code...

			formcheck.dispose($('obsolete-field'));
		&lt;/script&gt;
		(end code)

	See also:
		&lt;FormCheck::register&gt;
	*/
	dispose : function(element) {
		this.validations.erase(element);
	},

	/*
	Function: addListener
		Private method

		Add listener on fields
	*/
	addListener : function(el) {
		el.errors = [];

		if (el.validation[0] == 'submit') {
			el.addEvent('click', function(e){
				new Event(e).stop();
				if (this.onSubmit(e)) this.form.submit();
			}.bind(this));
			return true;
		}

		if (!el.isChild) {
			el.addEvent('blur', function() {
				if(!this.fxRunning &amp;&amp; (el.element || this.options.display.showErrors == 1) &amp;&amp; (this.options.display.checkValueIfEmpty || el.value)) this.manageError(el, 'blur');
			}.bind(this));
		//We manage errors on radio
		} else if(el.isChild &amp;&amp; el.type == 'radio') {
			//We get all radio from the same group and add a blur option
			var radioGroup = this.form.getElements('input[name=&quot;'+ el.getProperty(&quot;name&quot;) +'&quot;]');
			radioGroup.each(function(radio){
				radio.addEvent('blur', function(){
					if(!this.fxRunning &amp;&amp; (el.element || this.options.display.showErrors == 1) &amp;&amp; (this.options.display.checkValueIfEmpty || el.value)) this.manageError(el, 'click');
				}.bind(this));
			},this);
		}
	},

	/*
	Function: manageError
		Private method

		Manage display of errors boxes
	*/
	manageError : function(el, method) {
		var isValid = this.validate(el);
		if (method == 'testonly') return isValid;
		if ((!isValid &amp;&amp; el.validation.contains('required')) || (el.value &amp;&amp; !isValid)) {
			if(this.options.display.listErrorsAtTop &amp;&amp; method == 'submit') this.listErrorsAtTop(el);
			if (this.options.display.indicateErrors == 2 ||this.alreadyIndicated == false || el == this.alreadyIndicated) {
				if(!this.firstError) this.firstError = el;
				this.alreadyIndicated = el;

				if (this.options.display.keepFocusOnError &amp;&amp; el == this.firstError) {
					(function(){el.focus()}).delay(10);
				}
				this.addError(el);
				return false;
			}
		} else if ((isValid || (!el.validation.contains('required') &amp;&amp; !el.value))) {
			this.removeError(el);
			return true;
		}
		return true;
	},

	/*
	Function: validate
		Private method

		Dispatch check to other methods
	*/
	validate : function(el) {
		el.errors = [];
		el.isOk = true;

		//skip validation for disabled fields and trim if specified
		if (!this.options.validateDisabled &amp;&amp; el.get('disabled')) return true;
		if (this.options.trimValue &amp;&amp; el.value) el.value = el.value.trim();

		el.validation.each(function(rule) {
			if(el.isChild) {
				if (!this.validateGroup(el)) el.isOk = false;
			} else {
				var ruleArgs = [];

				if(rule.match(/target:.+/)) return;
				var ruleMethod = rule;
				if(rule.match(/^.+\[/)) {
					ruleMethod = rule.split('[')[0];
					ruleArgs = eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, &quot;'$1'&quot;));
				}

				if (this.regex.contains(ruleMethod) &amp;&amp; el.get('tag') != &quot;select&quot;) {
					if (this.validateRegex(el, ruleMethod, ruleArgs) == false) {
						el.isOk = false;
					}
				}
				if (rule.match(/confirm:.+/)) {
					ruleArgs = [rule.match(/.+:(.+)$/)[1]];
					if (this.validateConfirm(el, ruleArgs) == false) {
						el.isOk = false;
					}
				}
				if (rule.match(/differs:.+/)) {
					ruleArgs = [rule.match(/.+:(.+)$/)[1]];
					if (this.validateDiffers(el, ruleArgs) == false) {
						el.isOk = false;
					}
				}
				if (ruleMethod == 'words') {
					if (this.validateWords(el, ruleArgs) == false) {
						el.isOk = false;
					}
				}
				if (ruleMethod == 'required' &amp;&amp; (el.get('tag') == &quot;select&quot; || el.type == &quot;checkbox&quot;)) {
					if (this.simpleValidate(el) == false) {
						el.isOk = false;
					}
				}
				if(rule.match(/%[A-Z0-9\._-]+$/i) || (el.isOk &amp;&amp; rule.match(/~[A-Z0-9\._-]+$/i))) {
					if(eval(rule.slice(1)+'(el)') == false) {
						el.isOk = false;
					}
				}
			}
		}, this);
		return ( el.isOk ) ? true : false;
	},

	/*
	Function: simpleValidate
		Private method

		Perform simple check for select fields and checkboxes
	*/
	simpleValidate : function(el) {
		if(el.get('tag') == 'select'){
			if(!el.multiple) {
				if(el.selectedIndex &lt;= 0) {
					el.errors.push(this.options.alerts.select);
					return false;
				}
			} else {
				var selected = false;
				el.getChildren('option').each(function(el){
					if(el.selected) selected = true;
				});

				if(!selected){
					el.errors.push(this.options.alerts.select_multiple);
					return false;
				}
			}
		} else if (el.type == &quot;checkbox&quot; &amp;&amp; el.checked == false) {
			el.errors.push(this.options.alerts.checkbox);
			return false;
		}
		return true;
	},

	/*
	Function: validateRegex
		Private method

		Perform regex validations
	*/
	validateRegex : function(el, ruleMethod, ruleArgs) {
		var msg = &quot;&quot;;
		if (ruleMethod == 'length' &amp;&amp; ruleArgs[1]) {
			if (ruleArgs[1] == -1) {
				this.options.regexp.length = new RegExp(&quot;^[\\s\\S]{&quot;+ ruleArgs[0] +&quot;,}$&quot;);
				msg = this.options.alerts.lengthmin.replace(&quot;%0&quot;,ruleArgs[0]);
			} else if(ruleArgs[0] == ruleArgs[1]) {
				this.options.regexp.length = new RegExp(&quot;^[\\s\\S]{&quot;+ ruleArgs[0] +&quot;}$&quot;);
				msg = this.options.alerts.length_fix.replace(&quot;%0&quot;,ruleArgs[0]);
			} else {
				this.options.regexp.length = new RegExp(&quot;^[\\s\\S]{&quot;+ ruleArgs[0] +&quot;,&quot;+ ruleArgs[1] +&quot;}$&quot;);
				msg = this.options.alerts.length_str.replace(&quot;%0&quot;,ruleArgs[0]).replace(&quot;%1&quot;,ruleArgs[1]);
			}
		} else if (ruleArgs[0] &amp;&amp; ruleMethod == 'length') {
			this.options.regexp.length = new RegExp(&quot;^.{0,&quot;+ ruleArgs[0] +&quot;}$&quot;);
			msg = this.options.alerts.lengthmax.replace(&quot;%0&quot;,ruleArgs[0]);
		} else {
			msg = this.options.alerts[ruleMethod];
		}
		if ((ruleMethod == 'digit' || ruleMethod == 'number') &amp;&amp; ruleArgs[1]) {
			var valueres, regres = true;
			if (!this.options.regexp[ruleMethod].test(el.value)) {
				el.errors.push(this.options.alerts[ruleMethod]);
				regres = false;
			}
			if (ruleArgs[1] == -1) {
				valueres = ( el.value.toFloat() &gt;= ruleArgs[0].toFloat() );
				msg = this.options.alerts.digitmin.replace(&quot;%0&quot;,ruleArgs[0]);
			} else {
				valueres = ( el.value.toFloat() &gt;= ruleArgs[0].toFloat() &amp;&amp; el.value.toFloat() &lt;= ruleArgs[1].toFloat() );
				msg = this.options.alerts.digitltd.replace(&quot;%0&quot;,ruleArgs[0]).replace(&quot;%1&quot;,ruleArgs[1]);
			}
			if (regres == false || valueres == false) {
				el.errors.push(msg);
				return false;
			}
		} else if (this.options.regexp[ruleMethod].test(el.value) == false)  {
			el.errors.push(msg);
			return false;
		}
		return true;
	},

	/*
	Function: validateConfirm
		Private method

		Perform confirm validations
	*/
	validateConfirm: function(el,ruleArgs) {
		var confirm = ruleArgs[0];
		if(el.value != this.form[confirm].value){
			var msg = ( this.options.display.titlesInsteadNames ) ?
				this.options.alerts.confirm.replace(&quot;%0&quot;,this.form[confirm].getProperty('title')) :
				this.options.alerts.confirm.replace(&quot;%0&quot;,confirm);
			el.errors.push(msg);
			return false;
		}
		return true;
	},

	/*
	Function: validateDiffers
		Private method

		Perform differs validations
	*/
	validateDiffers: function(el,ruleArgs) {
		var differs = ruleArgs[0];
		if(el.value == this.form[differs].value){
			var msg = ( this.options.display.titlesInsteadNames ) ?
				this.options.alerts.differs.replace(&quot;%0&quot;,this.form[differs].getProperty('title')) :
				this.options.alerts.differs.replace(&quot;%0&quot;,differs);
			el.errors.push(msg);
			return false;
		}
		return true;
	},

	/*
	Function: validateWords
		Private method

		Perform word count validation
	*/
	validateWords: function(el,ruleArgs) {
		var min = ruleArgs[0];
		var max = ruleArgs[1];

		var words = el.value.replace(/[ \t\v\n\r\f\p]/m, ' ').replace(/[,.;:]/g, ' ').clean().split(' ');

		if(max == -1) {
			if(words.length &lt; min) {
				el.errors.push(this.options.alerts.words_min.replace(&quot;%0&quot;, min).replace(&quot;%1&quot;, words.length));
				return false;
			}
		} else {
			if(min &gt; 0)	{
				if(words.length &lt; min || words.length &gt; max) {
					el.errors.push(this.options.alerts.words_range.replace(&quot;%0&quot;, min).replace(&quot;%1&quot;, max).replace(&quot;%2&quot;, words.length));
					return false;
				}
			} else {
				if(words.length &gt; max) {
					el.errors.push(this.options.alerts.words_max.replace(&quot;%0&quot;, max).replace(&quot;%1&quot;, words.length));
					return false;
				}
			}
		}
		return true;
	},


	/*
	Function: isFormValid
		public method

		Determine if the form is valid

		Return true or false
	*/
    isFormValid: function() {
		this.form.isValid = true;
		this.validations.each(function(el) {
			var validation = this.manageError(el,'testonly');
			if(!validation) this.form.isValid = false;
		}, this);
		return this.form.isValid;
	},

	/*
	Function: isChildType
		Private method

		Determine if the field is a group of radio, of checkboxes or not.
	*/
	isChildType: function(el, validators) {
		var validator;
		if(el.type!=undefined &amp;&amp; el.type == 'radio') {
			return true;
		} else if(validator = validators.join().match(/group(\[.*\])/)) {
			var group = eval(validator[1]);
			this.groups[group[0]] = this.groups[group[0]] || [];
			this.groups[group[0]][0] = this.groups[group[0]][0] || [];
			this.groups[group[0]][1] = group[1] || this.groups[group[0]][1] || 1;
			this.groups[group[0]][0].push(el);
			el.groupID = group[0];
			return true;
		}
		return false;
	},

	/*
	Function: validateGroup
		Private method

		Perform radios validations
	*/
	validateGroup : function(el) {
		el.errors = [];
		if(el.type == 'radio') {
			var nlButtonGroup = this.form[el.getProperty(&quot;name&quot;)];
			el.group = nlButtonGroup;
			var cbCheckeds = false;

			for(var i = 0; i &lt; nlButtonGroup.length; i++) {
				if(nlButtonGroup[i].checked) {
					cbCheckeds = true;
				}
			}
			if(cbCheckeds == false) {
				el.errors.push(this.options.alerts.radios);
				return false;
			} else {
				return true;
			}
		// we have group of checkboxes
		} else if(el.type == 'checkbox') {
			//we get length of checked elements
			var checked = 0;
			this.groups[el.groupID][0].each(function(groupEl){
				if(groupEl.checked) checked++;
			});
			if(checked &gt;= this.groups[el.groupID][1]) {
				return true;
			} else {
				( this.groups[el.groupID][0].length &gt; 1 ) ?
					el.errors.push(this.options.alerts.checkboxes_group.replace('%0', this.groups[el.groupID][1])) :
					el.errors.push(this.options.alerts.checkbox);
				return false;
			}
		// we have unmanaged type
		} else {
			return false;
		}
	},

	/*
	Function: listErrorsAtTop
		Private method

		Display errors
	*/
	listErrorsAtTop : function(obj) {
		if(!this.form.element) {
			 this.form.element = new Element('div', {'id' : 'errorlist', 'class' : this.options.errorClass}).inject(this.form,'top');
		}
		if (typeOf(obj) == 'collection') {
			new Element('p').set('html',&quot;&lt;span&gt;&quot; + obj[0].name + &quot; : &lt;/span&gt;&quot; + obj[0].errors[0]).inject(this.form.element);
		} else {
			if ((obj.validation.contains('required') &amp;&amp; obj.errors.length &gt; 0) || (obj.errors.length &gt; 0 &amp;&amp; obj.value &amp;&amp; obj.validation.contains('required') == false)) {
				obj.errors.each(function(error) {
					new Element('p').set('html',&quot;&lt;span&gt;&quot; + obj.name + &quot; : &lt;/span&gt;&quot; + error).inject(this.form.element);
				}, this);
			}
		}
		window.fireEvent('resize');
	},

	/*
	Function: addError
		Private method

		Add error message
	*/
	addError : function(obj) {
		//determine position
		var coord = obj.target ? $(obj.target).getCoordinates() : obj.getCoordinates();

		if(!obj.element &amp;&amp; this.options.display.indicateErrors != 0) {
			if (this.options.display.errorsLocation == 1) {
				var pos = (this.options.display.tipsPosition == 'left') ? coord.left : coord.right;
				var options = {
					'opacity' : 0,
					'position' : 'absolute',
					'float' : 'left',
					'left' : pos + this.options.display.tipsOffsetX
				};
				obj.element = new Element('div', {'class' : this.options.tipsClass, 'styles' : options}).inject(document.body);
				this.addPositionEvent(obj);
			} else if (this.options.display.errorsLocation == 2){
				obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}).inject(obj,'before');
			} else if (this.options.display.errorsLocation == 3){
				obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}});
				if (typeOf(obj.group) == 'object' || typeOf(obj.group) == 'collection')
					obj.element.inject(obj.group[obj.group.length-1],'after');
				else
					obj.element.inject(obj,'after');
			}
		}
		if (obj.element &amp;&amp; obj.element != true) {
			obj.element.empty();
			if (this.options.display.errorsLocation == 1) {
				var errors = [];
				obj.errors.each(function(error) {
					errors.push(new Element('p').set('html', error));
				});
				var tips = this.makeTips(errors).inject(obj.element);
				if(this.options.display.closeTipsButton) {
					tips.getElements('a.close').addEvent('mouseup', function(){
						this.removeError(obj, 'tip');
					}.bind(this));
				}
				obj.element.setStyle('top', coord.top - tips.getCoordinates().height + this.options.display.tipsOffsetY);
			} else {
				obj.errors.each(function(error) {
					new Element('p').set('html',error).inject(obj.element);
				});
			}

			if (!this.options.display.fadeDuration || Browser.ie &amp;&amp; Browser.version &lt; 9 &amp;&amp; this.options.display.errorsLocation &lt; 2) {
				obj.element.setStyle('opacity', 1);
			} else {
				obj.fx = new Fx.Tween(obj.element, {
					'duration' : this.options.display.fadeDuration,
					'ignore' : true,
					'onStart' : function(){
						this.fxRunning = true;
					}.bind(this),
					'onComplete' : function() {
						this.fxRunning = false;
						if (obj.element &amp;&amp; obj.element.getStyle('opacity').toInt() == 0) {
							obj.element.destroy();
							obj.element = false;
						}
					}.bind(this)
				});
				if(obj.element.getStyle('opacity').toInt() != 1) obj.fx.start('opacity', 1);
			}
		}
		if (this.options.display.addClassErrorToField &amp;&amp; !obj.isChild){
			obj.addClass(this.options.fieldErrorClass);
			obj.element = obj.element || true;
		}

	},

	/*
	Function: addPositionEvent

		Update tips position after a browser resize
	*/
	addPositionEvent : function(obj) {
		if(this.options.display.replaceTipsEffect) {
			obj.event = function(){
				var coord = obj.target ? $(obj.target).getCoordinates() : obj.getCoordinates();
				new Fx.Morph(obj.element, {
					'duration' : this.options.display.fadeDuration
				}).start({
					'left':[obj.element.getStyle('left'), coord.right + this.options.display.tipsOffsetX],
					'top':[obj.element.getStyle('top'), coord.top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY]
				});
			}.bind(this);

		} else {
			obj.event = function(){
				var coord = obj.target ? $(obj.target).getCoordinates() : obj.getCoordinates();
				obj.element.setStyles({
					'left':coord.right + this.options.display.tipsOffsetX,
					'top':coord.top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY
				});
			}.bind(this);
		}
		window.addEvent('resize', obj.event);
	},

	/*
	Function: removeError
		Private method

		Remove the error display
	*/
	removeError : function(obj, method) {
		if ((this.options.display.addClassErrorToField &amp;&amp; !obj.isChild &amp;&amp; this.options.display.removeClassErrorOnTipClosure) || (this.options.display.addClassErrorToField &amp;&amp; !obj.isChild &amp;&amp; !this.options.display.removeClassErrorOnTipClosure &amp;&amp; method != 'tip'))
			obj.removeClass(this.options.fieldErrorClass);

		if (!obj.element) return;
		this.alreadyIndicated = false;
		obj.errors = [];
		obj.isOK = true;
		window.removeEvent('resize', obj.event);
		if (this.options.display.errorsLocation &gt;= 2 &amp;&amp; obj.element) {
			new Fx.Tween(obj.element, {
				'duration': this.options.display.fadeDuration
			}).start('height', 0);
		}
		if (!this.options.display.fadeDuration || Browser.ie &amp;&amp; Browser.version &lt; 9 &amp;&amp; this.options.display.errorsLocation == 1 &amp;&amp; obj.element) {
			this.fxRunning = true;
			obj.element.destroy();
			obj.element = false;
			(function(){this.fxRunning = false}.bind(this)).delay(200);
		} else if (obj.element &amp;&amp; obj.element != true) {
			obj.fx.start('opacity', 0);
		}
	},

	/*
	Function: focusOnError
		Private method

		Create set the focus to the first field with an error if needed
	*/
	focusOnError : function (obj) {
		if (this.options.display.scrollToFirst &amp;&amp; !this.alreadyFocused &amp;&amp; !this.isScrolling) {
			var dest; //moved this up to stop redclariations
			//This can changed to a single switch using default:
			if (!this.options.display.indicateErrors || !this.options.display.errorsLocation) {
				dest = obj.getCoordinates().top-30;
			} else {
				switch (this.options.display.errorsLocation){
					case 1 :
						dest = obj.element.getCoordinates().top;
						break;
					case 2 :
						dest = obj.element.getCoordinates().top-30;
						break;
					case 3 :
						dest = obj.getCoordinates().top-30;
						break;
				}
				this.isScrolling = true;
			}
			if (window.getScroll().y != dest) {
				if(Fx.Scroll) {
					new Fx.Scroll(window, {
						onComplete : function() {
							this.isScrolling = false;
							if (obj.getProperty('type') != 'hidden') obj.focus();
						}.bind(this)
					}).start(0,dest);
				} else {
					this.isScrolling = false;
					window.scrollTo(0,dest);	
				}
			} else {
				this.isScrolling = false;
				obj.focus();
			}
			this.alreadyFocused = true;
		}
	},

	/*
	Function: fixIeStuffs
		Private method

		Fix png for IE6
	*/
	fixIeStuffs : function () {
		if (Browser.ie &amp;&amp; Browser.version &lt; 9) {
			//We fix png stuffs
			var rpng = new RegExp('url\\(([\.a-zA-Z0-9_/:-]+\.png)\\)');
			var search = new RegExp('(.+)formcheck\.css');
			for (var i = 0; i &lt; document.styleSheets.length; i++){
				if (document.styleSheets[i].href.match(/formcheck\.css$/)) {
					var root = document.styleSheets[i].href.replace(search, '$1');
					var count = document.styleSheets[i].rules.length;
					for (var j = 0; j &lt; count; j++){
						var cssstyle = document.styleSheets[i].rules[j].style;
						var bgimage = root + cssstyle.backgroundImage.replace(rpng, '$1');
						if (bgimage &amp;&amp; bgimage.match(/\.png/i)){
							var scale = (cssstyle.backgroundRepeat == 'no-repeat') ? 'crop' : 'scale';
							cssstyle.filter =  'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src=\'' + bgimage + '\', sizingMethod=\''+ scale +'\')';
							cssstyle.backgroundImage = &quot;none&quot;;
						}
					}
				}
			}
		}
	},

	/*
	Function: makeTips
		Private method

		Create tips boxes
	*/
	makeTips : function(txt) {
		var table = new Element('table');
			table.cellPadding ='0';
			table.cellSpacing ='0';
			table.border ='0';

			var tbody = new Element('tbody').inject(table);
				var tr1 = new Element('tr').inject(tbody);
					new Element('td', {'class' : 'tl'}).inject(tr1);
					new Element('td', {'class' : 't'}).inject(tr1);
					new Element('td', {'class' : 'tr'}).inject(tr1);
				var tr2 = new Element('tr').inject(tbody);
					new Element('td', {'class' : 'l'}).inject(tr2);
					var cont = new Element('td', {'class' : 'c'}).inject(tr2);
						var errors = new Element('div', {'class' : 'err'}).inject(cont);
						txt.each(function(error) {
							error.inject(errors);
						});
						if (this.options.display.closeTipsButton) new Element('a',{'class' : 'close'}).inject(cont);
					new Element('td', {'class' : 'r'}).inject(tr2);
				var tr3 = new Element('tr').inject(tbody);
					new Element('td', {'class' : 'bl'}).inject(tr3);
					new Element('td', {'class' : 'b'}).inject(tr3);
					new Element('td', {'class' : 'br'}).inject(tr3);
		return table;
	},

	/*
	Function: reinitialize
		Reinitialize form before submit check. You can use this also to remove all tips from a form, passing the argument &quot;forced&quot; ( formcheck.reinitialize('forced'); )
	*/
	reinitialize: function(forced) {
		this.validations.each(function(el) {
			if (el.element) {
				el.errors = [];
				el.isOK = true;
				if(this.options.display.flashTips == 1 || forced == 'forced') {
					el.element.destroy();
					el.element = false;
				}
			}
		}, this);
		if (this.form.element) this.form.element.empty();
		this.alreadyFocused = false;
		this.firstError = false;
		this.elementToRemove = this.alreadyIndicated;
		this.alreadyIndicated = false;
		this.form.isValid = true;
	},

	/*
	Function: submitByAjax
		Private method

		Send the form by ajax, and replace the form with response
	*/

	submitByAjax: function() {
		this.fireEvent('ajaxRequest');
		new Request({
			url: this.form.action,
			method: this.form.method,
			data : this.form.toQueryString(),
			evalScripts: this.options.ajaxEvalScripts,
			onFailure: function(instance){
				this.fireEvent('ajaxFailure', instance);
			}.bind(this),
			onComplete: function(instance){
				this.fireEvent('ajaxComplete', instance);
			}.bind(this),
			onSuccess: function(result){
				this.fireEvent('ajaxSuccess', result);
				if(this.options.ajaxResponseDiv) $(this.options.ajaxResponseDiv).set('html',result);
			}.bind(this)
		}).send();
		return false;
	},

	/*
	Function: onSubmit
		Private method

		Perform check on submit action
	*/
	onSubmit: function(event) {
		this.reinitialize();
		this.fireEvent('onSubmit');
		
		this.validations.each(function(el) {
			var validation = this.manageError(el,'submit');
			if(!validation) this.form.isValid = false;
		}, this);

		if (this.form.isValid) {
			this.fireEvent('validateSuccess');
			//moved above to allow optional settings to this.form.submit and submitByAjax to be triggered by this option
			return (this.options.submitByAjax)? this.submitByAjax():this.options.submit;
			//if this.options.submit is false it can still rely on validateSuccess event
		} else {
			if (this.elementToRemove &amp;&amp; this.elementToRemove != this.firstError &amp;&amp; this.options.display.indicateErrors == 1) {
				this.removeError(this.elementToRemove);
			}
			this.focusOnError(this.firstError);
			this.fireEvent('validateFailure');
			return false;
		}
	}
});
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/moofloor-formcheck-for-mootools-1-4-x/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>MooTools Integers and Floats when used in math and currency</title>
		<link>http://www.timwickstrom.com/client-side-code/mootools/mootools-integers-and-floats-when-used-in-math-and-currency/</link>
		<comments>http://www.timwickstrom.com/client-side-code/mootools/mootools-integers-and-floats-when-used-in-math-and-currency/#comments</comments>
		<pubDate>Wed, 19 Oct 2011 19:46:42 +0000</pubDate>
		<dc:creator><![CDATA[Tim Wickstrom]]></dc:creator>
				<category><![CDATA[mooTools]]></category>

		<guid isPermaLink="false">http://www.timwickstrom.com/?p=223</guid>
		<description><![CDATA[We have all been there before, your creating a form and asking for a dollar amount, or some number with decimal points lets say amount of fuel purchased. Typically there is plenty of logic that needs to go into validating the user input, did they use a dollar sign? Did they use the correct amount of decimal points? Well MooTools and JavaScript to the rescue! The MooTools Example Usage: *Tested with mooTools 1.3 and 1.4]]></description>
				<content:encoded><![CDATA[<p>We have all been there before, your creating a form and asking for a dollar amount, or some number with decimal points lets say amount of fuel purchased.</p>
<p>Typically there is plenty of logic that needs to go into validating the user input, did they use a dollar sign? Did they use the correct amount of decimal points?</p>
<p>Well MooTools and JavaScript to the rescue!</p>
<h3>The MooTools</h3>
<pre class="brush: jscript; title: ; notranslate">
String.implement({
	cleanFloat: function() {
		return this.replace(/[^0-9.]/g,'').toFloat() || 0;
	}
});

String.implement({
	toCurrency: function() {
		return parseFloat(Math.round(this * 100) / 100).toFixed(2)
	}
});
</pre>
<h3>Example Usage:</h3>
<pre class="brush: xml; title: ; notranslate">
&lt;input type=&quot;input&quot; name=&quot;fuelCost&quot; id=&quot;fuelCost&quot; value=&quot;$4.28r9&quot; /&gt;
&lt;input type=&quot;input&quot; name=&quot;gallons&quot; id=&quot;gallons&quot; value=&quot;75.9869 Gallons&quot; /&gt;
&lt;script&gt;
window.addEvent('domready', function(){
	console.log(document.id('fuelCost').get('value').cleanFloat());
	// returns 4.289
	console.log(document.id('gallons').get('value').cleanFloat());
	// returns 75.9869

	// Now lets do some math
	console.log('$'+
		String.from(
			document.id('fuelCost').get('value').cleanFloat() *
			document.id('gallons').get('value').cleanFloat()
		).toCurrency()
	)
	// Return the total purchase price of $325.91
});
&lt;/script
</pre>
<h4>*Tested with mooTools 1.3 and 1.4</h4>
]]></content:encoded>
			<wfw:commentRss>http://www.timwickstrom.com/client-side-code/mootools/mootools-integers-and-floats-when-used-in-math-and-currency/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
