Posts Tagged ‘JavaScript’

May 23rd, 2008

How to create a non-Native jQuery event

Today I had the need to create a custom event using jQuery, in order to launch a customized form validation event from a global submit event. I did this so I could focus in on the first form field that had an error. My event from the global.js script, that is included on every page of IdeaPipe, looks like this:

$("form").submit(function () {
	var valid = $(this).validate();

	// if the form didn't validate then focus the input on the first error
	if (!valid)
		$(this).find(":input[error]:first”).focus();

	return valid;
});

This is pretty standard jQuery. What this code above does is set a custom function for the submit event for any <form /> tag on the page. The submit event will only be allowed to continue if a return value of true is returned from the function.

I was able to create this custom jQuery event with the following code:

jQuery.fn.extend({
	validate: function (fn) {
		if (fn) {
			return jQuery.event.add(this[0], “validate”, fn, null);
		} else {
			var ret = jQuery.event.trigger(”validate”, null, this[0], false, null);

			// if there was no return value then the even validated correctly
			if (ret === undefined)
				ret = true;

			return ret;
		}
	}
});

There are two different states to this method. Primarily because in JavaScript all parameters are optional for functions. So the two states of this function are:

  • validate(fn) - sets the event
  • validate() - fires the event

An example of setting the event is:

$("form.user-login").validate(function () {
	var userNameValid = ValidateLoginUserName();
	var passwordValid = ValidateLoginPassword();

	return userNameValid && passwordValid;
});

In this example the form is valid if both the login user name and password validate.

An example of using the event is the same as the method above.

$("form").submit(function () {
	var valid = $(this).validate();
	// do some stuff
	return valid;
});

This may not be the standard bind() and trigger() that most jQuery programmers are use to, but I needed an event that would return a value of true or false, so that I my submit event handler knows if it should focus on errors or continue the submit process.

Hope everybody finds this useful.

Tags: , ,

Posted in How To, JavaScript | kick it on DotNetKicks.com | Bookmark | View blog reactions | 1 Comment »

May 18th, 2008

How to create a YUI Compressor MSBuild Task

Recently for IdeaPipe I have been looking for ways to deliver my content more quickly and reduce unnecessary bandwidth use.

According to Yahoo’s Performance Team more than half of the viewers of the Yahoo websites start with an empty cache, which means the browser has to download all the resources for the first time. This combined with a high traffic website and unneeded white space and comments can really add up to a significant bandwidth use. There are many popular ways to minify your static content tax on your bandwidth, using many popular tools, as described in this excerpt from Yahoo:

In terms of code minification, the most widely used tools to minify JavaScript code are Douglas Crockford’s JSMIN, the Dojo compressor and Dean Edwards’ Packer. Each of these tools, however, has drawbacks. JSMIN, for example, does not yield optimal savings (due to its simple algorithm, it must leave many line feed characters in the code in order not to introduce any new bugs).

The goal of JavaScript and CSS minification is always to preserve the operational qualities of the code while reducing its overall byte footprint (both in raw terms and after gzipping, as most JavaScript and CSS served from production web servers is gzipped as part of the HTTP protocol).

The cream of the crop seems to be a tool Yahoo developed to deliver its own static text content scripts and styles, the YUI Compressor:

The YUI Compressor is JavaScript minifier designed to be 100% safe and yield a higher compression ratio than most other tools. Tests on the YUI Library have shown savings of over 20% compared to JSMin (becoming 10% after HTTP compression). Starting with version 2.0, the YUI Compressor is also able to compress CSS files by using a port of Isaac Schlueter’s regular-expression-based CSS minifier.

The YUI Compressor is a Java JAR file that can be download from Julien Lecomte Blog.

The YUI Compressor yielded exceptional results, however it was missing one thing. Integration in to my build and deployment process. In IdeaPipe I use a MSBuild script to compile, manipulate, and prepare for publishing. So naturally I built a MSBuild Task to minimize my JavaScript and CSS files.

The magic actually happens by invoking Java in an external process for each file passed in to the task.

Process process = new Process();
process.StartInfo = new ProcessStartInfo {
	FileName = @"c:\program files\java\jdk1.6.0_06\bin\java.exe",
	Arguments = String.Format(
		@"-jar ""C:\development\tools\yuicompressor-2.3.5.jar"" --type {0} --charset utf8 {1} -o ""{2}"" ""{3}""",
		type,
		ShowWarnings ? "--verbose" : String.Empty,
		newFile,
		oldFile
		),
	UseShellExecute = false,
	CreateNoWindow = true,
	RedirectStandardOutput = true,
	RedirectStandardError = true
};
process.Start();
process.WaitForExit(5000);

Then I read the warning from the standard error output and send them back to Visual Studio as a compile warning if the ShowWarning property is true.

string[] warnings = process.StandardError.ReadToEnd()
	.Replace(”\r”, String.Empty)
	.Split(new string[] { “\n\n” }, StringSplitOptions.RemoveEmptyEntries);

foreach(string warning in warnings)
	Log.LogWarning(null, null, null, oldFile, 1, 1, 1, 1, FormatWarning(warning), null);

To integrate this in to my MSBuild script I had to first register my task:

<UsingTask TaskName="ManagedFusion.Build.YuiCompress" AssemblyFile="$(ProjectDir)..\ManagedFusion.Build\bin\$(ConfigurationName)\ManagedFusion.Build.dll"/>

Then setup my ItemGroup for the files:

<ItemGroup>
	<JavaScriptContent Include="$(SourceWebPhysicalPath)\**\*.js" />
	<CssContent Include="$(SourceWebPhysicalPath)\**\*.css" />
</ItemGroup>

Then finally I setup my task to perform the minimization against the JavaScript and CSS files seperately:

<Target Name="AfterBuild">
	<!-- do other stuff to prepare for publishing -->
	<YuiCompress Files="@(JavaScriptContent)" Type="JS" />
	<YuiCompress Files="@(CssContent)" Type="CSS" />
</Target>

You can easily incorporate this in to your own MSBuild scripts or even your Visual Studio Project which is just an MSBuild file for compiling your source code for the project. I have included my source code below:

Download: YUI Compressor MSBuild Task Source

Note: There are a couple of static paths to be on the look out for and modify as necessary for your own code. In my code the Java runtime is loaded at c:\program files\java\jdk1.6.0_06\bin\java.exe and the YUI JAR is located at C:\development\tools\yuicompressor-2.3.5.jar.

Update (2008-5-21): Thanks George, apparently IIS doesn’t like serving straight C# files. So I added the code to my Coder Journal Source Control, so that it can be downloaded from there.

Tags: , , , , ,

Posted in ASP.NET, How To, Programming | kick it on DotNetKicks.com | Bookmark | View blog reactions | 7 Comments »

April 9th, 2008

Getting IIS 7 to Compress JavaScript

One of the many recommendations that Yahoo makes on optimizing your web site for high amounts of traffic, and to make the response time speedier to your user is GZip encoding all your static content. I usually do this as a standard for setting up any of my Web Servers, in addition to setting expiration headers on my static content, to ensure that I am serving as little content as possible.

IIS 7 has improved and simplified the compression and serving of static files by making it easier to setup and configure than previously in IIS 6.0. The IIS 7.0 compression works perfectly for CSS, HTML, and Text files, however JavaScript is another story. The JavaScript files on my IIS 7.0 server were not being compressed and served with GZip encoding, which is a major problem for any Web 2.0 site where 75% of your content severed per request is JavaScript. (I just made that number up, but it sounds right!)

I found Rick Strahl’s post on this very subject that he wrote up about a 9 months ago. It was helpful in diagnosing my problem, however it didn’t solve it. The HTTP compression is configured in IIS 7.0’s ApplicationHost.config file (c:\windows\system32\inetsrv\config\applicationhost.config), see below for the default settings:

<httpCompression directory="%SystemDrive%\websites\_compressed" minFileSizeForComp="0">
    <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
    <staticTypes>
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <add mimeType="application/javascript" enabled="true" />
        <add mimeType="*/*" enabled="false" />
    </staticTypes>
</httpCompression>

As you can see anything that starts with the MIME type of text or message is GZip encoded just fine. However there is also application/javascript as a compressible MIME type, there is nothing wrong with that, because there are 3 accepted ways to set a JavaScript MIME type.

  1. text/javascript
  2. application/x-javascript
  3. application/javascript

However the problem comes in when you look at the default MIME type mappings setup, in the same ApplicationHost.config file, a little further down.

...
    <mimeMap fileExtension=".jpg" mimeType="image/jpeg" />
    <mimeMap fileExtension=".js" mimeType="application/x-javascript" />
    <mimeMap fileExtension=".jsx" mimeType="text/jscript" /
...

As you may notice the MIME type for JavaScript files is set to application/x-javascript, which is not the same as the default in the compression section above. So I added the following MIME type, application/javascript, to my Web.config thinking I had the problem licked, and all that I had to do was change the default MIME type for JavaScript files.

<system.webServer>
    <staticContent>
        <remove fileExtension=".js" />
        <mimeMap fileExtension=".js" mimeType="application/javascript" />
    </staticContent>
</system.webServer>

However that didn’t work either, and it should have because the MIME type now matched my compression MIME type. I even verified the MIME type in fiddler. So I then tried my last option to change the MIME type to text/javascript, which is the defacto standard on the internet for JavaScript MIME types.

<system.webServer>
    <staticContent>
        <remove fileExtension=".js" />
        <mimeMap fileExtension=".js" mimeType="text/javascript" />
    </staticContent>
</system.webServer>

Finally, this was the key to getting the JavaScript GZip Compression working IIS 7.0. And this didn’t require me to modify the ApplicationHost.config file get it done. Which is something I love about the new IIS 7.0, I can do my whole server configuration through FTP and my Web.config file.

Tags: , , , ,

Posted in How To | kick it on DotNetKicks.com | Bookmark | View blog reactions | 5 Comments »

August 24th, 2007

Creating a more accurate JSON .NET Serializer

Recently I have been diving in to the world of REST and all the great things that come along with that. If you are not familiar with REST and what it means to have a REST Web Service for your site, you can go through the Digg API, which should give you a pretty good idea. My company has been contracted to build the framework for a new Web 2.0 initiative of one of our clients. You cannot do Web 2.0 if you are not using some kind of AJAX/REST combination.

With the inclusion of Microsoft AJAX.NET are some very useful tools that have been added to the web services library. My current focus in System.Web.Script.Serialization.JavaScriptSerializer, which takes you objects and turns them in to a JSON string that can be evaluated by JavaScript and reversed in to an object. JSON (pronounced Jason) is very useful in AJAX because you do not have to retrieve and parse XML through the XML Browser Request that powers current AJAX implementations.

I found the attribute support lacking in the JavaScriptSerializer when compared to the XmlSerializer, ScriptIgnoreAttribute compared to XmlRootAttribute, XmlAttributeAttribute, XmlIgnoreAttribute, XmlElementAttribute, XmlArrayAttribute, and XmlArrayItemAttribute. So I decided to extend the JavaScriptSerializer to use the XML attributes to serialize my objects and give me greater control over how they were written in to JSON text form. The added benefit was that my XML and JSON outputs serialized the exact same way when the web service generated them. Below I have included the code.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Web.Script;
using System.Web.Script.Serialization;
using System.Xml.Serialization;

namespace ManagedFusion.Script.Serializer
{
	public class XmlJavaScriptConverter<T> : JavaScriptConverter
	{
		public override object Deserialize(IDictionary<string,string> dictionary, Type type, JavaScriptSerializer serializer)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public override IDictionary<string,string> Serialize(object obj, JavaScriptSerializer serializer)
		{
			return SerializeObject(obj);
		}

		private IDictionary<string,string> SerializeObject(object obj)
		{
			IDictionary<string,string> values = new Dictionary<string,string>();
			Type type = obj.GetType();

			foreach (FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
				if (!info.IsDefined(typeof(XmlIgnoreAttribute), true))
					values.Add(SerializeName(info), SerializeValue(info.GetValue(obj), info));

			foreach (PropertyInfo info2 in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
			{
				if (!info2.IsDefined(typeof(XmlIgnoreAttribute), true))
				{
					MethodInfo getMethod = info2.GetGetMethod();
					if ((getMethod != null) && (getMethod.GetParameters().Length <= 0))
						values.Add(SerializeName(info2), SerializeValue(getMethod.Invoke(obj, null), info2));
				}
			}

			return values;
		}

		private string SerializeName(MemberInfo member)
		{
			string name = null;

			if (member.IsDefined(typeof(XmlElementAttribute), true))
			{
				object[] attrs = member.GetCustomAttributes(typeof(XmlElementAttribute), true);

				if (attrs.Length > 0)
				{
					XmlElementAttribute attr = attrs[0] as XmlElementAttribute;

					name = attr.ElementName;
				}
			}

			if (member.IsDefined(typeof(XmlAttributeAttribute), true))
			{
				object[] attrs = member.GetCustomAttributes(typeof(XmlAttributeAttribute), true);

				if (attrs.Length > 0)
				{
					XmlAttributeAttribute attr = attrs[0] as XmlAttributeAttribute;

					name = attr.AttributeName;
				}
			}

			if (member.IsDefined(typeof(XmlArrayAttribute), true))
			{
				object[] attrs = member.GetCustomAttributes(typeof(XmlArrayAttribute), true);

				if (attrs.Length > 0)
				{
					XmlArrayAttribute attr = attrs[0] as XmlArrayAttribute;

					name = attr.ElementName;
				}
			}

			if (String.IsNullOrEmpty(name))
				name = null;

			return name ?? member.Name;
		}

		private object SerializeValue(object obj, MemberInfo member)
		{
			if (obj == null)
				return obj;

			// make sure the object isn’t an easily handled primity type
			if (Type.GetTypeCode(obj.GetType()) != TypeCode.Object)
				return obj;

			if (obj is IDictionary)
				return obj;

			if (obj is ICollection)
			{
				IList<object> list = new List<object>();
				object[] attrs = member.GetCustomAttributes(typeof(XmlArrayItemAttribute), true);
				string arrayName = null;

				if (attrs.Length > 0)
				{
					XmlArrayItemAttribute attr = attrs[0] as XmlArrayItemAttribute;

					arrayName = attr.ElementName;
				}

				if (String.IsNullOrEmpty(arrayName))
				{
					foreach (object o in (obj as ICollection))
					{
						if (Type.GetTypeCode(o.GetType()) != TypeCode.Object)
							list.Add(o);
						else
							list.Add(SerializeObject(o));
					}
				}
				else
				{
					foreach (object o in (obj as ICollection))
					{
						IDictionary<string,object> list2 = new Dictionary<string,object>();

						if (Type.GetTypeCode(o.GetType()) != TypeCode.Object)
							list2.Add(arrayName, o);
						else
							list2.Add(arrayName, SerializeObject(o));

						list.Add(list2);
					}
				}

				return list;
			}

			return SerializeObject(obj);
		}

		public override IEnumerable<Type> SupportedTypes
		{
			get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(T) })); }
		}
	}
}

