JavaScript Rap

Thu, 25 Oct 2012

Pamela Fox asked on Twitter to help her with creating JavaScript Rap and I was not able to resist to try. I decided to copy my verses here, so they wouldn’t lost on Etherpad:

You code in JS, but you mixing the types
And always forget the syntax for “splice”.
To type “double equal” is like rolling the dice
And you scared to death … of prototypes

It’s time to stand up and get some respect,
Just go to ECMA and start reading spec.
Unpuzzle, unlock it, uncode and decrypt
And gain yourself power of true JavaScript

You add number to string and string to array
Divide it by object (you know it’s ok)
The prototype chain you can see through the code…
It was a great journey from Netscape to Node.

  New Gig

Thu, 3 May 2012

Adobe Badge So, I’ve started new page in my career. In a great company with great products and great history, but most importantly with great opporunities for me to make impact on the Web. That’s what I always wanted to do: change the Web. Raphaël shaped the Web a little; as much as a niche JavaScript library can. It’s mission is not over yet, but I have desire to hit a bigger drum.

I started my work at Adobe as Senior Computer Scientist. I will work on Web Standards and represent Adobe at W3C. Instead of just complaining about how something is bad or ugly, I hope, I could actually change things. Or create new things that wouldn’t be ugly, right?. We shall see. Either way, I am excited to turn around this corner and see what is waiting for me over there. There is a great team of people deeply passionate about web standards and I am lucky that I could work alongside those people. And, by the way, we are hiring. Do you want to change the world too? ;)

  Something New

Tue, 17 August 2011

I tweeted recently about new operator: ‘if your JS library API expecting me to write “new” — you’re doing it wrong.’ Let me clarify it a bit.

I am not saying that using new operator is a bad idea, it’s an essential operator, but asking me to use it with your API is a bad idea. Lets look at box2d.js API for example. Because it was supposed to copy C++ API, it took all C-like decisions and copied it straight to JavaScript. It’s ok for something that was ported, but I see the same patterns in APIs created in JavaScript from the scratch.

Example:

var m_world = new b2World(new b2Vec2(0.0, -9.81), true);

I would say it should look something like this instead:

var m_world = box.world(0.0, -9.81);

Why is it better?

  • Saves characters to type, makes code less verbose
  • If you put new by mistake… nothing will happen
  • User shouldn’t care if your function returns object literal or instance of some “class”

I will illustrate last point on Raphaël code. Lets say you want to create a circle and you want to take it bounding box and log x and width of it:

var c = paper.circle(x, y, r),
    bb = c.getBBox();
    console.log(bb.x, bb.width);

With new approach I should write it like this:

var c = new paper.Circle(x, y, r),
    bb = new BBox(c); // global? may be Raphael.BBox()?
    console.log(bb.x, bb.width);

I found last one to be harder to read, error-causing and generically not nice.

  Raphaël 1.5

Tue, 17 August 2010

In a meanwhile Raphaël 1.5 has been released. What is new and why such a version bump? Here is transcript from git commit:

1.5.0
• fixed IE8 issue with the HTML element named Raphael
• fixed precision for arcs in IE
• added caching to getPointAtSegmentLength function
• added ability to do more than one animation of an element at the same time
• added "cubic-bezier()" as an easing method
• added new syntax for animation (keyframes)
• hsl2rgb now accept h as degree (0..360), s and b as % (0..100)
• show="new" instead of target="blank" for SVG
• added angle method
• added snapTo method
• cached popup || activeX for IE
• fixed insertAfter
• fixed timeouts for animation
• added customAttributes

Lets take a look at most important updates: custom attributes and keyframes. Custom attribute could be created like this:

paper.customAttributes.segment = function (x, y, r, a1, a2) {
    var flag = (a2 - a1) > 180,
        clr = (a2 - a1) / 360;
    a1 = (a1 % 360) * Math.PI / 180;
    a2 = (a2 % 360) * Math.PI / 180;
    return {
        path: [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]]
    };
}

This will create new attribute segment, which you could set up like el.attr({segment: [x, y, r, a1, a2]});. Basically the function we defined earlier will translate our custom attribute into set of common attributes, in our case segment will become a path. The important thing is that now as an attribute segment could be animated. See demo: growing segments of the pie.

What about keyframes? Well, you can simply specify animation in additional format:

el.animate({
    "20%": {cy: 200, easing: ">"},
    "40%": {cy: 100},
    "60%": {cy: 200},
    "80%": {cy: 300, callback: function () {}},
    "100%": {cy: 200}
}, 5000);

This is more like CSS 3 animation does it. And as a bonus you now can run multiple different animations at the same time over the same element. See demo: asynchronous animation.

Note: If you upgrade you could have issue with animation of translation or rotation. To fix it simply add el.stop() before call to animate method, because animate wouldn’t stop older animation anymore.

  typeof, == and ===

Fri, 16 July 2010

In JavaScript nothing is what it looks like; you rarely work with actual values.

Most of the time values morph from one type to another, just so they can morph to yet another type later on. That's the way of language. And if that weren't enough, I recently found that the way I was writing some functions in JavaScript was wrong. It wasn’t particularly my fault, though, as it looks like most of us did it wrong the way.

The root of evil is our use of the typeof operator.

Lets look at this example:

var attrWhiteList = /^(text|size|color|weight|true)$/;
function setAttr(name, value) {
    if (typeof name == "string" && attrWhiteList.test(name)) {
        this[name] = value;
    } else if (typeof name == "object" && name) {
        for (var key in name) if (attrWhiteList.test(key)) {
            this[key] = name[key];
        }
    }
}

We can call the setAttr function like this:

setAttr("size", 20);

or like this:

setAttr({size: 20, name: "Jason"});

And when we call it successfully an attribute is set. And it’s not only setting what we send, it additionally checks that attributes appear in a white-list before setting them. All seems good. Or is it?

Let’s rewrite the function with our revised view in mind:

var attrWhiteList = /^(text|size|color|weight|true)$/;
function setAttr(name, value) {
    if (arguments.length > 1) {
        if (attrWhiteList.test(name)) {
            this[name] = value;
        }
    } else if (name === Object(name)) {
        for (var key in name) if (attrWhiteList.test(key)) {
            this[key] = name[key];
        }
    }
}

Simply, our second approach robustly deals with any parameters that the function receives, while the first will not:

setAttr("size", 20);
setAttr({size: 20, name: "Jason"});

Those two work as before, but there are many parameters that the first version can’t cope with:

setAttr(true, true);

var f = function () {};
f.size = 5;
setAttr(f);

setAttr(new String("text"), "Jason");
setAttr(["text"], "Jason");
setAttr(new Boolean(true), true);
setAttr({toString: function () {return "text";}}, "Jason");

You’re probably saying: “Nobody will write code like that”. And you’re probably right, but people could easily write code like this:

setAttr(a, b);

What am I trying to say? In JavaScript you coerce types all the time, whether you intend to or not. You can fight it, or you can use it.

It doesn’t matter what type the variable is, it’s more important how you can use it. So, for example, instead of asking “Is it a number?” it’d be better to ask “Is it numeric?”

var isnan = /^(NaN|-?Infinity)$/;
function isNumeric(num) {
    return !isnan.test(+num);
}

or maybe “Is it object?” or “Is it array?”

function is(o, type) {
    type = String(type).toLowerCase();
    return  (type == "null" && o === null) ||
            (type == typeof o) ||
            (type == "object" && o === Object(o)) ||
            (type == "array" && Array.isArray && Array.isArray(o)) ||
            Object.prototype.toString.call(o).slice(8, -1).toLowerCase() == type;
}

is(function(){}, "object"); // true
is(function(){}, "function"); // true
is([], "object"); // true
is(new String("string"), "string"); // true

The difference can be subtle as it’s between “Is this variable’s type ‘object’?” and “Is this an object?”.

As you know a function is an object and function… new Number is an object and a number…

This way of thinking about JavaScript isn’t limited to typing—it impacts the comparison operator, also.

You’ll often hear that “==” is a bad part of JavaScript. I not so sure. It is often better to use “===” instead of “==” only if you don’t clearly understand how those operators work or are simply in doubt.

It depends on what level of equality you really need.

Typing: typeof a === "object"
looks as ridiculous to me as: ((typeof(a)) === ("object"))

