31

Here; have the full, uncompressed source code for Unicoin you code monkeys!

http://dev.stackoverflow.com/content/js/stub-uc.js

This section looks pretty interesting:

var loadingVersions = {};
var powerupDeferreds = {};
function registerPowerup(id, factory, noNeedToCache) {
    var def = powerupDeferreds[id];
    var pu = factory();
    pu.id = id;
    if (pu.inherits) {
        loadPowerup(pu.inherits, abstractCacheBreakers[pu.inherits.substring(0, 4)] || "0").done(function () {
            var parent = registeredPowerups[pu.inherits];
            for (var k in parent) if (parent.hasOwnProperty(k) && !pu.hasOwnProperty(k))
                pu[k] = parent[k];
            registeredPowerups[id] = pu;
            def.resolve();
        });
    } else {
        registeredPowerups[id] = pu;
        def.resolve();
    }
    if (!noNeedToCache) {
        var val = factory.toString();
        var ver = loadingVersions[id];
        if (ver) {
            val = ver + ":" + val;
            delete loadingVersions[id];
        }
        ls_set("uc:pu" + id, val);
    }
}

function loadPowerup(id, expectedVersion) {
    var def = powerupDeferreds[id];
    if (def)
        return def;
    powerupDeferreds[id] = def = $.Deferred();
    var cached = ls_get("uc:pu" + id);
    if (cached) {
        var cachedVersion = "0";
        cached = cached.replace(/^(\w+):/, function (whole, ver) { cachedVersion = ver; return "" });
        if (cachedVersion === expectedVersion) {
            // indirect call so we don't inerit this scope (at least in modern browsers)
            registerPowerup(id, eval.call(window, ("(" + cached + ")")), true);
            return def;
        }
    }
    if (expectedVersion !== "0")
        loadingVersions[id] = expectedVersion;
    StackExchange.loadJsFile("unicoin/powerups/" + id + ".js?v=" + expectedVersion, /* doNotLocalize=*/true);
    return def;
}

function loadAndRunPowerup(id, option) {
    var def = $.Deferred();
    var version = "0";
    id = id.replace(/\?(\w*)$/, function (whole, ver) { version = ver; return "" });
    loadPowerup(id, version).done(function () {
        var pu = registeredPowerups[id];
        if (pu.setOption)
            pu.setOption(option);
        pu.enable();
        pu.enabled = true;
        def.resolve(pu);
    });
    return def.promise();
}

function loadAndRunPowerups(powerups) {
    for (var i = 0; i< powerups.length; i++) {
        loadAndRunPowerup.apply(null, powerups[i]);
    }
}

I think the key here is

StackExchange.loadJsFile("unicoin/powerups/" + id + ".js?v=" + expectedVersion, /* doNotLocalize=*/true);

If I'm right and we can figure out what the ids are, then

  • We can illicitly gain powerups without buying them!
  • We can save the powerup source code and use them even after April Fools' Day is over!
    • This means that UNICORN VOTING ANIMATIONS CAN BE HERE FOREVAR!!!

