Archive for the ‘ajax’ Category

Taking your webapp offline

Sunday, August 21st, 2011

I’ve recently done a few things that would benefit from having an offline mode. Empowered by Appcache Facts I tried to make the newly published La Vuelta 2011 results page an offline-capable app.

Goal

The goal of this exercise is twofold:

  1. To force caching of resources that slow down the loading of your page.
  2. To actually be able to use the app when offline.

Solution

It’s as simple as writing a cache manifest. It pays to name it [something].manifest, as Apache already serves the correct header with that suffix. What you do is list all the files you want cached, add stuff you allow on the network (check Appcache Facts and Dive Into HTML5 – Offline Web Applications) and it all works. Except it doesn’t.

CACHE MANIFEST
# version 1

CACHE:
resource.js

NETWORK:
*
http://*
https://*

AJAX stuff

If you’re using AJAX to get data from a server using jsonp/script, your framework of choice (jQuery in my case) will probably default to using a no-cache approach to loading it. This will mean that it will request the file + some suffix to prevent browser caching. This will mean that the resource will not be available if you’re actually online.

You can use navigator.onLine to switch cache on/off, but I suggest you first try requesting the no-cache resource and if it errors out, request the cached resource. The benefit is that even if you are online but the server is not, the users will still see the data / use the app.

$.ajax({
	dataType: 'script',
	url: 'resource.js',
	cache: false,
	success: successHandler,
	error: function () {
		$.ajax({
			dataType: 'script',
			url: 'resource.js',
			cache: true,
			success: successHandler
		});
	}
});

iPad issues

Fixing the AJAX meant that it worked properly on the desktop (test with Firefox that has offline mode) and in Safari on the iPad. The trouble started when I added the app to Home Screen – data failed to load. It was the same app as in Safari and it should have worked.

After some debugging I found out that the data actually was loaded but the eval failed (I was using $.getScript). Some weird testing showed that the problem was a newline character in the data. As I really liked the newline there I added some code to the error handling that removed the newline and evaled the data, then ran success. And it worked!

$.ajax({
	dataType: 'script',
	url: 'resource.js',
	cache: false,
	success: successHandler,
	error: function () {
		$.ajax({
			dataType: 'script',
			url: 'resource.js',
			cache: true,
			success: successHandler,
			error: function (xhr) {
				eval(xhr.textResponse.replace(/\n/g, ''));
				successHandler();
			}
		});
	}
});

Debugging

It’s somewhat hard to debug offline stuff. I suggest using a clearly visible version indicator in the file to make sure you know which version of the file you’re looking at. Also remember that the first time you load the app after changing the file & cache manifest it is served from cache. At the same time the manifest is checked and on the iPad the files are downloaded after everything else is done (app finished loading and rendering).

It works!

After these problems were solved I used the aforementioned navigator.onLine to hide stuff that normally comes from the network but is not relevant to the offline app (banners, share links, like/+1 buttons) and you can now check the La Vuelta 2011 results page.

The Cross-Origin Request Sharing (CORS) knot

Tuesday, August 3rd, 2010

As a developer for a company that does a lot of cross-domain posting I was happy to see CORS finally happening. I’ve authored jQuery.windowName plug-in to add support for cross-domain posting in the past and this was my way out – no more hacks as browser support for CORS grows.

Yeah right.

CORS is supported on XMLHttpRequest object in Gecko and Webkit. You just request something on a different domain, and if the response has the right header with the right value you get the data. In IE you have to use a new object called XDomainRequest which is somewhat similar to XMLHttpRequest, but without readystate, onreadystate event handler and basically any header getters/setters.

Sounds ok and when I first tried it I was quite happy with how it worked. But unfortunately nothing is that simple in the land of browsers. When it fails, it’s a whole new game…

Gecko (Firefox)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}

Webkit (Safari, Chrome)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ”}
  • 304 response: xhr = {status: 0, statusText: ”}

IE

Same domain requests made with XMLHttpRequest, cross domain requests made with XDomainRequest.

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: onload invoked
  • 304 response: onload with cache, onerror otherwise
Cross domain request without proper headers:
  • 200 response: onerror invoked
  • 304 response: onerror invoked

Where’s the knot?