The brackets don’t hurt, but putting them everywhere just because you used to is redundant. And it’s the same with “===”.

Taking a look at our “is” function, you’ll notice I don’t care if type is a string or not as long as it equals (==) “object” or “null”, etc. We’re embracing the flexibility of the language, and not fighting against it.

Don’t code as though the types are fixed, they’re not.

Next time you’re going to use typeof or “===” think about whether it’s the best choice. Remember that in JavaScript nothing is always what it looks like.

  Raphaël 1.4

Fri, 07 May 2010

So, Raphaël 1.4 is released with couple of new features:

I added resetScale method, which will reset scale counter on the object, so next time you will scale it, it will happen absolutely, not relatively.

Scaling text is not supported. I added partial support in 1.4: now position of the text will change, but not the actual size.

Fixed annoying long lasting bug in IE for rectangle with rounded corners. There are couple of other bugs have been fixed.

New method drag was added, due to huge demand. Now you can make something draggable as simple as: element.drag(onmove, onstart, onend); I will update documentation soon, but so far you can take a look at the updated demos.

Last is the big one: multitouch support, now all mousedown, mousemove and mouseup are replaced with touch event handlers on touch devices. In additon drag method will allow you to drag multiple objects at the same time. Watch the video. Or try the demo yourself if you are lucky iPhone/iPod Touch/iPad holder.

  JavaScript Inheritance Pattern

Mon, 01 Mar 2010

More and more people on the Web digging into JavaScript and using it for daily tasks. It is always fascinating to watch how they approach inheritance in JavaScript. Most of the time two patterns are used:

First pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
o2.prototype = new o1;

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

The only problem with this one is that it uses the constructor of the first object to create middle object, which is going to be a prototype for o2. There is no guarantee that this constructor will be empty. Adding an alert in it will make the code unusable. A simple workaround leads us to second pattern.

Second pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
var f = function () {};
f.prototype = o1.prototype;
f.prototype.constructor = o2;
o2.prototype = new f;

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

Now, it is important to note that there is no way to do clean inheritance in ECMAScript 3. Fixing constructor in line: “f.prototype.constructor = o2;” makes it enumerable, which means it will show up in a “for in” loop. This pattern creates a real prototype chain. Changing o1 prototype will affect not only john, but also bill. Depending on the model this could be good or bad thing. You inherit everything: to remove a method, one has to overwrite it with undefined.

Downsides: bill.a is two steps away. The more you use this pattern the more steps away it will move, costing time and processing. And, as I mentioned, the constructor property is fake. It is enumerable.

There is a third pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
for (var method in o1.prototype) {
   o2.prototype[method] = o1.prototype[method];
}

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

This does the same as the others without prototype chain creation. Method “a” is nicely shared across two objects. You could alter o1 prototype without worrying about o2. Just as in previous case, depending on the model this could be a good or bad thing.

In my experience, the most common case is when you create objects’ constructors in the beginning and don’t alter their prototypes later on. Altering prototypes during your script is a tricky and dangerous procedure. I guess there are some cases when it is the best solution, but I can’t think of any of them off top of my head.

What I am trying to say is that the second pattern above will serve 99% of the current scripts where “inheritance” is used but long prototype chains make every call to the object’s property run through the whole chain and those runs are not free.

This pattern also makes the “instanceof” operator work, but I am not sure how useful it is in general. (I mean is (obj instanceof o1 === "a" in obj) always true? No. So, what the point?)

I reckon the second pattern only makes sense when you are about to mutate your objects during your script and benefit from this connection between prototypes. Otherwise you are creating infrastructure you are not going to use, but still have to pay the maintenance for. Why do that?

I prefer the third pattern for most cases.

  #jsquizz

Fri, 04 Dec 2009

Recently I started to ask twitterverse JavaScript related questions, just for fun. I called it “jsquizz”, but twitter is very fast media, so I decided to copy some of this questions here, so I could easily find them later.

