Node Roundup: Internationalisation
Dialect
Dialect (License: MIT, npm: dialect) by Pau Ramon Revilla is an internationalisation API for Node that stores translations in memory. The translations are periodically synced to MongoDB, so it’s fast and easy to serve multiple clients with.
The author’s example demonstrates this:
var dialect = require('dialect').dialect({current_locale: 'es', store: {mongodb: {}}});
// connects to the store
dialect.connect(function () {
// syncs the memory dictionaries with the store
dialect.sync({interval:3600}, function (err, foo) {
d.get('Hello World!'); // => Hola mundo
});
});
The mongodb property expects an object with database, host, and other connection options. I’d prefer to see a connection URI here, but it’s fairly easy to get going with MongoDB or SQLite.
The API has a lot of features that I’ve come to expect from a good i18n library — plurals, contexts, and interpolation are all supported.

The best thing about Dialect, however, is dialect-http (License: MIT, npm: dialect-http). This is a nice little web app that translators can use to find missing translations and fill them in. It’s one of the better open source examples of this type of tool that I’ve seen.
I’m having some trouble running dialect-http; I think the author might need to try running it on a fresh npm install with a blank database.
node-jus-i18n
node-jus-i18n (npm: jus-i18n) by Nicolas Chambrier also supports plurals, context, and interpolation. It comes with support for various data stores: module, file, gettext, and he’s also working on various databases. The documentation is still a little bit confusing, but he’s written a lot of examples and future plans.
There’s integration with Express, which means req objects get an i18n object:
app.configure(function() {
i18n.enableForApp(app, { // options (all are optional, you can pass {} or undefined
"locale": "en", // default locale
"catalogue": "messages", // catalogue to load
"locales": undefined, // locales to load
}, function(err) { // called when i18n has loaded messages
...
});
});
req.i18n.translate(...)
And there are helpers for use in templates:
_('Hello, {name}', {name: userName});
plural('You have {n} messages', nbMessages);
Polyglot
Polyglot (npm: polyglot) by Ricardo Tomasi is aimed squarely at Express apps. Ricardo suggests some helper aliases in the README:
app.helpers({
__: i18n.translate
, languages: i18n.languages
, n: i18n.plural // optional
})
As you can see, it does basic translation lookups and plurals. I’m not sure what’s going on with the README, because it refers to express- and express-voyage as package names, but I installed it with polyglot as suggested by the package.json file.
jQuery Roundup: jQuery UI Tabs Redesign, jquery-floating-widget, Ninja UI
jQuery UI 1.9 Milestone 5 – Tabs Redesign
The jQuery UI blog covered the most recent milestone release in jQuery UI 1.9 Milestone 5 – Tabs Redesign. This version updates the Tabs API to make it more consistent with other jQuery UI widgets.
Details of the changes are available in this post: Tabs API Redesign.
jquery-floating-widget
I was trying to vertically float an advert using CSS so it was always visible on the page, but only scrolled up to a certain point. It was hard to get it to behave properly with the particular layout I was using, so I ended up writing some JavaScript to do it.
If only I had jquery-floating-widget! This plugin, by Takeru Suzuki, does exactly what I needed. Just drop it in a page with $('.floating-widget').floatingWidget(); and some suitable CSS (detailed in this blog post) and you should get something like this: jquery-floating-widget demo.
Ninja UI

Ninja UI (GitHub: ninja / ui, License: Apache 2.0) by Jamie Hoover and Faisal Jawdat is a plugin that includes lots of UI widgets and icons.
The API looks a bit like jQuery UI:
/* Tabs */
var $tabs = $.ninja().tabs({
choices: [{
html: 'One',
select: function () {
console.log('Local tab function called.');
}
},
{
html: 'Two',
},
{
html: 'Three',
}],
choice: 2
}).select(function (event) {
console.log('Global tab function called returning: ' + event.html);
});
$examplesBody.append($title.clone().text('Tabs'), $tabs);
I pulled this from the examples.js file on the project’s site.
Code Review: Calipso
Code Review is a series on DailyJS where I take a look at an open source project to see how it’s built. Along the way we’ll learn patterns and techniques by JavaScript masters. If you’re looking for tips to write better apps, or just want to see how they’re structured in established projects, then this is the tutorial series for you.
This week’s code review is on Calipso by Clifton Cunningham. I only found out about Calipso today, but so many people seemed to want to learn more about Express apps that I thought it would be worth checking it out.
About Calipso
Calipso (GitHub: cliftonc / calipso, License: MIT) is a content management system, inspired by popular tools like WordPress and Drupal. It already has some cool features:
- Create themes with the Express-compatible view rendering engines and Stylus
- Background scheduler for importing content from feeds
- Modular structure
- Internationalisation
Installation
You’ll need Node, npm, and MongoDB to use Calipso.
Check the code out with Git, then set up the packages with npm:
$ git clone git@github.com:cliftonc/calipso.git
$ cd calipso
$ npm install
$ node app
Then visit http://localhost:3000/ to see a suspiciously empty site.
Usage

