miscoded

the web is a hack

Subscribe to RSS feed

Posts tagged with "javascript"

5-part JS quiz (link)

, ,

Came across the blog post so, you think you know JavaScript? by Dmitry Baranovskiy. It's a pretty nice little 5-part quiz on JavaScript quirks. I admit I did have one wrong answer (the fourth one), what's your score?

Rather than just linking to it I'll explain all the examples and why exactly you get the result you do ... but not just yet smile I'll first let my dear readers dive in and take the quiz. Explanations will follow in comments (unless you all beat me to it, of course).

Taleo - The Leader in On Demand Unbalanced Quotes

, , ,

Load getacooljob.com in Opera, see error page (http://aac.taleo.net/servlets/CareerSection?art_ip_action=FlowDispatcher&flowTypeNo=13&pageSeq=1&art_servlet_language=en&csNo=2%27%3E%3C/head%3E%3Cbody%20bgcolor=). At first sight it might look like a standard browser sniffer saying "go away and don't dare becoming our customer until you've found a browser we like"... until you look a bit closer at that address. See the junk at the end?

&csNo=2'%3E%3C/head%3E%3Cbody%20bgcolor= decodes to
&csNo=2'></head><body bgcolor=
and it's caused by the lack of a closing quote for the META tag's content attribute on the previous page:

<META HTTP-EQUIV="refresh" content="01; url='http://aac.recruitsoft.com/servlets/CareerSection?art_ip_action=FlowDispatcher&amp;flowTypeNo=13&amp;pageSeq=1&amp;art_servlet_language=en&amp;csNo=2'>


The site apparently belongs to Taleo which is the "Leader in On Demand Talent Management". As they aren't talented enough to balance their quotes or use a validator to spot markup mistakes, the level of hype obviously exceeds their technical competence..

See also statements like "this Career Section does not allow the use of the BACK or FORWARD browser buttons during the application process" (source), the hideous sniffing here
function stopEvent() {
var userAgent = window.navigator.userAgent;
var appName = window.navigator.appName;
if ((appName.indexOf("Explorer") >= 0) && (userAgent.indexOf("Mozilla/3") >= 0) && (userAgent.indexOf("Mac") >= 0)) {
return (true);
} else {
return (false);
}
}

and their "anti-multiple-tabs" JavaScript
//---------------------------------------------------------------------
// This method increments the count of browser using the career section
// on the current session in the document's cookie.
// This code is inserted only when IE browser is used
//---------------------------------------------------------------------
function setMultiBrowserDetection()
{
if (window.navigator.cookieEnabled)
{
// The multibrowser problem doesn't occur where cookies are not available
var cs_cnt = -1;
var index = document.cookie.indexOf("cs_cnt");
var countbegin = -1;
var countend = -1;
if (index == -1)
{
cs_cnt = 1;
}
else
{
countbegin = (document.cookie.indexOf("=", index) + 1);
countend = document.cookie.indexOf(";", index);
if (countend == -1)
{
countend = document.cookie.length;
cs_cnt = eval(document.cookie.substring(countbegin, countend));
}
else
{
if (countend < countbegin)
{
cs_cnt = 0;
}
else
{
cs_cnt = eval(document.cookie.substring(countbegin, countend));
}
}
if (cs_cnt < 1)
{
cs_cnt = 1;
}
else
{
cs_cnt = cs_cnt + 1;
}
}
document.cookie = "cs_cnt="+cs_cnt;
if (cs_cnt > 1)
{
// More than one browser is using the current session
window.document.location = "http://aac.taleo.net/servlets/CareerSection?art_ip_action=MultipleBrowserConflict";
}
}
}
complete with eval() instead of parseInt() (meaning that anyone who can get a cookie set on this site can XSS them).. When you stop shaking your head, I'd like you to opine on how much less talented a web site could possibly get..

new adventures in date parsing

, , ,

Besides the famous getYear ECMAScript date parsing is so badly specified that it is full of quirks. Opera has a good share of them..

Some random testing in IE7, Firefox 2 and Opera 9 shows that a simple new Date(somestring) dishes up several surprises:

  • Opera seems to be the only one that tries to parse European date formats, though it defaults to U.S. format for ambiguous dates. Hence, "1/2/2008" is January 2nd while "30/10/2008" is October 30th in Opera.
  • Greater than expected values for a specific part increases the whole date value in IE and Firefox. In other words, passing in January 32nd returns February first.
  • ..but this is more thoroughly implemented in Firefox than IE, "1/1/2008 23:59:60" gives you January 2nd in the former and NaN in the latter.
  • Opera defaults to values from current timestamp on computer for any input that doesn't make sense. In other words, if you pass in January 32nd on April 11th, January 11th is not-quite-expected outcome..
  • The IE and Firefox parsers support both m-d-y and y-m-d formats. So if the input is ambiguous, how do they know which is which? Elementary, dear Watson: if the first integer is greater than 70 it is assumed to be a year. If it is less than 70 it is assumed to be a month. "69-1-1" is obviously a 1906 date while "70-1-1" is new year's day 1970. Ah, the magic of arbitrary heuristics.
  • Speaking of the magic number "70", why can Firefox parse "1 january 70" but says "invalid date" for "1 january 69"??
  • ..and why does Opera refuse to recognise single digits as valid years, just substituting the current year instead? Opera, pretending "1 january 9" is today's date is NOT the correct fix for those Y2K bugs.. cry
  • Finally, a quirk I'll leave my readers to ponder the inner workings of: if I tell Opera to parse "january 100 2007" on this very day, April 11th 2007 I get "Thu, 11 Jan 2007", which is consistent with the observation that it replaces out-of-range values with current values. Now, who can explain what Opera does if you parse "january 101 2007"?


Executive summary: good thing we're writing browsers and not time machines!

interesting jQuery stuff

, , ,

The other day (well, night actually) I was looking at a problem on a site that used the jQuery library. Though I've heard of it, I haven't seen jQuery on live sites before and I thought it shows off some quite interesting features of JavaScript.

The latest version of the library is available so you can see what I'm talking about.

A function can call itself recursively as constructor

Just inside the jQuery function we find:

if ( window == this )
	return new jQuery(a,c);


This clever little trick means you don't need to use the "new" keyword all over your code to create a new jQuery object. You can just do
var obj=jQuery()
and the this check will detect that it wasn't called as a constructor and call itself recursively, this time using the "new" keyword to define the expected jQuery object.

I have a question about the if clause though - it presumably means trouble if you change the "this" object and use something else than window. Say,
var obj = jQuery.call(document)
would probably break something. I don't know jQuery well enough to tell if that would be a problem, but the check could perhaps be
if(this.constructor!=arguments.callee)
to catch all cases?

Using || operator to default to an argument inside a function call

You may be used to seeing the || operator being used to provide a default value for a variable. For example
var username=prompt('Your name please')||'anonymous'
will set the variable to "anonymous" if the user cancels the prompt or doesn't type any value.

It still took me a while to understand what is going on here:

return this.setArray(
	// HANDLE: $(array)
	a.constructor == Array && a ||

	// HANDLE: $(arraylike)
	// Watch for when an array-like object is passed as the selector
	(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

	// HANDLE: $(*)
	[ a ] );


The basic idea is: if the object a is an array, we pass it to the function directly. If it is an object that is somewhat similar to an array (for example a NodeList), we pass the result of calling the jQuery.makeArray function which will take all the elements/nodes and add them to an actual array. Otherwise, we just pass an array containing nothing but the object a.

The key to understanding this is to understand that the statement
a.constructor == Array && a
actually "returns" a if the first comparison is true. It is probably better phrased as "evaluates to a" but it may be easier explained as "returns".

Similarly,
(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a )
will go through all the conditions, and if they all hold true return the result of calling the makeArray function.

The || operator ties it all together and ensures the first clause that evaluates to something is chosen as input to the function.

You can use Array.push to add array elements to any object

The next interesting snippet is inside the setArray function itself:
setArray: function( a ) {
	this.length = 0;
	[].push.apply( this, a );
	return this;
}


Look at [].push.apply( this, a );. What happens here is that the array literal's "push" method is used to push element a onto the this object which isn't an array but a jQuery object. That's right, push is generic and can be used with any object. It means the object gets a ".length" property like an array has and can be iterated with for loops. Example:

var obj = new Object(); /* Yes: an object, not an array. */
var str='';
Array.prototype.push.call(obj, 'Hello');
Array.prototype.push.call(obj, ' World');

for(var i=0;i<obj.length;i++){
	str+=obj[i];
}
alert(str);


Array methods being generic is part of the amazing flexibility of JavaScript, but this feature of the language might not be well known among authors.

About the jQuery code, I wonder if their approach of creating an empty array literal to use its push method is slower or faster than my Array.prototype lookup. Perhaps the question will inspire a reader to do some performance testing?

quirky arguments

, , ,

The humble "arguments" variable that always exists inside a function call in JavaScript has demonstrated some unexpected behaviour today. This makes me really puzzled because I can not find anything in the ECMA-262 specification that mandates the behaviour - yet all browsers do the same thing in a very obscure corner of their implementations.. Why do we all implement it that way if we weren't explicitly instructed to by the specification?

Here is the issue in a minimal snippet of JavaScript:

function test(){}

test.arguments='something';

test();

alert(test.arguments); // test.arguments is now null ??


so..? Inside the function body we have a local variable called "arguments", but this variable also becomes a property of the function itself, and is subsequently reset to null. Consider

function test(){ alert(arguments==test.arguments); }

which says "true" and shows us that the local variable and the function property refer to the same object.

Some side-effects of this implementation include:

  • "arguments" is a reserved word as function property name, it can not be used
  • We can work out whether a function has been called by a script or not! Consider
    Function.prototype.arguments=true;
    function test(){};
    .
    . /* function definitions and calls here */
    .
    var testWasCalled = ! test.arguments;

    "testWasCalled" is now true if and only if the script has used the "test" function during its execution. Might be a handy trick for trying to optimise large scripts wink


..but where in the spec is this specified? In other words, is it a bug or a feature?

event capture explained

, , , ...

What is event capture, and what are the implementation gotchas that cause problems for browsers and webmasters? Read on for details..

Read more...

spot a subtle JS error here..

, , ,

The other day I ran into weird errors while editing the Y!Mail patch in browser.js. The whole thing stopped working, and it took me a while to see the problem. Can you see it?

// need to fake someMethod
Element.prototype.someMethod=function(){
    // faking someMethod here
}
// we also have an insertBefore problem so let's fix that too
(function(oF){
    // tweaking insertBefore here
})(Element.prototype.insertBefore);


What was wrong? Hats off if you can see it without running the code.. smile

Y!RegExp quiz

, , , ...

This snippet is brought to you by Yahoo!Japan blogs. Quiz of the day: what is the RegExp meant to do and why does it fail?

if(img.match(/(.[jJ][pP][eE]?[gG]|.[gG][iI][fF]|.[pP][nN][gG])/)) {
return true;
}
return false;


(Yes, the variable name is a hint.)