var date = +new Date;
// much shorter than new Date().getTime()
var a = "</script>"; // wouldn’t work
var a = "</s" + "cript>"; // usual way to workaround
var a = unescape("<%2fscript>");
// unusual way to workaround
var a = "<\/script>"; // shorter way
var a = 120000; // make it shorter?
var a = 12e4;
var a = Math.floor(b);
// if b is always positive, we could make it shorter
var a = ~~b;
var a = new Boolean(false); // how to quickly check if (a)?
alert(typeof a); // "object"
alert(!!a); // always true, because a is an object
alert(!!+a);
// right result, because “+” is calling valueOf
// method and convert result to number “0..1”
alert(a>0); // even shorter
[0, 1, 2][0, 1, 2] // what the result of this expression?
alert([0, 1, 2][0, 1, 2]); // 2
// first brackets is array declaration,
// second brackets contains comma
// operator, so it is almost equivalent to
alert([0, 1, 2][2]); // 2

  Raphaël 1.2.1

Tue, 27 Oct 2009

In Raphaël 1.2 release I did two major changes that I want to explain: IE optimisation and animation revamp.

For drawing in IE Raphaël uses VML. VML syntax for path is different to SVG: it uses different letters for commands and it doesn’t support floating point numbers. So to insure correct work I have to replace commands and round the numbers. But this not the end, IE doesn’t support quadratic curves and arcs. To work it around I convert everything into cubic curves. Conversion takes time. In IE we facing world slowest JavaScript engine, so on big paths performance could go down to unacceptable level. In 1.2 I optimized it a bit. I convert path to curves only if it has cubic curves or arcs, otherwise I just replace commands and round the numbers. Unfortunately I can’t come with a better solution at the moment, but if you have any, drop me a line.

The animation in Raphaël was done not in the best way: for each animation I created separate timer, so in case if you animate multiple objects at the same time, they are moving asynchronously. I unite all animation under the same timer and it made animation smoother in Safari and more accurate in all browsers. But there is still an issue with the fact that when you animate a large group of elements it is impossible to start animation for each element at the same time. The worst JavaScript engine the bigger is the gap between animations and on long run it is quite noticeable. To fight this I introduced new method animateWith, which first argument is an element you wish to stick to. Raphaël uses it internally when you animate set, so I would say the easiest way to animate two elements synchronously is to unite them in set and call animate method on the set.

In the video I created 150 circles and animated them to the same X. You can easily see the difference.

  The Emperor’s New Clothes

Thu, 01 Oct 2009

It was an argument in web standards talks for a while. Tables for layout, style attributes, JavaScript event attributes, inline script tags and even font tags are ok. Take a look at google.com. Right. Google.com is probably most visited page in the web, so if <font size=-1>blah</font> is smaller than correct semantic HTML and CSS, even by one byte, then it worth it. Because every byte counts. With such a traffic every byte of home page costs some quite visible money to the company. (I even heard a stories about some guy, who managed to reduce size of the google.com page by couple of characters and received a bonus from the company…) Google just put size of the page as a priority above valid mark up, right?

Not really.

If you look at the source code of the google.com home page, you will see that size of it could be decreased a lot. In particular inline JavaScript can be compressed at least by 10%.

Consider following example

This is code from google.com:

if(window.addEventListener)window.addEventListener("load",a,false);else if(window.attachEvent)window.attachEvent("onload",a);google.timers.load.t.prt=(new Date).getTime();

This is how it can be written:

var w=window,e=w.addEventListener,v=w.attachEvent;e&&e("load",a,!1);v&&v("onload",a);google.timers.load.t.prt=+new Date;

I just saved 51 byte! There are more, literally kilobytes of code to be saved, but it looks like nobody cares. Wait, if nobody cares about page size, why we have ridiculous font tags in the page? May be I don’t understand some obvious thing or may be the King is naked.

  Raphaël 1.0 RC1

Thu, 10 Sep 2009

Recently I released Raphaël 1.0 RC1. The most noticeable change for users of the library is differences in the API: removing moveTo, lineTo and friends from the path object. Why did I decide to do this? The biggest drawback of these methods is that they apply immediately. That means that if you draw path consisting of three segments, path element is updated three times – each time with increased number of segments, so total number of segments drawn is 1 + 2 + 3 = 6. The more segments your path has, the longer it takes to draw. In geometric progression.