So what are you waiting for, all you JS coders? Help me hack Unicoin so it can be here forever!

  • That deferred anti pattern hurts my eyes :( Seriously - what's wrong with return loadPowerUp(id,version).then(function(){ ... })? What's the point of using deferred objects and promises if you're not using them correctly and are using them as callbacks? – Benjamin Gruenbaum Apr 1 '14 at 21:10
  • 5
    Why don't you just look at the source code? That's what the developers where I work do. – Nick Craver Apr 1 '14 at 21:37
  • @NickCraver I tried. (And I finally succeeded; see my answer below :D) – Doorknob Apr 1 '14 at 21:37
  • Apologies for extreme n00bishness, but why can't you just add // ==UserScript== // @name [Name here] // @namespace [stuff here] // @description [stuff here] // @include [ALL TEH SITEZ] // @version [number here] // @grant [stuff here] // ==/UserScript== to the top of stub-uc.js and call it a UserScript? – The Guy with The Hat Apr 1 '14 at 22:01
  • 2
    This question appears to be off-topic because it is about programming. :-P – mhlester Apr 2 '14 at 4:22
  • @TheGuywithTheHat Unicorn Voting 2014 Chrome user script extension. – Jason Sturges Apr 2 '14 at 5:40
  • @JasonSturges ...isn't that exactly the same thing as what I posted on Stack Apps? Why are you spamming that all over my posts? – Doorknob Apr 2 '14 at 12:10
  • 1
    @Doorknob It's wrapped to enable Google Chrome extension - your user script as is didn't work. – Jason Sturges Apr 2 '14 at 14:25
20

UPDATE: Yes! Victory for reverse-engineering of the unicorn voting animations!

UPDATE 2: I made a userscript! Find it here on Stack Apps!

Here I shall document my progress so far.

This URL: http://meta.stackoverflow.com/unicoin/available-powerups looks promising....

{"powerups":[{"name":"Voting animations","useid":"f1bb643faf2c4382a56a7dfb945269c8","purchaseid":"9612a9430ef7ef2f6b3a064a83260996","cost":99,"owned":true,"description":"Happy unicorn animations every time you vote. You know you want it."},{"name":"No downvotes, please","useid":"b4519d83340a4064adc84e0fe0913b14","purchaseid":"53446efe7a8a25df46c27dda89fa22f5","cost":80,"owned":true,"description":"Remove the downvote arrows from all your questions and answers."},{"name":"Bobblehead","useid":"de274e4442324c499defc115544c2983","purchaseid":"79391fd64d184dd224ac79d0a01d02e4","cost":75,"owned":true,"description":"All the users too stiff for your taste? You can change that!"},{"name":"Colorful comments","useid":"a91005683eaa4617834345d09fda1399","purchaseid":"8cef362f3bf6cbf54bd009be5baf4c93","cost":30,"owned":true,"description":"Make comments more interesting by giving them personality."},{"name":"No sidebar widget","purchaseid":"eec5b866ea0da6041ae33e88ffcdab64","cost":20,"owned":false,"description":"Access Unicoins through the link in the footer instead."},{"name":"Cut everyone's rep","purchaseid":"734257255700bda6d877c1bf888f45e7","cost":45,"owned":false,"description":"Divide everybody's reputation (except your own) by ten."},{"name":"Adopt-a-tag","purchaseid":"f29f250e83fb902cb58dba7a5c7d61c0","cost":50,"owned":false,"description":"Choose a tag and have your own avatar appear on it everywhere."},{"name":"Guaranteed answer","purchaseid":"b656db0c5c4ab9a4a39cb916de2a63b8","cost":50,"owned":false,"description":"No more need to wait: We guarantee you an answer on all of your questions."},{"name":"Marquee","purchaseid":"f50d472beed81e0160dbf60bd1a15cd9","cost":55,"owned":false,"description":"Make question titles livelier by scrolling them like a ticker."},{"name":"Because unicorns","purchaseid":"aeac6a6e7d86cae21a4d925daf5a9fb1","cost":60,"owned":false,"description":"\"Closed because unicorns\" is the only valid close reason."},{"name":"Purple Q&A","purchaseid":"e352e537edd73af633e987f9300208f2","cost":65,"owned":false,"description":"Make your posts stand out with a distinguished shade of purple."},{"name":"Top bar color chooser","purchaseid":"22a2577a4838be35438146d5d05e7b4b","cost":80,"owned":false,"description":"Dark gray too boring for you? Customize your top bar's color scheme."},{"name":"Reputation to the max","purchaseid":"b9e0a8c95e4feedf11a882fcb47561e0","cost":90,"owned":false,"description":"Increase your own reputation by a settable factor."}]}

Yes yes yes! When I go to http://meta.stackoverflow.com/content/js/unicoin/powerups/f1bb643faf2c4382a56a7dfb945269c8.js, I see this:

StackExchange.uc.registerPowerup("f1bb643faf2c4382a56a7dfb945269c8",function(){return{"enable":function(){function e(e){var t=$.Deferred(),n=$("<img />").attr("src",p+e).css({"position":"fixed","top":0,"left":0,"width":1,"height":1}).appendTo("body");if(n[0].complete)n.remove(),t.resolve();else{var i=!1;n.bind("load readystatechange",function a(e){(this.complete||"complete"==this.readyState&&"readystatechange"==e.type)&&(i||(i=!0,n.unbind("load readystatechange",a),n.remove(),t.resolve()))})}return t.promise()}function t(e,t,n,i,a,o){var s=$("<div />").css({"width":i,"height":a,"backgroundImage":"url("+p+e+")","position":"absolute","top":n,"left":t,"display":"none"}).appendTo("body");return o||s.fadeIn(),s.set=function(e){this.css("background-position","0px "+-a*e+"px")},s.finish=function(e){this.fadeOut(function(){$(this).remove(),e&&e()})},s}function n(n){var i="1Fba1.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-100,e.top-40,151,100),o=0,s=setInterval(function(){a.set(o++),11==o&&n.css({"position":"relative"}).animate({"left":400,"top":-100},1e3,"linear").animate({"left":600,"top":50},500,"linear").animate({"left":0,"top":0},1500,"linear",function(){d=!1}),20==o&&(clearInterval(s),a.finish())},150)},o=e(i);return o.run=a,o}function i(n){var i="1XY5D.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-100,e.top-50,172,100),o=0,s=setInterval(function(){a.set(o++),20==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function a(n){var i="cMyYU.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left,e.top+50,80,120),o=0,s=setInterval(function(){a.set(o++),20==o&&(clearInterval(s),setTimeout(function(){a.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}function o(n){var i="4q8j8.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-60,e.top-80,120,101),o=0,s=setInterval(function(){a.set(o++),a.css("left",e.left-60-o),20==o&&(clearInterval(s),setTimeout(function(){a.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}function s(n){var i="cBop8.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-50,e.top-20,125,150),o=0,s=setInterval(function(){o++;var e=o%18;e>9&&(e=17-e),a.set(e),40==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function r(n){var i="tjvfW.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-130,e.top-20,120,117),o=0,s=setInterval(function(){o++;var e=o%29;e>14&&(e=29-e),a.set(e),80==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function c(n){var i="lyX66.png",a=parseInt(n.css("font-size")),o=function(){d=!0;var e=n.offset(),o=t(i,e.left-80,e.top-40,120,109),s=0,r=setInterval(function(){s++;var e=s-3;e>19&&(e=18-(e-20)%8,15>e&&(e=30-e)),13==s&&n.animate({"fontSize":0}),o.set(Math.max(0,e)),40==s&&(clearInterval(r),n.animate({"font-size":a},1e3),o.finish(function(){d=!1}))},150)},s=e(i);return s.run=o,s}function l(n){var i="xBS3c.png",a=function(){d=!0,n.css({"position":"relative"});var e=n.offset(),a=$(window).width()-140,o=t(i,a,e.top-20,140,102),s=0,r=setInterval(function(){s++,a-=20,a<e.left+30&&n.css("left",a-e.left-30),o.set(s%15),o.css("left",a),-140>a&&(clearInterval(r),setTimeout(function(){n.hide().css("left",0).fadeIn("slow")},2e3),o.finish(function(){d=!1}))},50)},o=e(i);return o.run=a,o}function u(n){var i="RXgxY.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left+300,e.top-130,100,114),o=0,s=setInterval(function(){a.set(o++),18==o&&(horn=t(i,e.left+290,e.top-40,15,12,!0),horn.set(159),horn.show().animate({"left":e.left+30,"top":e.top+15},700,"linear")),20==o&&(clearInterval(s),setTimeout(function(){a.finish(),horn.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}var d=!1,p="//i.stack.imgur.com/";window.evaluator=function(e,t){if(d)return null;var p,f=[o,r,a,i],h=[c,l,u,s,n],g=t?f:h;return p=g[Math.floor(Math.random()*g.length)](e),p&&!p.cancel&&(p.cancel=function(){}),p}},"disable":function(){delete window.evaluator}}});

More reverse-engineering is needed.... >:O

So here's the functions for unicorn animations when voting....

function n(n){var i="1Fba1.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-100,e.top-40,151,100),o=0,s=setInterval(function(){a.set(o++),11==o&&n.css({"position":"relative"}).animate({"left":400,"top":-100},1e3,"linear").animate({"left":600,"top":50},500,"linear").animate({"left":0,"top":0},1500,"linear",function(){d=!1}),20==o&&(clearInterval(s),a.finish())},150)},o=e(i);return o.run=a,o}function i(n){var i="1XY5D.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-100,e.top-50,172,100),o=0,s=setInterval(function(){a.set(o++),20==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function a(n){var i="cMyYU.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left,e.top+50,80,120),o=0,s=setInterval(function(){a.set(o++),20==o&&(clearInterval(s),setTimeout(function(){a.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}function o(n){var i="4q8j8.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-60,e.top-80,120,101),o=0,s=setInterval(function(){a.set(o++),a.css("left",e.left-60-o),20==o&&(clearInterval(s),setTimeout(function(){a.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}function s(n){var i="cBop8.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-50,e.top-20,125,150),o=0,s=setInterval(function(){o++;var e=o%18;e>9&&(e=17-e),a.set(e),40==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function r(n){var i="tjvfW.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left-130,e.top-20,120,117),o=0,s=setInterval(function(){o++;var e=o%29;e>14&&(e=29-e),a.set(e),80==o&&(clearInterval(s),a.finish(function(){d=!1}))},100)},o=e(i);return o.run=a,o}function c(n){var i="lyX66.png",a=parseInt(n.css("font-size")),o=function(){d=!0;var e=n.offset(),o=t(i,e.left-80,e.top-40,120,109),s=0,r=setInterval(function(){s++;var e=s-3;e>19&&(e=18-(e-20)%8,15>e&&(e=30-e)),13==s&&n.animate({"fontSize":0}),o.set(Math.max(0,e)),40==s&&(clearInterval(r),n.animate({"font-size":a},1e3),o.finish(function(){d=!1}))},150)},s=e(i);return s.run=o,s}function l(n){var i="xBS3c.png",a=function(){d=!0,n.css({"position":"relative"});var e=n.offset(),a=$(window).width()-140,o=t(i,a,e.top-20,140,102),s=0,r=setInterval(function(){s++,a-=20,a<e.left+30&&n.css("left",a-e.left-30),o.set(s%15),o.css("left",a),-140>a&&(clearInterval(r),setTimeout(function(){n.hide().css("left",0).fadeIn("slow")},2e3),o.finish(function(){d=!1}))},50)},o=e(i);return o.run=a,o}function u(n){var i="RXgxY.png",a=function(){d=!0;var e=n.offset(),a=t(i,e.left+300,e.top-130,100,114),o=0,s=setInterval(function(){a.set(o++),18==o&&(horn=t(i,e.left+290,e.top-40,15,12,!0),horn.set(159),horn.show().animate({"left":e.left+30,"top":e.top+15},700,"linear")),20==o&&(clearInterval(s),setTimeout(function(){a.finish(),horn.finish(function(){d=!1})},2e3))},100)},o=e(i);return o.run=a,o}

And the main function used for all of them:

function t(e,t,n,i,a,o){var s=$("<div />").css({"width":i,"height":a,"backgroundImage":"url("+p+e+")","position":"absolute","top":n,"left":t,"display":"none"}).appendTo("body");return o||s.fadeIn(),s.set=function(e){this.css("background-position","0px "+-a*e+"px")},s.finish=function(e){this.fadeOut(function(){$(this).remove(),e&&e()})},s}

Let's beautify that:

function t(e, t, n, i, a, o) {
    var s = $("<div />").css({
        "width": i,
        "height": a,
        "backgroundImage": "url(" + p + e + ")",
        "position": "absolute",
        "top": n,
        "left": t,
        "display": "none"
    }).appendTo("body");
    return o || s.fadeIn(), s.set = function (e) {
        this.css("background-position", "0px " + -a * e + "px")
    }, s.finish = function (e) {
        this.fadeOut(function () {
            $(this).remove(), e && e()
        })
    }, s
}

function n(n) {
    var i = "1Fba1.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 100, e.top - 40, 151, 100),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), 11 == o && n.css({
                        "position": "relative"
                    }).animate({
                        "left": 400,
                        "top": -100
                    }, 1e3, "linear").animate({
                        "left": 600,
                        "top": 50
                    }, 500, "linear").animate({
                        "left": 0,
                        "top": 0
                    }, 1500, "linear", function () {
                        d = !1
                    }), 20 == o && (clearInterval(s), a.finish())
                }, 150)
        }, o = e(i);
    return o.run = a, o
}

function i(n) {
    var i = "1XY5D.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 100, e.top - 50, 172, 100),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), 20 == o && (clearInterval(s), a.finish(function () {
                        d = !1
                    }))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

function a(n) {
    var i = "cMyYU.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left, e.top + 50, 80, 120),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), 20 == o && (clearInterval(s), setTimeout(function () {
                        a.finish(function () {
                            d = !1
                        })
                    }, 2e3))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

function o(n) {
    var i = "4q8j8.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 60, e.top - 80, 120, 101),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), a.css("left", e.left - 60 - o), 20 == o && (clearInterval(s), setTimeout(function () {
                        a.finish(function () {
                            d = !1
                        })
                    }, 2e3))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

function s(n) {
    var i = "cBop8.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 50, e.top - 20, 125, 150),
                o = 0,
                s = setInterval(function () {
                    o++;
                    var e = o % 18;
                    e > 9 && (e = 17 - e), a.set(e), 40 == o && (clearInterval(s), a.finish(function () {
                        d = !1
                    }))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

function r(n) {
    var i = "tjvfW.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 130, e.top - 20, 120, 117),
                o = 0,
                s = setInterval(function () {
                    o++;
                    var e = o % 29;
                    e > 14 && (e = 29 - e), a.set(e), 80 == o && (clearInterval(s), a.finish(function () {
                        d = !1
                    }))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

function c(n) {
    var i = "lyX66.png",
        a = parseInt(n.css("font-size")),
        o = function () {
            d = !0;
            var e = n.offset(),
                o = t(i, e.left - 80, e.top - 40, 120, 109),
                s = 0,
                r = setInterval(function () {
                    s++;
                    var e = s - 3;
                    e > 19 && (e = 18 - (e - 20) % 8, 15 > e && (e = 30 - e)), 13 == s && n.animate({
                        "fontSize": 0
                    }), o.set(Math.max(0, e)), 40 == s && (clearInterval(r), n.animate({
                        "font-size": a
                    }, 1e3), o.finish(function () {
                        d = !1
                    }))
                }, 150)
        }, s = e(i);
    return s.run = o, s
}

function l(n) {
    var i = "xBS3c.png",
        a = function () {
            d = !0, n.css({
                "position": "relative"
            });
            var e = n.offset(),
                a = $(window).width() - 140,
                o = t(i, a, e.top - 20, 140, 102),
                s = 0,
                r = setInterval(function () {
                    s++, a -= 20, a < e.left + 30 && n.css("left", a - e.left - 30), o.set(s % 15), o.css("left", a), -140 > a && (clearInterval(r), setTimeout(function () {
                        n.hide().css("left", 0).fadeIn("slow")
                    }, 2e3), o.finish(function () {
                        d = !1
                    }))
                }, 50)
        }, o = e(i);
    return o.run = a, o
}

function u(n) {
    var i = "RXgxY.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left + 300, e.top - 130, 100, 114),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), 18 == o && (horn = t(i, e.left + 290, e.top - 40, 15, 12, !0), horn.set(159), horn.show().animate({
                        "left": e.left + 30,
                        "top": e.top + 15
                    }, 700, "linear")), 20 == o && (clearInterval(s), setTimeout(function () {
                        a.finish(), horn.finish(function () {
                            d = !1
                        })
                    }, 2e3))
                }, 100)
        }, o = e(i);
    return o.run = a, o
}

And I've done it! :D Run this code on this page and watch the vote button area on my question.

var p = "http://i.stack.imgur.com/"

function t(e, t, n, i, a, o) {
    var s = $("<div />").css({
        "width": i,
        "height": a,
        "backgroundImage": "url(" + p + e + ")",
        "position": "absolute",
        "top": n,
        "left": t,
        "display": "none"
    }).appendTo("body");
    return o || s.fadeIn(), s.set = function (e) {
        this.css("background-position", "0px " + -a * e + "px")
    }, s.finish = function (e) {
        this.fadeOut(function () {
            $(this).remove(), e && e()
        })
    }, s
}

function n(n) {
    var i = "1Fba1.png",
        a = function () {
            d = !0;
            var e = n.offset(),
                a = t(i, e.left - 100, e.top - 40, 151, 100),
                o = 0,
                s = setInterval(function () {
                    a.set(o++), 11 == o && n.css({
                        "position": "relative"
                    }).animate({
                        "left": 400,
                        "top": -100
                    }, 1e3, "linear").animate({
                        "left": 600,
                        "top": 50
                    }, 500, "linear").animate({
                        "left": 0,
                        "top": 0
                    }, 1500, "linear", function () {
                        d = !1
                    }), 20 == o && (clearInterval(s), a.finish())
                }, 150)
        }
    a()
}

n($('.vote-count-post:first'))

Now it's time for a userscript!

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged .