JS1k demo page v4.1

2015-01-11

I've also updated the demo pages of the compo with v4 of the demo page. This was relatively painless although the verification takes a very long time. These demos are distracting yo :p

The migration was fairly easy because everything uses a pretty basic shim. In fact, I don't think I really broke any demos. And I patched some back up in the process.

Apparently there's been a change in the canvas api where the fill(), clip(), and stroke() methods now validate their arguments. Before they ignored bad args and this was used to save a byte in the normal way when golfing (putting an expression in an otherwise empty function call when you know the argument is ignored will usually save you one byte in the form of a comma or semi colon).

Luckily context objects are writable. And luckily all affected demos only used the context exposed by the shim. This meant I could monkey patch in a proxy function which did the validation of the argument and ignored it if it was invalid. In hindsight I could have probably just dropped that argument... Ohwell whatever.

Code:
var ctx = iwin.a, fill = ctx.fill, clip = ctx.clip, stroke = ctx.stroke, p2d = iwin.Path2D;
ctx.fill = function(r){ fill.call(ctx, r === 'evenodd' ? 'evenodd' : 'nonzero' ); };
ctx.stroke = function(p){ if (p && p2d && p instanceof p2d) stroke.call(ctx, p); else stroke.call(ctx); };ctx.clip = function(p){ if (p && p2d && p instanceof p2d) clip.call(ctx, p); else clip.call(ctx); };

Note that it's important to get the proper Path2D constructor instance. Above iwin is the window object of the iframe that carries the demo. Had I gotten window.Path2D instead the instanceof check would always fail.

The above patch fixes most demos affected by the api change, greatly reducing the outstanding bugs.

I also decided to apply the display:block css to a canvas for demo's that clearly needed it. By "need" I am talking about demo's that had a 20px scrollbar. I was a little torn about adding the patch, but then I realized that it did not really matter at this point. The judging has been done and applying this patch only helps the site look better. I honestly don't think it affects anyone negatively. I'm also not modifying the submissions directly.

After applying this a new demo started failing. This was due to a scale not receiving two args. At first I wanted to plug that, but then I realized, "wait, what should this call do if not scale?". Further investigation turned up that the demo was working fine in the v3 page. It wasn't trying to scale, it was calling another method. But because I added a method to the context, I had screwed up the for-in iteration order and the demo was now calling a key it did not intend to call. Luckily that demo wasn't tripping over the invalid argument problem so I could simply make sure the patch wasn't applied in this case. Dodged a bullet there...

Well nearly dodged it. I had to figure out a "proper" fix for the demo by p01. He was doing it both. I ended up attempting to preserve the enumeration order, disturbed by the proxies above, by simply assigning ctx.scale to itself before applying the hacks above. That leads to ctx.scale (still) enumerating before ctx.stroke, meaning ctx.s is last set to stroke. Normality restored. For now. Except for the other two demos that still don't work with it. So they're just not getting the patch so they don't crash.

So a certain number of demos have this css rule applied to their canvas. Not all because some rely on it to be inline in order to center them. There's a manual check for the id for this when writing the template. Unfortunately it's nearly impossible to apply a general "100%" patch because many demos resize in different ways and will screw up if they suddenly get a real fullscreen canvas. Oh well. Let's call them growing pains ;)

One demo is broken by nature because it tries to preserve window.onmousemove without checking whether it exists in the first place. Should have tackled this during the contest, but ohwell.

And this demo is broken because it sets a variable in a mouse event but attempts to read from it before the mouse event. But because it uses an interval the error is non-blocking. Similarly this is failing because it reads before it writes. Also non-blocking.

This audio demo no longer works. By the description it looks like it's using an old version of the api. I don't have time to dig that deep into fixing it :p

The websocket demo... I dunno, I'm sure the api changed or something. Not fixing it.

Onward to 2011. To my surprise there were no demos that suffered from the display:block scrollbar problem. The earlier demos had more real problems. Like the call error in is pretty much unavoidable. The input of this one doesn't seem to work in the v3 page either. Not sure if it ever worked. Cba to debug it (no errors).

One interesting backwards break was for this jumping game, which uses the enumeration hack (of course) and creates keys on key[6]. Where before that leads to ctx.c being ctx.fillRect. But I think a new method was added named ctx.drawFocusIfNeeded. And obviously that method now got unexpectedly assigned, leading to an error. My fix was simply to reassign fillRect so it was enumerated and assigned to .c last, again. Tricky.

