How FuncUnit works
FuncUnit is a mashup of a bunch of awesome JavaScript projects. It can be useful knowing how they all work together to make FuncUnit tick. This article details the components and how they fit together. This is a primer for anyone wanting to contribute to FuncUnit.
FuncUnit is is comprised of the following parts:
- Selenium - Provides browser automation
- QUnit - A browser-based testing an assertion API.
- jQuery - Used for CSS selector and DOM utilities.
- EnvJS - A simulated browser environment.
- Syn - A synthetic event library.
FuncUnit supports 2 modes: Browser and Selenium mode.
Browser ModeBrowser mode is when you open up your browser to a funcunit html page. This page includes the funcunit script which has inside it QUnit, jQuery, Syn and the FuncUnit API.
When you use FuncUnit (or S), it basically sends those commands to a popup window. This is pretty straightforward.
Selenium ModeSelenium mode is used when you use envjs to open the funcunit html page. Here, EnvJS reads the html page and then loads and runs any script tags it finds (just like a normal browser). But instead of sending commands to a popup window, funcunit.js starts selenium, serializes those commands, and send them to a custom Selenium server build. The selenium server has jQuery and Syn and will run the commands the same way browser mode ran the commands. Selenium server will return the results to FuncUnit which reports them to the console.
The SecretNow that you know the secret to FuncUnit's awesomeness - how you are able to write a test with just a browser and funcunit.js, but automatically run it - please help us make this project even better. Here are some things we want to work on:
- An in-browser Selenium like IDE.
- Cucumber-like API.
- Plugins for testing Flash / Silverlight integration.
FuncUnit - Fun Web Application Testing
Last week we released Syn and mentioned how all testing solutions suffer from some fatal flaw. Today, we are releasing a beta of FuncUnit, which has "solved" JavaScript web application testing.
Features- Functional Testing - Test complex behavior like drag-drop.
- High Fidelity - Create identical events that a mouse and keyboard create. Run tests in a real browser.
- Automated - Test multiple browsers from the command line.
- Easy to Write - Write, run, and debug tests in the browser, without configuring a server or other tool. Write tests in a familiar jQuery-style API.
- Free - It's open source! Save yourself $5,000 for similar solutions.
- Multi-system - Runs in IE 6+, FF 2+, Safari 4+, Opera 9+, Chrome, on PC, Mac, and Linux.
FuncUnit is a web application testing framework that combines Selenium, jQuery, QUnit, and Envjs. It focuses on making testing as easy and fun as possible.
Writing tests is a chore and won't be done unless it's stupidly easy. Plus, tests won't get run unless they can be run automatically.
The problem with other automated testing solutions is that everyone has to install and configure cumbersome external software to even begin writing tests. After setting up the software, you write the tests with some obtuse API in a language other than JavaScript. But the worst part is debugging these tests across the software-browser bridge. The tests are almost impossible to debug!
We fixed this.
Write, Run, and Debug with the BrowserFuncUnit lets you write tests by just creating an html file that includes funcunit.js.
Simply open the html page in your browser and it runs your tests. If you made a mistake, or your app breaks, use your favorite debugger (Firebug) to catch the problem. It's easy.
Automate Your TestsWhen you've setup your testing server with the browsers you want to support, running the same tests is as simple as opening the test page with envjs:
envjs http://test.myco.com/tests/filemanager.html
We typically set this up to run as a nightly build.
APIFuncUnit's API is super sweet. If you know QUnit and jQuery, it will be second nature. The following tests the handy autosuggest from phpguru. See it in action here. (Turn off your popup blocker!)
module("autosuggest",{
setup : function(){
S.open('autosuggest.html')
}
});
test("JavaScript results",function(){
S('input').click().type("JavaScript")
// wait until we have some results
S('.autocomplete_item').visible(function(){
equal( S('.autocomplete_item').size(),
5,
"there are 5 results")
})
});
Demos
Make sure you have your popup blocker off!
- A basic AutoComplete
- Jupiter's Phui tests (Runs Menu, Grid, Sortable, Filler) [Some might break in IE, this is a problem with the widgets, not FuncUnit].
JavaScriptMVC's FuncUnit docs.
UseSetting up FuncUnit is almost exactly like setting up QUnit. After downloading FuncUnit into a public folder on your server (or a local file on your hard drive):
- Create an empty test script (ex: mytest.js).
- Create a qunit like page (ex: mytest.html) that will load your test and display your results. Use this as a template (It's also in the download).
- Have that page include funcunit.js, qunit.css, and your test script (mytest.js)
Open mytest.html, you should see something like:

*** you probably want to change the name of these files ***
Writing TestsAll interaction with the tested page is done through the FuncUnit object. This is aliased as S. S is to FuncUnit as $ is to jQuery.
Typically a test is broken down in 5 parts:
- Opening a page
- Performing user actions (like clicking and typing).
- Waiting for the page to complete its response (animations and Ajax)
- Getting properties of the page (like the text of an element)
- Asserting the properties are correct (like the text == "Hello World")
Testing a dynamic webpage is largely asynchronous. To avoid LOTS of nested callback functions, most FuncUnit commands are queued. But these commands take a callback function that runs after the command completes, but before the next command runs.
For example, the following performs 3 drags that each take a second. But, between the second and third drag, it checks the #start element's text.
S("#start")
.drag({ to:"#first", duration: 1000 })
.drag({ to:"#second", duration: 1000 },
function(){
equals(S("#start").text(),"2 drops")
})
.drag({ to:"#third", duration: 1000 })
Opening a page
To open a page is simple. Write:
S.open("path/to/mypage.html")
The path to the page should be relative to the FuncUnit test page (mytest.html). Typically an open should be done in a module's setup function.
ActionsActions are used to simulate user behavior such as clicking, typing, moving the mouse. FuncUnit provides the following actions:
- click - clicks an element (mousedown, mouseup, click).
- dblclick - two clicks followed by a dblclick.
- rightClick - a right mousedown, mouseup, and contextmenu.
- type - Types characters into an element.
- move - mousemove, mouseover, and mouseouts from one element to another.
- drag - a drag motion from one element to another.
- scroll - scrolls an element.
The following might simulate typing and resizing a "resizable" textarea plugin:
S('textarea').click().type( "Hello World" );
S('.resizer').drag( "+20 +20" );
Waits
Waits are used to wait for a specific condition to be met before continuing to command. Waits look like most jQuery setter methods. For example, the following waits until the width of an element is 200 pixels and tests its offset.
var sm = S("#sliderMenu");
sm.width( 200, function(){
var offset = sm.offset();
equals( offset.left, 200)
equals( offset.top, 200)
})
You can also provide a test function that when true, continues to the next action or wait command. The following is equivalent to the previous example:
var sm = S("#sliderMenu");
sm.width(
function( width ) {
return width == 200;
},
function(){
var offset = sm.offset();
equals( offset.left, 200)
equals( offset.top, 200)
}
)
Getters
Getters are used to test the conditions of the page. Most getter commands also correspond to a jQuery method of the same name.
The following checks that a textarea, which can only be resized vertically, is 20 pixels taller after a drag, but the same width:
var txtarea = S('textarea'),
// save references to width and height
startingWidth = txtarea.width(),
startingHeight = txtarea.height();
S('.resizer').drag("+20 +20", function(){
equals(txtarea.width(),
startingWidth,
"width stays the same");
equals(txtarea.height(),
startingHeight+20,
"height got bigger");
});
Assertions
FuncUnit uses qUnit for assertions so you can use:
- ok - ok(state, message)
- equals - equals(actual, expected, message)
- same - same(actual, expected, message)
Running tests in the browser is easy - just open the FuncUnit page. But running tests via the command line is equally as easy. Simply open your test page with envjs:
envjs http://localhost/tests/grid.html
Or if you are using Mac/Linux:
./envjs http://localhost/tests/grid.htmlSetting Up Your Test Server
We'll be putting this up in JavaScriptMVC's FuncUnit docs.
ConclusionWe believe FuncUnit finally makes testing JavaScript approachable. We hope you can use to to write better, more robust applications that move web development along faster than ever. If you have questions or comments please post on FuncUnit's Google Group.
Thanks!FuncUnit is a mashup of some of the best open source tools available:
- Selenium - Browser automation
- QUnit - Testing API
- jQuery - CSS selector and DOM utilities (your app does not need jQuery)
- EnvJS - Simulated browser environment
- Syn - A synthetic event library
- FuncUnit Homepage
- Google Group
- GitHub
- @funcunit on twitter
Syn - a Standalone Synthetic Event Library.
Syn is used to simulate user actions such as typing, clicking, dragging the mouse. It creates synthetic events and performs default event behavior.
Features- Enables complex JavaScript functional testing.
- Standalone (does not need jQuery).
- Cross Browser (IE6+, FF1.5+, Opera 9.6+, Safari 4+, Chrome 4+) and multi-platform (Win, Safari, Linux).
- 99.9% browser sniffing free (only 1 sniff to prevent crashing Safari).
syn.js (59KB. Size doesn't matter. Users don't download this)
DemoSynthetic Recorder Demo. If you find a bug, it's the recorder and not syn.js's fault. But please let us know! Eventually, we will turn the recorder into something like the Selenium IDE.
DocumentationJavaScriptMVC's Syn documentation.
WhyTesting rich, dynamic web applications sucks. At Jupiter, we've tried almost every testing solution available (qUnit, Quick Test Pro, Selenium, JsUnit, Env.js, TestCase) and all of them suffer from some fatal flaw.
Problems:
- Manual - A tester has to run the tests manually on every supported browser. People are lazy.
- Unit Tests Only - We need to test the app as a whole and complex UI behavior like drag-drop.
- Low fidelity - We need to make sure the tests are reporting accurate results.
- Difficult to write - We sling JS like a ninja monkey throws poo. We want to write tests in a nice JS API.
- Expensive - A QTP license is 5k a person! I'd rather buy a vacation.
- Support - We want to test Mac and Linux browsers.
We've solved all of these problems in our upcoming FuncUnit testing framework. It's a mashup of qUnit, Selenium, Rhino, and Env.js. But its core library, Syn, which does the work of simulating user actions with very high fidelity, is what we are releasing today.
UseUsing Syn feels good and should be done with a clean conscience, as it may lead to feelings of guilty pleasure. There's only several things a user does and these correspond nicely to the Syn API:
- click - a mousedown and mouseup on the same element.
- dblclick - two clicks in rapid succession.
- type - typing a sequence of characters.
- key - typing a single character
- move - moving the mouse.
- drag - a mousedown followed by a move and a mouseup.
These user interactions are called actions. The following is an example of clicking an element with id='hello', typing "Hello World", and then dragging your mouse from that element to an element with id='trash'.
Syn.click( {},'hello' )
.type( 'Hello World' )
.drag( $('#trash') );
* jQuery is used to get the trash element for brevity. It is not required.
You'll notice that Syn is chainable. You want to use chaining because actions don't complete immediately (thank IE's .focus() for this). Provide a callback to know when a command has completed and use a delay to for timeouts between commands. The following asserts that 'hello' has been removed from the page:
Syn.click( {},'hello' )
.type( 'Hello World' )
.delay() //waits 600ms seconds by default
.drag( $('#trash') , function() {
ok( $('#hello').length == 0, "removed hello" )
});
Notice that an element id is absent from the type and drag methods. If an element id is not provided, the previous command's element is used.
Lets explore a how to use each action in detail.
Click and DblclickA click is the following sequence of events followed by the event's default actions:
- mousedown
- focus (Called only if the element was focusable, and mousedown's default actions were not prevented)
- mouseup
- click
A dblclick is just two clicks followed by a dblclick event. The options for a click action become properties on the event. The following sets clientX and clientY for the mousedown and mouseup of a click action:
Syn.click({clientX: 533, clientY: 100}, 'foo')
A few important points on click:
- ClientX and ClientY are relative to the viewport (window).
- You can provide pageX and pageY for positions relative to the page (such as jQuery's offset).
- If jQuery is available, the clientX and clientY will be set to the center of the element.
- If you want to set the control key values of the click action, you have to type or key that button.
The key action is the following sequence of events followed by the event's default actions:
- keydown (Except for the print key)
- keypress (For keys that generate a keypress)
- focus (for keys that change focus, like \t)
- keyup
The type action calls the key action for each character. Check the keycodes documentation for a list of key names. The following types "JavaScriptMVC" then presses backspaces to delete the "MVC", leaving "JavaScript".
Syn.type( 'JavaScriptMVC\b\b\b', 'foo' )
Special Characters
Some characters don't have a type-able key such as right, ctrl, and delete. To type these keys you just have to surround them with [ and ]. The following types JavaScriptMVC, moves the cursor to the left 3 times, and then types delete 3 times:
Syn.type( 'JavaScriptMVC[left][left][left][delete][delete][delete]', 'foo')
Command Keys
The command keys ctrl, alt, and shift often want to be held while another key is pressed. For these keys, you have to call the key like [command], and release the key with [command-up]. The following types JavaScriptMVC, selects the MVC text, and then deletes it.
Syn.type( 'JavaScriptMVC[shift][left][left][left][shift-up]\b', 'foo')Drag and Move
A move action is the following events
- mousemove
- mouseout (the cursor leaves an element)
- mouseover (the cursor enters an element)
A move creates a linear sequence of mouse move, outs and overs between two points. A drag action is a mousedown followed by a move and a final mouseup. To make a drag motion from one point to another over 2 seconds:
Syn.drag(
{
from: {clientX: 100, clientY: 100},
to: {clientX: 200, clientY: 200},
duration: 2000
},
'foo')
Or if you want to provide pageX and pageY:
Syn.drag(
{
from: {pageX: 100, pageY: 100},
to: {pageX: 200, pageY: 200}
},
'foo')
You can shorten this to:
Syn.drag(
{from: '100x100', to: '200x200' },
'foo')
But most commonly, you want to drag one element to another element. To drag the foo to the bar element:
Syn.drag(
document.getElementById('bar'),
'foo')
This drags from the center of one element to the other.
ConclusionYou should care about syn if you:
- Work on the Selenium or some other functional testing library.
- Want to test complex UI behavior.
Our naive hope is that Syn might be the Sizzle of functional testing - a standalone, extendable, drop in, synthetic event library.
Organize jQuery Widgets with jQuery.Controller
Do you like organized code and hate nested functions that are impossible to reuse? Want to be able to extend plugins and widgets? Do you want these widgets to clean themselves up after they are removed? Great! JavaScriptMVC-extracted jQuery.Controller is the easiest and most robust way to build jQuery widgets.
Features- Extendable widgets.
- Deterministic and debug-able code.
- Auto cleanup.
- Namespaced widgets.
jquery.controller.js (minimized 7.8kb) [includes jquery.class.js, jquery.event.destroyed.js]
Demos DocumentationJavaScriptMVC's controller docs.
UseUsing controller is easy. Extend $.Controller with the name and methods of your widget. Methods that look like "event" or "selector event" are bound (or delegated) when a new controller is created. The init method is called after the events are bound.
The following creates a tab widget.
// create a new Tabs class
$.Controller.extend("Tabs",{
// initialize code
init : function(el){
// activate the first tab
this.activate( $(el).children("li:first") )
// hide other tabs
var tab = this.tab;
this.element.children("li:gt(0)").each(function(){
tab($(this)).hide()
})
},
// helper function finds the tab for a given li
tab : function(li){
return $(li.find("a").attr("href"))
},
// on an li click, activates new tab
"li click" : function(el, ev){
ev.preventDefault();
this.activate(el)
},
//hides old activate tab, shows new one
activate : function(el){
this.tab(this.find('.active').removeClass('active')).hide()
this.tab(el.addClass('active')).show();
}
})
// creates a Tabs on the #tabs element
$("#tabs").tabs();
There are a few important things to notice:
- Creating a Controller class creates a jQuery plugin with the underscored class name. In this case, it created the tabs plugin: $("#tabs").tabs();
- The tabs() plugin creates a new Tabs instance on each element matched by the selector. The tabs instance is created with the element and any parameters passed to tabs(). In this case it is called like new Tabs(el).
- When an instance of Controller is created, events are bound, this.element is set to the Controller's element, and init is called to setup the widget.
Many jQuery widgets suffer from an "everything-but-the-kitchen-sink" mentality. They pack every conceivable type of related functionality. This causes software bloat and slower load times for users. The root problem is the lack of a standard way to organize and extend widgets.
Controller fixes this problem by using Class. The following extends the Tabs widget above to make history enabled tabs.*
//inherit from tabs
Tabs.extend("HistoryTabs",{
// ignore clicks
"li click" : function(){},
// listen for hashchanges
"hashchange" : function(){
var hash = window.location.hash;
this.activate(hash === '' || hash === '#' ?
this.element.find("li:first") :
this.element.find("a[href="+hash+"]").parent()
)
}
})
// create a history tabs
$("#historytab").history_tabs()
*This assumes you are using something like Ben Alman's jQuery Hashchange Event Plugin.
By using Controller, we are able to keep functionality bloat to a minimum while providing a base class for future complex functionality.
Deterministic CodeHave you ever tried understanding someone else's jQuery code? You'll probably see a nested mess of anonymous callback functions wrapped in a self calling anonymous function. Despite there being as many jQuery widgets as there are atoms in the universe, there isn't a great way to organize jQuery functionality.
If you're working on a big project, or with other developers, unorganized code can create a big problem real fast. You need code that is deterministic - if you see functionality expressed in the page, you know where that functionality is expressed in code.
Controller provides this by organizing all entry points to a widget (methods and events) and adding the controller's name to the element's className. We'll break these down in the next two sub-sections; but the result is if something happens on the page, say an "li" in a ".tabs" element is clicked, you know to look for "li click" in a Tabs controller.
Organized Methods and Events
Controllers' prototype properties organize public methods and event handlers. This makes finding 'where stuff gets happens' in a widget really easy. For example, if you wanted to activate the last tab in a Tabs, you could call:
$('#tabs').tabs("activate", $("#tabs li:last"))
But the REALLY clever thing about controller is how it organizes event handlers. By naming a function like "event" or "selector event", Controller knows to listen for those events. Event handlers are self labeling! We typically refer to these methods as actions.
Controller even lets you parameterize actions, making it possible to configure the event or selector when the controller is created. And, with the jquery.controller.subscribe.js plugin, you can listen to OpenAjax messages. The following are example actions:
// listens for all clicks in this controller
click : function(el, ev){...}
// changes in .foo .bar element in this controller
".foo .bar change" : function(el, ev) {...}
// listen to this.options.activateEvent events in an li
"li {activateEvent}" : function(){...}
//listen for OpenAjax hub "recipe.created" messages
"recipe.created subscribe" : function(called, recipe){...}
//listens for drag events with jquery.event.drag.js
".drag dragover" : function(el, ev, drag, drop){ ... }
Controller Element Label
Controllers label their element's className with their name. For example, after a HistoryTabs controller is added to the #historytab element, it would look like:
<div id='historytab' class='history_tabs'></div>
You can also see all controllers on an element with $("#foo").data('controllers').
In summary, Controller organizes functionality so it is easy to find. It provides a flexible but orderly way to organize widgets.
Auto-magic CleanupIf you're building a JavaScript application, removing a widget from the page is almost as important as adding one. Unfortunately, the vast majority of jQuery plugins are designed to enhance a static page and this important feature is ignored. Typically, the only way to remove a widget is to remove its element. Even when the element is removed, the widget is often still bound to other objects such as the document which causes errors.
Controller makes cleaning up widgets easy, and in most cases automatic. It also lets you remove a controller without removing the element.
In the previous section, we showed how controller auto-binds actions. Controller also auto-unbinds those actions when the controller is destroyed. A controller can be destroyed in two ways:
- The controller's element is removed from the page with jQuery manipulators like .remove(), .html().
- The controller's destroy method is called like:
$('.hider:first').controller().destroy()
Controller also provides bind and delegate methods that allow you to listen to events on elements outside the controller's element. These event handlers will automatically be removed when the controller is destroyed.
The following controller displays "Click to Hide" and hides itself when the document is clicked.
$.Controller.extend("CloseOnClick",{
init : function(){
// call close on document clicks
this.bind(document, 'click', 'close');
// remove and save old content
this.oldChildren = this.element.children().remove();
// show text to hide
this.element.html("<p>Click to hide</p>")
},
close : function(){
this.element.hide()
},
destroy : function(){
.... //we will fill this in later
}
});
$('.hider').close_on_click()
When a 'hider' element is removed, or destroy is called on a CloseOnClick controller, the controller's event handlers are unbound, removing the document click handler.
If your widget has made any modifications to the element, overwrite the destroy function to set things back to their original state.
The following sets back the original contents of the element:
destroy : function(){
this.element.children.remove();
this.element.append(this.oldChildren)
this.oldChildren = null;
// always call super to call base destroy
this._super()
}
Namespaces
It's important to keep plugins from stomping on each other. If you create a Controller with a namespace, it includes the namespace in the jQuery plugin. For example:
$.Controller.extend("Jupiter.Tabs");
// creates -->
$("#tabs").jupiter_tabs()
This isn't exactly namespacing and not 100% protection against collision. But in practice, we've not had any problems.
ConclusionsController is JavaScriptMVC's, and by extension, Jupiter's secret-sauce. It helps us build functionality in a robust and repeatable way. It's hard to express the amazing feeling walking onto a project with 50 controllers I have never seen and instantly start contributing and fixing bugs.
Controller also helps us develop faster because we don't have to invent a new pattern for every widget.
Controller is not for all jQuery plugins. If you are building something very small, unlikely to be extended, or DOM related, controller is probably not the best fit. But if you find yourself needing to organize your team's jQuery code around a proven pattern, take Controller for a spin.
A Simple, Powerful, Lightweight Class for jQuery
jQuery's functional programming style is downright elegant for manipulating the DOM. But it lacks the structure and code reuse patterns that professional scripters need. John Resig's Simple Class inspired, jQuery.Class is a lightweight, but powerful class-like system that bridges the gap between jQuery's functional programming style and Object Oriented Programming.
Features- Static and prototypal inheritance
- Introspection
- Namespaces
- Setup and initialization methods
- Easy callback creation
jquery.class.js (minimized 2kb)
DemoExtend a tabs widget to a history tabs widget demo.
DocumentationJavaScriptMVC's Class docs.
UseTo create a monster class with static and prototype properties:
$.Class.extend("Monster",
// static properties
{
count: 0
},
// prototype properties
{
// constructor function
init : function(name){
//save the name
this.name = name;
this.energy = 10;
//increment the static count
this.Class.count++;
}
})
//create a monster
var dragon = new Monster('dragon');
Inheritance
To extend (or inherit) from a class just call .extend on the class and provide the new name of your class and the static and prototype properties:
Monster.extend("SeaMonster",{},{
swim : function(distance){
this.energy -= distance
}
})
If you want to call an base class's static or prototype method from the extended class:
SeaMonster.extend("LochNessMonster",{},{
swim : function(distance){
this._super(distance/2);
}
})
Notice how _super calls the base class's method.
IntrospectionYou can access the full and short name of the class for groovy introspection techniques. The following uses the class's name to change where the ajax request is sent:
$.Class.extend("Model",{
findAll : function(success){
// use the class's name to find items
$.get('/'+this.shortName.toLowerCase()+'s',
success,
'json')
}
},{});
// create a task model
Model.extend('Task')
//use it to make a request to /tasks
Task.findAll(function(tasks){
alert(tasks.length);
})
Namespaces
Namespaces are a GREAT idea. It's nice when code doesn't clobber each other.
$.Class.extend("MyCo.Project.MyClass");
MyCo.Project.MyClass.shortName //-> MyClass
MyCo.Project.MyClass.fullName //-> MyCo.Project.MyClass
Setup and Initialization Methods
When a class or instance of a class is created, setup and init methods are called on the class or instance. Setup is called before init and can be used to normalize arguments to init. Init is the class or instance's constructor function. But unlike normal constructor functions, you don't have to worry initialization code that will prevent further inheritance*.
The following makes sure all classes extending Controller will have their init function called with a jQuery element instead of an element:
$.Class.extend("Controller",{
setup : function(el){
arguments[0] = jQuery(el);
return arguments;
}
})
// A dummy tabs class
Controller.extend('Tabs',{
init : function(el){
this.element = el;
}
})
var tab1 = new Tabs($("#foo")),
tab2 = new Tabs($("#bar")[0]);
//call jQuery methods on element
tab1.element.show();
tab2.element.hide();
*You probably won't need setup methods, but it's cool if you need a lot of pre-processing for extending classes. The problem with normal constructor functions is that they typically need to be callable without arguments to be able to setup a correct prototype chain.
Easy Callback CreationSimilar to jQuery's proxy, Class provides a static and prototype callback function that returns a callback that has 'this' or the context set to the Class or instance.
The following uses callback to return a function that will call the new task's show method with 'this' set to the new task.
$.Class.extend("Task",{
init : function(name){
this.name = name
},
get : function(){
$.get('task',
{name:this.name},
this.callback('show'),
'text')
},
show : function(instructions){
alert(this.name+instructions)
}
})
new Task("dishes").get()
The callback function lets you curry arguments and even gets a little Aspect Oriented Program-ish and lets you chain multiple callbacks*:
$.Class.extend("Task",{
init : function(name){
this.name = name
},
get : function(date, callback){
$.get('task',
{name:this.name},
// call show, then callback, curry date
this.callback(['show', callback], date),
'text')
},
show : function(date, instructions){
// return arguments for next function
return [this.name, date, instructions]
}
})
new Task("dishes").get(
new Date(),
function(name, date, instructions){
console.log(name, date, instructions);
}
)
*Chaining callbacks is helpful when there are multiple processing steps before some final callback is called. It makes it so you don't have to curry the final callback over and over again. ;
ConclusionsUse classes where appropriate. There has been some fun poked at classy-ing up jQuery. But there is an important difference between an API for modifying the DOM and a sortable-paginated-groupable grid.
jQuery lacks a clear and repeatable pattern for organizing code. It also lacks tools to extend and add functionality to existing widgets. This is to jQuery's strength. It makes jQuery useful to everyone.
But, as code gets more complex and teams get bigger, the need for patterns and tools to bridge the jQuery to Object Oriented Architecture gap becomes unavoidable. We hope jQuery.Class can help.
Set inner/outer width/height with jQuery Dimensions.Etc Plugin
You often want to animate or set an object to be a specific width or height on the page. But if that element has padding, border, or margin, it will probably take up more space than you expect. The jquery.dimensions.etc.js plugin lets you set and animate the outerWidth, outerHeight, innerWidth and innerHeight of DOM elements.
Downloadjquery.dimensions.etc.js (minified 2kb)
Features- Set and animate innerWidth, innerHeight, outerWidth, and outerHeight.
- Fast. Uses the curStyles plugin.
Change the dimensions of an element in the dimensions demo.
DocumentationRead JavaScriptMVC's dimensions docs.
UseSetting and animating the dimensions with the innerWidth, innerHeight, outerWidth, and outerHeight helpers is pretty straightforward:
// include padding
$('#foo').innerHeight(20)
$('#foo').animate({innerWidth: 80})
// include padding + border
$('#bar').outerHeight(40)
$('#bar').animate({outerWidth: 40})
// padding + border + margin
$('#zed').outerHeight(50, true)
$('#zed').animate({outerWidthMargin: 600})
The numeric value is the length of the dimension in pixels.
These helpers adjust the height and width of the element but include the padding, border, and margin in their calculation. For example, setting outerHeight(40) on an element with "padding:5; border: solid 1px black" will have it's height set to 40 - 2*5 - 2*1 = 28.
ConclusionsWe use outerWidth and outerHeight quite often to make an element the size we want it to appear on the page. It enables us to use and adjust border and padding on most widgets we create. Before outerWidth and outerHeight, we had to manually adjust for border and padding. Hopefully you can avoid our problem by making use of this plugin. Good luck.
The week in qooxdoo (2010-08-27)
Another week in retrospect:
Deprecated codeWith revision r23139, we removed the deprecated code introduced with version 1.2 of qooxdoo. So now trunk is completely free of deprecations again. For more details, take a look at the corresponding bug report.
Using a deputyWe had a request to add the possibility to specify a deputy as a model in the list data binding. This can be especially handy when using a list or select box within a form. The form demo now shows how to create a form including a select box, all nicely done with data binding.
FixesFor a complete list of tasks accomplished during the last working week, use this bugzilla query.
New Contrib: CanvasCellTobi Oetiker did it again: another high-quality addition made it into qooxdoo-contrib:
That's what he said about CanvasCell: "Haven't you all been dying to get a chance to embed little charts into your qooxdoo tables? Looks so much more fun than all this text and numbers. The structure of the canvascell renderer makes it very easy to add your own plotter objects for creating other nice plots. If you load excanvas, this will even work on IE6-8.
Have a look at the demo source to see how to use the things. If you come up with your own plotters, I'll be glad to add them. I have made CanvasCell available in qooxdoo contrib, so you can grab your copy". Excellent!
JSConf.euThe European JavaScript Conference is for sure going to be (again) an absolutely top-notch event, nothing less than the annual hotspot of JavaScript goodness. Of course, we just have to be there as well. If you happen to have a ticket, or are in Berlin anyway at that time (weekend of Sep. 25./26, 2010) feel free to drop us a note in advance, so we could possibly arrange for some decent qooxdoo get-together.
Have a nice weekend!
The Official jQuery Podcast – Episode 32 – Dave Ward
Our guest this week is Dave Ward (http://encosia.com), who is the co-host of the Tekpub video series on jQuery, Mastering jQuery. We talk about the Tekpub series as well as caching lengths with the Google CDN and Microsoft CDN amoungst other CDN features. We also learn when you shouldn’t wait for the document to ready to fire off your jQuery code.
You can subscribe to the show in iTunes or via the raw RSS feed or you can download the MP3.
Links from the show:
- jQuery Mobile
- jQuery Conference 2010: Boston
- Call for speakers
- Polymorphic Podcast – jQuery Secrets with Dave Ward
- Master jQuery on TekPub
- 3 reasons why you should let Google host jQuery for you
- Using CDN Hosted jQuery with a Local Fall-back Copy by Jon Galloway
- Caching and Google’s Ajax Libraries API — Cache your jQuery by Paul Irish
- HTML5 Boilerplate
- The Official Guide to HTML5 Boilerplate – Paul Irish on Nettuts
- Do you know about this undocumented Google CDN feature?
- Documentation on jQuery UI Themes hosted on Google CDN – lower right side
- Don’t let jQuery’s $(document).ready() slow you down
Follow the show on twitter for up to date information regarding upcoming guests at http://twitter.com/jquerypodcast. Follow Ralph and Rey on Twitter as well.
You can send feedback about this episode or send in questions to the call-in number (804) 4jQuery, (804) 457-8379.
“jQuery Theme” created by Jonathan Neal
Get Multiple Computed Styles FAST with the curStyles jQuery Plugin
Do you use jQuery's css or curCSS functions to get multiple styles on the same element? Don't you know this is slow? The JavaScriptMVC extracted curStyles plugin boosts performance when reading multiple computed styles on a single element.
Downloadjquery.curstyles.js (minified 0.9kb) [works with any version of jQuery]
Features- Gets multiple computed style values on a single element.
- Takes hyphenated (padding-left) or camelCase (paddingLeft) names.
- Performs faster than curCSS.
See how to make a 2-3X faster jQuery.fn.height plugin in the curStyles demo.
DocumentationJavaScriptMVC's curStyles docs.
UseCall curStyles on your jQuery element to get a JavaScript object of style-value pairs. For example:
$('#foo').curStyles('paddingTop','marginTop')
might return something like:
{
paddingTop: '2px',
marginTop: '12px'
}
The values returned are the element's computed style values.
Computed Style ValuesAn element's computed style value is the measurable value (typically in px) of all the styles applied on the element. In IE, these values are calculated from properties on the currentStyle property of an element. In all other (standard) browsers, these values are found using getComputedStyle.
A Faster jQuery.fn.heightThe following makes a jQuery.fn.height plugin and is shown in the demo.
$.fn.fastHeight = function(){
//start with the offsetHeight
var sum = this[0] && this[0].offsetHeight;
// subtract out the current values
$.each(
this.curStyles(
"borderTopWidth",
"borderBottomWidth",
"paddingTop",
"paddingBottom"),
function(name, val){
sum -= parseInt(val) || 0;
});
return sum;
}
Conclusions
This plugin is very useful for optimizing performance critical paths. If you need to get only a handful of computed properties from an element over and over again, you can typically double its speed by using curStyles.
jQuery Conference 2010: Boston Announcement

The jQuery Project is very excited to announce the jQuery Conference 2010: Boston on October 16-17, 2010. The conference will be held at the Hilton Boston Logan in Boston, Massachusetts. The best part of this announcement is that Tickets are on sale now!
This venue is the largest that the project has worked with to date (Harvard Law School in ‘07, the MIT Stata Center in ‘08 and Microsoft New England Research Center in ‘09) and we expect to sell out very quickly.
A brief synopsis of some of the content that you’ll be able to expect:
- jQuery
- jQuery Mobile
- jQuery UI
- jQuery Plugins
- Complex Application Development
- jQuery Case Studies
- Plus much more
Speakers currently include jQuery Team Members:
We are still accepting speaker submissions for Boston, so please submit your proposed talk today! Deadlines for submissions is September 6, 2010 at 11:59PM PST.
In addition to two days of jQuery sessions, we’ll once again be offering a full day of jQuery Training prior to the event. The jQuery Training will be offered by Bocoup and will be hosted at the Bocoup Loft. The training will cover the following topics:
- What is jQuery?
- What is the DOM?
- jQuery and Selectors
- jQuery and Methods
- Get stuff do something: DOM traversal and manipulation
- jQuery and Events
- jQuery and AJAX
- jQuery and Effects
- Using plugins
- jQuery UI and the widget factory
- Debugging techniques with jQuery
Tickets for the jQuery Boston Training may be purchased on the Training Events Page.
Check out the jQuery Conference 2010: Boston event site for up-to-date information.
Convert Form Elements to JavaScript Object Literals with jQuery formParams Plugin
Want to quickly extract form data to a more usable format? Of course you do! We use JavaScriptMVC's formParams plugin constantly to turn form data into much easier to manipulate JavaScript Objects. We're releasing formParams standalone so everyone can save a little time on this extremely common task.
Downloadjquery.formparams.js (minified 1kb) [works with all versions of jQuery]
Features- Converts form data into object literals like: {foo: 'bar'}
- Creates nested objects with input names like: 'foo[bar]'
- Can convert values that look like numbers and booleans to Numbers and Booleans.
- Works without a form element.
JavaScriptMVC's formParams documentation.
UseJust call formParams on any jQuery wrapped element (form or not):
$('#myform').formParams() //-> Object
#myform html might look like:
<form id='myform'> <input type='text' name='donate' /> <input type='text' name='credit[name][first]' /> <input type='text' name='credit[name][last]' /> <input type='text' name='credit[number]' /> <input type='text' name='credit[expire]' /> <input type='text' name='credit[security]' /> </form>
The result of formParams might look like:
//$('#myform').formParams() ->
{
donate: 50.55,
credit : {
name : {
first: "Justin",
last: "Meyer"
},
number: "0000-2342-4654-5555"
expire: "5/4/2011",
security: 555
}
}
Sweet huh? You can turn off the Number/Boolean conversion (leaving all values as strings) by calling form params with false:
$('#myform').formParams(false) //-> Object
Oh, and one other cool thing ... if you have a bunch of checkboxes with the same name like:
<input type='checkbox' checked='checked'
name='items' value='model' />
<input type='checkbox' checked='checked'
name='items' value='view' />
<input type='checkbox'
name='items' value='controller'/>
formParams will put the values of the checked checkboxes in an array like:
{items: ['model','view']}
How handy is that!
ConclusionsformParams is quite handy. Because it already depends on an existing form, it's very useful for adding unobtrusive AJAX behavior to forms. We'll often use it to validate a JavaScript object literal before converting the literal to JSON with jquery-json before sending it to a JSON-REST service. That typically looks like:
$('form#recipe').submit( function( ev ) {
ev.preventDefault();
var recipe = $(this).formParams().recipe;
if( !recipe.title && recipe.instructions ) {
alert( 'provide a title and instructions' );
}else{
$.post("/recipes.json",
$.toJSON(recipe),
function(){ .... },
'json'
}
})
Enjoy!
The week in qooxdoo (2010-08-20)
Welcome back to another regular round-up of the week.
Overflow handlingAs promised last week when we announced the new overflow feature, we updated our applications to benefit from the toolbar overflow handling. We added specific code to the feed reader, api viewer and playground. To see it in action, resize your browser window and notice the toolbar items of those applications jump into a separate menu, located at the end of the toolbar itself.
Simulator: New locator typeThe qooxdoo user extensions for Selenium now support a new way to identify widgets, the "hybrid" locator. This allows test developers to combine multiple different strategies such as HTML ID, XPath and qooxdoo widget hierarchy in a single locator string, e.g.:
qxhybrid=options&&qxh=[@label=Foo]
This would find a widget with the qooxdoo property label set to Foo which is a child of another widget which has a DOM element with the ID options.
Tasks accomplishedFor a complete list of tasks accomplished during the working week, use this bugzilla query.
New Contrib: QxDyGraphsTobi Oetiker announced that he wrote "a little qooxdoo wrapper for using Dan Vanderkams wonderful dygraphs charting library from within the qooxdoo
infrastructure. It is checked into qooxdoo-contrib under QxDyGraphs". Try the demo in the contrib demobrowser. Cool (again), Tobi!
Christian Boulanger "released a minor update of the RpcConsole contribution to bring it closer to strict json-rpc standards compliance. The update to json-rpc version 2.0 will have to wait until the new RPC layer is implemented (not before the end of this year), but now the console should support sending requests to strict 1.0-compliant servers (which it did not do before)". There is more information available, the code can be found in qooxdoo-contrib. There is also an online demo, you can for instance run automated tests using the "Run Tests" menu. Thanks, Christian!
Real-life example: SavaLast but not least there was the addition of another real-life example to the corresponding wiki page: Sava is a repair center management application, which allows you to create a quote, take an order, manage customers, monitor inventories, send bills, create repair reports.
Interestingly, Sava seems to exist for quite some time now, as early versions of it were implemented in qooxdoo 0.5, then 0.6 and finally rebuilt with the latest qooxdoo versions, taking advantage of state-of-the-art features like the data binding layer. Nice theming, btw, and thanks for sharing this success story with the qooxdoo community!
That's it for today, see you again next week.
The Official jQuery Podcast – Episode 31 – Filament Group
In our 31st episode, we talk with the team at Filament Group, Todd Parker, Patty Toland and Scott Jehl. Filament Group is a design firm in Boston, MA and both Todd and Scott are members of the jQuery team. We discuss ThemeRoller, which Filament Group created as well as Progressive Enhancement and their new book Designing with Progressive Enhancement. We also discuss the role Filament Group will have in the next couple of months while working on the just announced jQuery Mobile project, the initial designs that have been shared were designed by the Filament Group.
You can subscribe to the show in iTunes or via the raw RSS feed or you can download the MP3.
Links from the show…
- Filament Group Website
- Filament Group Lab
- FileUpload Widget from the Lab
- Menu Widget from the lab
- Style button from the lab
- Visualize from the lab
- Progressive Enhancement
- jQuery UI ThemeRoller
- Designing with Progressive Enhancement Book
- Enhance.js
- WAI-ARIA
- jQuery Mobile
- jQuery Cookbook
- Questions: Question 1 Part 1 & Question 1 Part 2 & Question 2
GitHub Issues Update
Three months ago we were thrilled to announce the introduction of our front end to GitHub Issues. Since then we have worked on specific bugs in the applications and added small features here and there.
Today we are excited to announce the first major revision to our GitHub Issues! Below is a list of all the new features Nick Small and Randy Luecke have worked hard to introduce.
- Editable Issues
- Swap orientation button in the browser version
- Additional keyboard shortcuts
- Additional columns with the ability to show and hide them by right clicking the table header
- Additional search filters
- Additional improvements for keyboard
- Support for previewing issues before you submit them
- Bulk operations on multiple issues at once
- Notifications for unsaved comments and issues
- OAuth
- Full support for tagging
…and of course various bug fixes!
You can view the application by visiting githubissues.heroku.com, or you can download the updated desktop version (read more about how we make this work with NativeHost).
In addition to this new version of the application Randy has worked hard on a browser extension to automatically convert GitHub URLs to use our Cappuccino app.
You can customize it to do any of the following:
- Automatically redirect you from GitHub.com issues to our application
- Prompt you each time you enter an issues page to redirect you to our application
- Click the button in the toolbar to redirect you to our application.
If you are looking at a GitHub repository and click the button in the toolbar you will be redirected to that repository’s issues. If you’re not viewing a repository and click the button in the toolbar you will be redirected to the Issues application where you can select a repository and issue from there.
This plugin has been open source and is available on GitHub, but if you just want to install it you can download it here.
We hope you are as excited about this revision as we are, and we hope this update will make our application even more useful to you!
The jQuery Project is Proud to Announce the jQuery Mobile Project
Mobile web development is an emerging hot topic in the web development community. As such, the jQuery Team has been hard at work on determining the strategy and direction that the jQuery Project will take. Today, we are proud to announce the jQuery Mobile Project. We’ve launched a new site at jquerymobile.com that publicly outlines our strategy, research and UI designs.
As always, we want to hear from you. We’ve created a new Mobile jQuery forum to collect feedback from the community. Please feel free to join in on the discussion and read more in the announcement.
The week in qooxdoo (2010-08-13)
Hi all! The weekly wrap-up of happenings in and around qooxdoo is here.
TeamA new member has joined the core team here at 1&1. It's Tino Butz, and Tino is no stranger to us. We have been working together for quite some time, since Tino was formerly on the Gmx.com mail client team. As a consequence, Tino has in-depth knowledge of qooxdoo, and a lot of application development expertise as well
. Welcome, Tino!
qxoo is keeping up traction. We pushed it on our demo web site as a download, and provided some cool code samples. Even custom events and single-value data binding are working. Check it out!
Toolbar Overflow We closed quite an old bug this week which was an enhancement for the Toolbar: overflow handling. We ended up not implementing a "right out of the box" behavior, but realized a fairly general infrastructure where almost everything is possible for the application developer. As a first proof of concept, the toolbar demo includes this overflow handling (check the "Overflow handling" checkbox and resize your browser window, to see a "More" menu appear and disappear). We plan to add it to the Apiviewer and Feedreader next week. BugsHere is a canned query for all the issues that were resolved this week.
InspectorWe spent some time to improve our Inspector application. We fixed some minor bugs, refactored the code and also added a feature which makes it now possible to inspect Inline applications. The screenshot shows the Inspector running on our Showcase demo (which is an Inline app), with all of the Inspector windows open (Selenium, Objects, Widgets, Properties and Console - clockwise, starting top-left). So try it out
.
Burak Arslan has pushed out another update to his joint SOAP efforts, soaplib 0.9.2-alpha2 and qxsoap-0.6.1-beta1, which allow you to communicate complex class hierarchies between server and client app. See the details and download instructions here.
That wraps it up for this time. Enjoy your weekend!
jQuery London 2010 Postponed
After much deliberation, The jQuery Project Operations Team has decided to postpone our planned London event this year. Unfortunately, we were unable to secure a suitable venue that would allow us to provide the type of professional-quality event the community has come to expect within a budget sustainable by the project. We regret any inconvenience this may have caused and are actively working on planning a European event for 2011.
compareDocumentPosition plugin for jQuery
Being able to quickly compare two elements' positions in the browser is extremely useful for a variety of tasks. The DOM Level 3 specification describes the compareDocumentPosition method. It returns a bitmask with a whole bunch of useful information. But not all browsers support it :(
For a really solid walkthrough of compareDocumentPosition, check out John Resig's Comparing Document Position article. In this article, he gets close to providing a fast compareDocumentPosition for all browsers. JavaScriptMVC's standalone jquery.compare plugin steals his ideas and DOES provide a fast compareDocumentPosition in all the browsers jQuery supports.
Downloadjquery.compare.js (minified 0.8kb) [works with all versions of jQuery]
Demo DocumentationJavaScriptMVC's jquery/dom/compare docs.
UseTo use the compare plugin, just call compare on your jQuery wrapped elements with another element or jQuery element:
$('#foo').compare( $("#bar") ) //-> Number
The number is actually a bitmask that represents multiple several pieces of relational information about the two elements. Here's what each bit means:
Bits Number Meaning 000000 0 Elements are identical. 000001 1 The nodes are in different documents (or one is outside of a document). 000010 2 Node B precedes Node A. 000100 4 Node A precedes Node B. 001000 8 Node B contains Node A. 010000 16 Node A contains Node B.This means if a compare results in a 10, node B is before and contains node A. Typically, you just want to know one bit's information. To do this, use the bitwise AND operator (&) like:
if( $('#foo').compare( $("#bar") ) & 2 ){
//do something because #bar is before #foo
}
How it works
The plugin uses all the techniques in John's article, but fixes the 'Safari' problem. Older versions of Safari lack compareDocumentPosition and sourceIndex. This makes comparing node order (the 2 and 4 values of the bitmask) challenging. A naïve approach might be to walk up the parent nodes until an intersection happens. But, as I've learned from event delegation, calling parentNode is surprisingly slow. But using document Ranges and comparing them is fast. The following code adds the 2 and 4 values in Safari:
var range = document.createRange(),
sourceRange = document.createRange(),
compare;
range.selectNode(this[0]);
sourceRange.selectNode(b);
compare = range.compareBoundaryPoints(Range.START_TO_START,
sourceRange);
number += (compare === -1 && 4)
number += (compare === 1 && 2)
This code uses Safari's implemented compareBoundryPoints method in the DOM Level 2 Traversal-Range recommendation. CompareBoundaryPoints compares 2 ranges. You have to provide which parts of the ranges to compare and it returns -1,0, or 1 to indicate the the boundary positions.
Upgrade for Thunderbird 3.0 Users
Starting today, Thunderbird version 3.0 will ask users if they want to upgrade to Thunderbird version 3.1. We put this upgrade prompt in Thunderbird so that users are notified when a new version is available.

- deferring the decision for 24 hours (“Later”),
- declining the offer (“Never”), or
- accepting the free upgrade (“Get the New Version”).
The offer screen appears after 60 seconds of keyboard inactivity to ensure that we don’t interrupt anyone’s current work. If a user declines the offer and later changes their mind, they can get the upgrade at any time by selecting “Check for Updates” from Thunderbird’s “Help” menu.
In the near future (perhaps as soon as next week), we will be providing a similar notification to our Thunderbird 2 users. In addition to the new search tools and tabbed email, Thunderbird 3.1 adds several features to help with the transition from Thunderbird 2 and from Thunderbird 3.0.