To avoid this I could introduce some method “draw” that could be called after you define all segments, but this doesn’t look like an elegant solution and doesn’t suit the library’s name. There is a soluton that is compact, simple and you could easily work with it. Say, you want to change some point on the path. There is no interface for this apart from SVG path, and I can’t think of any elegant and easy to grasp API for this. Manipulating strings is what JavaScript can do very well. So I removed these methods from the library to external plugin, which you could concatenate with the Raphaël if you really rely on them. I suggest you learn SVG path syntax: it is simpler than it looks.

Other big thing is adding support for angle in arc. This is very rare thing, and frankly, I haven’t seen it in the wild, but without it SVG path support wasn’t complete. To make it happen I rewrote arcTo for VML completely. Now it converts arc to bezier curve, because VML doesn’t have support for angles in arc segments. The same method is now used in animation (for SVG & VML), which makes animation more smooth. Conversion of the whole path into bezier curves let me do path bounding box calculations more precisely, especially in VML. In fact, Safari doesn’t calculate bounding box for path correctly either.

I also worked on improving performance by applying caching and simply optimizing code.

  Mermaids & Fishermen

Mon, 03 Aug 2009

Imagine the ocean. The ocean where live mermaids and lots of fisherman are chasing their luck. If you ask mermaids and fisherman about ocean they will tell you the same story: “I love ocean. Ocean is my home. I live in breath of the ocean only…” etc. But in fact their approach to ocean is very different. Fisherman care only about money they will get for fish. Ocean is just a big reservoir for them. For mermaids it is truly their home. They care about balance and purity of the ocean. Some fishermen don’t even believe that mermaids exist, some haven’t even heard of them. Fishermen can’t see the ocean as mermaids do, some of them tried: put their head under the water, everything is blurred, they can’t breathe. Pull head back. Never did it again. Some mermaids are tempted by the money they could make as a fisherman, but they lost their magic and become just “yet another fisherman”. Fishermen are winning. Huge tankers crossing the ocean and are taking enormous amount of fish from the ocean ruining the balance. Mermaids can’t stop fisherman, because they talk different language and can’t understand each other.

This ocean is called “the World Wide Web”.

There are web developers, who care about web, standards, semantic, accessibility, etc. They are mermaids. And there are web developers, who care about result only. If the web page is a soup of HTML, inline CSS and inline JavaScript, that’s ok as long as performance tests show us that it is even 0.5 ms faster.

Who are you? Fisherman or mermaid?

  We need to talk

Wed, 22 Jul 2009

I am very lucky person. I met personally so many great people, like Cameron Adams, John Allsopp, Lachlan Hardy, Russ Weakley, Tim Lucas and many more others who, I hope, will forgive my laziness. The problem is, I don’t remember when last time I was talking to any of them (except Lachlan who is my colleague at Atlassian). We proud of our community, we see each other on conferences and other events, but there is no time to talk there. I mean really talk, share ideas, search for advice, etc. This is only possible with some limited amount of people in the room. I know these guys for at least two years and I have never really talk to anyone of them. Ever. This is very sad. What to do about it? The simplest way is to grab the person you want to talk to and invite him for lunch. Most of us are working somewhere near CBD, so we should catch up more frequently. Send an e-mail to person you know for ages and invite him/her for lunch. Everybody will win. Do it today.

  Objects in JavaScript (part II)

Mon, 27 Apr 2009

I will not write about prototypical inheritance in JavaScript today. Instead lets take a look at one misunderstanding:

“Changing prototype on the constructor will magically update not only new objects, but also all existing ones…”

This is totally wrong. Do not trust an author who uses word “magic” while explaining JavaScript (or anything).

function C() {} //constructor
var x = new C();
C.prototype.prop = 2;
var y = new C();
alert(x.prop);
alert(y.prop);

This code will alert “2” and “2”, which kind of proves quoted concept: object x gains new property prop after it was created. Magic? Not at all. Lets look at similar example:

function C() {} //constructor
var x = new C();
C.prototype = {prop: 2};
var y = new C();
alert(x.prop);
alert(y.prop);

This will alert “undefined” and “2”. Huh?

In the first case neither of objects (x or y) have property prop. What they have is a hidden reference to prototype. (Hidden, because only interpreter internally can access it.) When you ask for property prop JavaScript can’t find it in the object itself and look for it in linked prototype object, found it there and return it. So for you, as a programmer, it is no visual difference where property is stored: in the object itself or in its prototype. When you add new property to prototype object x didn’t change. When you ask for property prop JavaScript finds it in updated prototype.

