All entries from Jul 2008

23 Jul 2008

JavaScript line numbering for code samples

Whoopsie! While poking around on this site the other night I was horrified to discover it was pretty badly b0rked in IE. Not quite sure how I managed to miss that, but hopefully I've put it right now.

As bad luck would have it, one of the things that was broken was a javascript line-numbering function that I'd posted here previously. Best make amends with a corrected version for any readers I've lead astray, eh?

So where previously we had:

function codeLineNumbering() {
    $$('pre.code code').each(function(code){
        var count = code.innerHTML.split("\n").length - 1;
        var lines = $A($R(1,count)).join("\n");
        code.insert({before:'<pre class="line"><code>'+lines+'</code></pre>'});
    });
}

We now have:

function codeLineNumbering() {
    $$('pre.code code').each(function(code){
        var nodes = code.childNodes;
        var count = 1;
        var lines = [];
        for (var i=0; i < nodes.length; i++) {
            if (nodes[i].nodeType != 3) continue;
            var matches = nodes[i].nodeValue.match(/[\r\n]/g);
            if (matches) count += matches.length;
        }
        for (var i=1; i < count; i++) { lines.push(i); }
        code.up().insert({before:'<pre class="line"><code>'+lines.join("\n")+'</code></pre>'});
    });
}

The crux of the issue was that innerHTML seems to behave slightly differently in IE. Other browsers give you a faithful recreation of what's present in the document source, while IE gives you it's reconstituted whitespace-insensitive version.

In the old version of the code I was simply splitting innerHTML on newlines and counting how many lines I had in the resultant array. But with IE's interpretation of innerHTML that doesn't work so well since the odd newline may have been ditched or smooshed into its neighbour.

As a workaround, the substitute function uses the child nodes of the syntax-highlighted section of the document instead, which seem to be more faithful to the original source. I iterate through these looking for plain text nodes, and counting up how many newlines or carriage returns are present in each.

As a caveat I should say that I believe some of this behaviour is dependent on what your doctype and white-space CSS property are set to. Also, the code above makes the assumption that newlines will only be present in root text nodes and not nested within child elements. That's a safe assumption given the method of syntax highlighting I'm using, but it may not be for you.

Tsk, the perils of coding in public.

0 comments

18 Jul 2008
11 Jul 2008
10 Jul 2008
8 Jul 2008

A minor morsel

I haven't posted in ages, so I might as well use this to break radio silence. It's a solution to this little programming brain teaser from Dustin Diaz. I don't think it's as much of a puzzle as he seemed think, given by the responses in the comments and how long it took me, but regardless:

var listA = ['a', 'b', 'c', 'c', 'd', 'e', 'e', 'e', 'e', 'e', 'f', 'e', 'f', 'e', 'f', 'a', 'a', 'a', 'f', 'f', 'f'];
var listB = [];

var count = 0;

listA.forEach(function(val,key,arr){
    var matchPrev = (val == arr[key - 1]);
    var matchNext = (val == arr[key + 1]);

    count = matchPrev ? count+1 : 0;

    if (count == 2) {
        val = '<span>' + val;
    }
    if (count >= 2 && !matchNext) {
        val += '</span>';
    }

    listB.push(val);
});

console.log(listB.join(' '));

As some of the commenters' solutions show, it can be done in a one-line regular expression, but I was sticking to the "rules" implicit in the original post.

0 comments

Other dates

Visit the archive if you wish to skip merrily through time, tumbling this way and that at your leisure.