Respect the List

As part of my work, I help people rewrite the content for their website. When combing through the content of a website, I often come across content shoved into a list, like this:

To be eligible for the Franken scholarship, students must:

  • Be a U.S. citizen; Be eligible for federally-funded financial aid
  • Be enrolled full-time in a degree program. Full-time is a minimum of 12 units, determined fifteen days into the semester
    • If a student is enrolled in less than 12 units, the Franken scholarship must be billed back
    • If the scholarship is reduced, it can not be reinstated at a later date
  • Students must maintain a 2.0 cumulative GPA. Transfer students may have their initial eligibility based on units from their previous institution

What’s wrong with this? Lists are supposed to make content easy to read, right? But somehow this content isn’t easy to read at all, it’s a real pain.

But where did this list go wrong? When a reader encounters a list within a body of text, they expect the list to abide by certain rules. But when a list violates this implicit contract, it’s not a list, it’s just a series of staccato paragraphs surrounded by unnecessary margins and bullet images.

So what makes a good list? Let’s talk about it.

Keep Lists Short

In general, a writer includes a list to provide a quick summary of the major points of a topic. However, if the list includes a large number of items or the text within each item is too long, the list is no longer fulfilling its purpose. In other words, what’s the point of an outline if the outline is almost as long as the source content?

As a general rule, try to keep the number of items within a list to around five and keep the text in each list item on one line. Of course, I don’t have any research to back this up and there are exceptions to every rule, but we have to start somewhere.

Keep List Items Consistent

Let’s look at another example:

To make a sandwich, you’ll need:

  • 2 slices of bread
  • 1 leaf of lettuce
  • 1 Slice of Cheese. Cheddar is a nice, basic choice, but you should also try spicing things up with Pepper Jack!
  • Some tomato
  • Lunch meat (not necessary if you want a vegetarian sandwich)

It’s really difficult to grasp the overall structure and pattern of the list: it’s a jumble of choppy, discordant sentences. Within a list, each list item should have a similar format. This provides the list with a patterned flow that will make it easier to read and understand:

To make a sandwich, you’ll need:

  • 2 slices of bread
  • 1 leaf of lettuce
  • 1 slice of cheese
  • 2 slices of tomato
  • 1 piece of lunch meat (optional)

Now the list is much easier to read; the reader knows what to expect moving from one list item to another. You may scoff: but what about that whole cheese aside? You completely omitted it! Ask yourself: does someone skimming your list care about that? If it’s so important, put it in a later paragraph or maybe, after streamlining the list, you’ve realized the content wasn’t so important after all.

List Items Should Be Atomic

Each list item should focus on one thing. There’s no exception to this. This one thing could be anything—a book title, a programming tip, a color—but each list item should only contain one of these things.

Avoid Nested Lists

Of course there’s going to be exceptions to this rule, but avoid putting a list inside another list. This is like having two conversations at once. It’s confusing. Why not put that inner list into its own list and reference it from the outer list?

Fit Content to Lists

Finally, remember that the mere presence of a list does not make content easier to read and understand. If a particular piece of content won’t abide by the above rules, then rewrite the content. If you still can’t make the content work as a list, then maybe it shouldn’t be a list. Really, there’s nothing wrong with a series of concise paragraphs.

If readers were having a trouble parsing your website’s content, shoving black circles next to that same content won’t make it any easier. Respect the list.

Posted in Writing | 2 Comments

Unobtrusive Validation in ASP.NET 4.5

While listening to the 2011 BUILD presentations, I overheard one of the speakers talking about “unobtrusive validation” in ASP.NET 4.5 Web Forms. Well, I had to see this for myself.

Let me give you a little background: validation on the web is a tricky business. To ensure the integrity of data feeding into your web application, you definitely want to validate User input server-side, but to create a responsive experience for your User, you also want to validate the input values through client-side JavaScript. Thus, you end up writing duplicate validation code which can become a real maintenance headache.

ASP.NET Web Forms “solves” this dilemma, by allowing you to put a Validator Server Control on your page:

<asp:TextBox ID="Username" runat="server"></asp:TextBox>

<asp:RequiredFieldValidator
	ErrorMessage="Username is required!"
	ControlToValidate="Username"
	runat="server"></asp:RequiredFieldValidator>

<asp:RegularExpressionValidator
	ErrorMessage="Username can only contain letters!"
	ControlToValidate="Username"
	ValidationExpression="^[A-Za-z]+$"
	runat="server"></asp:RegularExpressionValidator>

These Validator Server Controls handle both the client-side and server-side validation for you. That’s right: you define the validation rules in one place, and everything is taken care of for you. Neat, right? The generated markup has some inline CSS, but it’s not terrible:

<input name="Username" type="text" id="Username" />

<span id="ctl02" style="color:Red;">Username is required!</span>

<span id="ctl03" style="color:Red;visibility:hidden;">Username can only contain letters</span>

Unfortunately, to make this all work, a mess of inline JavaScript is dumped onto your page:

<script type="text/javascript">
//<![CDATA[
var Page_Validators =  new Array(document.getElementById("ctl02"), document.getElementById("ctl03"));
//]]>
</script>

<script type="text/javascript">
//<![CDATA[
var ctl02 = document.all ? document.all["ctl02"] : document.getElementById("ctl02");
ctl02.controltovalidate = "Username";
ctl02.errormessage = "Username is required!";
ctl02.isvalid = "False";
ctl02.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
ctl02.initialvalue = "";
var ctl03 = document.all ? document.all["ctl03"] : document.getElementById("ctl03");
ctl03.controltovalidate = "Username";
ctl03.errormessage = "Username can only contain letters";
ctl03.evaluationfunction = "RegularExpressionValidatorEvaluateIsValid";
ctl03.validationexpression = "^[A-Za-z]+$";
//]]>
</script>

<script type="text/javascript">
//<![CDATA[

var Page_ValidationActive = false;
if (typeof(ValidatorOnLoad) == "function") {
	ValidatorOnLoad();
}

function ValidatorOnSubmit() {
	if (Page_ValidationActive) {
		return ValidatorCommonOnSubmit();
	}
	else {
		return true;
	}
}
//]]>
</script

Gross.

Keep in mind that this is in addition to a whole separate external script that includes all of the core validation logic. What’s even going on here? Let’s break it down:

  1. A Page_Validators array is created to contain all of the validator elements on the page
  2. Properties (like “errormessage” and “initialvalue”) are added to those same validator elements
  3. Validation is initialized (via ValidatorOnLoad)

So how does ASP.NET 4.5 clean things up? Instead of dumping inline JavaScript onto the page, ASP.NET 4.5 includes some new client-side scripting that piggybacks on the HTML5 custom data attributes. If you’re not familiar, with HTML5 you can add arbitrary descriptive attributes to an HTML element by prefixing these custom attributes with “data-”:

<span class="game" data-id="3" data-comment="Fun, but takes a long time">Monopoly</span>
<span class="game" data-id="4" data-comment="Nerve-wracking!">Jenga</span>

In this example we tack on “id” and “comment” attribute values to provide some additional data that we can use in our client-side scripting. ASP.NET 4.5 runs with that idea to implement a more “unobtrusive” approach to validation. Here’s the generated markup in ASP.NET 4.5:

<span
	data-val-controltovalidate="Username"
	data-val-errormessage="Username is required!"
	id="RequiredFieldValidator1"
	data-val="true"
	data-val-evaluationfunction="RequiredFieldValidatorEvaluateIsValid"
	data-val-initialvalue=""
	style="visibility:hidden;">Username is required!</span>

<span
	data-val-controltovalidate="Username"
	data-val-errormessage="Username can only contain letters!"
	id="RegularExpressionValidator1"
	data-val="true"
	data-val-evaluationfunction="RegularExpressionValidatorEvaluateIsValid"
	data-val-validationexpression="^[A-Za-z]+$"
	style="visibility:hidden;">Username can only contain letters!</span>

As you can see, all those properties (Step 2 from earlier) have now been flipped into HTML5 custom data attributes. And the best part: no inline JavaScript!

But where did all of that inline JavaScript go? Earlier I mentioned that all of that messy inline JavaScript was in addition to an external script containing the core validation script. The client-side scripting to create the Page_Validators array (Step 1 from before) and initialize the validation (Step 3) has been rolled up into that external script and wrapped within an anonymous function. Here’s the abridged code:

// If jQuery is available, create an anonymous function that executes immediately
if (window.jQuery) { (function ($) {

var dataValidationAttribute = "data-val"

function parse(selector) {
	// parseSpecificAttribute searches for elements with "data-val" set to "true"
	var length = parseSpecificAttribute(selector, dataValidationAttribute, Page_Validators);
	return length;
}

function loadValidators() {
	// Look at the similarities between this code and the inline code from earlier
	if (typeof (ValidatorOnLoad) === "function") {
		ValidatorOnLoad();
	}
	if (typeof (ValidatorOnSubmit) === "undefined") {
		window.ValidatorOnSubmit = function () {
			return Page_ValidationActive ? ValidatorCommonOnSubmit() : true;
		};
	}
}

$(function () {
	if (parse(document)) {
		loadValidators();
	}
});

} (jQuery)); }

Actually, believe it or not, it appears that this addition to the script has been sitting out there since ASP.NET 4.0 (look for yourself). I wonder if this feature was planned to be released at an earlier time? (Or is it a secret option nested deep somewhere?)

UPDATE: I was not aware that ASP.NET 4.5 is an in-place update to ASP.NET 4.0. This is likely the reason why I see the extra scripting in my ASP.NET 4.0 projects.

Either way, it’s definitely a step in the right direction for ASP.NET Web Forms, and I would love to see this trend continue.

Unfortunately, I have to be a jerk here and admit that I’m not completely satisfied: I would argue that most of the validation information shouldn’t be stored within the HTML at all. The validation error messages, for example, would be better stored in a separated JavaScript data structure. Right now, we’ve essentially replaced inline client-side script bloat with data attributes bloat.

Furthermore, it would be nice to see some integration with some of the HTML5 input validation attributes: HTML5 includes both a “pattern” attribute and a “required” attribute that perform the same function as the validators used in the examples above (in the browsers that support those attributes, of course).

Posted in Development | Tagged , , , , | Leave a comment

Developing a Web Application Locally with IIS7 and Web Developer Express

This post will guide you through developing a web application locally with IIS7 and Web Developer Express.

1. Install IIS7

Of course, the first thing we’ll need to do is install Internet Information Services (IIS). IIS will be the web server that hosts our web application.

2. Install Visual Web Developer 2010 Express

Next, we’ll install Visual Web Developer 2010 Express, the free integrated development environment (IDE) that we’ll use to build our web application.

3. Create the Web Application

Open up Visual Web Developer and select “File” > “New Project…”. For this tutorial, create a new project with the “ASP.NET Empty Web Application” template under “Visual C#” > “Web” (or “Visual Basic” > “Web”, if you prefer Visual Basic):

Creating a new web application project in Visual Studio

Let’s create a page so we have something to look at when we debug our web application. Go to “Project” > “Add New Item…”, choose “Web Form”, and name it “Default.aspx”:

Adding a Default.aspx to the project

Add a little text to the page (right between the body tags):

<body>
	<form id="form1" runat="server">
	<div>
		Welcome to my website!
	</div>
	</form>
</body>

If you want, you can actually stop at this point. Visual Web Developer contains a built-in server that you can use right away. Just click the green arrow at the top (or press “F5″) and your default web browser should pop open, displaying your new web application. However, often times you’ll want your local development environment to closely match the environment of your actual web application. In other words, if your web application will be hosted on Apache, you’ll want the local web application running on Apache (using something like WampServer). For our purposes, if our web application’s going to be running on IIS, we should host our local web application on IIS.

Before we move onto the next step, make sure you build your web application (go to “Debug” > “Build {Your Project’s Name}”).

4. Create a Local DNS Mapping

Let’s create a local DNS mapping so we can access our local web application through a friendly URL. Open up your hosts file (located in %systemroot%\system32\drivers\etc\) and add a line like the following:

127.0.0.1	example.local

This will redirect all requests to “example.local” back to our local IIS. I like to use “local” as the top-level domain for all of my local websites, but you can use whatever hostname you want here: “examplewebsite.com,” “mywebsite.local,” or “example.sandwich.”

5. Add a Website in IIS

Alright, we’re almost there, now let’s get our web application hosted! Open IIS Manager, right-click the “Sites” node in the left-hand menu and select “Add Web Site…”:

Adding a new website in IIS Manager

For simplicity, use the hostname you chose in Step 4 as your “Site name” and provide that value under the “Host name” field as well. Finally, in the “Physical path” field, provide the directory containing the “Default.aspx” file you created in Step 3:

Configuring the new website in IIS Manager

6. Test Your Web Application

Alright, now let’s test our handiwork. Pop open a browser and navigate to your web application (using the hostname you provided in Step 4). If everything’s working, you should see your Default.aspx page. Congratulations!

7. Debug Your Web Application

The real power of locally developing a web application in IIS comes with debugging your web application’s code right from Visual Studio. Head back into Visual Web Developer, open “Solution Explorer” (go to “View” > “Other Windows” > “Solution Explorer”), and open the “Default.aspx.cs” page (you may need to expand “Default.aspx” to find it):

The Solution Explorer in Visual Web Developer Express

Add the following code to the Page_Load method:

protected void Page_Load(object sender, EventArgs e)
{
	string rawUrl = Request.RawUrl;
}

Add a breakpoint (by clicking in the left sidebar alongside the code) on the closing brace of the Page_Load method:

Adding a breakpoint

Now go back into “Solution Explorer”, right-click the root node of your web application, and select “Properties”. This will open the settings screen for your web application. Select the “Web” tab on the left-hand menu and find the “Use Local IIS Web Server” option (under “Servers”). Select this option and provide your web application’s URL in the “Project URL” field:

Setting up debugging for local IIS

Now try pressing the green arrow at the top or F5 to start debugging. Your web browser should fly open and Visual Studio will display the “Default.aspx.cs” file, stopping at your breakpoint. Awesome, now you’ve got a web application running on your local IIS and you’re debugging that same web application right within Visual Web Developer Express!

Posted in Development | Tagged , , | Leave a comment

Create an Iframe Portal

To get a little practice with CSS3 keyframe animations, let’s do something a little strange: create an iframe portal like the portals in Portal. Check out the demo in either Firefox or Chrome (click anywhere to shoot a portal). Here’s a screenshot of the the end result:

Iframe Portal

To start, let’s make an iframe that looks like a portal. Here’s our markup:

<div class="portal-wrapper">
	<iframe src="" frameborder="0" scrolling="no" class="portal"></iframe>
</div>

We need the wrapper div so that we can expand the dimensions of our portal from the center (this will make sense in a minute). Here’s the CSS:

.portal-wrapper
{
	display: none;
	height: 500px;
	position: absolute;
	text-align: center;
	width: 200px;
}

.portal
{
	border-radius: 100px;
	box-shadow: 0 0 10px #0071F5,
			0 0 20px #0071F5,
			0 0 40px #0071F5,
			0 0 60px #0071F5;
}

If you’re like me, you might be wondering: wait, we can use border-radius on an iframe? Yeah, apparently you can and it works pretty well. We use a high border-radius to round the edges and use multiple box-shadows to create a soft blue glowing effect.

If we wanted this to look more like a portal from the actual game, then we would probably want to use an inset box-shadow to make the glow appear over the actual content of the iframe. However, this won’t work with an iframe (it just doesn’t display).

Now let’s set up the animations. First, we’ll create an animation for opening the portal:

@-webkit-keyframes portal-open
{
	0%
	{
		margin-top: 250px;
		height: 0;
		width: 0;
	}

	100%
	{
		margin-top: 0;
		height: 500px;
		width: 200px;
	}
}

With this declaration, we are creating a simple animation (called “portal-open”) that expands the portal from no width and height to its full size. We also animate the top margin so that the portal will expand from the center. We apply that animation with the following CSS:

-webkit-animation-name: portal-open;
-webkit-animation-duration: 1s;
-webkit-animation-timing-function: ease;

We can summarize that in one line:

-webkit-animation: portal-open 1s ease;

Let’s create another animation:

@-webkit-keyframes portal-active
{
	0%
	{
		box-shadow: 0 0 10px #0071F5,
					0 0 20px #0071F5,
					0 0 40px #0071F5,
					0 0 60px #0071F5;
	}

	50%
	{
		box-shadow: 0 0 0 #0071F5,
					0 0 10px #0071F5,
					0 0 30px #0071F5,
					0 0 50px #0071F5;
	}

	100%
	{
		box-shadow: 0 0 10px #0071F5,
					0 0 20px #0071F5,
					0 0 40px #0071F5,
					0 0 60px #0071F5;
	}
}

With this animation, we dim the surrounding box-shadow and then revert it to create a light glow animation around the portal’s edges. We’ll apply it with the following CSS:

-webkit-animation-name: portal-active;
-webkit-animation-duration: 1.5s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;

We can also get this down to one line:

-webkit-animation: portal-active 1.5s linear infinite;

Now we just add the following to our CSS to trigger both animations:

-webkit-animation: portal-open 1s ease,
			portal-active 1.5s linear infinite;

Of course, we’ll want to trigger these animations through JavaScript. There’s a few different ways we could do this. We could attach the animation properties to elements in the CSS and then flip the animation-play-state from “paused” to “running.” However, the specification suggests that this property may eventually be deprecated and we can’t easily reset an animation with this property.

An alternative method would be to simply add the CSS animation properties using JavaScript. But this is messy; we want to keep our CSS separate from our JavaScript.

Our last option is to toggle the animation-name property. With this method we can declare everything in our CSS and then toggle that property within our JavaScript:

$(function() {

	var PORTAL_HEIGHT = 500;
	var PORTAL_WIDTH = 200;

	var ANIM_PORTAL_OPEN = 'portal-open, portal-active';
	var ANIM_PORTAL_CLOSE = 'portal-close';

	var $portalWrapper = $('.portal-wrapper');
	var $portal = $('.portal');

	$(document).click(function(event) {

		// Clear the animations
		$portal.css({
			'-webkit-animation-name': ANIM_PORTAL_CLOSE,
			'-moz-animation-name': ANIM_PORTAL_CLOSE,
			'-ms-animation-name': ANIM_PORTAL_CLOSE,
			'animation': ANIM_PORTAL_CLOSE
		});

		// Retrieve the User's mouse position
		var mouseX = event.pageX;
		var mouseY = event.pageY;

		// Position and display the portal
		$portalWrapper
			.hide()
			.css({
				'top': mouseY - PORTAL_HEIGHT/2,
				'left': mouseX - PORTAL_WIDTH/2
			})
			.show();

		// Trigger the animations
		$portal.css({
			'-webkit-animation-name': ANIM_PORTAL_OPEN,
			'-moz-animation-name': ANIM_PORTAL_OPEN,
			'-ms-animation-name': ANIM_PORTAL_OPEN,
			'animation': ANIM_PORTAL_OPEN
		});

	});

});

Since we declared everything in our CSS (including the animation-name), we need to add a completely different value (“portal-close”) to clear the animations. If we provide a blank value, the animation-name property will default to the value defined in our CSS, which will keep our open animation from resetting.

Posted in Design, Development | Tagged , , , | Leave a comment

Responsive vs Adaptive Web Design

Here’s the quick and dirty difference:

  • A responsive design fluidly changes to fit any browser size
  • An adaptive design changes to fit a defined set of browser sizes

Further Reading

For a little more context and explanation (along with the benefits to either approach), here are some resources on the difference:

Examples of Adaptive Designs

At the end of the day, it’s best to see it for yourself. Here are some examples of adaptive designs. Slowly resize your browser and notice the sudden shifts in the layout:

Examples of Responsive Designs

In these examples, notice how the layout changes almost immediately to fit the browser size:

Examples were pulled from this excellent post.

Posted in Design | Tagged , , | 1 Comment

Extending the UCLA Mobile Web Framework Geolocation API

If you tried out my UCLA Mobile Web Framework (MWF) treasure hunt demo, you may have noticed that the geolocation was a little spotty. Depending on your phone, service provider, geolocation settings, or any number of other variables, you might be (virtually) digging up treasure in a completely different location than someone (physically) standing right next to you.

I was chatting with one of my buddies at work (Justin Mead), and he explained that the geolocation’s intermittent inaccuracy was because the demo was using getCurrentPosition, rather than polling with watchPosition. watchPosition is a handy little function in the Geolocation API that continuously checks the User’s current position and fires off a function when their position changes. A User’s position could change when he or she moves, but could also change as their mobile device uses different techniques to more accurately determine their location.

So, to extend my demo, I added a getExactPosition function to the UCLA MWF geolocation API:

mwf.touch.geolocation.getExactPosition = function(minAccuracy, timeout, onSuccess, onError) {

	var geo;

	switch(this.getType())
	{
		case 1:
			geo = navigator.geolocation;
			break;
		case 2:
			geo = google.gears.factory.create('beta.geolocation');
			break;
		default:
			onError && onError('No geolocation support available.');
			return;
	}

	var watchID = geo.watchPosition(
		function(position) {

			if(position.coords.accuracy <= minAccuracy) {
				navigator.geolocation.clearWatch(watchID);
				onSuccess && onSuccess({
					'latitude': position.coords.latitude,
					'longitude': position.coords.longitude,
					'accuracy': position.coords.accuracy
				});
			}

		},
		function() {
			onError && onError('Geolocation failure.');
		},
		{
			enableHighAccuracy: true,
			timeout: timeout
		}
	);

	return true;
};

You'll notice that this closely mimics UCLA MWF's getPosition function. Like the original, this new function calls callback functions upon a successful retrieval of geolocation data (onSuccess) and also upon failure (onError). Also like the original, this function uses Google Gears if the HTML5 Geolocation API is not supported.

Now, let's bite into the meat of this new function: rather than directly calling getCurrentPosition, this new function begins continuously polling the User's position with watchPosition. After each successful polling attempt, an anonymous function is called that checks whether or not the current reading of the User's geolocation API has an accuracy value less than or equal to the specified minAccuracy (which is measured in meters). Upon retrieving geolocation data with appropriate accuracy, clearWatch is called to stop the constant polling. Finally, if this process takes longer than the provided timeout parameter, the error callback function is called.

This, of course, has the negative side effect of slowing down the application as a mobile device may take a considerable amount of time to determine the User's location within a tight accuracy range. In a real application, if exact geolocation data was not necessary, we would probably want to begin by using the initial geolocation data and then progressively enhance the application as more accurate geolocation data become available. At the very least, it would be helpful to provide the User with some feedback as to the current progress of determining their position.

Posted in Development | Tagged , , | Leave a comment

Device Classification in the UCLA Mobile Web Framework

UPDATE: The device classifications covered in this article are now fully documented. As the UCLA Mobile Web Framework is rapidly evolving, you’re better off getting the information straight from the source.

The UCLA Mobile Web Framework (MWF) classifies devices into three basic categories: basic, standard, and full. These categories are hierarchical (a “full” device can do everything a “standard” device can) and serve an important purpose within the framework by determining what JavaScript and CSS are dynamically loaded by the framework.

While these categories are not fully documented yet, we can peek at the source code and see how the framework classifies each device:

Basic

Every device (including a modern smart phone, an old Nokia phone with rudimentary Internet access, and a desktop computer) is considered basic. Basic devices will load the basic stylesheets, the core JavaScript libraries, and any custom JavaScript or CSS specified for basic devices. Unless disabled, the utility libraries will also load (Google Analytics and the script for enabling the favicon), although they may not work on basic devices.

Standard

A standard device supports:

  • Cookies
  • JavaScript DOM modification
  • JavaScript events with addEventListener

A standard device will load everything that a basic device will load, any custom JavaScript or CSS specified for standard devices, and any of the standard framework libraries (if you enable them). Right now, the only standard framework library is the geolocation library.

Full

A full device supports all of the things a standard device supports and:

  • Ajax
  • CSS3 border radius
  • CSS3 box shadow
  • CSS3 gradients

As you can probably guess, a full device will load everything that a standard device loads, any custom JavaScript or CSS for full devices, and any enabled full framework libraries. The full libraries include the transitions library, the touch transitions library, and a script for including the Apple touch icon.

Other Classifications

Beyond these three categories, the framework also uses two additional categories: iPhone and preview. If the framework determines a device is an iPhone, then two additional scripts are loaded: one for hiding the Safari address bar and another for resizing the page on orientation.

Finally, if the User is in preview mode, the device loads additional scripts for displaying a preview menu that allows the User to manually switch among these device categories.

How It Works

If you’re wondering how all of this device detection works, it’s a pretty clever little process:

  1. User visits a page generated by the UCLA MWF
  2. Framework determines that a device information cookie has not been set
  3. Framework JS library detects the device’s capabilities (using Modernizr), and determines the device category
  4. Framework JS library stores this information in a cookie and reloads the page
  5. Server-side framework library pulls the information from the cookie and uses it to serve dynamic JS and CSS

Example

Let’s wrap up all of this up with a quick example. Let’s say you have the following:

<script type="text/javascript" src="http://m.ucla.edu/assets/js.php?
	standard_libs=geolocation
	&full=http%3A%2F%2Fwww.treasure.loganfranken.com%2Fscript%2Fmain.js
	"></script>

In this example, the geolocation library will only load up for standard and full devices, while the external script (main.js) will only load for full devices.

Posted in Development | Tagged | 2 Comments

Set Up UCLA Mobile Web Framework on WampServer

UPDATE: If you read this post before, you might be wondering where all of the stuff about WURFL went. Well, since version 1.2, the UCLA Mobile Web Framework no longer depends on WURFL. This actually makes the installation process much smoother. Device capabilities are now determined by storing information from Modernizr in a cookie.

I wanted to set up my own local copy of the UCLA Mobile Web Framework (UCLA MWF) on WampServer. It was a pretty straightforward process, but I wanted to detail it anyway because I think it gives good insight on the internals of the framework.

1. Download and install WampServer

If you’ve never used WampServer before, you’re going to love it. It’s Apache, MySQL, and PHP all rolled up into a single installation for Windows.

2. Get a copy of the UCLA Mobile Web Framework

You’ll probably want to pull down your own copy of the framework from the GitHub repository. If you need help with git, GitHub has some wonderful tutorials. Git Immersion is also a fantastic resource.

Alternatively, you can just directly download the latest version of the UCLA MWF.

Either way, you’ll want to put your copy (or your local repository) of the UCLA MWF in C:\wamp\www\mwf.

3. Create the required directories

The UCLA MWF requires a few directories (detailed in the System Installation wiki article). These directories will be located directly under the base directory (C:\wamp\www\mwf\).

  • var\mobile
  • var\mobile\cache
  • var\mobile\cache\img
  • var\mobile\cache\simplepie

cache\img is where the compressed images that get fed through the image compressor will be stored. Similarly, cache\simplepie is the cache used by the feed API (the UCLA MWF’s feed API uses SimplePie).

4. Set up the domain name

With a default installation of WampServer, we can access the framework in a browser via http://localhost/mwf/root. Let’s set up a more direct URL to the root.

First, add the following entry to the end of your hosts file:

127.0.0.1	uclamwf.local

This will redirect all requests to the URL “uclamwf.local” back to your local machine (127.0.0.1 is the IP address corresponding to localhost). Keep in mind you could use any name you want here: “mwf.local”, “uclamwf.com”, whatever you like.

Now that we have the requests coming back to the local machine, we need WampServer to pick up the requests and direct them to the correct place. Find the Apache configuration file httpd.conf in C:\wamp\bin\apache\Apache2.2.17\conf (your Apache version may differ), and make the following edits (source):

Find the following line:

Listen 80

And change it to:

Listen *:80

Next, add the following to the end of the file:

NameVirtualHost *:80
<VirtualHost 127.0.0.1>
	DocumentRoot "c:/wamp/www"
	ServerName localhost
</VirtualHost>

<VirtualHost 127.0.0.1>
	DocumentRoot "c:/wamp/www/mwf/root/"
	ServerName uclamwf.local
</VirtualHost>

Start up WampServer, navigate to http://uclamwf.local/, and you should get an error (this is a good thing!).

5. Configure the framework

To round off this installation, we just need to edit a few configuration details. Open global.php in C:\wamp\www\mwf\config, find where site_url and site_assets_url are defined, and update them with the following values:

Config::set('global', 'site_url', 'http://uclamwf.local/');
Config::set('global', 'site_assets_url', 'http://uclamwf.local/assets/');

And we’re finished! Now you can navigate to http://uclamwf.local in your browser. Furthermore, now you can start referencing the local copy of your library within your projects:

<link rel="stylesheet" href="http://uclamwf.local/assets/css.php" type="text/css">
<link rel="stylesheet" href="http://uclamwf.local/assets/js.php" type="text/css">

6. Additional configuration options

We’ve configured everything required by the framework, but there are a few optional pieces left to set up. If we want to make full use of image compression, we’ll need to change the cache path in image.php (in the config directory):

Config::set('image', 'cache_dir', 'C:/wamp/www/mwf/var/mobile/cache/img/');

Similarly, we’ll need to update the path for the feed API as well (feed.php in auxiliary/feed.php):

Config::set('auxiliary/feed', 'cache_path', 'C:/wamp/www/mwf/var/mobile/cache/simplepie');

We’re all set! Now you can start playing around with your own working local copy of the framework.

Posted in Development | Tagged , , | Leave a comment

Create a Treasure Hunt Game With the UCLA Mobile Web Framework

The UCLA Mobile Web Framework (UCLA MWF) is a cross-platform mobile web framework from UCLA. It allows developers to quickly create mobile websites with some basic markup and a few CSS classes. To test it out, I created a small demo, called Treasure Hunt. It’s a simple mobile game where you can bury treasure in a certain spot (using geolocation) and someone else can dig it up. Fork it on GitHub!

The UCLA MWF is wonderfully easy to set up. You start by adding a few simple bits of markup in the head:

<meta name="viewport" content="height=device-height,width=device-width;
	initial-scale=1.0; maximum-scale=1.0; user-scalable=no;" />
<meta name="format-detection" content="telephone=no" />

As explained in the Markup Mobile Entities Wiki entry, the first meta tag helps ensure a consistent experience across the various mobile devices. “width=device-width” and “initial-scale=1.0″ sets the default width of the viewport to fit to the device’s width (rather than having the device assume the page is designed for desktops and zooming out). “maximum-scale=1.0″ and “user-scalable=no” prevent the User from zooming in and out, which will be unnecessary as the UCLA MWF page will fit like a glove in the device’s viewport.

The second meta tag isn’t crucial, but it will prevent newer devices from automatically (and possibly erroneously) converting numbers to links unless we explicitly specify a phone number using the “tel:” syntax.

Next we include the CSS and JavaScript required by the framework:

<link rel="stylesheet" href="http://m.ucla.edu/assets/css.php" type="text/css">
<script type="text/javascript" src="http://m.ucla.edu/assets/js.php
	?no_ga&no_icon&standard_libs=geolocation"></script>

This is where UCLA MWF really shines: as you can see, we aren’t directly referencing a CSS or JS file, we are referencing a PHP file. This PHP handler checks with the WURFL repository to determine the capabilities of the User’s current device and returns different CSS and JS depending on the device (as of version 1.2, this is no longer true). As a result, while a newer smartphone will receive the full experience (touch transitions, gradients, etc.), an older, simpler device will receive a slightly degraded experience more appropriate for their phone. However, here’s the kicker: both phones receive the same content and the developer only provides one set of markup.

Note that the UCLA MWF is intended to be hosted on your own server (in other words, don’t pull resources from UCLA’s servers for any real development). To that end, Google Analytics is enabled by default (so you can quickly report visitor statistics to a centralized Google Analytics configuration). For this demo, we’re going to disable it with the “no_ga” in the query string for the JS handler. Similarly, we add “no_icon” to remove the default UCLA MWF favicon. Of course, the whole crux of this demo is the geolocation, so we add “standard_libs=geolocation” to include the geolocation library.

The UCLA MWF also contains a nifty minifier, although you need to provide the encoded absolute path to the stylesheet/script to the handler:

<link rel="stylesheet" href="http://m.ucla.edu/assets/min/css.php?
	standard=http%3A%2F%2Ftreasure.loganfranken.com%2Fstyle%2Fmain.css"
	type="text/css" />

Now we can add in our markup:

<div class="content-full content-padded">
	<h1 class="content-first light">Nearby Treasure</h1>
	<div class="content-last center">
		<p class="status center">Treasure Hunt!</p>
	</div>
</div>

<a href="index.php?action=dig" class="button-full button-padded">Dig</a>
<a href="index.php?action=search" class="button-full button-padded">Search</a>
<a href="index.php?action=bury" class="button-full button-padded">Bury</a>

As you can see, we just put together a little bit of markup and we get the following:

UCLA MWF Treasure Hunt Standard

Which, on basic devices, degrades to:

UCLA MWF Treasure Hunt Basic

Finally, to get at the geolocation data, the UCLA MWF provides a straightforward getPosition function that either uses Google Gears or the standard HTML5 geolocation library, depending on the device’s capabilities:

// Retrieve location
mwf.touch.geolocation.getPosition(
	function(pos) {

		// Send dig request
		self.model.requestDig(
			pos['latitude'],
			pos['longitude'],
			function(result) { ... }
		);

	},
	function(err) {

		// Retrieving location failed
		self.view.showStatus('Digging failed. Geolocation isn\'t enabled',
			true);
		return;

	}
);

You can, of course, dig into the full source code on GitHub and, more importantly, you can play the Treasure Hunt demo right now!

The UCLA MWF is off to a great start, and I’m excited to watch it evolve. They have a growing base of support and adoption from other universities and their commitment to a “least common denominator” mobile platform should be reassuring to institutions looking for a more accessible mobile framework.

Posted in Development | Tagged , , , , | 3 Comments

HTML5 Canvas Balloon

Today we’re going to draw a balloon on the HTML5 canvas element. Why not, right? Here’s the final result:

HTML5 Canvas Balloon

The balloon shape is basically a circle stretched out in various places. However, it’s not that straightforward to simply draw a circle and then stretch it with the canvas API. Instead, we need to recreate a circle using four cubic Bézier curves:

Circle Bezier Curve Count

After beginning this process, I discovered that it is actually impossible to create a perfect circle using cubic Bézier curves. Instead, you can only get a very close approximation using a constant called “kappa”. So to calculate the length of the “handles” (or the distance from a control point to a corresponding point on the curve), we use the following formula:

var KAPPA = (4 * (Math.sqrt(2) - 1))/3;
var handleLength = KAPPA * radius;

Now we can create a near-perfect circle using four, separate cubic Bézier curves, created with the bezierCurveTo method:

// Begin balloon path

gfxContext.beginPath();

// Top Left Curve

var topLeftCurveStartX = centerX - radius;
var topLeftCurveStartY = centerY;

var topLeftCurveEndX = centerX;
var topLeftCurveEndY = centerY - radius;

gfxContext.moveTo(topLeftCurveStartX, topLeftCurveStartY);
gfxContext.bezierCurveTo(topLeftCurveStartX, topLeftCurveStartY - handleLength,
			topLeftCurveEndX - handleLength, topLeftCurveEndY,
			topLeftCurveEndX, topLeftCurveEndY);

// Top Right Curve

var topRightCurveStartX = centerX;
var topRightCurveStartY = centerY - radius;

var topRightCurveEndX = centerX + radius;
var topRightCurveEndY = centerY;

gfxContext.bezierCurveTo(topRightCurveStartX + handleLength, topRightCurveStartY,
			topRightCurveEndX, topRightCurveEndY - handleLength,
			topRightCurveEndX, topRightCurveEndY);

// Bottom Right Curve

var bottomRightCurveStartX = centerX + radius;
var bottomRightCurveStartY = centerY;

var bottomRightCurveEndX = centerX;
var bottomRightCurveEndY = centerY + radius;

gfxContext.bezierCurveTo(bottomRightCurveStartX, bottomRightCurveStartY + handleLength,
			bottomRightCurveEndX + handleLength, bottomRightCurveEndY,
			bottomRightCurveEndX, bottomRightCurveEndY);							

// Bottom Left Curve

var bottomLeftCurveStartX = centerX;
var bottomLeftCurveStartY = centerY + radius;

var bottomLeftCurveEndX = centerX - radius;
var bottomLeftCurveEndY = centerY;

gfxContext.bezierCurveTo(bottomLeftCurveStartX - handleLength, bottomLeftCurveStartY,
			bottomLeftCurveEndX, bottomLeftCurveEndY + handleLength,
			bottomLeftCurveEndX, bottomLeftCurveEndY);

Once we have our circle in place, we can stretch out the curves to create the balloon shape. I just fiddled around and came out with arbitrary values for the factors used to stretch the shape. For example, I stretched out the bottom using a heightDiff value, calculated as follows:

var heightDiff = (radius * 0.4);

We can now modify the above circle equation to use these values:

var balloonBottomY = centerY + radius + heightDiff

// Updated Bottom Right Curve Value
var bottomRightCurveEndY = balloonBottomY;

// Updated Bottom Left Curve Value
var bottomLeftCurveStartY = baloonBottomY;

To add color to the balloon, I used a radial gradient with, again, some arbitrary values thrown in there:

var gradientOffset = (radius/3);

var balloonGradient =
	gfxContext.createRadialGradient(centerX + gradientOffset,
					centerY - gradientOffset, 3,
					centerX, centerY,
					radius + heightDiff);

	balloonGradient.addColorStop(0, this.lightColor.rgbString());
	balloonGradient.addColorStop(0.7, this.darkColor.rgbString());

	gfxContext.fillStyle = balloonGradient;
	gfxContext.fill();

To calculate the gradient colors, I found this fantastic color manipulation library, that makes working with colors a snap:

this.baseColor = new Color(color);
this.darkColor = (new Color(color)).darken(0.3);
this.lightColor = (new Color(color)).lighten(0.3);

Check out the full script!

Posted in Development | Tagged , , | Leave a comment