In the second case we assign new object as a prototype. Now object x still refers to the old prototype, but object y refers to new. x and y do not share prototype anymore. Obviously old prototype doesn’t have property prop. Even worse, now you lost the only access point to it. Despite these two objects were created with the same syntax they are way different.

One more thing. As you know every object by default receives property constructor, which refers to its constructor (surprise). But just as prop constructor property doesn’t exists in object itself, but rather in its prototype. By rewriting prototype we rewrite constructor property as well:

alert(x.constructor); // "function C() {}"
alert(y.constructor); // "function Object() { [native code] }"

So, when you rewriting prototype you can’t rely on constructor property anymore. But rewriting prototype is a main technique of inheritance in JavaScript.

I will write about inheritance next time.

  Objects in JavaScript (part I)

Thu, 16 Apr 2009

Recently I was talking to friend of mine about objects in JavaScript. He is writing JavaScript for living and very good at it, however I found that he doesn’t understand some core features of the language. So, in case you have this gap in understanding JavaScript, I’ll try to explain.

Friend of mine give cite me one book:

“The interesting thing about ECMAScript primitive values for Booleans, numbers, and strings is that they are pseudo-objects, meaning that they actually have properties and methods.”

I am really sorry, but this doesn’t make any sense.

First lets take a look at this example:

var a = 5;
a.t = 3;
alert(a.t);

It will alert “undefined”. Why? If “a” is a “pseudo-object” then why it doesn’t keep my property? The thing is, “a” is not an object. Not even “pseudo-object”. It is primitive number. It doesn’t have properties. As you know JavaScript convert variable from one type to another on the fly:

var b = "w" + a + [1, 2, 3];

In this example number “a” and array [1, 2, 3] will be converted to string on the fly. The same is happening with anything before “.” operator, JavaScript simply converts left hand side parameter to object. So, at the second line of the example JavaScript creates new object Number with value equals to “a” (5 in our case), then create new property “t” with value 3. But then this object is not assigned back to variable “a”, it is just disappear in garbage. Third line will again create new object and will try to read it property “t”, which is undefined.

Primitive types like boolean, number and strings are not objects, they could be converted to objects. What the rule? JavaScript has six built-in types: null, undefined, number, string, boolean and object. The conversion rule is simple: if input is object, leave it as is; if input is null or undefined, throw an exception; otherwise create new object (new Number(input) or new String(input) or new Boolean(input)). Hope this small bit will help somebody to understand objects in JavaScript a bit better. Next type I write about prototype and friends.

  So, you think you know JavaScript?

Tue, 31 Mar 2009

Quick test for real understanding of JavaScript core beyond closures and scopes. Here five small scripts. Try to answer what will be alerted in each case without running them in the console. Then you could create a test file and easily check your answers. Ready?

if (!("a" in window)) {
    var a = 1;
}
alert(a);
var a = 1,
    b = function a(x) {
        x && a(--x);
    };
alert(a);
function a(x) {
    return x * 2;
}
var a;
alert(a);
function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);
function a() {
    alert(this);
}
a.call(null);

  Raphaël 0.7

Thu, 12 Mar 2009

New Raphaël release:

  • Plugins functionality (Raphael.fn.myshape = function () {};)
  • New method “set” as a replacement for depricated “group”
  • getBBox for text is fixed in IE
  • Added point of origin in rotation
  • Added support for “src” attribute in attr method
  • Added setWindow method for iframes support
  • Added setSize method, so you can resize canvas
  • Provide alternate simple syntax for gradients: “90-#000-#fff”, “‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›”
  • Added multiline text support
  • Added event handlers: Element.click(function), Element.unclick(function)

  Washington Post Using Raphaël

Wed, 25 Feb 2009

screenshot from washingtonpost.com As stated on Ajaxian Washington Post start using Raphaël. Frankly, this is company named Evri, who is working for Washington Post and creates fancy widgets for them, uses Raphaël for drawing purposes. Anyway, Raphaël is on washingtonpost.com and it is time to celebrate. To see it by yourself you can visit this page.