2013-02-06

Dart is a great language for shell programming, too


I asked Twitter nicely yesterday. The only response:


I don't dislike writing bash scripts. I hate it. I also hate the lack of portability, as pointed out by +Daniel Steigerwald:


Great idea, +Daniel Steigerwald, but why use NodeJS when I could use Dart?

If you fetch the latest version of the Dart Bag-of-Tricks, you'll notice a new binary bin/bench.

If you're running bash and dart is in your path you can execute this directly. Otherwise run it through dart: dart bin/bench.

If you add bot.dart/bin to your path you can do things like this:
There is also a paramater -r, --run_count    (defaults to "20")

Dart is more than just a good tool for web apps.

Happy hacking.

2013-01-31

Headless Browser Testing: Dart, DumpRenderTree, drone.io

When writing any software, even for the web, I try to do as much testing as possible outside the browser.

A lot of great testing can be done of algorithms, data models, and business logic without booting up Chrome.

Node.js enabled this with Javascript. Dart has had an out-of-browser--read console--story since day one.

But some things you must test in a browser.

The Dart Bag of Tricks (BOT) has had browser tests since the beginning. There are only a few tests that must be run in the browser, but I make sure to run all tests both on the console and in the browser if they can run both places.

Browser tests are great, but they don't fit well into a build workflow or a continuous integration tool.

For that you need a way to run and control a browser (or a browser-like thing) via the console and get results out. Enter DumpRenderTree.

DumpRenderTree - Chrome without the chrome

DumpRenderTree (DRT) is a great little tool hidden in the guts of WebKit.

By default, DRT prints out an obscure text format representing the hierarchy of elements on the provided page.

Here's the output for DumpRenderTree example/fract/fract_demo.html

Content-Type: text/plain
layer at (0,0) size 808x820
  RenderView at (0,0) size 800x600
