Dr Nic

Dr Nic’s Tumblelog

Everyday I have 100s of blog articles to read [Ed: mostly I read the title and press the 'N' button], so I’m fearful of dumping random ideas and links into this blog. This blog is for dumping random code chunks and releasing fancy-pancy new projects. Its mostly devoid of opinion.

Solution: a tumblelog Tumblelog RSS full of random scraps and chunky opinion. By random I mean, things related to Ruby, Rails, Javascript, etc that fit nicely amongst the things we all care about already. Plus pictures of me and my family, because I’m just narcissistic enough to think you might care :)

Tumblr made it too easy to not start a Tumblelog.

Supporting JSON callbacks in Rails

Now you know how to write JavaScript widgets, now you need to know how to help others write widgets for your data.

Here’s a short-one act play to describe the issue at hand.

Actors in this play

You: You run a website that you built with Rails. Its got data in it. You’re a hero of the working class, a family man, and own a hybrid car, but you ride a bike to work. You vote, you contribute to the local neighbourhood newspaper, and you love Autumn because the leaves turn pretty colours. You’re not a fool.
Ulrich: Ulrich is a user of your site. Ulrich is a needy, web2.0 super-consumer. Ulrich started using your site when it was first profiled on TechCrunch, Ajaxian, and the Times magazine, but since then he’s found Theo’s website.
Theo: Theo runs a third-party website. Theo’s website has its own data too. It doesn’t have Ulrich’s data. Theo’s site wasn’t written about in TechCrunch, nor Ajaxian, nor even the local church foldover.

[Prelude] Theo just received his third email from Ulrich. Ulrich wants to port his data from Your website to Theo’s. Theo in turn, now has you on Skype…
You: I’m not very happy with the idea of giving up my data.
Theo: What happened to Open APIs? Making the user’s data transferrable with the user?
You: You read too many 37signals blog articles. Even Flickr – web two of all web two sites – doesn’t give up its data.
Theo: But some of my users…
You: You mean Ulrich. He’s emailed me too
Theo: Yeah, Ulrich. Ulrich wants to embed his data from your site into my site.
You: We already have a JSON API..
Theo: Its no good.
You: Works just fine. Returns lovely JavaScript data. Our users use it within our site to write their own widgets. Its one of our big draw cards.
Theo: Its no good. It doesn’t support callbacks.
You: What the?
Theo: Without a callback function there is no way my <script> tags can trigger any functionality when they retrieve the data. Ulrich could include his JavaScript widget into his page on mysite with your data if it was possible to specify a callback function in your API.
You: Instead of just raw JSON data?
Theo: Yeah. Lots of other sites offer it: CoComment, Delicious, Google Data, etc. They all allow callback=myFunction in the URL parameters.
You: [thinking to yourself] Rails don’t support callbacks out of the box. Bugger.
You: Sure, I’ll work on something.
Theo: Thanks, hopefully this gets Ulrich off my back. Should drive us both more traffic too.
You: The wikipedia page for JSON doesn’t mention callbacks.
Theo: Its a wiki. Add it yourself.
You: Smart arse.

The end.

Supporting JSON callbacks in Rails

Piece of cake.

But first, to support pure JSON API data objects from Rails, you have a handy to_json method on the Object class. So, in the partial/view you are rendering, e.g. person_json.rhtml (or person.rjs if you using MinusR plugin), you include:

<%= @object.to_json %>

Yeah, that was tough.

Now, for callback support. Change the above to:

<%= "#{params[:callback]}(" if params[:callback] -%>
<%= @object.to_json -%>
<%= ")" if params[:callback] -%>

Done.

Update: render_json method

Tim Lucas wrote a great article introducing a render_json method to add to your base ApplicationController. It will automatically return JSON data that you pass it, wrapped automatically in a callback or assigned to a variable (or both) if the parameters include a callback or variable value.

Read it, its good.

Debugging Javascript in IE7 – how to clear your Javascript cache

JavaScript first appeared in December 1995 within the Netscape browser.

131 months later and Microsoft latest attempt to provide JavaScript support in its browser is still faulty – deleting all browser files does not delete JavaScript files from your cache.

You try to develop JavaScript for IE7 and with each change you make the browser ignores it. “Delete files…” and refresh page. Nothing. “Delete all…” and refresh page. Nothing. “Delete all…” and restart browser and “Delete all…” and refresh page. Nothing.

