Difference between revisions of "Talk:1190: Time"

Explain xkcd: It's 'cause you're dumb.
Jump to: navigation, search
Line 543: Line 543:
 
When I saw this comic last night and that there was no explanation up, I thought to myself "How zen."  I figured that Randall was going through a calm streak before throwing us the utterly ridiculous April 1st comic.  Did it come early, or does he have something even bigger planned for us? [[Special:Contributions/76.106.251.87|76.106.251.87]] 07:05, 26 March 2013 (UTC)
 
When I saw this comic last night and that there was no explanation up, I thought to myself "How zen."  I figured that Randall was going through a calm streak before throwing us the utterly ridiculous April 1st comic.  Did it come early, or does he have something even bigger planned for us? [[Special:Contributions/76.106.251.87|76.106.251.87]] 07:05, 26 March 2013 (UTC)
 
:Sorry, did you miss the bit where this comic updates every 30 minutes and all the server error messages being caused by the massive traffic to both the wiki and the main xkcd website? '''[[User:Davidy22|<u>{{Color|purple|David}}<font color=green size=3px>y</font></u><font color=indigo size=4px>²²</font>]]'''[[User talk:Davidy22|<tt>[talk]</tt>]] 07:08, 26 March 2013 (UTC)
 
:Sorry, did you miss the bit where this comic updates every 30 minutes and all the server error messages being caused by the massive traffic to both the wiki and the main xkcd website? '''[[User:Davidy22|<u>{{Color|purple|David}}<font color=green size=3px>y</font></u><font color=indigo size=4px>²²</font>]]'''[[User talk:Davidy22|<tt>[talk]</tt>]] 07:08, 26 March 2013 (UTC)
 +
::Well, when I said "last night" and "no explanation", I implied that I wasn't aware of that at the time, which is why I thought what I did.  Of course, it is now "now" and there <i>is</i> an explanation, so that should answer your question.  Also, since it's not April 1st, and Randall has consistently released something major on that day, the jury is still out, leaving my question quite open (though I was really only asking for opinions). [[Special:Contributions/76.106.251.87|76.106.251.87]] 07:20, 26 March 2013 (UTC)

Revision as of 07:20, 26 March 2013

Pretty sure we're just getting trolled with this one 99.108.190.136 04:48, 25 March 2013 (UTC)

Can't tell if this is emo xkcd or trolling xkcd. Alpha (talk) 04:53, 25 March 2013 (UTC)


Something seems a little fishy because the image url is different than normal. Bugefun (talk) 04:55, 25 March 2013 (UTC)

Maybe the comic slowly changes throughout the day. Alpha (talk) 04:56, 25 March 2013 (UTC)
Oh god, it does. Alpha (talk) 04:57, 25 March 2013 (UTC)
When uploading different versions of the image, use the naming convention time[iterationNumber].png. We'll compile all the images into one and display them as per Traffic Lights. Davidy²²[talk] 05:05, 25 March 2013 (UTC)
Alright, so the comic appears to be switching between two states here: between this and this. If nothing new happens, I'll get to clipping the comics together. Davidy²²[talk] 05:28, 25 March 2013 (UTC)
Whoop, nope, this just came up. Is there more to come? Davidy²²[talk] 05:34, 25 March 2013 (UTC)
Alright, so a new one is posted every half-hour. Whoopee. Davidy²²[talk] 06:06, 25 March 2013 (UTC)
And there's a new one! Megan leaning back and looking up...
Well, the image changed, who has the time to make a script to catch the new images and compile them into a gif? https://dl.dropbox.com/u/932170/time.png Statharas.903 (talk) 07:14, 25 March 2013 (UTC)

72.21.198.66 05:11, 25 March 2013 (UTC)It could be a reference to the old proverb " time and tide wait for none" Cueball and the girl could be waiting for the tide in the beach! (Just a guess)72.21.198.66 05:11, 25 March 2013 (UTC)

The picture does chance with time. The URL includes a changing timestamp that I can't decipher. Compare these two URLS (which have slightly different images: http://imgs.xkcd.com/comics/time/8eb156cce408df8bb83528382d6a2aa2ce6c74f3c573fd12b058cd1c56420672.png http://imgs.xkcd.com/comics/time/1e349a579b5f9b5ed487ddf7e88244b70330941ddedac9c6abf6ed2e3f589b97.png

Perhaps there is a way to hack the URL to view future images. 199.30.248.121 05:29, 25 March 2013 (UTC)

I would also like to add that knowing randall, these are not the only images. For all we know, the image will still be changing in 5 years while a tree grows in front of them. My point is: Are the URLs hackable, or did he encrypt them? 199.30.248.121 05:33, 25 March 2013 (UTC)

Likely there is a way to hack the URLs; they look like some sort of hash, probably a hashed timestamp. Of course, he could easily have added some salt to the hash, making it significantly *harder* to hack. But they're strings of a specific length, so it should be pretty easy to bruteforce it, fetch all the images, and then (maybe) reverse-engineer the sequence. *That* all depends on how many of them there are. 76.90.249.178 05:44, 25 March 2013 (UTC)

Good god, do you see how many digits are *in* that hash? The sun'll have burned out by the time we've tested every possible combination of digits. Davidy²²[talk] 05:47, 25 March 2013 (UTC)

It seems that the image is updated every 1/2 hour. 152.23.97.150 06:17, 25 March 2013 (UTC)

Given that the images switch back and forth between other images already seen, and that the comic should be viewable in the future, it seems unlikely that it's any thing like a simple sha256 of part of the timestamp. I think it's more likely a function of half-hours and minutes (assuming we continue to get a new possible image every half-hour). 99.153.248.206 06:59, 25 March 2013 (UTC)
The images do cycle, yes. But for some reason I have never seen the img where Megan is looking behind her. Also wouldn't it be difficult to show a sequential story (like the rising tide) if the previous images keep cycling ?

Hash appears to be SHA-256. I tried some obvious hashes ("1", "11901", "1190_1", "1190.1") to no avail. Maybe this is HMAC-SHA256? Also, I would suggest trying Unix timestamps. 131.156.236.149 06:19, 25 March 2013 (UTC)

I've been trying to make educated guesses as to what's being hashed here: http://www.xorbin.com/tools/sha256-hash-calculator ... he could also be using hash(hash2(value)) which would be virtually impossible to crack. 99.153.248.206 06:59, 25 March 2013 (UTC)

It's entirely possible that the "hash" is actually randomly generated. Just a thought. 129.21.119.153 07:03, 25 March 2013 (UTC)

Alright, this is probably not going to work, but I'm trying to exploit Randall's awesomeness here. Maybe he decided to take the time-stamps from the user? I don't know if that's even possible... That would then allow people in different time zones to obtain different images simultaneously. (What's the corollary of Godwin's law for a bunch of math-and-science nerds and relativity? Is there one?) Clicking the img src url on the comic's html page, give me this: http://imgs.xkcd.com/comics/time/752687b61523144c61736cd89f8c153dc41e19128f72d78d44947ff800f057fa.png : Never mind.. apparently others see the same image too.

Could he be doing this live? Monitoring the discussion on the net? Collaborative, crowdsourced comic-ing? Reminds me of those you-decide-what-the-character-does-next-and-flip-to-appropriate-page parallel plot novels.

220.224.246.97 07:14, 25 March 2013 (UTC)

Let's just compare the two pictures and see how the bottom right changes, which I believe is water and they are indeed waiting for the tide. Statharas.903 (talk) 07:19, 25 March 2013 (UTC)

I'm adding urls to pictures bellow, edit freely.
They change every 5 minutes, will try to keep track.

http://www.explainxkcd.com/wiki/images/f/f8/time.png http://imgs.xkcd.com/comics/time/1e349a579b5f9b5ed487ddf7e88244b70330941ddedac9c6abf6ed2e3f589b97.png http://imgs.xkcd.com/comics/time/752687b61523144c61736cd89f8c153dc41e19128f72d78d44947ff800f057fa.png http://dl.dropbox.com/u/932170/timeasdf.png http://dl.dropbox.com/u/932170/time6.png

I have uploaded all the different images onto the wiki, in the order that they were revealed. To avoid needless duplication of effort, I'll put them up in the explanation page. Davidy²²[talk] 07:44, 25 March 2013 (UTC)

It just went back to the second image... 220.224.246.97 07:59, 25 March 2013 (UTC)

And now changed to something new. http://imgs.xkcd.com/comics/time/cdcc6b46b32c53f8596cd0106958b42c4260b9cbc022e6d94054147aa6554960.png
The images do look alike, but they're all different. Thanks David. Statharas.903 (talk) 08:04, 25 March 2013 (UTC)
No..I checked the random string. They're exactly the same. In fact, now it's gone back to the second image. Again. 220.224.246.97 08:07, 25 March 2013 (UTC)

Just found this JavaScript code embedded in the comic HTML source (Update: Reformatted to prevent eye-bleeding):

(function (e) {
    "use strict";

    function t() {
        this.data = {}
    }
    function n() {
        this.listeners = new t
    }
    function r(e) {
        setTimeout(function () {
            throw e
        }, 0)
    }
    function i(e) {
        this.type = e
    }
    function s(e, t) {
        i.call(this, e), this.data = t.data, this.lastEventId = t.lastEventId
    }
    function g(e, t) {
        var n = Number(e);
        return (n < 1 ? 1 : n > 18e6 ? 18e6 : n) || t
    }
    function y(e, t, n) {
        try {
            typeof e[t] == "function" && e[t](n)
        } catch (i) {
            r(i)
        }
    }
    function b(t, r) {
        function B() {
            L = d, N !== null && (N.abort(), N = null), C !== 0 && (clearTimeout(C), C = 0), S.readyState = d
        }
        function j(e) {
            var t = L === p || L === h ? N.responseText || "" : "",
                n = null;
            if (L === h) {
                var r = f ? t !== "" ? N.getResponseHeader("Content-Type") : "" : N.contentType;
                if (r && v.test(r)) {
                    L = p, T = !0, x = u, S.readyState = p, n = new i("open"), S.dispatchEvent(n), y(S, "onopen", n);
                    if (L === d) return
                }
            }
            if (L === p) {
                t.length > k && (H = !0, T = !0);
                var o = 0,
                    a = t.indexOf("\r", k),
                    l = t.indexOf("\n", k);
                while (a !== -1 || l !== -1) {
                    a === -1 || l !== -1 && l < a ? (o = l, l = t.indexOf("\n", o + 1)) : (o = a, a = t.indexOf("\r", o + 1));
                    var m = t.slice(k, o),
                        B = D;
                    D = t.slice(o, o + 1) === "\r", k = o + 1;
                    if (!B || m.length !== 0 || D) {
                        _.push(m);
                        var j = _.join("");
                        _.length = 0;
                        if (j !== "") {
                            var I = "",
                                q = j.indexOf(":", 0);
                            q !== -1 && (I = j.slice(q + (j.slice(q + 1, q + 2) === " " ? 2 : 1)), j = j.slice(0, q)), j === "data" ? A.push(I) : j === "id" ? O = I : j === "event" ? M = I : j === "retry" ? (u = g(I, u), x = u, b < u && (b = u)) : j === "retryLimit" ? b = g(I, b) : j === "heartbeatTimeout" && (w = g(I, w), C !== 0 && (clearTimeout(C), C = setTimeout(R, w)))
                        } else {
                            if (A.length !== 0) {
                                E = O;
                                var U = M || "message";
                                n = new s(U, {
                                    data: A.join("\n"),
                                    lastEventId: O
                                }), S.dispatchEvent(n), U === "message" && y(S, "onmessage", n);
                                if (L === d) return
                            }
                            A.length = 0, M = ""
                        }
                    }
                }
                k !== t.length && (_.push(t.slice(k)), k = t.length)
            }
            H && P === 0 && (H = !1, P = setTimeout(F, 80)), L !== p && L !== h || !(e || k > 1048576 || C === 0 && !T) ? C === 0 && (T = !1, C = setTimeout(R, w)) : (L = c, N.abort(), C !== 0 && (clearTimeout(C), C = 0), x > b && (x = b), C = setTimeout(R, x), x = x * 2 + 1, S.readyState = h, n = new i("error"), S.dispatchEvent(n), y(S, "onerror", n))
        }
        function F() {
            P = 0, j(!1)
        }
        function I() {
            j(!1)
        }
        function q() {
            j(!0)
        }
        function R() {
            C = 0;
            if (L !== c) {
                j(!1);
                return
            }
            if (navigator.onLine === !1) {
                C = setTimeout(R, 500);
                return
            }
            if (m && e.document && (e.document.readyState === "loading" || e.document.readyState === "interactive")) {
                C = setTimeout(R, 100);
                return
            }
            N.onload = N.onerror = q, N.mozAnon === undefined ? N.onprogress = I : N.onreadystatechange = I, T = !1, C = setTimeout(R, w), k = 0, L = h, A.length = 0, M = "", O = E, _.length = 0, D = !1, N.open("GET", t + ((t.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(E) + "&r=" + String(Math.random() + 1).slice(2)), !0), N.withCredentials = o, N.responseType = "text", f && N.setRequestHeader("Accept", "text/event-stream"), N.send(null)
        }
        t = String(t);
        var o = Boolean(a && r && r.withCredentials),
            u = g(r ? r.retry : NaN, 1e3),
            b = g(r ? r.retryLimit : NaN, 3e5),
            w = g(r ? r.heartbeatTimeout : NaN, 45e3),
            E = r && r.lastEventId && String(r.lastEventId) || "",
            S = this,
            x = u,
            T = !1,
            N = new l,
            C = 0,
            k = 0,
            L = c,
            A = [],
            O = "",
            M = "",
            _ = [],
            D = !1,
            P = 0,
            H = !1;
        r = null, n.call(this), this.close = B, this.url = t, this.readyState = h, this.withCredentials = o, R()
    }
    function w() {
        this.CONNECTING = h, this.OPEN = p, this.CLOSED = d
    }
    t.prototype = {
        get: function (e) {
            return this.data[e + "~"]
        },
        set: function (e, t) {
            this.data[e + "~"] = t
        },
        "delete": function (e) {
            delete this.data[e + "~"]
        }
    }, n.prototype = {
        dispatchEvent: function (e) {
            var t = String(e.type),
                n = this.listeners,
                i = n.get(t);
            if (!i) return;
            var s = i.length,
                o = -1;
            while (++o < s) {
                var u = i[o];
                try {
                    u.call(this, e)
                } catch (a) {
                    r(a)
                }
            }
        },
        addEventListener: function (e, t) {
            e = String(e);
            var n = this.listeners,
                r = n.get(e);
            r || n.set(e, r = []);
            var i = r.length;
            while (--i >= 0) if (r[i] === t) return;
            r.push(t)
        },
        removeEventListener: function (e, t) {
            e = String(e);
            var n = this.listeners,
                r = n.get(e);
            if (!r) return;
            var i = r.length,
                s = [],
                o = -1;
            while (++o < i) r[o] !== t && s.push(r[o]);
            s.length === 0 ? n["delete"](e) : n.set(e, s)
        }
    }, s.prototype = i.prototype;
    var o = e.XMLHttpRequest,
        u = e.XDomainRequest,
        a = Boolean(o && (new o).withCredentials !== undefined),
        f = a,
        l = a ? o : u,
        c = -1,
        h = 0,
        p = 1,
        d = 2,
        v = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i,
        m = /AppleWebKit\/5([0-2][0-9]|3[0-4])[^\d]/.test(navigator.userAgent);
    w.prototype = n.prototype, b.prototype = new w, w.call(b), l && (e.EventSource = b)
})(this),
function () {
    function e(e) {
        (new Image).src = "http://xkcd.com/events/" + e
    }
    function t() {
        location.hash == "#verbose" && console.log.apply(console, arguments)
    }
    try {
        var n = "http://c0.xkcd.com/stream/comic/time?method=EventSource",
            r = new EventSource(n);
        t("connecting to event source:", n), r.addEventListener("open", function (t) {
            e("connect_start")
        }, !1), r.addEventListener("error", function (t) {
            e("connect_error")
        }, !1), r.addEventListener("loadtest", t, !1), r.addEventListener("comic/time", t, !1), r.addEventListener("comic/time", function (e) {
            var n = JSON.parse(e.data),
                r = document.getElementById("comic").getElementsByTagName("img")[0],
                i = Math.round(Math.random() * n.spread);
            t("waiting", i, "seconds before displaying comic", n.image), setTimeout(function () {
                r.src = "http://imgs.xkcd.com/comics/time/" + n.image
            }, i * 1e3)
        }, !1)
    } catch (i) {
        e("js_error")
    }
}();

I'm no programmer but this looks important to me...

Doesn't really help. The script basically changes the image when something happens (probably some time passes, although it's possible there is more hidden there). WHAT image then appears is not directed by the script, but by the site. Specifically, the image displayed as first is taken from http://c0.xkcd.com/redirect/comic/time, while the script asks for http://c0.xkcd.com/stream/comic/time?method=EventSource&r=(somenumber) ... which is, if you get correct "r", probably some json containing the image url. So, even if you hack the script, you will not get all possible urls. -- Hkmaly (talk) 09:17, 25 March 2013 (UTC)
... actually, given that the script part doesn't seem to do anything just now, it's even possible it's for later (ie, starts producing images when the correct time come). Or maybe there is a bug somewhere in the code :-). -- Hkmaly (talk) 09:27, 25 March 2013 (UTC)
Thanks for explaining. Why hasn't anyone posted this before? Could "location.hash" possibly have anything to do with the method used to generate the image hash key? Also, why is this code so difficult to follow (Obfuscation?)? So many questions... Sorry if this is just a huge waste of Time.
location is the URI of the page. location.hash is the part of the uri after the # character. If you go to https://xkcd.com/1190/#verbose, you'll see some debugging output in your browser's debugging console (Firefox: Web Console or Firebug, Chrome: Development Tools). But nothing to decode the algorithm... :-( --83.243.48.2 10:01, 25 March 2013 (UTC)
Well, I don't know what's doing it, but there's definitely some script (probably this script) that's refreshing the image automatically. I left the comic open for an hour or so and noticed the image had changed. I refreshed with #verbose in Chrome right before the 30 minute mark and got the following in the console.
connecting to event source: http://c0.xkcd.com/stream/comic/time?method=EventSource time07.min.js:1
s {type: "comic/time", data: "{"spread":5,"image":"832a7f13ca0fadc46e93475bb617d78211e32c81c3af0e289a51f8f149707759.png"}", lastEventId: "e2992bf0-9557-11e2-8001-1c6f659cb250"} time07.min.js:1
waiting 0 seconds before displaying comic 832a7f13ca0fadc46e93475bb617d78211e32c81c3af0e289a51f8f149707759.png time07.min.js:1
Resource interpreted as Image but transferred with MIME type application/octet-stream: "http://xkcd.com/events/connect_start". time07.min.js:1
s {type: "comic/time", data: "{"spread":5,"image":"847265673986f085460bf1a95b96f7171bcd9a4f1f0a598b2188307d03bcfaa3.png"}", lastEventId: "79580fe8-9558-11e2-8001-1c6f659cb250"} time07.min.js:1
waiting 4 seconds before displaying comic 847265673986f085460bf1a95b96f7171bcd9a4f1f0a598b2188307d03bcfaa3.png time07.min.js:1
connection error i {type: "error"} time07.min.js:1
Resource interpreted as Image but transferred with MIME type application/octet-stream: "http://xkcd.com/events/connect_error".
The script seems to poll the server every minute or two. It's different from before, where the image server itself redirected to the correct image. The auto refresh was probably always intended, but not quite ready when the comic went live. It may have turned out to be necessary too, so the image server doesn't have to do all the work. 129.21.119.153 14:45, 25 March 2013 (UTC)