Then to serialize the object in my code I have the following code in place.

JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
jsSerializer.RegisterConverters(new List<JavaScriptConverter>(new JavaScriptConverter[] { new XmlJavaScriptConverter<T>() }));
string response = jsSerializer.Serialize(SerializableObject);

The serializer from above outputs the following code.

{"timestamp":"\/Date(1187968133328)\/","maxCount":10,"count":1,"terms":[{"term":"dvd hollywood"}]}

From this object as the reference.

[XmlRoot("search")]
public class GetTermsResponse
{
	private string[] _terms;
	private int _maxCount;

	[XmlAttribute("timestamp")]
	public DateTime TimeStamp
	{
		get { return DateTime.Now; }
		set { ; }
	}

	[XmlAttribute("maxCount")]
	public int MaxCount
	{
		get { return _maxCount; }
		set { _maxCount = value; }
	}

	[XmlAttribute("count")]
	public int Count
	{
		get { return Terms.Length; }
		set { ; }
	}

	[XmlArray("terms")]
	[XmlArrayItem("term")]
	public string[] Terms
	{
		get { return _terms; }
		set { _terms = value; }
	}
}

So that wasn’t too hard the next step for this code is to make it so it can deserialize the JSON string back to an object, but the chances of that happening are almost 100/1 against the use of that functionality, because POST using JSON is a very dangerous activity and shouldn’t be attempted unless you know all the problems that may occur. So this code above should work in most real world examples. Happy coding.

Tags: , , ,

Posted in ASP.NET, C#, Programming | kick it on DotNetKicks.com | Bookmark | View blog reactions | 1 Comment »