Best javascript questions in April 2012

'\n\t\r' == 0 is true?

20 votes

Today when I was doing some experiments with ==, I accidentally found out that "\n\t\r" == 0. How on earth does "\n\t\r" equal to 0, or false?

What I did is:

var txt = "\n";  //new line
txt == 0;        //it gives me true

And that really annoy me. So I did more:

var txt = "\r";  //"return"
txt == 0;        //true

var txt = "\t";  //"tab"
txt == 0;        //true

It does not make sense, at all. How's that happen? And more crazy is this:

//Checking for variable declared or not

var txt ="\n\t\r";
if(txt!=false){
    console.log("Variable is declared.");
}else{
    console.log("Variable is not declared.");
}

What it gives me is Variable is not declared.

How is it equal to 0, or false???

This behaviour might be surprising but can be explained by having a look at the specification.

We have to look at the what happens when a comparison with the equals operator is performed. The exact algorithm is defined in section 11.9.3.


string == integer

The step we have to look at is #5:

5. If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.

That means the string "\n" ("\r", "\t") is converted to a number first and then compared against 0.

How is a string converted to a number? This is explained in section 9.3.1. In short, we have:

The MV (mathematical value) of StringNumericLiteral ::: StrWhiteSpace is 0.

where StrWhiteSpace is defined as

StrWhiteSpace :::
    StrWhiteSpaceChar StrWhiteSpace_opt

StrWhiteSpaceChar :::
    WhiteSpace
    LineTerminator

This just means that the numerical value of strings containing white space characters and/or a line terminator is 0.
Which characters are considered as white space characters is defined in section 7.3.


string == boolean

The step we have to look at is #7:

7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

How booleans are converted to numbers is pretty simple: true becomes 1 and false becomes 0.

Afterwards we are comparing a string against a number, which is explained above.


As others have mentioned, strict comparison (===) can be used to avoid this "problem". Actually you should only be using the normal comparison if you know what you are doing and want this behaviour.

Is the Gideon Sundback zipper doodle[Google,24th April] completely javascript?

16 votes

Is the Gideon Sundback google doodle implemented in javascript ?

I tried to trace through firebug, but could not really "get" its implementation details ?

Any thoughts on how it might be implemented ?

Yes it's JavaScript.

It uses the <canvas> element.

Here is the code:

(function () {
    var c = !0,
        d = !1;
    try {
        window.google || (window.google = {});
        google.doodle || (google.doodle = {});
        var f = google.doodle,
            h, j, k, l, m, n, o = 0,
            p = 23,
            q, r, s, u, v, w, x, y = 142,
            z = 356,
            A = 311,
            B = 0,
            C = 0,
            D = 30,
            E = 15,
            F = d,
            G, H = d,
            I = d,
            J = {
                back: {
                    src: "/logos/2012/sundback12-hp-s.png",
                    height: 31,
                    width: 24,
                    x: 0,
                    y: 0
                },
                s: {
                    src: "/logos/2012/sundback12-hp-l.jpg",
                    height: 120,
                    width: 186,
                    x: 0,
                    y: 0
                },
                F: {
                    src: "/logos/2012/sundback12-hp-l.jpg",
                    height: 120,
                    width: 186,
                    x: 186,
                    y: 0
                },
                i: {
                    src: "/logos/2012/sundback12-hp-s.png",
                    height: 60,
                    width: 28,
                    x: 24,
                    y: 0
                },
                top: {
                    src: "/logos/2012/sundback12-hp-s.png",
                    height: 23,
                    width: 8,
                    x: 52,
                    y: 0
                },
                G: {
                    src: "/logos/2012/sundback12-hp-s.png",
                    height: 7,
                    width: 15,
                    x: 60,
                    y: 0
                },
                B: {
                    src: "/logos/2012/sundback12-hp-s.png",
                    height: 7,
                    width: 15,
                    x: 75,
                    y: 0
                }
            },
            K = function () {
                google.psy && google.nav && google.nav.go ? google.nav.go("/search?q=Gideon+Sundback&ct=sundback12-hp&oi=ddle") : window.location.href = "/search?q=Gideon+Sundback&ct=sundback12-hp&oi=ddle"
            },
            L = function (a, b, g, i, e) {
                a.beginPath();
                a.moveTo(b, g);
                a.lineTo(i, e);
                a.stroke()
            },
            M = function (a, b, g) {
                var i = document.createElement("canvas"),
                    e = i.getContext("2d"),
                    t = j.createLinearGradient(0, 0, 0, g);
                t.addColorStop(0, "#f5f5f5");
                t.addColorStop(1, "#f1f1f1");
                e.fillStyle = t;
                e.fillRect(1, 1, b - 2, g - 2);
                e.lineWidth = 1;
                e.strokeStyle = "#d0d0d0";
                L(e, 1, 0, b - 1, 0);
                L(e, b, 1, b, g - 1);
                L(e, b - 1, g, 1, g);
                L(e, 0, g - 1, 0, 1);
                e.fillStyle = "#444";
                e.textAlign = "center";
                e.font = "bold 11px Arial,sans-serif";
                e.fillText(a, b / 2, g * 2 / 3);
                return i
            },
            N = function (a, b) {
                var g = document.createElement("canvas"),
                    i = g.getContext("2d");
                i.fillStyle = "#fff";
                i.fillRect(0, 0, b, 29);
                i.lineWidth = 1;
                i.strokeStyle = "#c0c0c0";
                L(i, 0, 0, b, 0);
                i.strokeStyle = "#d9d9d9";
                L(i, 0, 29, b, 29);
                var e = a ? 0 : b;
                L(i, e, 0, e, 29);
                return g
            },
            O = "move,-ie-grabbing,-moz-grabbing,-o-grabbing,-webkit-grabbing,grabbing".split(","),
            aa = "move,-ie-grab,-moz-grab,-o-grab,-webkit-grab,grab".split(","),
            P = function (a, b) {
                if (a) for (var g in b) a.style.cursor = b[g]
            },
            ba = function (a) {
                if (!H && !Q()) {
                    F = H = c;
                    google.listen(window, "mouseup", R);
                    google.listen(window, "mousemove", S);
                    h.style.zIndex = "2000";
                    P(h, O);
                    k && P(k, O);
                    l.q.blur();
                    google.psy && K();
                    T();
                    S(a)
                }
            },
            R = function () {
                if (!I && H) {
                    I = c;
                    F = d;
                    h.style.cursor = "auto";
                    if (k) k.style.cursor = "auto";
                    ca()
                }
            },
            S = function (a) {
                if (F) {
                    a ? a.stopPropagation && a.stopPropagation() : window.event.cancelBubble = c;
                    a.preventDefault ? a.preventDefault() : a.returnValue = d;
                    a = a || window.event;
                    G = !a ? [0, 0] : [(a.clientX || a.targetTouches && a.targetTouches[0] && a.targetTouches[0].clientX || 0) + (document.body.scrollLeft || document.documentElement.scrollLeft || 0), (a.clientY || a.targetTouches && a.targetTouches[0] && a.targetTouches[0].clientY || 0) + (document.body.scrollTop || document.documentElement.scrollTop || 0)];
                    a = B;
                    B = Math.max(0, G[1] - o - 30 - J.i.height);
                    if (f.z > 0) {
                        f.z--;
                        var b = B - a;
                        Math.abs(b) > 50 && (B = a + b * 0.25)
                    }
                    U()
                }
            },
            T = function () {
                var a = document.getElementById("mgmhppd");
                o = a ? a.offsetHeight : 0;
                a = o + 30;
                h.style.top = a + "px";
                var b = document.getElementById("ftby");
                if (b && b.parentNode) p = b.parentNode.offsetHeight;
                h.height = x = document.body.clientHeight - (o + (H ? 0 : 30 + p));
                var b = 833,
                    g = document.getElementById("gbqf");
                g && (b = Math.max(b, g.offsetWidth));
                h.width = v = Math.max(b, document.body.clientWidth);
                w = v / 2;
                G || (G = [0, 0]);
                G[0] = w;
                if (k) {
                    b = k.style;
                    b.top = h.style.top;
                    b.left = w - 236 + "px";
                    if (l) b.height = google.style.getPageOffsetTop(l) - a + "px"
                }
                da();
                U()
            },
            V = function (a) {
                a.addColorStop(0, "rgba(255,255,255,0)");
                a.addColorStop(0.01, "#eee");
                a.addColorStop(0.55, "#fff");
                a.addColorStop(0.58, "#b1b1b1");
                a.addColorStop(0.68, "#dedede");
                a.addColorStop(0.98, "#dedede");
                a.addColorStop(0.99, "rgba(255,255,255,0)")
            },
            da = function () {
                q = w - C + 1;
                r = w + C;
                s = q - 42;
                u = r + 42
            },
            U = function () {
                if (j) {
                    j.clearRect(0, 0, v, x);
                    var a = s - B,
                        b = u + B;
                    j.fillStyle = "#fff";
                    f.g || j.fillRect(0, 0, v, x);
                    j.beginPath();
                    j.moveTo(0, 0);
                    j.lineTo(a, 0);
                    j.arc(a, B, B + 21, -Math.PI / 2, 0, d);
                    j.lineTo(s, x);
                    j.lineTo(0, x);
                    j.lineTo(0, 0);
                    j.fill();
                    j.beginPath();
                    j.moveTo(v, 0);
                    j.lineTo(b, 0);
                    j.arc(b, B, B + 21, -Math.PI / 2, Math.PI, c);
                    j.lineTo(u, x);
                    j.lineTo(v, x);
                    j.lineTo(v, 0);
                    j.fill();
                    W(J.s, -42, y, c);
                    W(J.F, -42, y, d);
                    if (H) {
                        W(J.v, 0, A, c);
                        W(J.w, 0, A, d)
                    }
                    if (H) {
                        var g = !l || l.q.dir != "rtl";
                        W(J.search, -57, z, g);
                        W(J.C, -57, z, !g)
                    }
                    g = j.createRadialGradient(a, B, B, a, B, B + 42);
                    V(g);
                    j.fillStyle = g;
                    j.fillRect(a, 0, B + 42, B);
                    a = j.createRadialGradient(b, B, B, b, B, B + 42);
                    V(a);
                    j.fillStyle = a;
                    j.fillRect(r, 0, B + 42, B);
                    a = j.createLinearGradient(s, 0, q, 0);
                    V(a);
                    j.fillStyle = a;
                    j.fillRect(s, B, 42, x - B);
                    a = j.createLinearGradient(u, 0, r, 0);
                    V(a);
                    j.fillStyle = a;
                    j.fillRect(r, B, 42, x - B);
                    a = J.B.height + 5;
                    for (b = 0; b < x; b = b + a) {
                        W(J.G, 3, b + a / 2, c);
                        W(J.B, 3, b, d)
                    }
                    a = A - 50;
                    a = Math.min(1, (a - (B - 50)) / a);
                    if (H && a > 0) {
                        j.save();
                        j.globalAlpha = a;
                        W(J.v, 0, A, c);
                        W(J.w, 0, A, d);
                        j.restore()
                    }
                    X(J.back, r - J.back.width / 2, B);
                    j.save();
                    a = 0;
                    G && (a = Math.atan((w - G[0]) / J.i.height));
                    j.translate(r + a * 6, B + 12 + Math.abs(a) * 6);
                    j.rotate(a);
                    X(J.i, 0 - J.i.width / 2, 0);
                    j.restore();
                    X(J.top, r - J.top.width / 2, B + 5)
                }
            },
            X = function (a, b, g) {
                b > h.width || g > h.height || j.drawImage(a.a, a.x, a.y, a.width, a.height, b, g, a.width, a.height)
            },
            W = function (a, b, g, i) {
                var e = i ? q : r;
                if (g > B) {
                    e = i ? e + b - a.width : e - b;
                    X(a, e, g)
                } else {
                    var t = 0;
                    B != 0 && (t = (B - g) / B);
                    b = B + 42 + b;
                    if (i) {
                        b = b - a.width;
                        e = e - 42 - B + Math.cos(t) * b
                    } else e = e + 42 + B - Math.cos(t) * b;
                    j.save();
                    j.translate(e, B - Math.sin(t) * b);
                    j.rotate(Math.PI * 2 + (i ? -t : t));
                    X(a, 0, 0);
                    j.restore()
                }
            },
            ea = function () {
                google.psy || Y()
            },
            Z = function (a) {
                var b = document.getElementById("hplogo-i");
                if (b) b.style.display = a ? "" : "none"
            },
            Y = function (a) {
                window.clearTimeout(f.A);
                if (n && m) n.style.position = m.style.position = "";
                if (h && (!a || !google.psy && google.browser.product.FIREFOX)) h.style.display = "none";
                if (k) k.style.display = "none";
                Z(c);
                google.unlisten(window, "resize", T);
                google.unlisten(window, "mouseup", R);
                google.unlisten(window, "mousemove", S)
            },
            fa = function () {
                if (google.msg) {
                    f.o && google.msg.unlisten(40, f.o);
                    f.o = function () {
                        H || Y();
                        f.g = c;
                        google.msg.unlisten(40, f.o);
                        f.p ? Y() : U();
                        return c
                    };
                    google.msg.listen(40, f.o);
                    f.k && google.msg.unlisten(64, f.k);
                    f.k = function () {
                        T();
                        google.msg.unlisten(64, f.k);
                        return c
                    };
                    google.msg.listen(64, f.k);
                    f.n && google.msg.unlisten(67, f.n);
                    f.n = function () {
                        H || Y();
                        google.msg.unlisten(67, f.n);
                        return c
                    };
                    google.msg.listen(67, f.n)
                }
            },
            Q = function () {
                var a = window.location.href;
                return a.indexOf("#") > -1 && /[^a-z]q=/.test(a) ? c : d
            },
            $ = function () {
                if (!f.D && google.dstr && google.rein) {
                    f.D = c;
                    google.dstr.push(ea);
                    google.rein.push($)
                }
                f.cpDestroy = Y;
                f.cpInit = $;
                a: {
                    for (var a = document.forms, b = ["f", "gs", "tsf", "gbqf"], g = 0, i; i = b[g++];) if (i = a[i]) {
                        l = i;
                        break a
                    }
                    l = null
                }
                m = l.btnK;
                n = l.btnI;
                if (!(a = !l)) if (!(a = !l.q)) if (!(a = l.q.value.length > 0)) if (!(a = Q())) {
                    h = document.getElementById("hplogo-z");
                    if (!h) {
                        h = document.createElement("canvas");
                        document.body.appendChild(h)
                    }
                    if (h.getContext) {
                        j = h.getContext("2d");
                        Z(d);
                        h.id = "hplogo-z";
                        a = h.style;
                        a.left = "0";
                        a.position = "absolute";
                        a.display = "";
                        a.visibility = "";
                        a.zIndex = "-1";
                        if (k = document.getElementById("hplogo-c")) {
                            k.onmouseover = U;
                            k.onmousedown = ba;
                            k.ontouchstart = ba;
                            k.style.display = "";
                            P(k, aa)
                        }
                        a = c
                    } else a = d;
                    a = !a
                }
                if (a) Z(c);
                else {
                    window.clearTimeout(f.A);
                    C = B = 0;
                    E = 15;
                    D = 30;
                    I = H = d;
                    f.g = d;
                    f.p = d;
                    f.z = 5;
                    f.g = d;
                    fa();
                    for (var e in J) {
                        a = J[e];
                        if (a.src) {
                            a.a = new Image;
                            a.a.onload = U;
                            a.a.src = a.src
                        }
                    }
                    if (l && n && m) {
                        J.search = {
                            a: M(m.innerText || m.textContent || m.value, m.offsetWidth, m.offsetHeight),
                            height: m.offsetHeight,
                            width: m.offsetWidth,
                            x: 0,
                            y: 0
                        };
                        J.C = {
                            a: M(n.innerText || n.textContent || n.value, n.offsetWidth, n.offsetHeight),
                            height: n.offsetHeight,
                            width: n.offsetWidth,
                            x: 0,
                            y: 0
                        };
                        e = Math.floor(google.style.getWidth(l.q) / 2);
                        J.v = {
                            a: N(c, e),
                            height: 29,
                            width: e,
                            x: 0,
                            y: 0
                        };
                        J.w = {
                            a: N(d, e),
                            height: 29,
                            width: e,
                            x: 0,
                            y: 0
                        }
                    }
                    google.listen(window, "resize", T);
                    T();
                    y = Math.max(0, google.style.getPageOffsetTop(l) - o - 15 - J.s.height - 30);
                    z = google.style.getPageOffsetTop(m) - o - 30;
                    A = google.style.getPageOffsetTop(l) - o - 30;
                    if (l && n && m) {
                        b = google.style.getWidth(n) - google.style.getWidth(m);
                        a = e = 42;
                        b > 0 ? e = e + b : a = a - b;
                        b = l.q.dir != "rtl";
                        n.style.position = m.style.position = "relative";
                        n.style[b ? "left" : "right"] = e + "px";
                        m.style[b ? "right" : "left"] = a + "px"
                    }
                    U()
                }
            },
            ca = function () {
                B = B + D;
                D = D + 2;
                G[0] = G[0] + (w - G[0]) * 0.3;
                if (B > x) {
                    C = C + E;
                    E = E + 5;
                    da()
                }
                U();
                if (C < w) f.A = window.setTimeout(ca, 30);
                else {
                    G[0] = w;
                    f.p = c;
                    if (google.psy && f.g) Y();
                    else {
                        Y(c);
                        K()
                    }
                }
            };
        google.x ? google.x({
            id: "DOODLE"
        }, $) : $()
    } catch (ga) {
        google.ml(ga, d, {
            cause: "DOODLE"
        })
    };
})();