The problem with this is the lack of error handling. Firefox will return status 0 on all errors, but will indicate what the response was with statusText. It will also return 0 on a 304 response, which is a bit weird. Webkit on the other hand only returns status 0 on failed requests, but without telling you what’s wrong. You get the same error when a preflight request fails, a 500 error on the server or the real request fails. IE with its own separate XDomainRequest implementation simplifies this two handlers – onload and onerror. Unfortunately it raises an onerror request when a 304 is returned on empty cache.

jQuery issues

If you’re working with jQuery (and possibly other libraries) you’re in for a treat. Opera 9.5 falsely reports status = 0 when a HTTP status 304 is returned from the server. For this reason jQuery treats status === 0 as a successful request. Thus you have no way of knowing whether your request was a success or a failure.

Untying the knot

If you intend to use jQuery’s built-in methods you’ll need to do your own bookkeeping:

  1. Know when a request is a CORS request
  2. Wait for complete event
  3. If data is empty trigger error handling

For this to work you obviously always need some data. If empty data is valid you’re screwed.

Easy way out

You might have noticed before that I made a jQuery.windowName plugin. It still has the same name, but now also supports CORS requests and does some of the CORS related bookkeeping. As the latest version is still in testing you can get it directly at http://friedcell.net/js/jQuery.windowName/jQuery.windowName.plugin.1.0.0.js (test page).

Enhanced by Zemanta

Know a JavaScript developer?

Tuesday, July 21st, 2009

If you haven’t heard yet, we’re looking for a JavaScript developer at Zemanta. I think the ad says it all.

Are you the frontend developer we are looking for?

Zemanta is developing a platform for contextually enhancing content and your job would be to help us develop tools that make this easy and fun for writers and readers alike.

If you have an exceptional understanding of Javascript and the internals of browsers, thrive on challenges and love learning new skills, then we would love to talk to you. Knowing Python and Django framework is a plus, but not a prerequisite.

“I have nothing to offer but blood, toil, tears and sweat.”

(Winston Churchill on life at a startup)

Well, not quite. Working in a startup means working in an ever-changing environment. We expect you to be flexible, do what needs to be done when needed, but offer flexibility in return. We care about good work and meeting deadlines. We don’t care where or when you do it, as long as you keep true to mutual agreements which include occasional meetings and we promise not to overburden you with work. A self-reliant member of a team is how you see yourself.

Schools you might have attended are none of our concern. We care only about how good of a developer and person you are. We expect you to send us examples of your work or explain persuasively why we should hire you. Zemanta is an international company, so your application, as much of our communication, will have to be in English.

Please send your application saved as an HTML or TXT document to [email protected].

Closing date for applications: 31.7.2009

In a time when practically all pages include some sort of JavaScript I am surprised that we don’t have more JavaScript developers popping up – this now surely is a full time job even in Slovenia. So you are one challenge yourself and send your job application and if you know one, send him our way…

Reblog this post [with Zemanta]

Speaking about the web of data

Wednesday, February 25th, 2009

Today at 19:00 CET I’ll be speaking at a local web meet-up about the web of data. There’ll be a live feed of the talk available and since I’ll be speaking in English you can tune it. This is a quick translation of the abstract posted on Slovenian sites:

Numerous services are emerging on the web that provide data in a computer friendly form through APIs, microformats, feeds,… Even your blog is actually a database as it syndicates its content via feeds and new posts trigger a ping you can subscribe to.

This fact opens new ways of collaboration – so called mash-ups, but this isn’t really a new concept. What’s new about it is the fact that we don’t use this word anymore as all the new services are some sort of a mash-up leveraging existing services. But accessing data is not the only way to leveraging these services – it’s becoming increasingly easy to create an application that lives in other applications without their approval through browser extensions and bookmarklets.

Marko Mrdjenovič from Zemanta will talk about what you can do to make your site more mash-up friendly and why that’s becoming increasingly important. As a developer I’ll also present what options you have and give a few tips on what to do and what to avoid when developing these kind of apps.

If you have any questions during the talk use twitter to tell me and I’ll try to answer them. Or put them in the comments.

Update: The video is now online. It’s in English so go watch it and tell me what you think.

Reblog this post [with Zemanta]

Getting into the graph

Sunday, January 11th, 2009

Designing forms online is a hard job. What you need to do is find the right balance between the consumer experience which is generally worse as the number of fields grows and the amount of content you or your employers want to get from the user in a single form. With this you’re defining the conversion rate of a form – less fields will, ceteris paribus, give you a higher conversion rate – which means that you should strive to have less fields in a form (although recently I saw an argument on twitter that you want for people to put some effort into forms to avoid registration from people who will never use your service1). Having many fields in a form has two major effects – users cannot easily scan the form to see if they’re actually willing to give away all the information that you want and they don’t want the form to take a lot of time. Since you sometimes can’t lower the number of required fields you can try to lesser the pain of filling them – by auto pre-filling them.

This is especially true of contact/feedback and registration forms. On contact forms the only thing consumers want to give is the message, on the other hand what you want is at least a name and a way to contact them (the others in your company might want more, but that’s a different storypost). The registration form is somewhat similar in the way that you can make it really simple by just using email and password fields but then again it’s nice if you have a way of addressing your users other than an email address and if your application is at least a bit local you also want their location.

The other side of the story is obviously that consumers don’t want to fill the same forms all the time. As time passes they’re minds are saying “Not again…” which is not that far enough from “Why can’t they get it from some place I already use”.

Into the graph

There have been a lot of attempts at solving this issue of repetitive entering of same data. There’s a way to do it with OpenID, which unfortunately isn’t ready for mass use since not many people use it. Other possibilities are public APIs of numerous services that allow you to get at least some user information if you have some data about them.

One of these APIs is Flickr‘s as it allows access to user information if you have the user’s email through flickr.people.findByEmail and then flickr.people.getInfo as is neatly demostrated by huffduffer which uses it to retrieve your Flickr avatar.

What you have now is the user’s name, “preferred” username, location and avatar (which you might have had already if the user is registered with gravatar).

The graph

Social Graph Platform Wars
Image by davemc500hats via Flickr

The other place where you can get decent information about the user is Google’s SocialGraph API. With it you get lots of services from which you can get all kinds of public information about a user. The problem with this API is that you first need a relevant entry point which must be a URL that the user owns. This can be a Flickr user URL (that you have if you successfully completed the previous step) but these don’t always produce very good results.

What seems to produce better results are twitter URLs – the only problem wasis how to get one. An easy way to get the users twitter URL is demonstrated in a blog post by Chris Heilmann. This will get you all the information you get from Flickr for all the users that are currently logged in to their twitter account. Seems that this is not possible anymore!

If you’re still not getting anything you can still ask a user for a URL – be it their blog or any social network service account. Or you can let her log in using other services that will give you a good starting point, like Facebook Connect or OpenID.

Data from the graph

What SocialGraph gives you is a list of services the user is registered with. You can also get their list of friends – if any of them are also users of your service you can suggest they also connect on your service. You might also get to know whether they use OpenID and suggest them that they use that login to log into your service too. Or you might be able to figure out what their blogs are – especially when they’re claimed on Technorati.

This information can also help you get in touch with your users as you can automatically contact them on services where you also have an account, like Facebook, twitter, friendfeed and others.

The thing is that once you have all this information you should be able to get all the public information the user has exposed on any of these services. And you can use this information to help the user by pre-filling forms or use it in other ways that a user might find helpful, but not spooky.

What you shouldn’t do is use this information in ways that might scare the user. For example if you can get the birth date from any of the services, don’t hide the fact that you did – offer a form to fill the date and prefill it. This way the user won’t be spooked if you send them a happy birthday note or you greet them with a happy birthday note.

This form should get your Twitter data and then get some of your services via Google’s SocialGraph. It’s all nicely wrapped in an hCard.

  1. Sorry I forgot your name as I’m not yet used to bookmarking twitter statuses. back
Reblog this post [with Zemanta]

jQuery window.name transport is out…

Wednesday, October 1st, 2008
JQuery intermediate site

Image by Phillie Casablanca via Flickr

After a week of mostly testing and fine-tuning the code I finally released the windowName transport plugin for jQuery. You can get the plugin here but I suggest you first check the plugin page.

I need help testing

If you have an obscure browser / OS combination that is supported by jQuery I urge you to test the plugin. There are no good test pages yet so my temporary test page will have to do. The test page POSTs the querystring passed to it to a nonlocal domain and should open a JavaScript alert with the same querystring plus php=true.

On a sidenote – I figured out that the page looks better without the background images. So I changed the theme – let me know what you think.

Reblog this post [with Zemanta]