A real “fist through the monitor” moment if there ever was one.

If you find this page via Google and you suffer the above pain, then consider this article your Support Group. “My name is Nic, I need JavaScript to work on IE7, and I have a problem.”

So did Daniel. He also had a solution. Good man, Daniel.

So, run the following Ruby script (if you use a different language, can you copy the equivalent into the comments, please, for fellow Support Group members?) to find and delete all JavaScript files in your cache.

files = ENV['USERPROFILE'] + '\\Local Settings\\Temporary Internet Files\\**\\*.js'
Dir[files.gsub(/\\\\/,'/')].each {|file| File.delete file; puts "Deleted: #{file}"}; nil

Create a script for it or just rerun that last line each time you want to clear all JavaScript in the cache.

The Support Group session is over. Coffee and cake is at the front of the room.

DIY widgets – How to embed your site on another site

UPDATE: Demo and tutorial fixed for IE7.

UPDATE: Opera-supporting code.

I used to wonder how Google AdSense worked. How did those ads from over at Google get on another site over here? This article ignores how Google did it and shows you how you can do it. That is, shows you how to embed JavaScript widgets on other people’s websites.

Demonstration

As a demonstration, the following yellow box and text is dynamically generated from a 3rd party web server without using Ajax:

The embedded code

Only one line of code is required to activate the widget to perform the preceding trick:

<script src="http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js"></script>

You can copy that line into any HTML page (even a static file on your computer) and it will generate the same HTML view. Sexy magic.

Cross Site Scripting

The ability for Google and you to embed dynamic content on someone else’s site is called Cross Site Scripting (XSS). If you read the Wikipedia page, the first sentence announces warnings and dire consequences:

Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications which allow malicious web users to inject HTML or client-side script into the web pages viewed by other users.

XSS rocks and Wikipedia is wrong

So, Wikipedia and Google differ in their thinking. I’m with Google. XSS rocks. Wikipedia is for 15 year olds who are too lazy to research their own essays.

Its one thing to provide users of your sites with a REST API or include microformats into your HTML, and let them get their dirty hands on your data.

But its another level of mutual lovey-dovey-ness entirely between for users to be able to embed something from your site into their site. Not just images, but dynamic, sex-on-a-stick content. Like Google AdSense, or Sphere (see the “Sphereit” icon at the bottom of any TechCrunch article), or all the various wacky Digg tools (where you can embed “Digg this” etc into your page and look supercool).

How to do this

If you’ve been diligent enough to learn Javascript, you too can do this at home without parental supervision.

Things you’ll need

The following things are lying around on the internet for you to use:

  1. A copy of someone else’s website, say like an article from Ajaxian [1]. This will be called the mock page.
  2. Some data you want to embed into the site. Relax, it will look pretty when the users sees it. Your data must be in JSON format. But I’ll explain this later on.

The run-thru of what will happen

The user will load up the webpage (e.g. Ajaxian mock page) that has a small <script src="http://yoursite.com/magic_xss.js"></script> snippet in it [2]. When the page is loaded, the magic_xss.js file is loaded too. The user doesn’t know nor care.

When the magic_xss.js file is loaded it will do a couple of things:

  1. Install any stylesheets it needs
  2. Insert an empty, invisible HTML element into the page (e.g. <div id="my_magic_xss" />).
  3. Read in any variables (e.g. Google Adsense requires the website owner to specify a number of variables, such as google_ad_format)
  4. Fetch any additional Javascript files or data. This is where even more dynamic magic can be performed. When requesting the additional data, you could pass back the current document’s URL or the current users’s IP address, and the webserver could return data that is relevant to that URL or IP address/geographic location. Clever, eh.
  5. Insert new HTML into the #my_magic_xss element based on the data that is returned from your own server. Your server – not the host website’s server.

XSS is not Ajax

Technically speaking, Ajax uses the XmlHttpRequest object within the browser to fetch content. Unfortunately, in all browsers, it can only fetch content from the same server as the webpage.

That’s why XSS is interesting – we’re fetching data from a 3rd party server. Your server. So, we can’t use XmlHttpRequest.

The tutorial

Hereafter is the tutorial to explain all this. No specific webserver is required for the tutorial – the data returned will be a static file, though you will implement this with your own webapp to be dynamic.

The data