Before obfuscation...

(function (global) {
    "use strict";

    function Map() {
        this.data = {}
    }
    function EventTarget() {
        this.listeners = new Map
    }
    function throwError(e) {
        setTimeout(function () {
            throw e
        }, 0)
    }
    function Event(type) {
        this.type = type
    }
    function MessageEvent(type, options) {
        Event.call(this, type), this.data = options.data, this.lastEventId = options.lastEventId
    }
    function getDuration(value, def) {
        var n = Number(value);
        return (n < 1 ? 1 : n > 18e6 ? 18e6 : n) || def
    }
    function fire(that, property, event) {
        try {
            typeof that[property] == "function" && that[property](event)
        } catch (e) {
            throwError(e)
        }
    }
    function EventSource(url, options) {
        function close() {
            currentState = CLOSED, xhr !== null && (xhr.abort(), xhr = null), timeout !== 0 && (clearTimeout(timeout), timeout = 0), that.readyState = CLOSED
        }
        function onProgress(isLoadEnd) {
            var responseText = currentState === OPEN || currentState === CONNECTING ? xhr.responseText || "" : "",
                event = null;
            if (currentState === CONNECTING) {
                var contentType = isXHR ? responseText !== "" ? xhr.getResponseHeader("Content-Type") : "" : xhr.contentType;
                if (contentType && contentTypeRegExp.test(contentType)) {
                    currentState = OPEN, wasActivity = !0, retry = initialRetry, that.readyState = OPEN, event = new Event("open"), that.dispatchEvent(event), fire(that, "onopen", event);
                    if (currentState === CLOSED) return
                }
            }
            if (currentState === OPEN) {
                responseText.length > charOffset && (wasAct = !0, wasActivity = !0);
                var i = 0,
                    i1 = responseText.indexOf("\r", charOffset),
                    i2 = responseText.indexOf("\n", charOffset);
                while (i1 !== -1 || i2 !== -1) {
                    i1 === -1 || i2 !== -1 && i2 < i1 ? (i = i2, i2 = responseText.indexOf("\n", i + 1)) : (i = i1, i1 = responseText.indexOf("\r", i + 1));
                    var line = responseText.slice(charOffset, i),
                        oldWasCR = wasCR;
                    wasCR = responseText.slice(i, i + 1) === "\r", charOffset = i + 1;
                    if (!oldWasCR || line.length !== 0 || wasCR) {
                        responseBuffer.push(line);
                        var field = responseBuffer.join("");
                        responseBuffer.length = 0;
                        if (field !== "") {
                            var value = "",
                                j = field.indexOf(":", 0);
                            j !== -1 && (value = field.slice(j + (field.slice(j + 1, j + 2) === " " ? 2 : 1)), field = field.slice(0, j)), field === "data" ? dataBuffer.push(value) : field === "id" ? lastEventIdBuffer = value : field === "event" ? eventTypeBuffer = value : field === "retry" ? (initialRetry = getDuration(value, initialRetry), retry = initialRetry, retryLimit < initialRetry && (retryLimit = initialRetry)) : field === "retryLimit" ? retryLimit = getDuration(value, retryLimit) : field === "heartbeatTimeout" && (heartbeatTimeout = getDuration(value, heartbeatTimeout), timeout !== 0 && (clearTimeout(timeout), timeout = setTimeout(onTimeout, heartbeatTimeout)))
                        } else {
                            if (dataBuffer.length !== 0) {
                                lastEventId = lastEventIdBuffer;
                                var type = eventTypeBuffer || "message";
                                event = new MessageEvent(type, {
                                    data: dataBuffer.join("\n"),
                                    lastEventId: lastEventIdBuffer
                                }), that.dispatchEvent(event), type === "message" && fire(that, "onmessage", event);
                                if (currentState === CLOSED) return
                            }
                            dataBuffer.length = 0, eventTypeBuffer = ""
                        }
                    }
                }
                charOffset !== responseText.length && (responseBuffer.push(responseText.slice(charOffset)), charOffset = responseText.length)
            }
            wasAct && progressTimeout === 0 && (wasAct = !1, progressTimeout = setTimeout(p, 80)), currentState !== OPEN && currentState !== CONNECTING || !(isLoadEnd || charOffset > 1048576 || timeout === 0 && !wasActivity) ? timeout === 0 && (wasActivity = !1, timeout = setTimeout(onTimeout, heartbeatTimeout)) : (currentState = WAITING, xhr.abort(), timeout !== 0 && (clearTimeout(timeout), timeout = 0), retry > retryLimit && (retry = retryLimit), timeout = setTimeout(onTimeout, retry), retry = retry * 2 + 1, that.readyState = CONNECTING, event = new Event("error"), that.dispatchEvent(event), fire(that, "onerror", event))
        }
        function p() {
            progressTimeout = 0, onProgress(!1)
        }
        function onProgress2() {
            onProgress(!1)
        }
        function onLoadEnd() {
            onProgress(!0)
        }
        function onTimeout() {
            timeout = 0;
            if (currentState !== WAITING) {
                onProgress(!1);
                return
            }
            if (navigator.onLine === !1) {
                timeout = setTimeout(onTimeout, 500);
                return
            }
            if (webkitBefore535 && global.document && (global.document.readyState === "loading" || global.document.readyState === "interactive")) {
                timeout = setTimeout(onTimeout, 100);
                return
            }
            xhr.onload = xhr.onerror = onLoadEnd, xhr.mozAnon === undefined ? xhr.onprogress = onProgress2 : xhr.onreadystatechange = onProgress2, wasActivity = !1, timeout = setTimeout(onTimeout, heartbeatTimeout), charOffset = 0, currentState = CONNECTING, dataBuffer.length = 0, eventTypeBuffer = "", lastEventIdBuffer = lastEventId, responseBuffer.length = 0, wasCR = !1, xhr.open("GET", url + ((url.indexOf("?", 0) === -1 ? "?" : "&") + "lastEventId=" + encodeURIComponent(lastEventId) + "&r=" + String(Math.random() + 1).slice(2)), !0), xhr.withCredentials = withCredentials, xhr.responseType = "text", isXHR && xhr.setRequestHeader("Accept", "text/event-stream"), xhr.send(null)
        }
        url = String(url);
        var withCredentials = Boolean(xhr2 && options && options.withCredentials),
            initialRetry = getDuration(options ? options.retry : NaN, 1e3),
            retryLimit = getDuration(options ? options.retryLimit : NaN, 3e5),
            heartbeatTimeout = getDuration(options ? options.heartbeatTimeout : NaN, 45e3),
            lastEventId = options && options.lastEventId && String(options.lastEventId) || "",
            that = this,
            retry = initialRetry,
            wasActivity = !1,
            xhr = new Transport,
            timeout = 0,
            charOffset = 0,
            currentState = WAITING,
            dataBuffer = [],
            lastEventIdBuffer = "",
            eventTypeBuffer = "",
            responseBuffer = [],
            wasCR = !1,
            progressTimeout = 0,
            wasAct = !1;
        options = null, EventTarget.call(this), this.close = close, this.url = url, this.readyState = CONNECTING, this.withCredentials = withCredentials, onTimeout()
    }
    function F() {
        this.CONNECTING = CONNECTING, this.OPEN = OPEN, this.CLOSED = CLOSED
    }
    Map.prototype = {
        get: function (key) {
            return this.data[key + "~"]
        },
        set: function (key, value) {
            this.data[key + "~"] = value
        },
        "delete": function (key) {
            delete this.data[key + "~"]
        }
    }, EventTarget.prototype = {
        dispatchEvent: function (event) {
            var type = String(event.type),
                listeners = this.listeners,
                typeListeners = listeners.get(type);
            if (!typeListeners) return;
            var length = typeListeners.length,
                i = -1;
            while (++i < length) {
                var listener = typeListeners[i];
                try {
                    listener.call(this, event)
                } catch (e) {
                    throwError(e)
                }
            }
        },
        addEventListener: function (type, callback) {
            type = String(type);
            var listeners = this.listeners,
                typeListeners = listeners.get(type);
            typeListeners || listeners.set(type, typeListeners = []);
            var i = typeListeners.length;
            while (--i >= 0) if (typeListeners[i] === callback) return;
            typeListeners.push(callback)
        },
        removeEventListener: function (type, callback) {
            type = String(type);
            var listeners = this.listeners,
                typeListeners = listeners.get(type);
            if (!typeListeners) return;
            var length = typeListeners.length,
                filtered = [],
                i = -1;
            while (++i < length) typeListeners[i] !== callback && filtered.push(typeListeners[i]);
            filtered.length === 0 ? listeners["delete"](type) : listeners.set(type, filtered)
        }
    }, MessageEvent.prototype = Event.prototype;
    var XHR = global.XMLHttpRequest,
        XDR = global.XDomainRequest,
        xhr2 = Boolean(XHR && (new XHR).withCredentials !== undefined),
        isXHR = xhr2,
        Transport = xhr2 ? XHR : XDR,
        WAITING = -1,
        CONNECTING = 0,
        OPEN = 1,
        CLOSED = 2,
        contentTypeRegExp = /^text\/event\-stream;?(\s*charset\=utf\-8)?$/i,
        webkitBefore535 = /AppleWebKit\/5([0-2][0-9]|3[0-4])[^\d]/.test(navigator.userAgent);
    F.prototype = EventTarget.prototype, EventSource.prototype = new F, F.call(EventSource), Transport && (global.EventSource = EventSource)
 })(this),