A fresh Calipso install makes a lot more sense when logged in, so log in with admin/password first. There’s a settings section under Admin which allows modules to be turned on. Content can be created using the navigation under Content, and the Content Type section is used to manage the types of pages a site will use.
App Structure
Calipso has a structure that is deceptively similar to most Express apps. The app.js file is fairly light — it just deals with booting the application. The configuration files can be found in conf/ which are split into environments (development, production, test). There’s also a file for managing settings which saves them to the database.
There aren’t view or controller directories because this part of the app is split into themes and modules.
Loading Settings
Settings are loaded from a similar pattern to something I use to separate out potentially lengthy Express settings:
module.exports = function(app,express) {
// Database connection
app.set('db-uri', 'mongodb://localhost/calipso-dev');
// ...
}
The environment configuration files are then loaded by the configuration manager:
var NODE_ENV = global.process.env.NODE_ENV || 'development';
app.configure(NODE_ENV, function() {
require("./"+NODE_ENV+".js")(app, express);
loadConfig(app, defaultConfig, function(err, config) {
app.set('config', config);
next(err);
});
});
Database
Mongoose is the database library, so there are a lot of schemas kicking around:
AppConfigSchema: Stores the site’s various settingsAppModule: Module status, embedded withinAppConfigSchemaUserandRole: Used by the user module for managing usersTag: Used to create tag cloudsTaxonomyMenu: Used by the taxonomy module for creating menusScheduledJob: Stores background tasks that are run by cronMediaandMediaGallery: Used to manage uploadsContentandContentType: Generic content management
The Mongoose library is passed around inside modules:
var MediaGallery = new calipso.lib.mongoose.Schema({
name: {type: String, required: true},
description: {type: String, required: true},
author: {type: String, required: true},
ispublic: {type: Boolean, required: true, default: false},
created: {type: Date, default: Date.now},
updated: {type: Date, default: Date.now}
});
This is true for other shared libraries as well. The calipso module is passed around everywhere so modules don’t need to keep loading the same libraries.
From what I can tell, the author has used sensible data types and embedded documents where they make sense.
Calipso Modules
Calipso abstracts Express away from modules, and makes heavy use of Step to get parallel execution for some boot time and reloading speed improvements.
Modules are defined like this:
exports = module.exports = {
init: init,
route: route,
install: install,
reload: reload,
disable: disable,
about: {
description: 'Sample content to test themes.',
author: 'cliftonc',
version: '0.2.0',
home: 'http://github.com/cliftonc/calipso'
}
};
Every property except about is a function. Some have signatures with req, res like Express actions, others have extra values from Calipso, and init takes module, app, next.
Routing
Calipso’s routing is Express middleware, which has allowed the author to distance Calipso’s concept of modules from Express’s API.
Logging
Calipso logs a lot of stuff, so the author has decided to use winston which is an asynchronous logging library. This is actually a very flexible library — it can log to the console, files, Loggly, or potentially anywhere.
Forms
Forms are generated using CalipsoForm, which takes readable JSON input and produces HTML. This is used inside modules, and there’s a good example in modules/community/sample-content/:
function renderSampleContentPage(req, res, template, block, next) {
calipso.form.process(req, function(incomingForm) {
var sampleForm = {
id: 'sample-content-form',
cls: 'sample-content-form',
title: 'Sample Content Form',
// ...
calipso.form.render(sampleForm, incomingForm, req, function(form) {
calipso.theme.renderItem(req, res, template, block, {
form: form
});
next();
});
Internationalisation
I was extremely pleased to see some effort towards internationalisation in Calipso. It has its own translation code which is exposed as Connect middleware. That means the translation module can access the session to determine what language is selected.
The translation module keeps a cache around for lookups, which are just a case of looking up the string based on the English string, then replacing any embedded values. Curly braces are used for interpolation: "{name} Profile":"{name} Profile".
There are a few modules that provide similar functionality, available through npm. I’ve been looking at node-jus-i18n and Polyglot.
Conclusion
Calipso builds on Express, but it feels very different to a typical Express app because of the module loading system. Many apps would benefit from using the settings loading approach, and I’m definitely going to look at using winston in my next project.
Passing around the shared calipso object is interesting as well, it reduces the need to keep loading key libraries.
There’s a lot of Mongoose code here as well, so if you’re looking to learn more about that it might be worth digging in. And, if you’re interested in learning how to write Calipso modules, Clifton writes extremely detailed code comments so it shouldn’t be too hard to get started with his template module.
Fractal Lab, Disabling WebGL, Bitcoin Miner
Fractal Lab

Fractal Lab by Tom Beddard is a WebGL fractal explorer. It has great quality rendering of all kinds of strange 2D and 3D fractals, with tonnes of options and a great interface.
If you want to get started with the really cool stuff, click Fractal library then select something that looks interesting. There are tabs on the right-hand-side of the rendering with further options. Be careful with the number of iterations and max steps, else you might redline your CPU for a bit!

There are keyboard shortcuts which control the position of the camera (WASD + QE and ZX for speed); the controls make it feel like walking around strange 3D landscapes.
This is no mere WebGL demo, it’s fully-featured. In fact, it’s probably one of the best fractal apps that I’ve played with for some time (and I’m a Processing nerd!)

The author also wrote a blog post about Fractal Lab here: Fractal Lab – A WebGL based web application for rendering 2D and 3D fractals in real-time.
Disabling WebGL
In How To Disable WebGL, James McQuaid talks about how to disable WebGL in Chrome and Firefox for Windows 7. This is in response to the WebGL security issues reported last week.
James says:
Once WebGL is effectively sandboxed by Google and Mozilla, I will be happy to turn it back on. Until then, the eye candy will have to wait.
If you’re using a Mac or other unix you should be able to run Chrome/Chromium with --disable-webgl or edit Firefox’s settings with about:config.
JavaScript Bitcoin Miner
bitp.it is a JavaScript bitcoin miner. The author announced it in a post on the Bitcoin Forum.
He says:
We are hoping this will be viewed as an exciting new alternative to banner advertisement that actually has the potential of not being annoying to website visitor and actually generating bitcoins for the website operator.
That sounds pretty optimistic, but it’s extremely intesresting nonetheless.
Note that if you visit bitp.it you’ll actually be contributing to the generation of bitcoins in a shared pool.
Let's Make a Framework: Element Attributes Part 3
Welcome to part 63 of Let’s Make a Framework, the ongoing series about building a JavaScript framework.
These articles are tagged with lmaf. The project we’re creating is called Turing. Documentation is available at turingjs.com.
For the past two weeks I’ve been looking at accessing element attributes. This week I’ll demonstrate how to write element attributes.
API Design
I like the idea of using the same method for both getting and setting element attributes:
// Get
turing('selector').attr('name');
// Set
turing('selector').attr('name', 'value');
It’s fairly easy to remember this and easy to implement.
setAttribute
Remember element.getAttribute? Well, there’s also setAttribute. It was introduced in DOM Level 1, so browser support isn’t terrible.
Most of the cross-browser issues relating to getAttribute should apply to writing attributes because they were almost all related to correcting IE’s interpretation of the attribute’s capitalisation. That means we can use setAttribute in a very similar way.
Null Values
According to MDC:
Even though
getAttribute()returns null for missing attributes, you should useremoveAttribute()instead ofelt.setAttribute(attr, null)to remove the attribute.
This is present in jQuery’s attributes.js implementation:
if ( value === null ) {
jQuery.removeAttr( elem, name );
return undefined;
Note that we must distinguish between null and undefined — because we’re using attr to read and write values, undefined is an absence of a value which implies reading the attribute.
That means we need to use element.removeAttribute when null is passed. However, this method has poor browser support. When I tried jQuery’s implementation in IE6 I seemed to get an empty string instead of undefined, so I think a true cross-browser remove attribute method might be out of scope here.
To my knowledge, this is the closest I can get in IE:
function removeAttribute(element, name) {
if (element.nodeType !== nodeTypes.ELEMENT_NODE) return;
if (propertyFix[name]) name = propertyFix[name];
setAttribute(element, name, '');
element.removeAttributeNode(element.getAttributeNode(name));
}
Tests
I wrote this test to help me implement the core functionality for writing attributes:
'test setting attributes': function() {
var element = turing.dom.get('#attr-write')[0],
link = turing.dom.get('#attr-write a')[0],
input = turing.dom.get('#attr-write form input')[0],
button = turing.dom.get('#attr-write form button')[0];
turing.dom.attr(element, 'id', 'attr-test2');
assert.equal(turing.dom.attr(element, 'id'), 'attr-test2');
turing.dom.attr(element, 'class', 'example2');
assert.equal(turing.dom.attr(element, 'class'), 'example2');
turing.dom.attr(element, 'tabindex', 1);
assert.equal(turing.dom.attr(element, 'tabindex'), 1);
turing.dom.attr(link, 'href', '/somewhere');
assert.equal(turing.dom.attr(link, 'href'), '/somewhere');
// Forms
turing.dom.attr(input, 'value', 'changed-value');
assert.equal(turing.dom.attr(input, 'value'), 'changed-value');
turing.dom.attr(input, 'name', 'changed-name');
assert.equal(turing.dom.attr(input, 'name'), 'changed-name');
turing.dom.attr(button, 'name', 'changed-button-name');
assert.equal(turing.dom.attr(button, 'name'), 'changed-button-name');
turing.dom.attr(button, 'value', 'changed-button-value');
assert.equal(turing.dom.attr(button, 'value'), 'changed-button-value');
}
Implementation
Building on last week’s code, I added a check to see if the attribute value is null or undefined:
/**
* Get or set attributes.
*
* @param {Object} element A DOM element
* @param {String} attribute The attribute name
* @param {String} value The attribute value
*/
dom.attr = function(element, attribute, value) {
if (typeof value === 'undefined') {
return turing.detect('getAttribute') ?
element.getAttribute(attribute) : getAttribute(element, attribute);
} else {
if (value === null) {
return dom.removeAttr(element, attribute);
} else {
return turing.detect('getAttribute') ?
element.setAttribute(attribute, value) : setAttribute(element, attribute, value);
}
}
};
I reused the getAttribute capability test for writing attributes. I started off with a simple attribute setter for IE:
function setAttribute(element, name, value) {
if (propertyFix[name]) {
name = propertyFix[name];
}
return element.setAttribute(name, value);
}
But the button test failed so I had to add similar code from getAttribute:
if (name === 'value' && element.nodeName === 'BUTTON') {
return element.getAttributeNode(name).nodeValue = value;
}
Conclusion
Getting and setting attributes is extremely similar, and it just goes to show how frustratingly similar browser implementations have been. The strange case of IE’s removeAttribute behaviour still baffles me, but if I find a good solution I’ll write an update.
This week’s code is in commit c5625f8.
Node Roundup: TermKit, service.js, Writing Node Extensions
TermKit

TermKit (License) by Steven Wittens is a “next gen” command-line interface. It currently runs on Mac OS X, and is written using Node and Objective-C. I feel like it’s closer to something like Quicksilver than a typical terminal client — but it may shape up to be something pretty compelling for general use.
When I first saw TermKit I thought Steven had used some kind of incredible Node/Cocoa library, but it’s actually a clever mix of Node and WebKit. This seems like a good way to build native-feeling Mac apps with Node, in a similar way to how WebKit Inspector is built.
The most interesting thing about this project is it attempts to build on some of the conventions of terminal emulators, but explores embedding GUI concepts into the terminal — code is displayed with syntax highlighting, and images are previewed automatically. Although it’s early stages for this project there are some extremely encouraging ideas on display.
service.js
service.js by Cal Henderson builds on daemon.node (npm: daemon) to provide an easy way of turning Node scripts into init.d daemons:
#!/usr/local/bin/node
require('service').run({
lockFile: '/var/run/my_app.pid',
logFile : '/var/log/my_app.log',
});
This preamble sets up the necessary requirements for the script to function as an init script.
Writing Node Extensions
In How to write your own native Node.js extension, Olivier Lalonde explains the steps necessary to use libnotifymm.h from Node.
Writing a native Node extension can be cumbersome and verbose at times but it is well worth the hard earned bragging rights!
It’ll take a bit of C++ and waf knowledge to really appreciate what’s going on, but it does demonstrate that writing extensions for native libraries is within the reaches of mere mortals (rather than wizards).
jQuery Roundup: 1.6.1, Davis.js, diagonalFade
jQuery 1.6.1 Released
jQuery 1.6.1 was released last week which is the version with the new .prop() method and changes to .attr(). I’ve been talking about this in the Let’s Make a Framework posts covering element attribute reader implementations.
Davis.js
Davis.js (GitHub: olivernn / davis.js) by olivernn uses history.pushState to create an Sinatra/Express-like API for client-side apps:
var app = Davis(function () {
this.get('/welcome/:name', function (req) {
alert("Hello " + req.params['name'])
})
})
app.start()
One interesting thing about this library is it has a plugin API which can be used to share routes between applications. It also has an event API, which includes an unsupported event for falling back in browsers that don’t support pushState.
diagonalFade

diagonalFade (GitHub: jonobr1 / diagonalFade, License: Apache 2.0) by Jono Brandel is a diagonal matrix fade effect for jQuery. It’s got quite a few options for configuring the effect:
$('#container').diagonalFade({
time: 100,
fadeDirection_x: 'left-right', // "left-right" || "right-left"
fadeDirection_y: 'top-bottom', // "top-bottom" || "bottom-top"
fade: 'out', // "in" || "out"
complete: null // callback function
});
Code Review: Finance
Code Review is a new series on DailyJS where I take a look at an open source project to see how it’s built. Along the way we’ll learn patterns and techniques by JavaScript masters. If you’re looking for tips to write better apps, or just want to see how they’re structured in established projects, then this is the tutorial series for you.
The purpose of these code reviews is not to nitpick; it is simply to learn and hopefully educate. I intend to cover all aspects of JavaScript — the various server-side interpreters, browser-based code, jQuery plugins — anything I find with something to teach us.
This week’s code review is on Finance by TJ Holowaychuk.
Finance
Finance is a little accounting web app. It seems like something useful for logging things like business expenses (I always forget to do that!), but it could be used for any kind of payment.
Installation
You’ll need Node and npm to use Finance.
Check the code out with Git, then set up the packages with npm:
git clone https://github.com/visionmedia/finance.git
cd finance
npm install
Usage

Running node app.js should bring up an instance of Finance. No database setup or authentication is necessary. Try filling in a few payments to get a feel for the app.
App Structure
I was looking for a database library, but I found something interesting! TJ has opted to use a built-in database library, db.js, which writes JSON to a file. The default data is saved to /tmp/finance.db which is worth changing if you actually want to use the app.
Saving data just writes out JSON.stringify(this), and loading it involves reading the file and iterating over each value:
/**
* Load data.
*
* @param {Function} fn
* @api public
*/
Database.prototype.load = function(fn){
var self = this
, fn = fn || noop;
fs.readFile(this.path, 'utf8', function(err, json){
if (err) return fn(err);
var data = JSON.parse(json)
, keys = Object.keys(data)
, len = keys.length;
for (var i = 0; i < len; ++i) {
self[keys[i]] = data[keys[i]];
}
fn();
});
return this;
};
I like how he writes to this rather than an object inside Database.
Of course, this app is built with Express. TJ has structured his main app.js file in an interesting way — it loads the libraries, does the configuration, then loads controllers from separate files:
var month = app.resource('month', require('./controllers/month'));
var items = app.resource('items', require('./controllers/item'));
month.add(items);
This works by using the express-resource module. This patches Express, which is why the var Resource = require('express-resource') line looks slightly suspicious given that Resource doesn’t appear anywhere else.
I have a feeling we might see this practise become popular in Express app development. Even though it’s tempting to keep adding routes to app.js, keeping a slim main file makes managing code easier as a project grows over time (as I found with Nodepad).
Controller Methods
TJ uses res.end() in methods that don’t need to return anything:
exports.update = function(req, res, next){
var month = req.params.month
, id = req.params.item
, data = req.body.item[id]
, item = db.months[month].items[id];
try {
validate(data, 'entity');
validate(data, 'date', 'date');
validate(data, 'category');
validate(data, 'amount', 'number');
for (var key in data) item[key] = data[key];
item.tags = parseTags(item.tags);
db.save();
res.end();
} catch (err) {
res.send({ error: err.message });
}
};
I know that res.send() sends a HTTP 204 No Content response in Express, so presumably this ends the HTTP connection without returning any headers.
The controllers are written using RESTful methods: index, create, update, destroy. There’s also a validation method which throws exceptions when a validation fails. The controller method catches this and sends an error back using JSON: res.send({ error: err.message }).
Views
The views are written with Jade, naturally, and they’re very straightforward. Most of the interface is generated by client-side JavaScript, so the views are mostly placeholders. The items for the index action are rendered by the server rather than putting everything in the client. TJ uses the collection option for partial to do this:
!= partial('item', { collection: items })
!= partial('item', { object: {}})
Client-Side JavaScript
The client-side code is a fairly typical jQuery brain dump. I noticed that TJ aliased jQuery’s function with var j = $; because he hates typing $ (I think he bemoaned this on Twitter).
Conclusions
This is a small project that showcases some of TJ’s new Express modules, and isn’t intended to be used as a multi-user web app. The cunning use of JSON as the database is notable, purely because it makes installation and backup a breeze. Rather than forcing users to install modules for a database (and perhaps even the database itself), they can stick Finance on their laptops and potentially even leave the JSON file in something like Dropbox.
In fact, the entire project has a strangely self-contained yet efficient feel about it, which appeals to me as a way to build nifty little open source web apps.
Leaflet, Augment.js, Anatomy of a Mashup, 3 Dreams of Black, WebGL Security
Leaflet

Leaflet (GitHub: CloudMade / Leaflet, License: BSD) from CloudMade and maintained by Vladimir Agafonkin is a JavaScript library for building tile-based maps that work with desktop and mobile browsers.
It’s extremely easy to use, and includes most of the user interface elements required to work with maps:
// initialize the map on the "map" div with a given center and zoom
var map = new L.Map('map', {
center: new L.LatLng(51.505, -0.09),
zoom: 13
});
// create a CloudMade tile layer
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/YOUR-API-KEY/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18});
// add the CloudMade layer to the map
map.addLayer(cloudmade);
CloudMade are pushing OpenStreetMap as their data source:
CloudMade are here to make using OpenStreetMap data easier and more accessible. We support OpenStreetMap by sponsoring mapping parties, providing public relations and legal support, hosting developer events and contributing to the OSM code base.
Augment.js
Augment.js (GitHub: olivernn / augment.js, License: MIT) by olivernn brings features from JavaScript 1.8.5 to other browsers. It does this by patching missing functionality but leaving native methods alone.
If you’ve ever got annoyed at the lack of Array.prototype.indexOf or Array.prototype.reduce in IE then this is the library for you!
Anatomy of a Mashup

Anatomy of a Mashup by Cameron Adams breaks down a six minute mix of Daft Punk tracks using a novel HTML5/CSS3 interface.
Cameron has written a post about it, Anatomy of a Mashup: Definitive Daft Punk visualised in which he says:
Hopefully it gives you a new insight into the artform of the mashup, otherwise you can just stare at the pretty shapes.
I definitely enjoyed staring at the pretty shapes…
3 Dreams of Black

3 Dreams of Black is a WebGL music video, developed by developers at Google:
“3 Dreams of Black” is our newest music experience for the web browser, written and directed by Chris Milk and developed with a few folks here at Google
The mouse can be used to explore the scenes, and there’s even a hidden Mega Man and Reddit alien. Once the video has played out it’s possible to explore the last scene, flying behind some birds in a manner reminiscent of fl0wer.
This video also includes heavy use of shaders — it works well as a WebGL showcase. I’m actually quite jealous of whoever got to work on this!
WebGL as a Security Problem
In WebGL – A New Dimension for Browser Exploitation James Forshaw explores security issues relating to WebGL, including denial of service and cross-domain image theft.
While there is certainly a demand for high-performance 3D content to be made available over the web, the way in which WebGL has been specified insufficiently takes into account the infrastructure required to support it securely.
It’s interesting that there’s already a history of low-level problems with WebGL:
During the development of WebGL it seems that all the browser vendors supporting it have encountered issues with certain drivers being unstable or crashing completely.
This research got a lot of attention this week, so at least it will get more eyeballs on WebGL security issues. If ever there’s been a way to break out of a sandbox and exploit a machine, I’d imagine the low-level access WebGL potentially provides is a rich target.
Let's Make a Framework: Element Attributes Part 2
Welcome to part 62 of Let’s Make a Framework, the ongoing series about building a JavaScript framework.
These articles are tagged with lmaf. The project we’re creating is called Turing. Documentation is available at turingjs.com.
Last week I discussed retrieving element attributes, which should just involve calling getAttribute but ends up with added complexity to support every browser. This week I’ll look at building a cross browser implementation.
Tests
This test should cover the basics for getting attributes:
'test getting attributes': function() {
var element = turing.dom.get('#attr-test')[0],
link = turing.dom.get('#attr-test a')[0];
assert.equal(turing.dom.attr(element, 'id'), 'attr-test');
assert.equal(turing.dom.attr(element, 'class'), 'example');
assert.equal(turing.dom.attr(element, 'tabindex'), 9);
assert.equal(turing.dom.attr(link, 'href'), '/example');
}
It includes some examples that I know will break in IE: class will have to be mapped to className, href won’t return what we expect, and tabindex requires capitalisation.
Basic Implementation
This will work in WebKit and Firefox:
/**
* Get or set attributes.
*
* @param {Object} element A DOM element
* @param {String|Object} options The attribute name or a list of attributes to set
*/
dom.attr = function(element, options) {
if (typeof options === 'string') {
return element.getAttribute(options);
}
};
It fails in IE:
AssertionError In "==": Expected: example Found: null
Which is fine, we just need to account for the way class is accessed:
var propertyFix = {
'class': 'className'
};
function getAttribute(element, name) {
if (propertyFix[name]) {
name = propertyFix[name];
}
return element.getAttribute(name);
}
This is only good for browsers that need it (IE). Let’s use Turing’s capability detection:
turing.addDetectionTest('getAttribute', function() {
var div = document.createElement('div');
div.innerHTML = '<a href="/example"></a>';
if (div.childNodes[0].getAttribute('href') === '/example') {
return true;
}
// Helps IE release memory associated with the div
div = null;
return false;
});
This checks the unusual case of the extra parameter required to correct what href represents in IE. Then the main dom.attr method needs to be updated to take this into account:
/**
* Get or set attributes.
*
* @param {Object} element A DOM element
* @param {String|Object} options The attribute name or a list of attributes to set
*/
dom.attr = function(element, options) {
if (typeof options === 'string') {
return turing.detect('getAttribute') ?
element.getAttribute(options) : getAttribute(element, options);
}
};
Extra Parameters
As I mentioned last week, IE requires an extra parameter to make href return the value we expect. I’ve created a list of attributes that require this extra parameter:
getAttributeParamFix = {
width: true,
height: true,
src: true,
href: true
};
Then if the capability test fails, this will run:
function getAttribute(element, name) {
if (propertyFix[name]) {
name = propertyFix[name];
}
if (getAttributeParamFix[name]) {
return element.getAttribute(name, 2);
}
return element.getAttribute(name);
}
The part that reads element.getAttribute(name, 2) makes IE behave like other browsers. Using an object that contains a list of true values is a convenient way of doing this, I instinctively thought of ['width', 'height', 'src', 'href'].indexOf(name) !== -1) but then I remembered that IE doesn’t have indexOf.
Forms
Dealing with button attributes can have strange results in IE. In particular, accessing value can return the inner HTML. I’m still testing this to figure out what versions it affects, and what other attributes are involved, but so far I’ve done this:
if (name === 'value' && element.nodeName === 'BUTTON') {
return element.getAttributeNode(name).nodeValue;
}
These are the tests I’m using:
assert.equal(turing.dom.attr(input, 'value'), 'example');
assert.equal(turing.dom.attr(input, 'name'), 'e');
assert.equal(turing.dom.attr(button, 'name'), 'b');
assert.equal(turing.dom.attr(button, 'value'), 'example');
Chains
Supporting the chained API is a case of a quick stub:
/**
* Get or set attributes.
*
* @param {String|Object} options The attribute name or a list of attributes to set
* @returns {String} The attribute value
*/
attr: function(options) {
if (this.elements.length > 0) {
return dom.attr(this[0], options);
}
}
This is one of those interesting cases where it makes sense to access the first element in the current chain’s stack, and return a value rather than this.
Conclusion
More than anything else, getting attributes is a task in browser support. There are some subtleties to fully supporting IE (and I’m still not convinced this is 100%).
The last commit for this week was commit b16c498.
Node Roundup: NodeConf Slides, Kanso, Whiskey, Redback
NodeConf Slides
Ryan Dahl’s NodeConf slides includes some background on liboio with a focus on Windows support.
Today Node runs on Windows via Cygwin. This is an unacceptable port. Cygwin is slow, old, buggy. Users hate it
I’ve never really worked with Windows, so this was all new to me:
IOCP supports Sockets, Pipes, and Regular Files. That is, Windows has true async kernel file I/O. (In Unix we have to fake it with a userspace thread pool.)
The work on liboio certainly sounds fascinating — it seems like it won’t just be Windows users who benefit from the work going into the next generations (0.5, 0.6) of Node.
Isaac Schlueter’s slides are about npm 1.0, which covers global and local modules (in case you’re still confused). He also mentions an initiative called npat:
To get on the bandwagon:
npm set npat true
This is a kind of distributed continuous integration testing system, based on the CPAN Testing Service.
Kanso

Kanso (GitHub: caolan / kanso, npm: kanso, Apache 2.0 License) by Caolan McMahon is a Node module that makes writing CouchApps easier, with added focus on the client-side:
What’s needed is a harmonious environment which shares code between the browser and server, without ‘breaking the web’. Search engines and other users should still have access to key content, even if we accept a less featureful experience. This is the problem Kanso was designed to solve, by implementing CouchDB’s design doc API in the browser.
CouchApps are JavaScript and HTML5 applications served by CouchDB:
If you can fit your application into those constraints, then you get CouchDB’s scalability and flexibility “for free” (and deploying your app is as simple as replicating it to the production server).
Kanso provides command-line and libraries tools to make this whole process easier. If you’re still not sold, try following the Kanso tutorial which will walk you through the entire process of setting up Kanso and building a blog application.
Whiskey
Whiskey (Apache 2.0 License) is a test runner that runs each test file in a separate file, complete with timeouts, setup/teardown, test file initialization, friendly output, test coverage, and scope leak reporting.
It works in a similar way to Expresso in that tests are run with the whiskey command-line script.
Redback
Redback (GitHub: chriso / redback, npm: redback, MIT License) by Chris O’Hara is a high-level Redis library. It provides an interface to Redis data types, including: List, Set, SortedSet, Hash, Channel, and Cache.
Redback even has pub/sub provider support:
var channel = redback.createChannel('chat').subscribe();
// To received messages
channel.on('message', function(msg) {
console.log(msg);
});
// To send messages
channel.publish(msg);
jQuery Roundup: Instagram jQuery Plugin, jQuery.handleStorage, Morse.js
Instagram jQuery Plugin

Instagram jQuery plugin (GitHub: potomak / jquery-instagram) by Giovanni Cappellotto uses the instagr.am API to show a list of photos.
You’ll need to provide a client ID to use it, but the Instagram API documentation explains how to do this.
jQuery.handleStorage
jQuery.handleStorage (GPL) by Jason Gerfen provides a local storage database with AES support. Jason’s example encrypts the data in a form:
$('#myForm').handleStorage({ storage:'cookie', aes:true });
This uses the Gibberish AES library. It could be used without AES, however, and the plugin will use local storage (if available), a session, or cookies.
A related commercial product is 1Password which includes a HTML/CSS/JavaScript viewer for encrypted passwords. The 1Password application itself is for Windows, Mac OS, iOS or Android, but interestingly their database files include a HTML viewer that can decrypt saved passwords.
Ampersand
Ampersand by Nicholas Johnson is a small jQuery plugin for using Open Source Ampersands. This is a project to provide high-quality single-character fonts.
Morse.js

Morse.js (GitHub: mattt / Morse.js, MIT License) by Mattt Thompson transcribes morse code: $('p').morseCode({ bpm:12 });.
JavaScript.next, Front-end Guidelines, Crossroads.js
JavaScript.next
Brendan Eich gave a last-minute talk at JSConf 2011 about the future of JavaScript, spurred on by a CoffeeScript talk. There’s good coverage by Ian Elliot in JavaScript creator talks about the future which contains some interesting quotes from Brendan:
I advocated strongly for standardizing prototypal inheritance a la CoffeeScript’s class, super, and @ syntactic sugar.
We’ve seen all this before and what it did to the community during ECMAScript 4’s planning (which I covered on DailyJS in The History of JavaScript). I don’t know if this time anything will change, but there’s an undeniably growing movement that wants to push JavaScript’s syntax in new directions.
I’ve already seen some harsh commentary from JavaScript developers that I have a lot of respect for — but if we learned anything from ECMAScript it’s that progress should come in small steps rather than too much at once.
I’ve collected some other resources related to this talk:
- JS.next and You — notes by trevmex
- Hacker News coverage of JS.next
- Traceur — Google’s “JavaScript.next-to-JavaScript-of-today” compiler
And I’m still not sure if I should be writing JavaScript.next, JS.next, JS.Next, JS/Next, JS-Next, or “Harmony”!
Front-end Guidelines
Front End Development Guidelines (GitHub: taitems / Front-End-Development-Guidelines) is a document written by Tait Brown that attempts to collate useful HTML, CSS and JavaScript best practises that he’s learned along the years. The document is easy to read and contains a lot of things that I’ve found myself teaching junior developers and designers before.
Crossroads.js
Crossroads.js (millermedeiros / crossroads.js, MIT License) by Miller Medeiros is a routing library:
I consider the main target to be single page applications with complex navigation paths and also large websites that shares the same JS files across multiple pages or would benefit from this kind of approach. It is also very useful for server-side and RESTful applications.
The API supports “string rules” and regular expressions:
var route1 = crossroads.addRoute('/news/{id}', function(id){
console.log(id);
});
var route2 = crossroads.addRoute(/^\/lorem\/([a-z]+)$/, function(id){
console.log(id);
});
Routes can be removed, parsed, and validated.
Mibbu, LibCanvas, Zeon
Mibbu

Mibbu (GitHub: michalbe / mibbu, MIT License) by Michal Budzynski is a small games development framework that supports:
- Canvas or DOM rendering
- Sprites
- Collision detection
Michal has written an example Mibbu game which demonstrates the API. Here’s an extract:
var Game = new mibbu(500, 500);
Game.init();
var sprite = new Game.spr('img/reptile.png', 200, 200, 7, 0),
sprite2 = new Game.spr('img/reptile.png', 200, 200, 7, 0),
background = new Game.bg('img/bg.jpg', 6, "S", {x:0,y:0});
If you want to read more details, mibbu.eu is a good place to start. He’s also written a post about it: Mibbu – javascript html5 game framework.
If Mibbu sounds familiar, then you may have read about it when we covered OpenOdyssey which is a game implemented in an early version of Mibbu.
LibCanvas
LibCanvas (GitHub: theshock / libcanvas, LGPL) by Pavel Ponomarenko is a Canvas library which includes APIs for:
- Animation
- Mouse events
- Drag and drop
- Image preloading
There are a lot of examples on libcanvas.github.com that show off these features. A simple LibCanvas looped animation can be implemented fairly painlessly:
var libcanvas = new LibCanvas('canvas').start();
var shaper = libcanvas
.createShaper({
shape : new LibCanvas.Shapes.Circle(150, 75, 60),
fill : '#900',
stroke: 'red',
lineWidth: 7
});
// Changing size and color
shaper.animate({
props: {
radius: 15,
fill : '#2d2d2d',
stroke: '#4c4c4c',
lineWidth: 1
},
time : 1200,
onFinish: function (prevAnim, prevProps) {
shaper.animate({
props: prevProps,
fn : 'bounce-out',
time : 800,
onFinish: prevAnim.repeat
});
}
});
This library is built on Atom by the same authors:
Atom is compact JavaScript framework oriented on modern browsers, which allows to support quite broad list of features without keeping a lot of cruft necessary to implement them in old browsers.
Zeon

Zeon.js by Peter van der Zee is a tool for analysing JavaScript. It has a custom parser, and an interesting interface for exploring possible problems. It seems like the project is still at an early stage, but I’m looking forward to seeing what Peter does with it.
Let's Make a Framework: Element Attributes
Welcome to part 61 of Let’s Make a Framework, the ongoing series about building a JavaScript framework.
These articles are tagged with lmaf. The project we’re creating is called Turing. Documentation is available at turingjs.com.
Element Attributes
This DOM stuff really takes a lot of work, doesn’t it? In the last few parts we’ve been mucking around with CSS classes, so I thought it would be pertinent to address element attributes. The jQuery API allows attributes to be accessed with the .attr() method. jQuery actually distinguishes between attributes and properties:
As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() only retrieves attributes.
I’m fairly sure most frameworks either treat these the same or have a generic ‘get attribute/property/whatever value and interpret the parameter’ — consider MooTools’ Element.get (it also has getProperty).
jQuery is quite good at being explicit where it makes sense, so I’d like to look at element attribute manipulation without confusing it with DOM properties.
Getting Attributes
Element.getAttribute returns an attribute like this:
document.querySelector('#content-main article').getAttribute('role');
// "main"
What about when an attribute is not present? That’s actually an interesting point, which is mentioned in MDC’s documentation:
Essentially all web browsers return null when the specified attribute does not exist on the specified element. The DOM specification says that the correct return value in this case is actually the empty string, and some DOM implementations implement this behavior.
Consequently, you should use
hasAttributeto check for an attribute’s existence prior to callinggetAttribute()if it is possible that the requested attribute does not exist on the specified element.
Some frameworks don’t do this, but it’s probably a good idea — there’s a comment in jQuery mentioning the BlackBerry browser returning an empty string.
Browser Support
IE does something very strange with attributes that return URLs. Rather than leaving the URL alone it’ll normalize it. To force it to return the value we actually want we need to pass a second parameter to getAttribute:
element.getAttribute('href', 2);
The documentation for this is at the getAttribute Method page on MSDN.
Therefore, jQuery sets up hooks to handle this for every attribute that returns a URL:
jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
get: function( elem ) {
var ret = elem.getAttribute( name, 2 );
return ret === null ? undefined : ret;
}
});
});
IE6/7 also cause problems when working with attributes on form elements. To get around this, getAttributeNode is used (except for buttons):
formHook = jQuery.attrHooks.name = jQuery.attrHooks.value = jQuery.valHooks.button = {
get: function( elem, name ) {
var ret;
if ( name === "value" && !jQuery.nodeName( elem, "button" ) ) {
return elem.getAttribute( name );
}
ret = elem.getAttributeNode( name );
// Return undefined if not specified instead of empty string
return ret && ret.specified ?
ret.nodeValue :
undefined;
}
XML
If the document is not XML then the attribute name needs to be capitalised correctly for IE:
propFix: {
tabindex: "tabIndex",
readonly: "readOnly",
"for": "htmlFor",
"class": "className",
maxlength: "maxLength",
cellspacing: "cellSpacing",
cellpadding: "cellPadding",
rowspan: "rowSpan",
colspan: "colSpan",
usemap: "useMap",
frameborder: "frameBorder",
contenteditable: "contentEditable"
}
// Elsewhere:
jQuery.attrFix = propFix;
It seems like tabIndex behaves strangely in most browsers:
attrFix: {
// Always normalize to ensure hook usage
tabindex: "tabIndex"
}
jQuery’s comments include a reference to more details on handling this: Getting, setting, and removing tabindex values with JavaScript.
Conclusion
When I started writing this article I thought getting attributes would just be a case of using getAttribute, but thanks to browser inconsistencies it becomes a lot more work. jQuery’s use of hooks keeps most of this out of the core framework code, so it’s easy to see what has been added to support browser behaviour. I really like the way jQuery does this.
Next week I’ll look at actually building this thing.
References
Node Roundup: npm 1.0, brain, nTunes, everyauth
npm 1.0
npm 1.0 is out, and Isaac Schlueter has written a short but sweet post about it on the Node blog: npm 1.0: Released. This release nicely coincides with JSConf.
My Node tutorial part 23 has some tips for using npm 1.0 — I explain how global modules work. I’m convinced this change in global module management will encourage people to write more maintainable packages, so I hope 1.0 gets traction.
brain
brain (GitHub: harthur / brain, MIT License) by Heather Arthur is a machine learning library for neural networks and Bayesian classifiers. Bayesian classifiers have been famously used for spam filters, which is illustrated by Heather’s example:
var options = {
backend: {
type: 'localStorage',
options: {
name: 'emailspam'
}
}
};
var bayes = new brain.BayesianClassifier(options);
bayes.train('buy these great watches', 'spam', callback);
Look familiar?
It can be installed with npm install brain, and it’ll even work in browsers.
nTunes
nTunes (MIT License) by Nathan Rajlich is a REST API for controlling iTunes. There’s an npm package, but you might want to install this globally: npm install -g nTunes. Once it’s installed and you’ve run nTunes, it can be used like this:
~$ nTunes
______
/\__ _\
___ \/_/\ \/ __ __ ___ __ ____
/' _ `\ \ \ \ /\ \/\ \ /' _ `\ /'__`\ /',__\
/\ \/\ \ \ \ \ \ \ \_\ \/\ \/\ \ /\ __/ /\__, `\
\ \_\ \_\ \ \_\ \ \____/\ \_\ \_\\ \____\\/\____/
\/_/\/_/ \/_/ \/___/ \/_/\/_/ \/____/ \/___/
v0.1.0
HTTP Server started on port: 8888
Type 'help' for a list of runtime commands...
curl localhost:8888/current%20track/name
// "Tres Brujas"
curl -d value=50 localhost:8888/sound%20volume
// Volume changed to 50
everyauth
I haven’t tried everyauth yet, but it looks fairly painless to use. The author, Brian Noguchi, includes an example Express app, and it looks like everyauth takes a lot less configuration than other oauth libraries I’ve worked with before.
For example:
everyauth
.twitter
.myHostname('http://local.host:3000')
.consumerKey(conf.twit.consumerKey)
.consumerSecret(conf.twit.consumerSecret)
.findOrCreateUser( function (sess, accessToken, accessSecret, twitUser) {
return usersByTwitId[twitUser.id] || (usersByTwitId[twitUser.id] = twitUser);
})
.redirectPath('/');
This library hides the complexity of OAuth nicely, which is good because trying to deal with OAuth and OpenID is not exactly fun.
jQuery Roundup: jQuery 1.6, JsBloat, jQuery-URL-Parser, jQuery-Retina-Plugin
jQuery 1.6 Released
jQuery 1.6 is out and they’re already accepting proposals for 1.7.
The jQuery documentation includes a section showing changes relevant to 1.6: new or changed in 1.6. There are some interesting additions:
.prop()and.removeProp()can be used to manipulate properties, which makes.attr()’s behaviour more consistentdeferred.always()adds a handler to aDeferredobject, whatever the outcome.promise()returns aPromiseobject, which helps create a richer syntax for asynchronous operations (the animation examples illustrate it well)
JsBloat and jQuery
In jquery.com uses only 34% of jQuery by Michael Bolin, the author explains how he used JsBloat to track how much of jQuery is actually used on jquery.com. An ironic example, but it’s interesting to read the thinking behind it and Michael’s arguments for using the Closure compiler.
jQuery-URL-Parser
jQuery-URL-Parser (License) by Mark Perkins is a URI parser:
var url = $.url('http://allmarkedup.com/folder/dir/index.html?item=value');
url.attr('protocol'); // returns 'http'
url.attr('path'); // returns '/folder/dir/index.html'
It can also provide quick access to string parameters:
$.url('http://allmarkedup.com?sky=blue&grass=green').param('sky'); // returns 'blue'
I have a feeling people might want to use this for their own hash fragment routing, which the plugin makes easy:
$.url('http://test.com/#sky=blue&grass=green').fparam('grass'); // returns 'green'
jQuery-Retina-Plugin
jQuery-Retina-Plugin automatically loads high resolution images if a device has Apple’s retina display.
$(function() {
$('.retina').retina({'retina-suffix': '@2x'});
});
It determines this based on window.devicePixelRatio, so it should work with other high density displays (if the device and browser sets devicePixelRatio).
Node Tutorial Part 23: npm 1.0
Welcome to part 23 of Let’s Make a Web App, a tutorial series about building a web app with Node. This series will walk you through the major areas you’ll need to face when building your own applications. These tutorials are tagged with lmawa.
Click to show previous tutorials.
npm 1.0
I upgraded to npm 1.0.3 recently. It generated a list of packages that were incompatible, I haven’t bothered reinstalling them yet.
Isaac’s installation instructions seem to have solidified at:
curl http://npmjs.org/install.sh | sh
… but read the npm README before doing anything!
Changes in Nodepad
It takes a while to get used to npm 1.x, but I like the changes. A big change is the default installation path: running npm install package will install package in the local ./node_modules folder.
What this means for Nodepad is we no-longer need the require('package@version') syntax.
I’ve updated Nodepad to work with npm 1.0: commit 52e6b1.
npm install
From Nodepad’s directory, running npm install will build and install the dependencies to ./node_modules. That means it’s completely self-contained from everything else on your system. Running npm -g install will install all of the dependencies in NODE_PATH/nodepad/ — again, making the dependencies self-contained.
Using Nodepad is now fairly simple:
$ git clone git://github.com/alexyoung/nodepad.git
$ cd nodepad
$ npm install
$ mongod
$ node app.js
This is very similar to how npm bundle used to work. When using Express apps written by other people, their documentation may suggest running npm bundle, but npm install should work.
Search vs. List
The npm ls command used to search available packages. It now lists local packages:
$ npm ls -g
├─┬ express@2.3.2
│ ├── connect@1.4.0
│ ├── mime@1.2.1
│ └── qs@0.1.0
├── highlight@0.1.0
├── jade@0.10.6
├── markdown@0.2.1
├── n@0.4.1
Using npm search will search remote packages:
$ npm search nodepad
nodepad A notepad written with Node =alexyoung
Why the Change?
Isaac has been blogging and discussing npm 1.0 for some time on the Node blog and npm discussion group. A major contributing factor is Node’s 0.4 changes to the module loading system. These changes were mentioned in the Node 0.4 announcement:
require()now has a primitive understanding of package.json. It looks for the main script. This allows require() to work on package directories directly.
And:
A specially named directory, node_modules/, is searched in the current directory for any modules. This will hopefully encourage programmers to bundle modules rather than rely on a global namespace.
It might feel awkward to have to adapt existing applications to work with npm 1.0, but the changes should be minimal and should make module management easier for everyone.
J3D, GL64K, node-webgl
J3D

J3D is a 3D library for WebGL that has a Unity3d object/scene exporter. The author wrote a blog post about it entitled Taming WebGL with some background on the library:
J3D is a very simple engine that can load 3D models and textures, has a scene with a hierarchy of objects and can render everything using basic lights. Somewhere on the way I added the feature to export models from Unity3d which I thought would make the job of preparing assets much easier.
GL64K
GL64K is a competition to create a WebGL demo in 64k. If you’re not familiar with what they’re expecting, there’s an example called Glass. The first prize is $2000 and a copy of the OpenGL ES 2.0 Programming Guide, and entries must use skeleton.html. There’s only 7 days left to enter the competition!
node-webgl
I keep asking myself, “how can I write desktop-based OpenGL with Node?” It seems like Brian McKenna is working on a solution with node-webgl. It looks like the project is in early stages yet, but there’s a blog post with a screenshot to prove it works: node.js WebGL.
I hope to see more Node desktop integration.
Let's Make a Framework: CSS Classes
Welcome to part 60 of Let’s Make a Framework, the ongoing series about building a JavaScript framework.
These articles are tagged with lmaf. The project we’re creating is called Turing. Documentation is available at turingjs.com.
CSS Classes
I like to be able to easily change CSS classes on an element. Most JavaScript frameworks that I’ve encountered have an easy way of doing this. jQuery provides .addClass(), .removeClass(), .toggleClass() and .hasClass(). It’s a very simple API that’s easy to remember. Other frameworks work in a similar way.
Class Name Manipulation
Class names are manipulated using the DOM Level 2 className property: The HTMLElement interface: className attribute. This property is a string, so getting and setting individual class names requires a bit of string manipulation to implement a friendly API.
However… there’s also a new API called classList. This API looks a lot more like what web frameworks provide:
element.classList.add('className');
element.classList.remove('className');
element.classList.toggle('className');
element.classList.contains('className');
It might be working checking if the browser supports classList and using the native functions if available.
Implementing Adding Classes
I’d like this test to pass:
'test adding CSS classes': function() {
var element = turing.dom.get('#dom-test')[0];
// Invalid values should be ignored
turing.dom.addClass(element, null);
turing.dom.addClass(element, 10);
// This should change the className
turing.dom.addClass(element, 'newClass');
assert.equal(element.className, 'newClass');
turing.dom.addClass(element, 'class2');
assert.equal(element.className, 'newClass class2');
// Adding the same one twice should be ignored
turing.dom.addClass(element, 'class2');
assert.equal(element.className, 'newClass class2');
// Reset the value
element.className = '';
}
Adding classes should work like this:
- Ensure the value passed in is a string
- Ensure the element is a valid node (
ELEMENT_NODE) - Use
classListif available - If not, set the
classNameequal to the passed in value - If the
classNamehas already been set, append the value with a space - Make sure class names aren’t duplicated
What I came up with should do all of this with fairly easy to follow code:
/**
* Append CSS classes.
*
* @param {Object} element A DOM element
* @param {String} className The class name
*/
dom.addClass = function(element, className) {
if (!className || typeof className !== 'string') return;
if (element.nodeType !== nodeTypes.ELEMENT_NODE) return;
if (element.classList) return element.classList.add(className);
if (element.className && element.className.length) {
if (!element.className.match('\\b' + className + '\\b')) {
element.className += ' ' + className;
}
} else {
element.className = className;
}
};
The regular expression uses word boundaries to check if a class name has already been set — this will match spaces and the end of the string.
Removing Classes
Removing classes is pretty much the same. The tests are a bit more involved to make sure white space is handled correctly:
'test removing CSS classes': function() {
var element = turing.dom.get('#dom-test')[0],
testClasses = 'class1 class2 class3 class4';
// Invalid values should be ignored
turing.dom.removeClass(element, null);
turing.dom.removeClass(element, 10);
// Test a single class
turing.dom.addClass(element, 'newClass');
assert.equal(element.className, 'newClass');
turing.dom.removeClass(element, 'newClass');
assert.equal(element.className, '');
// Test multiple, making sure white space is as it should be
element.className = testClasses;
turing.dom.removeClass(element, 'class2');
assert.equal(element.className, 'class1 class3 class4');
element.className = testClasses;
turing.dom.removeClass(element, 'class1');
assert.equal(element.className, 'class2 class3 class4');
element.className = testClasses;
turing.dom.removeClass(element, 'class4');
assert.equal(element.className, 'class1 class2 class3');
// Reset the value
element.className = '';
}
I tried to use regular expressions again, replacing the old value then correcting white space. The second replace removes spaces that might get left at the start of the string:
/**
* Remove CSS classes.
*
* @param {Object} element A DOM element
* @param {String} className The class name
*/
dom.removeClass = function(element, className) {
if (!className || typeof className !== 'string') return;
if (element.nodeType !== nodeTypes.ELEMENT_NODE) return;
if (element.classList) return element.classList.remove(className);
if (element.className) {
element.className = element.className.
replace(new RegExp('\\s?\\b' + className + '\\b'), '').
replace(/^\s+/, '');
}
};
Chained API
I also added addClass and removeClass to the DOM chained API:
'test chained class manipulation API': function() {
turing('p').addClass('x1');
assert.ok(turing('p')[0].className.match(/\bx1\b/));
turing('p').removeClass('x1');
assert.ok(!turing('p')[0].className.match(/\bx1\b/));
}
These methods just loop through each element:
/**
* Add class names.
*
* @param {String} className A class name
* @returns {Object} `this`
*/
addClass: function(className) {
for (var i = 0; i < this.elements.length; i++) {
dom.addClass(this[i], className);
}
return this;
}
Conclusion
The fact that classList has appeared in some browsers makes this whole problem go away. I haven’t noticed any frameworks using classList — I’m not sure if there are any caveats to it. Also, my methods don’t cope with a list of class names in one string, which jQuery does (which is why jQuery’s implementation is more complicated than mine, see attributes.js).
This week’s code was commit e50328e.