Different ways of accessing attribute values using javascript

15 votes
document.getElementById('myId').style;

is one way of accessing the style attribute.. Also we can do the same using document.getElementById('myId').getAttribute('style');

What is the difference between these two ways of getting attribute values..And which one is preferable?

In the first example you're not accessing to the style attribute, but to the style property. The property's value can be anything, in case of the style property is an object. In the second example you're accessing to the style attribute of the tag. The attribute's value can be only string.

In case of some attributes there is a mapping between them. So if you set an attribute style on a HTML node, your style property is updated and your style is applied. However, this is not always true: a well known bug in some versions of IE (at least till IE7) is that sort of mapping is broken, so set an attribute is not reflected to the property.

So, if you want set an attribute on a HTML node, you have to use the second one. But if you want to access to the property of your object that represent a HTML node, you have to use the first one.

In case of the style, the first one is strongly recommended.

To make it clear with an example (in modern browsers):

document.body.style.border = "1px solid red";
console.log(document.body.style); // [object CSSStyleDeclaration]
console.log(document.body.getAttribute("style")); // "border: 1px solid red;"

What's are the key differences between Meteor, Ember.js and Backbone.js?

14 votes

Learning Ember.js / Backbone.js has been on my to-do list for a while. Now that Meteor is out, I am just wondering if anyone with experience of Meteor, Ember.js and Backbone.js can summarize the key differences and pros and cons of these three JavaScript frameworks for a person without any experience for any of them.