layer at (0,0) size 800x820
  RenderBlock {HTML} at (0,0) size 800x820
    RenderBody {BODY} at (8,8) size 784x804
      RenderHTMLCanvas {CANVAS} at (0,0) size 800x800 [bgcolor=#808080]
      RenderText {#text} at (0,0) size 0x0
#EOF
#EOF

DRT is controlled mostly through Javascript in the test page by via window.testRunner.

Here's the script block that lives in test/harness_browser.html:

<script>
  // only run if testRunner is defined -- we're in DRT
  if (window.testRunner) {

    // Don't dump the structure. Just the text of the output plus console output
    testRunner.dumpAsText();

    // Don't finish when layout is done. Instead, wait to be notified
    testRunner.waitUntilDone();

    // listen for messages from the test harness
    window.addEventListener("message", receiveMessage, false);

    // listen for unhandled exceptions
    window.addEventListener('error', onError);
  }

  // if there is an unhandled exception, tell DRT we're done
  function onError(event) {
    testRunner.notifyDone();
  }

  // if the test harness sends a done message, tell DRT we're done
  function receiveMessage(event) {
    if(event.data == 'unittest-suite-done') {
      testRunner.notifyDone();
    }
  }
</script>

And since everything is keyed off the existence of window.testRunner, none of this affects the behavior of tests running normally in a browser.

Here's the first few lines of output from DumpRenderTree test/harness_browser.html:

Content-Type: text/plain
PASS
All 109 tests passed
Collapse All
bot
 
(4/4)
bot - Enumerable
 
(19/19)
bot - Enumerable - group

Compare to the content running the tests in the browser:


Getting DumpRenderTree

DRT is part of Linux and Windows builds of the Dart Editor now (look in the chromium) directory. It will be part of builds for Mac soon.

If you want to get DRT that is compatible with the latest integration build of Dart - 0.3.2.0 (r17657) - grab this guy.

Just make sure the DumpRenderTree executable is on your path when running tests.

Bringing it all together

drone.io supports DumpRenderTree for Dart projects. Read about it here.

The trick is creating a script that returns an exit code of zero for success and not zero (1 is pretty standard) for failure.

Check out the script I created for BOT.

And let me know how it goes for you.

Happy hacking.

2013-01-11

Dart Widgets Dev Journal 1 - Basic Animations


tl;dr: Implementing features similar to Bootstrap is hard without basic animation support in Dart. Basic animation support is not as easy as you'd think. I've made a stab. Check out the demo.
I've started a new Dart project - Dart Widgets. The inspiration is Bootstrap, but with a focus on features that map cleanly to Web Components.

While digging into Bootstrap, I realized that there was a heavy dependency on jQuery--specifically the show/hide/toggle functionality. This is critical for expanders, menus, modals, pretty much everything.

Show, hide, toggle

It turns out implementing show/hide/toggle is not as easy as one might guess.

One could naively toggle display: none; in an element's style attribute and call it a day, but a lot of edge cases would be missed.

Throw in the ability to optionally animate the show/hide behavior and things get a lot more complicated.

A short list of the cases to ponder:

  • Element with a non-standard display value. (e.g. a div inherited a display of inline-block)
  • Element with a local display of none 
  • Element inherited a display of none
  • Show is called while an element is hiding and vice versa
  • Toggle is called 10 times before the first animation finishes
  • Show is called on an element with effect A while effect B is hiding it.
Huge props to the jQuery and jQuery-ui folks for tackling this problem. Their code served a great starting spot. I feel bad that they had to implement this logic in Javascript.

Test

I can't tell you how valuable test-driven development was in getting this project off the ground. It would have been next to impossible to ensure all of the edge cases were handled cleanly.

I need to write another post about how my approach to testing in Dart has evolved. Let's just say I didn't code up 939 tests by hand.
After a lot of head scratching, I think I ended up with pretty clean, extensible model.

As proof, I offer the code for doing a open/close door effect:

class DoorEffect extends Css3TransitionEffect {
  DoorEffect() : super(
    '-webkit-transform',
    'perspective(1000px) rotateY(90deg)',
    'perspective(1000px) rotateY(0deg)',
    {'-webkit-transform-origin': '0% 50%'}
  );
}

There are still a few things I'd like to get working--get tests working in Firefox, find a way to handle browser prefixes cleanly--but generally I'm happy with progress.

Check out the demo.

Check out the code.

Let me know what you think.

2012-11-16

Excited to see dart2js minified output getting smaller (real world numbers below)



I noticed r15005 turns on more renaming in dart2js with --minify so I thought I'd see the impact on Pop, Pop, Win!

The last time I did a build and deploy of PPW was on r14873.

Here are the sizes of game.dart.js

dart2js617,201 bytes
closure compiler621.07KB
gzip (via http)107.7KB

Notes:
  • dart2js is running with the --minify flag
  • I'm using the closure-compiler with default settings. Build from 17 Sept, 2012
  • Notice how the output is bigger from closure? This is because output is optimized for gzip. Not intuitive, until you see the network panel in Chrome Dev Tools.
  • gzip is the size Chrome sees over the wire. Most servers gzip payloads. It's critical to pay attention to this number and not just the size on disk.


Now with the bleeding edge build r15005

dart2js451,183 bytes~33% smaller
closure compiler388.71 KB~37% smaller
gzip (via http)90.2KB~16% smaller


Notes:
  • The output from dart2js is substantially smaller. This is just the work from the dart2js guys recently. Amazing.
  • This time the output from the closure is smaller than the input (~12%). I can only speculate why. Perhaps the new minified output is more optimizable?
  • The gain with gzip is not as big (~16%). I'm not surprised by this. PPW is a pretty big game. The json that stores the texture data alone is huge.

Keep in mind, the Dart source for the game is 1.5 MB as reported by dart2js.
info: compiled 1576654 bytes Dart -> 451183 bytes JavaScript in web/game.dart.js
Is it fair to say we're getting ~94% smaller output? It depends on how valid one considers the the Dart code size reported by dart2js.

But I certainly don't feel I need to justify the javascript output size for non-trivial apps anymore.

Great work folks!

2012-09-19

Dartsweeper: Dev Journal 3--Graphics, Animation, Audio


I've been busy the last few weeks. I've had to give more time to another gig, so I've slowed my progress on the Dartsweeper Pop, Pop, Win! But I've made some great tractions. Check out the demo at sweeper.j832.com.

Graphics and animation with TexturePacker

Graphics for the Pop, Pop Win! (PPW) are packaged using TexturePacker. TexturePacker takes a number of source images and produces an optimized texture atlas.

PPW has two texture files: one for square textures (that don't have transparency) and one for the textures that need transparency. Using a jpeg for the textures that don't require transparency saves a lot of bandwidth.

TexturePacker outputs a map file that provides the location of each source image in the output map. Initially I was given an XML file which I parsed with the dart-xml library. (Thanks, John!) I converted the XML to JSON which I stored as a string in code and deserialized with dart:json.

Then I realized I was being dumb.

First, I learned TexturePacker can output json directly. So long xml parsing.

Second, I realized that valid json can be provided as a literal for Map in Dart. Goodbye JSON decoding.

Using some simple canvas tricks, I can now easily display graphics and animations representing over one hundred source textures using only two input files.

Audio

Supporting audio is super easy with AudioContext. I started with the BufferLoader sample from an HTML5 Rocks tutorial and converted it to Dart.

I went a bit further and generalized all of my resource loading into an abstract ResourceLoader class which is then subclassed by ImageLoader and AudioLoader. I plan to migrate these classes into dartlib in the near future.

Easy.

Issues

In my last post, I mentioned an issue using offsetX and offsetY in MouseEvent in Firefox. The bug has been fixed so I removed my work around.

The other glaring issue is the lack of audio support on browsers other than Chrome. Chrome is the only browser that supports AudioContext (that I tried). I've dealt with cross-browser game audio before--see Agent8ball--and I'm not super excited to fix it here. In the mean, just use Chrome.

Next Steps


  • A few more graphics tweaks.
  • A few more sound effects.
  • Mobile support.
  • Some loading UI. 

You can help

Don't forget all the source is on GitHub. Open issues. Fork it.

Let me know if you have any questions.

Happy hacking...and popping.

2012-08-23

Dartsweeper: Dev Journal 2--now with Canvas

Looks can be deceiving

Looks about the same as last time, right? The visuals and interaction were designed to be almost identical, but the plumbing is very different.

The original, HTML DOM version used a table to render the game board. It's straight forward to let DOM elements handle layout and mouse interaction. Throw in a little CSS and you have a functional—albeit ugly—game.

But if one wants to make something really amazing, without fighting a Matryoshka doll of layout and CSS issues, one should leave the DOM behind.

Since I've decided leaving the third dimension for later, HTML canvas is an obvious choice. I also have a bit of of experience building games on canvas. See Agent 8 Ball and our work for Ze Frank.

Darts on Canvas

The working version of Dartsweeper does all of its drawing and click handeling via canvas. Canvas is an immediate mode graphics API. It's simple and straight-forward. It's also easy to have code complexity blow up once you start building anything complex.

When I was building out the UI layer for Throwing Stars for Mr. Frank, I wanted an object-oriented way to think about game elements. I wanted a star object and a wall object and a coin object that encapsulated the game physics and element rendering. So I put on my astronaut suit and built the PL javascript library. PL is short for Pixel Lab.

A big chunk of PL is under the retained namespace. It's a collection of types for retained mode graphics on canvas. I've ported most of the retained libraries to my dartlib project and they are used heavily in Sweeper.

Retained mode with Dartlib

I'll probably do a video at some point about the retained model in dartlib. The short version:
  • Stage: The glue between the DOM (a HTML Canvas element) and a root "element" in the retained world.
  • PElement: The base class for visual elements in the retained world.
    • Drawing is done with CanvasRenderContext2d. Super simple.
    • Zero or more transforms, which allow layout by the parent and fun effects
    • Parent and child pointers
      • Allow hit testing and mouse interaction
    • The P prefix stands for Pixel Lab. I'm trying to avoid naming conflicts with Element.
There's a bunch of other magic here, too. Mouse support. An attached property and attached event model. Support for correct Click behavior (mouse down the mouse up on the same element). Check out the samples in dartlib.

If you're feeling really adventurous, take a look at vote.dart. I have a lot of fun with retained graphics.

Issues

Cross-browser confusion

I've run into a couple of issues when trying to play the JS-compiled version of Dartsweeper on Firefox. 

In one case, I was using a webkit-specific version of requestAnimationFrame which doesn't exist on FireFox. Thankfully, Seth pointer me to the un-prefixed version of the API that works everywhere. A bug is open to removed the webkit prefixes.

In another case I discovered MouseEvent.offset[X|Y] don't exist in FireFox. There is a bug on this, too. I added a helper to dartlib provide the same functionality.

Growing pains with pub

I'm started tip-toeing into pub by migrating unit test code away from the copy in dartlib (which has been removed) to the SDK version.

Setting up pub has caused me a bit of stress since I use a custom homebrew formula to keep my SDK updated. Pub needs the DART_SDK environment variable to be set and homebrew installs versions in directories based on the version number. This causes a bit of headache as I need to delete the packages directory and rerun pub install every time I update my copy of Dart.

It's a bit of a headache, but it's purely related to how I install Dart. I'm hoping to tweak the homebrew formula to resolve this issue soon.

Next Steps

All of the work to migrate the game to canvas would be a bit silly if the output still looks like a poorly styled table.

After I finish up work storing high scores and created a touch-enabled interaction model, I'm going to jump into custom graphics, animation, and sound.

I'm excited. Stay tuned.

Oh, and you can help

All of the code is on github. File issues. Fork and add features. I'd love collaborators.

If you'd be interested in doing a hangout to discuss my graphics model, let me know.

Cheers.

2012-08-20

Are you ready to bet on the Web? Let's talk.



Are you maintaining a legacy applications written in VB6? Windows Forms? Java?
Are you relying on ActiveX and outdated browsers?
Are you unsure how to support new devices, new form factors, and new platforms?

Do you have a Web presence, but lack a Web mindset?

Let's talk.

As a consultant, I have had the opportunity to work on a lot of different projects and platforms.

Big enterprise apps with .NET.
Fun and engaging apps for iOS.

Beautiful games with Javascript and HTML5.


I've had pretty good luck having interesting work find me.

Now I'm turning the tables.

I've learned a lot building frameworks and applications both using enterprise development technologies that target a single platform and open source tools and technologies that target the standards-based Web.

I want to help bring tools and methodologies used by leading-edge companies and startups to organizations that feel stuck and left behind.

Is that you?

Does your an organization want to cross the chasm?

Let's talk.