The xmas demo gave me some headaches. Or actually just one; the winner. Apparently it was broken for a while now (why does nobody tell me :/). This is the first demo that manually did getContext calls. In fact, it did cloneNode calls. Sigh. It took me a while to figure out but eventually I hooked into cloneNode and made sure the clone also had a proxied context. Demo works now.

The last batch was the first compo. This one had over 700 entries and was more a wild wild west than anything else.

For example, this one expects whitespace before the canvas tag. It's doing body.childNodes[1] and expects it to be the canvas. The whitespace counts as a text node and I was omitting that. The fix was to simply add that whitespace back :) It can be that easy sometimes.

There was one demo that I edited directly. It had a \u200b appended at the end for .. well I don't know why. It was messing up my scripts and there was absolutely no point in that character being in there in the first place. So I just dropped it. You have to draw a line somewhere. Hey it makes the submission one byte more impressive eh ;)

And because this canvas thing was getting old, there was one demo that did mouse event checking on the html element. Worked in the frameset, broke in the iframe. It was checking the tag name onmousemove. In the frameset, that ended up being "HTML", but now it's "BODY". By far the easiest way to circumvent this problem was to replace the name in the demo itself. The number of bytes remained the same so I don't think it's a big deal.

Next is a demo that reloaded the page when you were game over. Where I may frown upon the reloading before, now it's just a problem because it's loading the main page in the frame causing duplicate headers. I've worked around it by replacing the m.location=m.location; with top.reload() which does the client-side reload of the demo. It's the same function as the reload button in the header triggers.

So with those fixes, the shim is updated with mainly these additions:

Code:
var Audio = iwin.Audio;
iwin.Audio = function(x) { return new Audio(x); }; // fix 312

// The first compo had _no_ shim, though some demos need the getcontext
// proxying while others fail with the shim. So it's a build step.
if (TOKEN_EXPOSE_SHIM) {
var canvas = idoc.getElementsByTagName('canvas')[0];

// shim: expose canvas, body, context for demo
iwin.a = canvas.getContext('2d');
iwin.b = idoc.body;
iwin.c = canvas;

var p2d = iwin.Path2D;
function wrap(ctx) {
// fix html5 api changes introduced (long) after the compos. invalid args were ignored back then.
// fill() clip() and stroke() now validate their args. these proxies squash the bad args.
var fill = ctx.fill, clip = ctx.clip, stroke = ctx.stroke;
ctx.scale = ctx.scale; // preserve enumeration order... (otherwise ctx.s ends up being `slice` where it ought to be `stroke`)
ctx.drawFocusIfNeeded = ctx.drawFocusIfNeeded; // for 986, makes sure key[6]=c hits on fillRect last, instead of drawFocusIfNeeded
ctx.ellipse = ctx.ellipse; // 871, otherwise fill becomes ellipse (key[2] + first cap) because we re-set it below
// proxies
ctx.fill = function(r){ fill.call(ctx, r === 'evenodd' ? 'evenodd' : 'nonzero' ); };
ctx.stroke = function(p){ if (p && p2d && p instanceof p2d) stroke.call(ctx, p); else stroke.call(ctx); };
ctx.clip = function(p){ if (p && p2d && p instanceof p2d) clip.call(ctx, p); else clip.call(ctx); };
return ctx;
}

if (TOKEN_PROXY_GETCTXT) {
// patch canvas element because 856 (and others) calls getContext despite the shim
// add a proxy to canvas (sigh) to return a special object that applies the patch...
var cvs = iwin.c;
var cNode = cvs.cloneNode;
cvs.cloneNode = function(){
// clones are not cloned themselve so only add getContext
var clone = cNode.apply(cvs, arguments);
var cloneGet = clone.getContext;
clone.getContext = function(){
return wrap(cloneGet.call(clone, '2d'));
};
return clone;
};

var get = cvs.getContext
cvs.getContext = function(){
return wrap(get.call(cvs, '2d'));
};
}
if (TOKEN_CANVAS_PROXY) { // wrap shim context. may break for-in hacks like in 1101
wrap(iwin.a)
}
}

Hope you enjoyed my second round of wtfjs1k. These should be the last for a while. I'm done migrating all demos to the new page. Other migration details should be quite boring.

I'm happy I was able to fix so many broken demo's. Let's hope I can keep on fixing them. Or even better, that none of them break anymore. ... Okay yeah, hope I can still fix them next time :) Note that the 2015 shim will not have all this crap. It will get a fresh shim and submitted demos must work with the state of the web at the time of the contest. After that ... it becomes my problem.

If you spot problems with a demo and think it can be fixed please let me know somehow :)