Specifically, I would like to know which tasks each framework is more suitable for, and why the others aren't.

Edit: now that I read a little bit more on Meteor, it seems to be more similar to Knockout.js rather than Backbone.js. So any comparison with Knockout.js is welcome too.

There is a nice run down/comparison of various MVx JS frameworks here http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/ it's followed by a good discussion in the comments too. I think I've seen Gordon (who wrote it) on here so maybe you'll get a reply from him.

I'd say if you are looking to learn this style of application development then on the one hand, the wealth of open source backbone examples around could be good for you. But on the other hand, although new, the Ember package is actually more complete IMO than backbone.

Both have give you the ability to implement things in a variety of ways, which can be confusing, but Ember provides more of the code that you would have to write yourself in backbone as standard which for me personally is more important for rapid prototyping than the wealth of backbone examples available.

There are more mature plugings for data persistence for backbone, but there is a great community buzz around Ember and lots of contrib libraries are making great progress. I've been pleasantly surprised with how quick I (and others) have had quality responses for a relatively new framework here on Stack Overflow.

When you say meteor we are talking about totally other stuff. If you wanted to do more with code re-use on both client and server side then take a look the recently open sourced yahoo mojito https://github.com/yahoo/mojito running on node.js - I've been messing around with it over the last week, and If you become familiar with backbone/ember or any of the others its a snap to get to grips with.

I should also say I have Knockout.js in use on some production systems but have never used it for a fully fledged 'application'. I've got it hooked up to a mongo interface for drilling down into and pageing logs stored in mongo. I really like it, but I wouldn't feel comfortable building anything too big in it.

Well, that's a bit of a ramble - isn't it :)

Quite happy to talk more about it with anyone who is interested. I've used a number of these frameworks in anger/production (including things not listed in the 'top 10' article) and people at work are sick of hearing me talk about JS i think :-D

Return "True" on Empty jQuery Selector Array?

14 votes

I'm working on creating a more semantic way of checking for elements with jQuery. Using $("#element").length > 0 just doesn't really feel very well worded to me, so I'm making my own selector addition for use in .is:

if($("#element").is(":present")) {
  console.log("It's aliiiveeee!!");
}

That part was easy, like this:

$.extend($.expr[':'],{
    present: function(a) {
        return $(a).length > 0;
    }
});

I want to go a step further, and make it easy to see if an element doesn't exist, using similar syntax:

$.extend($.expr[':'],{
    present: function(a) {
        return $(a).length > 0;
    },
    absent: function(a) {
        return $(a).length === 0;
    }
});

$(function() {
  if($("#element").is(":absent")) {
    console.log("He's dead, Jim.");
  }
});

But this part is surprisingly hard to do. I think it's because I'm paring down the returned elements to get a result, and paring the selector to .length === 0 is the same as asking for no elelements: it returns false no matter what.

I've tried a lot of different ways to reverse things and get this to return true when the element doesn't exist, and false if it does:

return $(a).length === 0;

return !($(a).length > 0);

if(!($(a).length > 0)) { 
  return true;
} 

if($(a).length > 0) { 
  return false;
} else {
  return true;
}

return !!($(a).length === 0);

// etc...

Is there an easy way to get this to just return true if the element doesn't exist, and false if it does?

The definition of is:

Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.

The problem is that since you have no elements, it's not possible that one of your elements matches some condition. jQuery is not even calling your code because there are no elements.

EDIT: To clarify slightly, your code is being called once for every element in your object (at least until one returns true). No elements means the code is never called. a in your code is always a single element.

EDIT2: With that in mind, this would be a more efficient implementation of present:

$.extend($.expr[':'],{
    present: function(a) {
        return true;
    }
});

How would one handle a file upload with Meteor?

12 votes

What would be the canonical way to handle a file upload with Meteor?

There currently doesn't seem to be a way to interact with the HTTP server or do anything related to HTTP.

The only things you can do is talk to server over the RPC methods exposed by Meteor.methods or talk to mongoDB directly over the mongoDB API exposed.

Why does Closure Compiler insist on adding more bytes?

12 votes

If I give Closure Compiler something like this:

window.array = '0123456789'.split('');

It "compiles" it to this:

window.array="0,1,2,3,4,5,6,7,8,9".split(",");

Now as you can tell, that's bigger. Is there any reason why Closure Compiler is doing this?

I think this is what's going on, but I am by no means certain...

The code that causes the insertion of commas is tryMinimizeStringArrayLiteral in PeepholeSubstituteAlternateSyntax.java.

That method contains a list of characters that are likely to have a low Huffman encoding, and are therefore preferable to split on than other characters. You can see the result of this if you try something like this:

"a b c d e f g".split(" "); //Uncompiled, split on spaces
"a,b,c,d,e,f,g".split(","); //Compiled, split on commas (same size)

The compiler will replace the character you try to split on with one it thinks is favourable. It does so by iterating over the characters of the string and finding the most favourable splitting character that does not occur within the string:

// These delimiters are chars that appears a lot in the program therefore
// probably have a small Huffman encoding.
NEXT_DELIMITER: for (char delimiter : new char[]{',', ' ', ';', '{', '}'}) {
  for (String cur : strings) {
    if (cur.indexOf(delimiter) != -1) {
      continue NEXT_DELIMITER;
    }
  }
  String template = Joiner.on(delimiter).join(strings);
  //...
}

In the above snippet you can see the array of characters the compiler claims to be optimal to split on. The comma is first (which is why in my space example above, the spaces have been replaced by commas).

I believe the insertion of commas in the case where the string to split on is the empty string may simply be an oversight. There does not appear to be any special treatment of this case, so it's treated like any other split call and each character is joined with the first appropriate character from the array shown in the above snippet.


Another example of how the compiler deals with the split method:

"a,;b;c;d;e;f;g".split(";"); //Uncompiled, split on semi-colons
"a, b c d e f g".split(" "); //Compiled, split on spaces

