JavaScript Archive
Touch of Canvas, and Firebug Profiling
- 2008-10-26 (Sun)
- JavaScript
These days, I work on Canvas Animation with JSTweener to make an animation by JavaScript. Now you can see it on the new home page of my site. Check it out and give me some comments, please.
So in this entry, I’d like to give a brief introduction of Canvas and how to do profiling in JavaScript with Firebug.
Canvas
Canvas is a (relatively) new HTML Element where you can draw pixels by JavaScript, originally developed by Apple, and adopted in Web Standard as HTML5. It has a nice set of drawing APIs, similar to Processing drawing APIs. The way it handles transformation looks more similar to Processing/OpenGL style.. Like to push translated matrix you can call context.save(), and to pop it context.restore(), stuff like that.
There seems to be no comprehensive Canvas 2D APIs yet online in English, it may be bacuase it’s still ongoing implementation, and there’s slightly difference to each browser… So I just list up some links that I used.
- HTML5: Specification of Canvas tag
- Apple ADC: A list of methods you can use Webkit
- Apple ADC: Using Canvas
- Opera: Canvas support in Opera 9.5
- Mozilla MDC: Canvas
- HTML5.jp: Canvas Reference (this site is the best comprehensive one, but written in Japanese. Learn Japanese! ;P)
And, as defacto standards, Internet Explorer doesn’t support Canvas yet. But wait! Canvas is not yet useless because of that! Google released a JavaScript library called explorercanvas. It has several of disadvantage, but it’s still good enough to offer the way to handle it. I’m wishing both Microsoft will make it happen and other Open source people will make explorercanvas better in future..
Animation and Profiling
Because I wanna do some animation on Canvas, I need 1) an animation engine and 2) massive optimization for speed. jQuery has easing plugin and seems to be available already in the core release, but… Hmm.. I just don’t feel like touching by glimpse at its document. Maybe next time.
I used JSTWeener for this time, which is a ported version of ActionsScript’s Tweener, originally developed by Zeh, and ported by Yuichi. Yet JS version doesn’t have some functionalities such as…Tweener.removeTweens() equivalent, still it has an advantage of ease of use as Tweener does, and it is stable.
Then I wrote some functions to draw vectors and image on canvas. I re-realized that my development machine is relatively faster than average ones!
It doesn’t do animation on other machines.
Profiling really helps in that situation. And luckily, Firebug has a profile functionality. Just call console.profile("arbitrary title") to start, console.profileEnd() to finish profiling. The first picture is the result of profiler, it shows in Firebug console, you can change sort keys, and sort order.
It works pretty well in Firefox 3 + Firebug 1.2 (both latest version at this time).
But in some environment, it does NOT seem to work. I couldn’t run profier in Windows XP + Firefox 2 + Firebug 1.2 on VMWare. I found a discussion Problem using console.profile, and the author(?) says that you need to call loadFirebugConsole() before hand. It enabled me to call console.profile(), but afterwards I still could not stop profiling.. So I just installed Firebug 1.3 beta and it works although it gave me lots of crashes eventually.
- Comments: 0
- Trackbacks: 0
jQuery.elementData() and event
- 2008-10-18 (Sat)
- JavaScript
Three weekends ago, my friends and I went to jQuery Camp 2008 at MIT. It was in Boston at the MIT Stata Center on Sunday, September 28th. I have been to Boston a few times, but I never been inside MIT (technically, it’s in Cambridge, which is different city than Boston…), and Stata Center was a beautiful Frank Gehry’s building.
And those friends who I went with are in the group called Flax, which I organized for young Design/Programming professionals around New York city area. We meet up every month and drink beers, talk about what’s interesting, and give one presentation every month.
So this entry is from my presentation at October Flax, about what I recently learned from jQuery Camp 2008.
In the second presentation John Resig gave at jQuery Camp, he was talking about several cool stuffs,
and jQuery.data() appeals to me a lot because I have done very similar thing! He called elementData(), but on the syntax, you do like jQuery.data(). What it does is bascially dictionary (more CS-precisely it is a Set or Associative array). You can store data with key under certain DOM Element. Though it seems no ways to fetch what vocaburary it has (like.. getAllKeys()). So I take this as a name-spaced dictionary designed for event handling. Why? That’s how my presentation goes.
Module aware design
Let’s start with how I get to think like this way. When you do Event in HTML, there’s several ways to do it. You can do <body.onload="do();">, or document.body.addEventListener('click', function(){do()}, false);. If you’re using prototype, you can do Evnet.observe(). In jQuery, you can do either $(document.body).bind('click'...); or $(document.body).click(...);. The issue is, either one of ways, when the event happens, the function context will be different, in other words, “this” keyword points different object.
Let’s see the world situation. Like in this picture, you wanna add Drop-down menu on each <li> tag. What I originally did was..
- build a Controller Object to each page
- then made a dictionary object in Controller Object
- register functions associated with each <li> in dictionary
- register events wrapping by this Controller’s event receiver
- every time event happens, it will lookup dictionary where the event happens
It works pretty well, until some point.
As this picture, if you have subsidiary (child) elements in <li> elements, what will happen? When the event happens, browser fires an associated function with a first argument as DOMEvent, and that has .target property. Why that is important? Because I used it as a key to look up the dictionary. But as shown in this picture, the targe might be different from what it was originally registered. I needed to tweak it by the event receving process, that was not beautiful.
Then, I listened to John Resing’s presentaiton, jQuery.elementData() idea. I can use this as dictionary and I can write wrapper function. My answer is building EventRunner.js.
- First you will register your event like this: EventRunner.register(dom, ‘click’, obj.click, obj)
- EventRunner actually register event with wrapping by EventRunner.receiveEvent()
- if it’s Firefox or Safari, just pass the .currentTarget object to EventRunner.perform()
- if not, use EventRunner.ascendEvent() will happen recursively…
Here’s some demos.
var EventRunner = {}
EventRunner.MAX_DEPTH = 5;
EventRunner.ascendEvent = function (ev, elm, counter)
{
counter = counter || 0;
//console.log(counter + ': ' + elm.tagName);
if ((elm.tagName.toUpperCase() === 'BODY') || (counter > EventRunner.MAX_DEPTH))
{
console.log(counter + ': ' + elm.tagName);
return false;
}
if (!EventRunner.perform(elm, ev))
{
EventRunner.ascendEvent(ev, elm.parentNode, ++counter);
}
}
EventRunner.perform = function (elm, ev)
{
var result = false;
var value = jQuery.data(elm, ev.type);
if (value instanceof Function)
{
try
{
result = {'result': value(ev)};
}
catch (err)
{
console.log('error occurs');
console.log(err);
//alert(err.message);
}
}
else if (value !== null)
{
result = value;
}
return result;
}
EventRunner.receiveEvent = function (ev)
{
if (ev.currentTarget)
{
EventRunner.perform(ev.currentTarget, ev);
}
else
{
EventRunner.ascendEvent(ev, ev.target);
}
}
EventRunner.register = function (dom, type, func, context)
{
if (!dom)
{
throw new Error('You have to pass DOM Object');
}
if (context)
{
func = EventRunner.scope(context, func);
}
if (dom === window)
{
$(dom).bind(type, func);
return;
}
jQuery.data(dom, type, func);
$(dom).bind(type, EventRunner.receiveEvent);
}
EventRunner.scope = function (target, func)
{
return function ()
{
func.apply(target, arguments);
}
}
- Comments: 0
- Trackbacks: 0
QUnit and implementing string reverse
- 2008-06-21 (Sat)
- JavaScript | Tech
Very recently, jQuery introduced their Unit Testing framework QUnit as a top level project. It’s simple but maybe good for me as a Unit Testing another-giving-a-shot-er to start with because not many methods to worry about(!). Though it aims to help jQuery plugin development, we can use for normal not-jQuery javascript development. So I tried a few things here.
Introduction.
It’s very simple. In order to run QUnit testing, what you need are 1) one html file, 2) basic jQuery setup (jQuery and jQuery-ui) and 3) QUnit specific files (testrunner.js and testsuite.css). The html file doesn’t have to be complex since testrunner will add the result automatically and dynamically. Only element you need is <div id="main"></div>. That’s it. But well, you may just wanna use the one they use.
By looking at some examples at jQuery.com, it seems like you can use QUnit for testing HTML structure and other stuff, which sounds pretty cool, but I haven’t tried that yet. I just started to build my own javascript library a while ago, so today, I implement one idea and tried TDD (Test Driven Development).
The example I built is on the last of this entry. On that page, besides what I explained before, I added 3 more javascript files. reverse.js is the actual implementation. test_reverse.js is the holder for test cases. Json.js is an utility, which is used in test case (well, i will explain about it on the future entry).
String.reverse()
What it does? reverse () is an extension to built-in String class and is a class (static) method. It receives one argument, a String instance and will return it with reverse order. For example, if you pass “dog” to String.reverse(), it will gives you back “god”. (I realized that I should do input checking).
Before start it, I had a few ideas of how to implement it. So first I wrote test cases based on induction. This is the point I am still not quite sure about. Anybody knows basic ideas or good tips of building test cases, let me know! What I did this time is:
- Test the minimum input
if the input is “a”, then it should return “a” - Test the minimum + 1 input
if the input is “ab”, then it should return “ba” - And test random input
and do random string.
by QUnit code, it describes as follows.
ok(String.reverse, 'String.reverse exists');
equals('a', String.reverse('a'), 'the one char');
equals('ba', String.reverse('ab'), 'the two chars');
var rand = gen_random();
equals(rand[1], String.reverse(rand[0]), 'and random string');
QUnit::ok() evaluates boolean value for the first argument, is equivalent to JUnit.assertTrue(). QUnit::equals() compares the first argument and the second argument, is equivalent to JUnit.assertEquals().
gen_random() is a function that I build for this test case, which will return a pair of strings, which are reverse order to each other.
String.reverse() implementation
Then, I started to write real codes. First, pick up a character from the last of the passed string, and added it to the result by String concatination. As you know, String in JavaScript is immutable, so every time, it calls a new String().
Then second, pick up a characeter from the last of the passed string, and added it to an array, then just join(”), to concatinate all chars by once.
Third one is a bit complex. It prepares two arrays, and it runs for-loop half number to the length of the passed string. One array pick up a character from the beginnning of the passed string, the other pick up a character from the last of the String. Then do join at very end.
They all passed the test cases, then I realize maybe I should do speed testing for each implementation. So here’s the test.
String Reverse Test: If you are using Internet Explorer, it looks like hang-up… I gave you a warning!!
While I was writing this entry, I suddenly remember that there is an instance method Array.reverse(). And I added to it. I was about to give a comment like… “Hmm, the (2) array join method is pretty first… But the difference between (2) and (3) may get shorter when the given string got a longer because it will make it half of counter of for-loop”… But now it found the much faster implementation… Have no comments on it… just use built-in methods.
Conclusion
- re-creatable number can be very persuasive
- test cases make it easier to do more test
- the first try may be a bit bother.
- and use the built-in methods.
- Comments: 0
- Trackbacks: 0
- Search
- Feeds
- Meta
- Links
- Ads!
-