Later on in the tutorial we’ll need some data to represent the dynamic content from your server. For the sake of the demo, you can use my data: http://drnicwilliams.com/wp-content/uploads/2006/11/people_list.js

Remember, in real life this would be a dynamic server request returning dynamic JSON data.

Here’s the JSON we’re going to be using:

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

Down the track, this bit of Javascript will be invoked on the browser. It will look for the function MyMagicXss.serverResponse and invoke it with the Array ['Dr Nic', 'Banjo', 'Angus']. The server function would be the same each time. The data would be different.

The embedded javascript

The point of showing you the dynamic data first is to show us that we need 3 things:

  1. A way to fetch and evaluate remote javascript without using XmlHttpRequest
  2. A function MyMagicXss.serverResponse
  3. A place to display these results on the host webpage

These three things are done by the original page that is loaded into the host webpage when the user visits. For our tutorial, this is the xss_magic.js file: http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js

This file looks like:

MyXssMagic = new function() {
  var BASE_URL = 'http://drnicwilliams.com/wp-content/uploads/2006/11/';
  var STYLESHEET = BASE_URL + "xss_magic.css"
  var CONTENT_URL = BASE_URL + 'people_list.js';
  var ROOT = 'my_xss_magic';

  function requestStylesheet(stylesheet_url) {
    stylesheet = document.createElement("link");
    stylesheet.rel = "stylesheet";
    stylesheet.type = "text/css";
    stylesheet.href = stylesheet_url;
    stylesheet.media = "all";
    document.lastChild.firstChild.appendChild(stylesheet);
  }

  function requestContent( local ) {
    var script = document.createElement('script');
    // How you'd pass the current URL into the request
    // script.src = CONTENT_URL + '&url=' + escape(local || location.href);
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

  requestStylesheet(STYLESHEET);
  document.write("<div id='" + ROOT + "' style='display: none'></div>");
  requestContent();
}

For the most part you should be able to reuse that and modify it as necessary.

No Javascript frameworks allowed

First point – we should not rely upon any Javascript frameworks (e.g. prototype or jquery) as they might conflict with any Javascript already active in the host page. So, our code is encapsulated in an object MyXssMagic which we hope is unique on any host page.

Step one – install stylesheets

  requestStylesheet(STYLESHEET);

So that your embedded HTML looks pretty you might want to preinstall your own stylesheets.

Step two – create the holder element

  document.write("<div id='" + ROOT + "' style='display: none'></div>");

This allows the code to subsequently know where to insert HTML. Here we’re going to install the HTML at the same location where the host inserted our <script src="..."> code.

Step three – request remote data

   requestContent();

which invokes…

  function requestContent( local ) {
    var script = document.createElement('script');
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

We’re going to load our data by creating a new <script src="..."> element into the page. We can’t use XmlHttpRequest, so this is all we have. This means that the response must be pure Javascript, and must invoke a callback function so that the browser does something with the data.

Step four – process response

The browser automatically executes the returned data (introduced earlier):

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

This invokes our callback function:

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

This inserts “Names: Dr Nic, Banjo, Angus” into the element #my_magic_xss, as per the demostration at the start of the article.

Magic!

Epilogue – dynamic interactions

Loading dynamic data and adding HTML into a host page is easy, as shown. Supporting user interactions, where you dynamically load new data and show new HTML, is slightly more complicated.

Remember the rule: the returned JSON data must be wrapped in a callback function (e.g. MyMagicXss.serverResponse(…data…)).

[1] I suggest Ajaxian.com to you because I fancy they might give me something free for doing it. Here’s hoping.

[2] This snippet got into the page because the authors of the site embedded it for you. You can’t make them. But copying a bit of text into a website template is common place thanks to Google AdSense etc, so unless your target audience is 1800th century quilting website owners, you should be fine. Write up some instructions, and give them the small chunk of HTML that they need to embed.

Give up RJS and go pure

RJS seems like a nice idea. Its Ruby with embedded Javascript.

Now put away the crayons and get back to pure Javascript with embedded Ruby with “MinusR” – RJS minus the R (for Ruby).

This is especially useful if you want a clean way to support non-Prototype/Scriptaculous frameworks, like jQuery. You can call the raw Javascript for these libraries, and embed your Ruby objects using Erb and a handy js helper to convert Ruby to JSON.

Its clean, healthy fun. Like carrots.

Makes me think about jogging, its just that healthy for you.