This time, since the original string already contains a comma (and we don't want to split on the comma character), the comma can't be chosen from the array of low-Huffman-encoded characters, so the next best choice is selected (the space).

How can I detect if a browser supports the blink tag?

12 votes

The HTML <blink> tag, in browsers that support it (i.e. Mozilla Firefox and Opera), makes its content blink on and off, resembling the effect of a slow strobe light.

I am writing a suite of polyfills for non-standard HTML, including the blink tag. The implementation of blinking behavior is pretty simple

(function blink(n) {
    var blinks = document.getElementsByTagName("blink"),
        visibility = n % 2 === 0 ? "visible" : "hidden";
    for (var i = 0; i < blinks.length; i++) {
        blinks[i].style.visibility = visibility;
    }
    setTimeout(function() {
        blink(n + 1);
    }, 500);
})(0);

(You can see this in action)

But this does not detect if the browser already supports the blink tag, and in browsers that already support it, there will be a double-blinking effect. I need some feature detection that determines if the browser supports blink, and if it doesn't then it falls back on my Javascript polyfill.

I do not want to do browser detection, because that solution is not scalable, and since people can disable blink behavior in their Firefox preferences, that solution is not effective.

Is there a way to detect support for the blink element?

I just did a little research on the matter and I think I may found an answer...

I'm sure you're aware of CSS property support detection? Well, there is a text-decoration: blink CSS property. So if the browser supports <blink> it must support the CSS property too!

This is normal CSS property detection i.e. to detect textDecoration is supported do this:

if (document.createElement("detect").style.textDecoration === "") {  
    // textDecoration supported
}  

Perhaps you could try something like this:

if (document.createElement("detect").style.textDecoration === "blink") {  
    // textDecoration: blink supported ?
}  

or along those lines...

Update

I have 4 browsers & so tested this with 4 browsers. Out of those 4 only FireFox supports the blink tag. <blink> is registered in the HTML document as a "Span" element in FF, but in the other 3 browsers it is registered as an unknown element.

<html>

<head>
<script type="text/javascript">
function investigate() {
    var blinker = document.getElementsByTagName("blink")[0];
    document.getElementById("monitor").innerHTML += blinker;
}
</script>
</head>

<body onload="investigate()">
<blink>Hello, blink!</blink>
<div id="monitor"> </div>
</body>

</html>

Output

Internet Explorer [7,8,9] not supported

Hello, blink!
[object]

Chrome [18] not supported

Hello, blink!
[object HTMLUnknownElement]

Safari [5] not supported

Hello, blink!
[object HTMLElement]

FireFox [3.6] supported

Hello, blink!
[object HTMLSpanElement]

Date constructor: numeric arguments vs. string argument giving different dates in some cases

12 votes

First of all, I think timezone probably has something to do with this. I'm in EST/EDT. Also, I'm testing this on chromium 17 / linux.

Now, let's say I create two dates like this:

// December 5

dateFromNumbers = new Date(2020, 11, 5);
dateFromString = new Date("2020-12-5");

It seems these dates should have identical timestamps, and they do:

+dateFromNumbers == +dateFromString; // true

...at least in this case. But in some cases, they don't:

// December 15

dateFromNumbers = new Date(2020, 11, 15);
dateFromString = new Date("2020-12-15");

+dateFromNumbers == +dateFromString; // false

What's going on here?

dateFromNumbers; // Tue Dec 15 2020 00:00:00 GMT-0500 (EST)
dateFromString;  // Mon Dec 14 2020 19:00:00 GMT-0500 (EST)

Looks like dateFromString is 5 hours earlier than dateFromNumbers in this case (EST is GMT - 5, I'm sure it's related somehow).

It seems to affect the ends of months October through December. Here's a fiddle that makes it easy to see which days differ (unless you are red-green colorblind, in that case it may be difficult to see, my apologies).

http://jsfiddle.net/9gBfX/

What gives?


Notes:

  • You can set your system timezone to EST/EDT to see the jsfiddle example as I'm seeing it.
  • Date's month numbers are zero-based; the 11 is not a typo.
  • This issue appears in every year that I checked.

After looking in V8's source code:

// Specification:
// Accept ES5 ISO 8601 date-time-strings or legacy dates compatible
// with Safari.
<...>
//  A string that matches both formats (e.g. 1970-01-01) will be
//  parsed as an ES5 date-time string - which means it will default
//  to UTC time-zone. That's unavoidable if following the ES5
//  specification.

Reading the surrounding code, it seems that a date-time string with both month and day 2 symbols long is considered a valid ES5 date-time string. Further down in the ES5 parser, after it parses the dates and times, there's a comment:

// Successfully parsed ES5 Date Time String. Default to UTC if no TZ given.

In case of "YYYY-MM-DD", once the code gets that far, the ES5 parser has successfully parsed the entire string, so it returns before the legacy parser gets a chance to localize the timezone. Otherwise (month/day are one symbol long) it's treated as a "legacy" datetime and the legacy parser gets to deal with it and localize it.

Jquery UI autocomplete combobox button click event

11 votes

I'm experiencing weird behavior with jquery ui autocomplete when using it to create a combobox. Whenever I click on the scrollbar to scroll through the list of results AND then click on my combobox button to close the results the results list closes and then opens again. I expect it to close the menu.

Steps to Repro

  1. open jsfiddle demo
  2. Type 'i' in the autocomplete OR hit the dropdown button.
  3. Click on the vertical scroll to scroll the results
  4. Click on the dropdown button

Script to Create Button

 this.button = $("<button type='button'>&nbsp;</button>")
    .attr({ "tabIndex": -1, "title": "Show all items" })
    .insertAfter(input)
    .button({
         icons: {
             primary: "ui-icon-triangle-1-s"
         },
         text: false
    })
    .removeClass("ui-corner-all")
    .addClass("ui-corner-right ui-button-icon")
    .click(function () {

        // when i put breakpoint here, and my focus is not on input, 
        // then this if steatment is false????

        if (input.autocomplete("widget").is(":visible")) {
            input.autocomplete("close");
            return;
        }

        // work around a bug (likely same cause as #5265)
        $(this).blur();

        // pass empty string as value to search for, displaying all results
        input.autocomplete("search", "");
        input.focus();
});

CSS (force long results menu to scroll)

.ui-autocomplete {
    max-height: 100px;
    overflow-y: auto;
    /* prevent horizontal scrollbar */
    overflow-x: hidden;
    /* add padding to account for vertical scrollbar */
    padding-right: 20px;
}
/* IE 6 doesn't support max-height
 * we use height instead, but this forces the menu to always be this tall
 */
* html .ui-autocomplete {
    height: 100px;
}

My solution could be closing the widget even if focus is transferred to widget itself and not the input element?

Any ideas how to modify this code so it behaves this way?

Based on issues with the various click and mouse events for the automplete widget, I came up with this: jsFiddle example.

jQuery:

var input = $('#txtComplete');

var data = [];
var isOpen = false;

function _init() {
    for (var idx = 0; idx <= 100; idx++) {
        data.push('item: ' + idx);
    };
    input.autocomplete({
        source: data,
        minLength: 0,
        open: function(event, ui) {
            isOpen = true;
        },
        select: function(event, ui) {
            isOpen = false;
        }
    });
}

function afterInit() {
    var button = $("<button type='button'>&nbsp;</button>").attr("tabIndex", -1).attr("title", "Show all items").insertAfter(input).button({
        icons: {
            primary: "ui-icon-triangle-1-s"
        },
        text: false
    }).removeClass("ui-corner-all").addClass("ui-corner-right ui-button-icon").click(function(event) {
        input.focus();
        if (isOpen) {
            input.autocomplete("close");
            isOpen = false;
        } else {
            input.autocomplete("search", "");
            event.stopImmediatePropagation();
        }
    });
}
$(window).click(function() {
    input.autocomplete("close");
    isOpen = false;
});
$(function() {
    _init();
    afterInit();
});​

How to open a native iOS app from a web app

11 votes

I'm trying to create an iOS web app, to be opened in full-screen mode from a link saved on the Home Screen. The web app needs to open a specific native app. I have already registered the url scheme for the native app, and verified that it works correctly - I can open the native app by typing the scheme directly into my safari address bar, for instance. I can also open it from other applications using the +openURL: method of UIApplication. I would like to also open it with certain arguments from a native web app that can be added to the home screen.

What I'm trying to do is use javascript like so inside the native app:

window.location = "myapp://myparam";

When using this code inside the web app I get an alert "Cannot Open myWebAppName - myWebAppName could not be opened. The error was "This URL can't be shown".". This same javascript when executed within Safari works correctly. I get the same result using window.location.replace("myapp://myparam").

The html for the web app is:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>untitled</title>
    <meta name="generator" content="TextMate http://macromates.com/">
    <meta name="author" content="Carl Veazey">
    <!-- Date: 2012-04-19 -->
    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta names="apple-mobile-web-app-status-bar-style" content="black-translucent" />
</head>
<body>
    <script type="text/javascript" charset="utf-8">
        if (window.navigator.userAgent.indexOf('iPhone') != -1) {
            if (window.navigator.standalone == true) {
                window.location = "myapp://myparam";
            } else {
                document.write("please save this to your home screen");
        };} else {
                alert("Not iPhone!");
                document.location.href = 'please-open-from-an-iphone.html';
        };
    </script>
</body>
</html>

What am I doing wrong here? I'm pretty inexperienced with javascript and mobile web so I suspect I'm just missing something obvious. Thanks!

tl;dr I have an app with a correctly functioning URL scheme that I'd like to launch from a web app stored on the home screen, and the normal javascript redirect methods don't seem to work.

While it's possible there's another way with some javascript I'm not aware of, to the best of my knowledge at this time it is not possible to do so :(

Why does /^(.+)+Q$/.test("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") take so long?

10 votes

When I run

/^(.+)+Q$/.test("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")

in Chrome or IE, it takes ~10 seconds to complete. (Firefox is able to evaluate it almost instantly.)

Why does it take so long? (And why/how is Firefox able to do it so quickly?)

(Of course, I'd never run this particular regex, but I'm hitting a similar issue with the URL regex at http://daringfireball.net/2010/07/improved_regex_for_matching_urls and it seems to boil down to this, i.e. there are certain URLs which will cause the browser to lock up)

For example:

var re = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i;
re.test("http://google.com/?q=(AAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

As indicated by thg435, it sounds like catastrophic back-tracking. There's an excellent article on this, Regular Expression Matching Can Be Simple And Fast.

It describes an efficient approach known as Thompson NFA. As noted, though, this does not support all features of modern regexes. For instance, it can't do backreferences. However, as suggested in the article:

"Even so, it would be reasonable to use Thompson's NFA simulation for most regular expressions, and only bring out backtracking when it is needed. A particularly clever implementation could combine the two, resorting to backtracking only to accommodate the backreferences."

I suspect Firefox may be doing this.

What kind of object shows up in the console as [object Text]?

9 votes

Say I have the following element:

<p id="thingy">Here is some <em>emphasized</em> text!</p>

In a Javascript console, I'll get its contents with jQuery:

> var theContents = $('<p id="thingy">Here is some <em>emphasized</em> text!</p>').contents();

theContents is now an array that looks like this:

> theContents
["Here is some ", <em>​emphasized​</em>​, " text!"]

So far so good; it seems to be an array, where elements 0 and 2 are strings, and element 1 is a jQuery object. If I output just the first element, it seems to confirm my guess:

> theContents[0]
"Here is some "

However, if I try to concatenate it with another string, I see that I am lacking some understanding:

> 'Hello! ' + contents[0];
"Hello! [object Text]"

So that variable looks like a string, but it is in fact some kind of object. For that matter, the jQuery object in the middle doesn't show up as a regular object, either; it shows up as the raw markup.

Another question references the same problem, but doesn't actually explain it. I've found that I can use contents[0].textContent to solve my real problem, but that still doesn't help me understand exactly what is going on here. Can someone explain to me in detail why all this is behaving the way it is?

Most jQuery functions return a jQuery object, which for convenience looks and works like an array for most purposes. To quote the documentation, "A jQuery object contains a collection of Document Object Model (DOM) elements" (or "nodes"). So you can assume that each member of the collection is a DOM node, not a string, and accordingly the object you're looking at is a Text node.

In JavaScript when you do string concatenation each operand that isn't a string is automatically converted to one by calling its toString() method. So, just as "1,2,3" is the string representation of the array [1, 2, 3] and "[object HTMLDivElement]" represents a node created with the HTML "<div>", "[object Text]" represents a Text node.

You can read the specification here. Since interface Text inherits from CharacterData it has a data property that contains the string itself. And so:

var theContents = $( '<p id="thingy">Here is some <em>emphasized</em> text!</p>' )
                    .contents();

console.log( theContents[0].data );
// => "Here is some "

console.log( "Hello! " + theContents[0].data );
// => "Hello! Here is some "

How do I save an infinite stack of AJAX content when a user leaves the page?

9 votes

I'm making a website with infinite scrolling. That is, as the user scrolls to the bottom of the page, a new block of content is appended to the bottom. It's very similar to Facebook. Here's an example of 3 pages loaded:

 _________
|         |
|    0    |
|_________|
|         |
|    1    |
|_________|
|         |
|    2    |
|_________|

When the user clicks something on the last page, I take them to a separate detail page. But if the user clicks back to the search results page, I have no memory of their previous location and must load page 0 again.

 _________
|         |
|    0    |
|_________|

I know of some old-school methods to solve this, but they each have some serious problems:

Hash URL

I could update the URL every time a new page is loaded. For example: www.website.com/page#2. When the user goes to the detail page and back, the URL tells me the last loaded page, so I load it for him:

 _________
|         |
|    2    |
|_________|

But this is poor user experience. Only page 2 is loaded. The user cannot scroll up to see older content. I can't just load pages 0, 1, and 2 because that would overload the server, considering that the back button accounts for 50% of web traffic (Jacob Nielsen study).

Local Storage

Cookies don't have the storage capacity to store many pages of data. Local Storage should have sufficient space. One idea is to store all the loaded pages into Local Storage. When the user goes back to the search results, I load from local storage instead of hitting the server. The problem with this approach is that a large chunk of my users are on browsers that don't support local storage (IE7).

Given the constraints of your problem the only plausible solution I can think of would be to cache the heights of the previous pages and then load them if the user scrolls up. This would pad your upward scrolling and allow you to trigger a load if the user looks up. Additionally if you wanted to get a little more fancy I'd try to figure out a way to freeze the scrollbar in order to prevent the user from scrolling up past the previous unloaded page. This way they wouldn't be able to trigger multiple page loads at a time.

Avoiding .call() and .apply() using .bind()

9 votes

I'm looking for a way to accomplish a certain task and that is, going from

jQuery.when.apply( null, promiseArray ).done(...)

to

when( promiseArray ).done(...)

As you might know, .bind() can get used to create something like default arguments and also doing some quite nifty stuff. For instance, instead of always calling

var toStr = Object.prototype.toString;
// ...
toStr.call([]) // [object Array]

we can do it like

var toStr = Function.prototype.call.bind( Object.prototype.toString );
toStr([]) // [object Array]

This is fairly cool (even if there is a performance penality invoking .bind() like this, I know it and I'm aware of it), but I can't really accomplish it for jQuerys .when call. If you got an unknown amount of promise objects, you usually push those into an array, to then be able to pass those into .when like in my first code snippet above.

I'm doing this so far:

var when = Function.prototype.apply.bind( $.when );

Now we can go like

when( null, promiseArray ).done(...)

This works, but I also want to get rid of the need to pass in null explicitly every time. So I tried

var when = Function.prototype.apply.bind( $.when.call.bind( null ) );

but that throws at me:

"TypeError: Function.prototype.apply called on incompatible null"

I guess I'm sitting over this for too long now and can't think straight anymore. It feels like there is an easy solution. I don't want to use any additional function to solve this issue, I'd absolutely prefere a solution using .bind().

See a complete example here: http://jsfiddle.net/pp26L/

This should work:

when = Function.prototype.apply.bind( $.when, null);

You just bind (or curry, if you prefer) the first argument of .bind and fix it to null.

Fiddle.

Converting Javascript function into a PHP function

9 votes

I am new to PHP and I'm trying to convert a simple Javascript function into a PHP function. So far I know to only convert the variables, I need help with the sort function and array filter. Thanks for any help!

var closeRoll = function( number ) {
  var rollArray = [36, 48, 60, 72];
  var closest = rollArray.filter(function(ele){
    return ele - number >= 0
  }).sort()[0];
  return closest;   
};

My half conversion:

function closeRoll( $number ) { 
  $rollArray = Array(36, 48, 60, 72);
  $closest = array_filter( $rollArray, function temp( $rollArray ) {
    return $rollArray - $number >= 0;
  }); //.sort()[0];
  return $closest;   
};

Scoping does not work quite like JavaScript; you can't simply refer to $number inside your callback function, because it does not have a scope chain.

You either have to make it global (not recommended but simple):

<?php
function doFilter($element) { // this could still be anonymous, but doesn't have to be
   return $element - $GLOBALS['number'] >= 0;
}

function closeRoll( $number ) { 
   $GLOBALS['number'] = $number;
   $rollArray = Array(36, 48, 60, 72);
   $closest = array_filter( $rollArray, 'doFilter' );
   return $closest;   
};
?>

Or you could utilize the use operator (an elegant solution):

<?php

function closeRoll( $number ) { 
   $rollArray = Array(36, 48, 60, 72);
   $closest = array_filter( $rollArray, function( $element ) use ($number) {
     return $element - $number >= 0;
   });
   return $closest;   
};

?>

How does this site(allaboutrajni) work only when you go offline(disconnect from internet)

8 votes

The following site only works if you are offline. http://www.desimartini.com/allaboutrajni.htm

So how do they do this. I have even seen YouTube do something similar. just open YouTube and disconnect net and refresh. You will see the page below.

enter image description here

HTML5 allows webpages to work offline by;

  1. Providing storage in terms of localStorage and sessionStorage.
  2. Providing events to let the page know when it's online/ offline
  3. The navigation.onLine property is a boolean which lets you know whether you're online or not.
  4. Giving application caches.

So basically, both pages store the assets it needs to work offline in either of those storage mediums, and checks whether it's online or offline when the page loads.

For more info, you could see http://www.html5rocks.com/en/features/offline

EDIT: Meh, infact the site you linked to works by downloading the flash content, and then pinging http://images.desimartini.com/allaboutrajni/config.xml every second to detect whether it's still connected to the internet. This website won't load if you disconnect and then refresh the page.

However, a page which uses true offline functionality is one such as http://htmlfive.appspot.com/static/stickies.html

What are some common ways to cause memory leaks using JQuery/JavaScript?

8 votes

My question assumes you are creating a web page that will be displayed for a "long time." I'm curious as to what are some of the common gotchas that will cause memory leaks when using JQuery/JavaScript in such a scenario? For instance what happens in terms of memory when you call $.remove() on a collection of elements? Thanks!

JavaScript uses garbage collection to reclaim the memory occupied by strings, objects, arrays, and functions that are no longer in use. This frees you, the programmer, from having to explicitly deallocate memory yourself and is an important part of what makes JavaScript programming easier than, say, C programming.

References : Check this for more and an answer on SO.

Memory issues in event registering mechanism MDN

var i;  
var els = document.getElementsByTagName('*');  

// Case 1  
for(i=0 ; i<els.length ; i++){  
    els[i].addEventListener("click", function(e){/*do something*/}, false});  
}  

// Case 2  
function processEvent(e){  
    /*do something*/  
}  

for(i=0 ; i<els.length ; i++){  
  els[i].addEventListener("click", processEvent, false});  
}  

In the first case, a new (anonymous) function is created at each loop turn. In the second case, the same previously declared function is used as an event handler. This results in smaller memory consumption. Moreover, in the first case, since no reference to the anonymous functions is kept, it is not possible to call element.removeEventListener because we do not have a reference to the handler, while in the second case, it's possible to do

myElement.removeEventListener("click", processEvent, false)

Best way to iterate over an array without blocking the UI

8 votes

I am needing to iterate over some large arrays and store them in backbone collections from an API call. What is the best way to do this without making the loop cause the interface to become unresponsive?

The return of the ajax request also blocks as the data returned is so large. I figure that I could split it up and use setTimeout to make it run asynchronously in smaller chunks but is there an easier way to do this.

I thought a web worker would be good but it needs to alter some data structures saved on the UI thread. I have tried using this to do the ajax call but when it returns the data to the UI thread there is still a time when the interface is unresponsive.

Thanks in advance

You break the processing of the array into chunks and do each chunk on a timer. Usually, you can afford to process more than one on each timer which is both more efficient and faster than only doing one per timer. This code gives the UI thread a chance to process any pending UI events between each chunk which will keep the UI active.

function processLargeArray(array) {
    // set this to whatever number of items you can process at once
    var chunk = 100;
    var index = 0;
    function doChunk() {
        var cnt = chunk;
        while (cnt-- && index < array.length) {
            // process array[index] here
            ++index;
        }
        if (index < array.length) {
            setTimeout(doChunk, 1);
    }

    doChunk();

}

processLargeArray(veryLargeArray);

Here's a working example of the concept - not this same function, but a different long running process that uses the same setTimeout() idea to test out a probability scenario with a lot of iterations: http://jsfiddle.net/jfriend00/9hCVq/

Reloading page when user arrives from back button

7 votes

I have a generic error page, that any handled error will redirect to. I have an admin page that when the user invokes an error, and the user is brought to the error page, hitting the back button from the error page cause the admin page to load improperly.

So what I need, is a way to reload the admin page when I come from the error page. I have tried setting no cache and such on the admin page, and checking for a postback, but nothing works. Setting no cache seems to do nothing, and any javascript on the admin page's document.ready function does not get called either. Is there any other ways to get this to happen?

EDIT:

I should also mention that I have noticed that a table is missing 2 cells I recently added. This makes me believe that there is a old state of the page being cached somewhere, although clearing the browser cache and restarting my server do not help at all.

Edit2:

Also, setting window.onload() gets nuked when I come back to the admin page

So it turns out this is just an IE9 issue. My onload method was just never getting called. Works fine in the other browsers.

The fix I had to make in order get this to work in IE9 as well was to append a query string to the page, so that when I come back to the page, IE9 will go back to the browser incise the query string has changed or needs to be re-evaluated.

Thanks to both of you guys, or help helped me find the issue.