function () {
    function record(name) {
        (new Image).src = "http://xkcd.com/events/" + name
    }
    function log() {
        location.hash == "#verbose" && console.log.apply(console, arguments)
    }
    try {
        var esURL = "http://c0.xkcd.com/stream/comic/time?method=EventSource",
            source = new EventSource(esURL);
        log("connecting to event source:", esURL), source.addEventListener("open", function (ev) {
            record("connect_start")
        }, !1), source.addEventListener("error", function (ev) {
            record("connect_error")
        }, !1), source.addEventListener("loadtest", log, !1), source.addEventListener("comic/time", log, !1), source.addEventListener("comic/time", function (ev) {
            var data = JSON.parse(ev.data),
                img = document.getElementById("comic").getElementsByTagName("img")[0],
                delay = Math.round(Math.random() * data.spread);
            log("waiting", delay, "seconds before displaying comic", data.image), setTimeout(function () {
                img.src = "http://imgs.xkcd.com/comics/time/" + data.image
            }, delay * 1e3)
        }, !1)
    } catch (e) {
        record("js_error")
    }
}();

79.180.173.88 09:48, 25 March 2013 (UTC)

http://imgs.xkcd.com/comics/time/426033682a26a0012a6f8e0c47287af91b7991a852d81c77402c937ffbd650c6.png

http://www.explainxkcd.com/wiki/images/1/1e/f46c6571393bee1ee649a7daae41f6328e63482506aef1e22607d22c47dd7027.png --Johnsmith (talk) 22:51, 25 March 2013 (UTC)

http://www.explainxkcd.com/wiki/images/b/b0/88e3a0c8bba935c669606d9134314f811a0961985f968dd5d329e4695acc67c8.png --Johnsmith (talk) 23:10, 25 March 2013 (UTC)


Is it just me or or did Randall manage to make all of us perform a Denial of Service on xkcd.com, and explainxkcd.com ? xkcd.com seems much slower, and I keep getting "500 Internal server error" when accessing this site (explainxkcd.com). I guess that's the effect of having everybody hit F5 every few minutes :) 193.239.192.194 11:57, 25 March 2013 (UTC)

Earlier today, the server handled all the image redirections. The script you see above went through several mutations (currently at #8), with each mutation it seems that Randall is adding more servers and trying to split the load between them. This is basically how a bot-net works - we all run code written by some evil genius, and he's changing the code as time passes to serve some hidden purpose. 79.180.173.88 15:44, 25 March 2013 (UTC)

   If he is using us as a botnet, then maybe the next comic will be something alluding to that.
   Probably like this: http://xkcd.com/350/

When I saw this comic last night and that there was no explanation up, I thought to myself "How zen." I figured that Randall was going through a calm streak before throwing us the utterly ridiculous April 1st comic. Did it come early, or does he have something even bigger planned for us? 76.106.251.87 07:05, 26 March 2013 (UTC)

Sorry, did you miss the bit where this comic updates every 30 minutes and all the server error messages being caused by the massive traffic to both the wiki and the main xkcd website? Davidy²²[talk] 07:08, 26 March 2013 (UTC)
Well, when I said "last night" and "no explanation", I implied that I wasn't aware of that at the time, which is why I thought what I did. Of course, it is now "now" and there is an explanation, so that should answer your question. Also, since it's not April 1st, and Randall has consistently released something major on that day, the jury is still out, leaving my question quite open (though I was really only asking for opinions). 76.106.251.87 07:20, 26 March 2013 (UTC)