Deezer Analytics

Extended information for Deezer

当前为 2015-11-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         Deezer Analytics
// @namespace    com.deezer.analytics
// @version      1.0
// @description  Extended information for Deezer
// @author       panthera.p
// @match        http://www.deezer.com/*
// @match        https://www.deezer.com/*
// @grant        unsafeWindow
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @require      https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// ==/UserScript==

(function(w, d, $, x)
 {
     var settings = {
         APP_ID: null,
         ACCESS_TOKEN: null,
         PROFILE_ID: null,
         REFRESH_INTERVAL: 3000,
         REFRESH_INTERVAL_BIG: 9000
     };

     var core = {
         variables: {
             ajaxQueue: [],
             ajaxIsRunning: false,
             infoBox: null,
             cache: {}
         },
         init: function()
         {
             GM_addStyle(" \
.deezer-icon-favorite { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABfUlEQVRYhc3WQUtUURjG8aMD0oC5UshF9AWqL6Bgi4LauQna2qYIBANhvkI73aiJK7dKkCKBYPvaD7aJaGXt1JIRJuLXwjtwHJyrc525pwfu7j7v/7963xPC/xiM4yXWsIF1vMFDVHJ6FTzK/l3Pumt4gVtXAY9hBU2dc4AZDES9ATzHj5xeE0sY7QS/j+85A9qzi2HcxF4XvW+42w6/g59dDGnlEz4X6B3gdizwscCQ62a3BZ9KAG9lMmA1ocBywH5CgXrAr4QCRwEnCQV+B3xJKFAPeJdQYCPgWUKBpwEjOEoAP8RwaxnNJxB4Ha/iqrMjUVa+4sZF17BRAryBe51O8lwJArOXPUoW+whfyIVnAoP6sxs2MXipQCYxhO0ewrcwdCV4m8RWD+Dvu4ZHElV8uAZ8B9VC8EiigrcF4CtynvBFRGpdwGs9A7dJvMLfHPAfzPQFHklM4/QC+Cmm+wqPJJ44v7YbeFwKPJKYwHH2TZQKjyQeYCoJvFf5B9P0m1799iUfAAAAAElFTkSuQmCC'); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-global { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADMklEQVRYhcXX3YtWVRQG8JPWzNQwmPaBkpEfpU6Z3RSVCV1Y0J1FWEl0IUF/QReJXVSCXUhBXzRORkR0EQWFEYFFkRVTmUlggmBokuNIYFRWM+nMr4uzXt7NmXPOe963ixaci3P2s9bz7HXWXnvvLGtoGMT9eAVf4SROx3MSYxjFfbioadwmxEvxMv7Q3H7HS1jyX4gHsB1TEXQGn2IL1mIJ5sezCOvxWGBmwmcS29DfLfkyHIgg05H2VR18bsETWIibExGwv3E2sCb+KZzCbR3wF+PdhGwKv5b8lnGs6US+MkhTu7EGP4DvSqug3E5hRVWwQRwqOHzQQfBCbMQzXYg4qGyV4LkC8CiuqCC+QF5wV8X7O10IgGeLAYdxVl5wv+ANLCohnod7sCcC/YPDXZILrpVp4F0x8HqHlG+QV/dpHOmBOLXRdFZ/ReAbasjnYif+xJU4Dy8kAQ9gFR5sKOAMhjI8FB8+7zD7BXgUNxVEvRf+9+IS/NBFFjZleDVenqwgvlReE61WPIXHMSfG+/EArsM18gw1tZ0ZvomXOyrS/mWF8/u4LHBXYwR/d0EOY5m86uFbDBcE3NUhwG/4UHezTm0i095saFVmW0A3DaYXm8zka5m8TS4uCNhV7Vtrz2MH9sazDz+avUdMpr9ga0kNbO5RwFD4D0u2YszBmwluIi3C9SUC+vB1DwKO4Dh+Rl9JXU0HbixN86wMhMOQvBZ+6kHIIyXxzseLMT6SYVO81DaicJ4vX+9fNCD/HnMr4uwNzMbWDFvLaHUnERFgsXbxVtmsvhK+w/K2fwaDrY+j4fRaEwHh83EN+e4av9YvH0k/Lpf3g2nc2lDA0xXkM7i2wmddcExiaXFwewQ4hHkNBDxcIWBPBX6B9ha+rQzQLz+9wkcKy6cEf3uFgA0l2D58EuP7KmPLz/rjAfxMbDYV2CGcK5BP4cIC7vKIBSfEMa5uZumx/BjW1mB3FwRMFMbXafePcVxfS544pheTGbyFZSW4PnnfPxvYg/F9Od7Wvpw0v5gkwYtXs2l5234Kd2JFYAZwt7zx7A9Mq9X2djUrCPl/LqclQsqu5+ci/T1fz/8FQaCDbDSez/AAAAAASUVORK5CYII='); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-social { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACP0lEQVRYhbXWzYtPURzH8XlozJRnG6NZeKiZkkQsCCtkQxaeN2bKw4bwD6BhFspDsRMlpUmyEnaUKPKQyUaSUohJEiXTzJh5WfzO5Lrdufeemfl96m7u+X6/n/e553vuOTU1Eyg0YjW2YTOmT2T9IvO9+Ox/9eEcGqttfly+7qCuWuZtGCgAgPaxGszCTnTiJDrQkhifjPslAJ7EGtfiMH5mFOvHWWFtMQNfCgD6URsD0FliVreEtUV7ifhyuwJLMVSiIOwPOQ34lhM3oGwj4kJJc+hJ5F3LiXsf8/mfRQAMYUrIO5YTdykG4EMEALSGvH05MRtjAF5HAswOeUdGGX9a2jwUuhdh3idsL5zOGB/AqliAoxEAdxN5tzPGT0WZh0KLIgA6Qs7U8DXS2hINkDObtN6iIcTvHiXmAVaOBWAZBgsAdoTYesWN+0pll9THQBzIKXg+EbenwDypHqyJgUhfMmBY+K9jvuwDK09D6CoL8CajwC//tl4rrshuwCIdLDJfEmabpRWp2GZcVNw3SX0azbgFJ/A1J7lX5SrWkspdqPxZ8idt3IyrkbMYxHUsSNSZhO4SuY9HEmpxCD8ijNPqxxk0hZp1KpfR0fQSbSMXiZvjME7rOaYFiDn4Hd734iG6sM7I9Uz2ATJe3Ugta1Nep3+vAsAwZuZusQRATMPFaG5ZgEdVMP+o7BUc8/BiAs3fYXkp8wREHTbhssqpFbMswyrHcjd2yWu4CKAGlX/8WqzHVmxPPBvC+8XjNfwLBQOVu7EhXnoAAAAASUVORK5CYII='); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-play { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABFElEQVRYheXXsStFYRgH4CtKopDJpIzKoMyKxV9gUkaTksmkjBaDUQZlNSgZ7JJBWRVlFYO6yYC6j+Hcb3LCPefc8w5+9c2/p773++ptNNrBCE7RxCcuMd+oK9jwPS0cYqwOwG4OIOUJy5GAlAtMRQLgAzvojwKk3GEhEkA2pEeqGNKCgJRHrEQCUs4wEQmAN2yiNwqQcoPZSADZl76HoShAygMWIwFkT/YAw1GAlHuMRwJg618Dwq4gdAivMRPxDF+xjr4fy7sEOMfkr8VdADxj6c/FFQJa2Mdox+UVAG4xV6i4JOAd2xgoVV4QcIXp0sUFAE2soqey8g4Ax/K+0RoAtaxmaznFtS6ngzjBS/vUsp5/AYRZv/U6zVdUAAAAAElFTkSuQmCC'); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-album { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACfklEQVRYhb3XyW+NURgG8KtCDI1xQTWIKa0h8QdYGZZCbCxE/wJDSrGSWFjZmGKKGIqwNaRFkEYiIcVOwoKIYdNGDIlWlcrP4js3Pjffvd+5t+pNzuac932e537vcO4pFCINU9CCC3iMHnwKqwfdaMdmTI7FjSFeiosYEG8DQeji4RDX4yiGAugXnMQGzMDnCCE/cRjjqyVvwvMA8hlbMLHE530VX+QxGmLJl6M3BHZiVhm/F1UIgHdYlke+UFJQcAKjK/g+qVJAUcTMcoAT8Cw4/kBLBfJReFCDAHiEcVmgBzOcL0u1FObgOD7WSF60/aXkTZKKzbLXWIE9+IbvuIZWrA2rFdcxGCmgT7oocTYysAPzKqRmAW5FYp0oBk1Cf0TAIdSFmEbsxqWwdgndgjrJ/MizPtQXsCnC+UaKvFWSilLrx7aUiJsRuBsLOJPjNIj5KfI8K4pYJOmmSnaqgKc5TlcD4Gx///J2rArrQmq/H40h5kYOdncBH3Kctgaw3am9SxkFeCV13hb2tudg9xbkt866ANae2luZIWB16rw97K3Pwf5eq4DVGQLWpM7PVyMgLwVbAlhbai8vBTuqSUF3jlNHAGvw97woV4R9mBFi8obSw9g2XFRDGzaLbMOYQXRXuJrFDaLR6IrA3VjNKD4ifhQfi8BLRnEIir2MOtGccQ8VcZbgdiTW6XRgpeu41IYkKdnhz3W8E/fwKxJjEAtK1Wf9IRkpO5D1+cbi/n8g78KYcjmcjlcjSP4S08vVUFHEMrwdAfI3WFqRPCVimqTQ/pXdwdQo8pKa2IuvwyAewD7lch4ppEEyrqt9nJ7D3JqJM4RkPc+Hwqr5ef4bS36vBfCJzJ4AAAAASUVORK5CYII='); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-artist { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABoklEQVRYhe3Xv0sXcRzHcQvFzCESnKJsjpZ+iEPaEIJLbuKiRBC0BEVLRA45GJi4ONVqDUGNron+B64qWBSEUBYkQoXho+FCvx33/d7nc9+7Jl/z5/16Poe7z72vpaXioB1DeIb32MI8WquEnsQYXmNbdsbLhp7FXSxitw60NtPNAlsxiDlsBADTmSkKPoNZfCwAbU4Al/GtSXAxAbRhrSR4IYErJcILCYwXBP3GMu7jXjMCNyKgP7CAW+hO9XypSmBT8koO4liDns9lCqxjGv04GthTisAqroVCqxDojQWXLdCdP3EocCiw//3oldwNfej4LwI4h5fY8W++O1hWqhHATfySn/IFMIq9AHhjAckmex2Tkqv2EWYaCUhWtE+BcHiaBW7DAwcPSr1kCQxEwOFOuuA43gYOZwkMR8B/4lS64FVEQZbAacnyEZKJ9PDFCDicqPPsTOXM7f09cyQ9+DACvpkFr+m6jQ+pmV28wYV6Q48jBJ40Eqjp7MElnEd73uGrgfAVdIYIRAfPc+AL6KoEXiMxgiV8lfwBvcMLDFXF/AM5bxpiusPCUgAAAABJRU5ErkJggg=='); background-repeat:no-repeat; background-size:10px 10px; background-position:5px center; } \
.deezer-icon-social-big { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC0UlEQVRYhbWW34tNURTH14wG5bcXIw9+dEfpNnPPWZ+1zzXGw4S8kAdcvKAYL4R/gMmPB+VH8SZKSpI8CW+UKPIj8iJJKYQkUaKZMXM9zLl1XMe5Z5uZXevprLW+n7XX2mdvkVFchUJhgpl1mdl6M1sDTBvN/JnLzLYD74Fqwn4CJwqFwoQxFQd664Tr7bqINI+JeBRFC4H+BgBVM9vyXwKdnZ0zVXWjmR0ADgFbS6XSnNr3jo6OScCtRgDAfV/tJmAP8C0lWR9wvNbbIAimAx8aAPSJSFNu9bjizKpU9arEvTWzLTl2Id+pcM4FwGCOhFVV3SEiArQAnzN8+yXvIAKn8ojH9jSxaxcy/F7n3n5VfegBMFgsFifH4Psz/M7kBgDeeABUgyBoi+N6MvxW+QA89wEol8uz4ri9//B5kFtcRMTMbnoA/JT4eJnZ0bThc84t8QIA9nkA3EjEXUv5fsRLXETEOVf0ANgqItLV1TWF4cun/piu9QbIqKbeXgItIiJmtjnNR1Vvq+ri/wFQYCALQFU3iIhUKpVxOQb3GdBTqVTG+UDszEh4suZnZts8WvYUWOoDUf/IqAJDxP/1KIrmk35hZdkgcDgvwIuUBN8lPnpBELSZ2bm0AWxkqrorUzwMw1JcbVqCctLXOdeqqqcbzU2dvUsVLpVKc4CDwKeM4I9Ab/JxIiKiqos87pJffwg751qB855VDKjqJefcglqeYrE4HrjYKNbM7tVimoDdwFffPiasDzjW3d09Mc7ZDFzP8H8SRdHC2kPiygiE66t6VC6Xp8YDPBv4kWjZHeBwGIbLa0P8rwtkpHY52dbErvy9gC9jADDU3t4+I+uEJQF8Bi63hWE4Ny/A3TEAeCt5n+BBEMwDHo+i+CtVJZd4YjWr6mrgLMO3lk9bhhi+li+a2abMgcu7gJYgCNpUdZlzboWZrQMqCVvpnFsBtI9U8DfK9pKa719G+wAAAABJRU5ErkJggg=='); background-repeat:no-repeat; background-size:20px 20px; background-position:5px center; } \
.deezer-icon-favorite-big { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAByElEQVRYhc2XsWsUQRSHX3IQPIhWClqIf8CocO97wzGskCsUtEszYKuNIggKQv6FdNpolFS2J4IJIgS01160kWAV7UxULhARbW5lPe7W7OVuxx+88s33LQ/ezIr8jwFOANeBVaALPAaWvffnY4yNUX0xxoaZXQCW+z1dYNXMrnnvj/8T3Gq1jgErwB7wa0RtqeoVEZkptM6Y2VXgU0nfHnAfODoUrqpngY8lBwzWhnNuPsuyw2b2skLfppmdHvzyU8DnCofk9Rp4M0bflvf+ZHHmr8Y45KC1ISIi3vuFBPC8zomZPUoo8ECAdwkF3grwNaHAtgDfEwp8E+B90hGY2dOEAl0xs8sJBaK02+0jwHYC+Bfn3Hy+Ce/ULWBmt/+s4hBCE9isUeBDp9M5NOw27NUA7wFnRl3Jt2oQuFn6KAHuTXHud0vh/cxOaTc8EZHZ/QiIc27OzNYnBVfVNefc3L7gRQlVXZuAwLPK8DwhhKaqvjgA/HkIoTkWPE+MsaGqD8eAr5Q94SsHWKoAX5oYeEDiBvCzBPyj/88wvQCLwO4Q+C6wOFV4HjO7xN9ru6eqF2uBFyQyYAfYMbOsVnhBouO9X0gCn1R+A3ZlGm2HugmiAAAAAElFTkSuQmCC'); background-repeat:no-repeat; background-size:20px 20px; background-position:5px center; } \
\
");          if (d.location.protocol == "http:")
                 d.location.href = d.location.href.replace("http:", "https:");
             
             $("html").addClass("deezer");
             settings.APP_ID = w.localStorage.getItem("deezer-appid") || w.prompt("Enter you \"Application ID\" from \"http://developers.deezer.com/myapps\". When creting an application, write \"www.deezer.com\" as \"Application domain\".");
             if (settings.APP_ID == null)
                 return;
             else
                 w.localStorage.setItem("deezer-appid", settings.APP_ID);

             core.createToken();
             setInterval(core.updateToken, 60000);
             setInterval(core.cache, 600000);
             setTimeout(core.load, 2000);
             setInterval(core.getDataCallAjax, 250);
         },
         load: function()
         {
             core.setDefaultProfile();
             core.createInfo();
             core.cache();

             album.init();
             cover.init();
             hearthis.init();
             profile.init();
         },
         createToken: function()
         {
             settings.ACCESS_TOKEN = w.localStorage.getItem("deezer-access-token") || null;

             if (d.location.hash && d.location.hash.indexOf("access_token") > -1)
             {
                 var access_token = d.location.hash.match(/access_token=([^&]+)/i)[1];
                 settings.ACCESS_TOKEN = access_token;
                 w.localStorage.setItem("deezer-access-token", access_token);
             }
             else if (settings.ACCESS_TOKEN == null)
                 d.location.href = "https://connect.deezer.com/oauth/auth.php?app_id=" + settings.APP_ID + "&redirect_uri=" + w.location.protocol + "//" + w.location.host + w.location.pathname + "&perms=basic_access,listening_history&response_type=token";
         },
         updateToken: function(fnFinish)
         {
             var iframe = $('<iframe/>')
             .css({
                 top: -10000,
                 left: -10000,
                 position: "absolute",
             })
             .attr("src", "https://connect.deezer.com/oauth/auth.php?app_id=" + settings.APP_ID + "&redirect_uri=" + w.location.protocol + "//" + w.location.host + w.location.pathname + "&perms=basic_access,listening_history&response_type=token")
             .appendTo("body");

             setTimeout(function()
                        {
                            var access_token = utils.document(iframe).location.hash.match(/access_token=([^&]+)/i)[1];
                            settings.ACCESS_TOKEN = access_token;
                            w.localStorage.setItem("deezer-access-token", access_token);
                            iframe.remove();

                            if (fnFinish)
                                fnFinish();
                        }, 10000);
         },
         setDefaultProfile: function()
         {
             settings.PROFILE_ID = x.USER.USER_ID;
         },
         createInfo: function()
         {
             core.variables.infoBox = $('<div/>')
             .addClass("nav-link")
             .attr("id", "DeezerInfo")
             .text("Loading...")
             .appendTo("#menu_navigation > div");
         },
         cache: function()
         {
             if (settings.PROFILE_ID != null && settings.ACCESS_TOKEN != null)
             {
                 core.getData("Loading albums...", "https://api.deezer.com/user/" + settings.PROFILE_ID + "/albums", function(data) { core.variables.cache.albums = data; });
                 core.getData("Loading artists...", "https://api.deezer.com/user/" + settings.PROFILE_ID + "/artists", function(data) { core.variables.cache.artists = data; });
                 core.getData("Loading followings...", "https://api.deezer.com/user/" + settings.PROFILE_ID + "/followings", function(data) { core.variables.cache.followings = data; core.cacheFollowings(); });
                 core.getData("Loading favorites...", "https://api.deezer.com/user/" + settings.PROFILE_ID + "/tracks", function(data) { core.variables.cache.favorites = data; });
                 core.getData("Loading history...", "https://api.deezer.com/user/" + settings.PROFILE_ID + "/history", function(data) { core.variables.cache.history = data; });
             }
         },
         cacheFollowings: function()
         {
             core.variables.cache.followingsDetail = {};

             var addToAjax = function(profileid, name)
             {
                 core.variables.cache.followingsDetail[profileid] = {};
                 core.getData("Loading albums (" + name + ")...", "https://api.deezer.com/user/" + profileid + "/albums", function(data) { core.variables.cache.followingsDetail[profileid].albums = data; });
                 core.getData("Loading artists (" + name + ")...", "https://api.deezer.com/user/" + profileid + "/artists", function(data) { core.variables.cache.followingsDetail[profileid].artists = data; });
                 core.getData("Loading favorites (" + name + ")...", "https://api.deezer.com/user/" + profileid + "/tracks", function(data) { core.variables.cache.followingsDetail[profileid].favorites = data; });
             };

             for (var i = 0; i < core.variables.cache.followings.length; i++)
                 addToAjax(core.variables.cache.followings[i].id, core.variables.cache.followings[i].name);
         },
         getData: function(info, url, fn, data)
         {
             core.variables.ajaxQueue.push({ info: info, url: url, fn: fn, data: data || [] });
         },
         getDataCallAjax: function()
         {
             if (core.variables.ajaxQueue.length > 0 && !core.variables.ajaxIsRunning)
             {
                 core.variables.ajaxIsRunning = true;
                 var ajaxItem = core.variables.ajaxQueue.splice(0, 1)[0];
                 core.variables.infoBox.text(ajaxItem.info + " (+" + core.variables.ajaxQueue.length + ")");

                 GM_xmlhttpRequest({
                     url: ajaxItem.url + "?limit=10000&access_token=" + settings.ACCESS_TOKEN,
                     onload: function(data)
                     {
                         if (data.responseText.indexOf("OAuthException") == -1)
                         {
                             var json = JSON.parse(data.responseText);
                             if (json.data)
                             {
                                 ajaxItem.data = ajaxItem.data.concat(json.data);
                                 if (json.next)
                                     core.variables.ajaxQueue.push({ info: ajaxItem.info, url: json.next, fn: ajaxItem.fn, data: ajaxItem.data });
                                 else
                                     ajaxItem.fn.call(null, ajaxItem.data);
                             }
                             else
                                 ajaxItem.fn.call(null, json);

                             if (core.variables.ajaxQueue.length == 0)
                                 core.variables.infoBox.text("...");

                             core.variables.ajaxIsRunning = false;
                         }
                         else if (data.responseText.indexOf("OAuthException") > -1)
                         {
                             ajaxItem.fn.call(null, null);
                             if (data.responseText.indexOf("private") == -1)
                             {
                                 setTimeout(function()
                                            {
                                                core.variables.ajaxQueue.push(ajaxItem);
                                                core.variables.ajaxIsRunning = false;
                                            }, 10000);
                             }
                             else
                             {
                                 core.variables.ajaxIsRunning = false;
                             }
                         }
                     },
                     onerror: function(error)
                     {
                         alert(error);
                     }
                 });
             }
         }
     };

     var album = {
         init: function()
         {
             GM_addStyle(" \
#AlbumRating { position:absolute; left:270px; top:120px; } \
#AlbumFriendsRating { position:absolute; left:530px; top:120px; } \
#AlbumFriendsCount { position:absolute; left:790px; top:120px; width:50px; height:40px; line-height:40px; text-align:center; padding-left:30px; font-weight:bold; color:#333333; font-size:16px; } \
div.deezer-albumrating { width:220px; height:40px; line-height:40px; text-align:center; padding-left:30px; } \
span.deezer-albumrating-counter { font-weight:bold; color:#333333; font-size:16px; } \
div.deezer-albumrating-meter { position:absolute; bottom:0; left:0; right:0; height:3px; background-color:#aaaaaa; } \
#AlbumRating div.deezer-albumrating-meter-fill { position:absolute; top:0; left:0; height:3px; background-color:#67a91f; } \
#AlbumFriendsRating div.deezer-albumrating-meter-fill { position:absolute; top:0; left:0; height:3px; background-color:#ede045; } \
span.deezer-album-track-rating { background-color:#67a91f; padding:2px 5px 2px 20px; color:#ffffff; margin-right:10px; border-radius:2px; font-weight:bold; } \
span.deezer-album-track-rating.deezer-album-track-inactive { background-color:#666666; } \
span.deezer-album-track-friendscount { background-color:#bcaf12; padding:2px 5px 2px 20px; color:#ffffff; margin-right:10px; border-radius:2px; font-weight:bold; } \
span.deezer-album-track-friendscount.deezer-album-track-inactive { background-color:#666666; } \
");
             setInterval(album.updateFriends, settings.REFRESH_INTERVAL_BIG);
             setInterval(album.updateScore, settings.REFRESH_INTERVAL);
             setInterval(album.updateTracks, settings.REFRESH_INTERVAL_BIG);
         },
         calculateRating: function(tracksNumber, totalTracks)
         {
             var rating = Math.pow(tracksNumber, 1 + Math.pow(totalTracks, -0.7)) / totalTracks;
             return rating > 1.0 ? 1.0 : rating;
         },
         updateScore: function()
         {
             if ($("#page_naboo_album").length == 1)
             {
                 var lovedTracks = $("a[data-target='lovetrack'].selected").length;
                 var allTracks = $("a[data-target='lovetrack']").length;

                 var albumRatingBox = $("#AlbumRating");
                 if (albumRatingBox.length == 0)
                 {
                     albumRatingBox = $('<div/>')
                     .attr("id", "AlbumRating")
                     .addClass("deezer-albumrating deezer-icon-favorite-big")
                     .appendTo("#naboo_catalog_heading");

                     var counter = $('<span/>')
                     .addClass("deezer-albumrating-counter")
                     .appendTo(albumRatingBox);

                     var meter = $('<div/>')
                     .addClass("deezer-albumrating-meter")
                     .appendTo(albumRatingBox);
                     $('<div/>')
                     .addClass("deezer-albumrating-meter-fill")
                     .appendTo(meter);
                 }

                 var rating = album.calculateRating(lovedTracks, allTracks);
                 if (rating == 0)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("TBD");
                 else if (rating > 0 && rating < 0.20)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("POOR (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.20 && rating < 0.4)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("AVERAGE (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.4 && rating < 0.6)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("GOOD (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.6 && rating < 0.8)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("EXCELLENT (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.8)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("OVERWHELMING (" + Math.round(rating * 100) + ")");
                 albumRatingBox.find("div.deezer-albumrating-meter-fill").width(rating * albumRatingBox.outerWidth());
             }
         },
         updateFriends: function()
         {
             if ($("#page_naboo_album").length == 1 && core.variables.cache.albums && core.variables.cache.favorites && core.variables.cache.followings && core.variables.cache.followingsDetail)
             {
                 var id = $("figure[data-id]").data("id");
                 var allTracks = $("a[data-target='lovetrack']").length;
                 var friendsTotal = 0;
                 var friendsRating = 0.0;
                 var friendsRatingCount = 0;
                 for (var j = 0; j < core.variables.cache.followings.length; j++)
                 {
                     if (core.variables.cache.followingsDetail[core.variables.cache.followings[j].id] && core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].albums)
                     {
                         var friendsAlbum = core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].albums.filter(function(a) { return a.id == id; });
                         friendsTotal += friendsAlbum.length;

                         if (core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites)
                         {
                             var friendsFavorites = core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites.filter(function(a) { return a.album.id == id; });
                             if (friendsFavorites.length > 0)
                             {
                                 friendsRating += album.calculateRating(friendsFavorites.length, allTracks);
                                 friendsRatingCount++;
                             }
                         }
                     }
                 }

                 var albumRatingBox = $("#AlbumFriendsRating");
                 var albumCountBox = $("#AlbumFriendsCount");
                 if (albumRatingBox.length == 0)
                 {
                     albumRatingBox = $('<div/>')
                     .attr("id", "AlbumFriendsRating")
                     .addClass("deezer-albumrating deezer-icon-social-big")
                     .appendTo("#naboo_catalog_heading");

                     var counter = $('<span/>')
                     .addClass("deezer-albumrating-counter")
                     .appendTo(albumRatingBox);

                     var meter = $('<div/>')
                     .addClass("deezer-albumrating-meter")
                     .appendTo(albumRatingBox);
                     $('<div/>')
                     .addClass("deezer-albumrating-meter-fill")
                     .appendTo(meter);

                     albumCountBox = $('<div/>')
                     .attr("id", "AlbumFriendsCount")
                     .addClass("deezer-icon-social-big")
                     .appendTo("#naboo_catalog_heading");

                     $('<span/>')
                     .addClass("deezer-albumrating-counter")
                     .appendTo(albumCountBox);

                     $('<div/>')
                     .addClass("deezer-albumrating-meter")
                     .appendTo(albumCountBox);
                 }

                 var rating = (friendsRatingCount > 0 ? friendsRating / friendsRatingCount : 0);
                 if (rating == 0)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("TBD");
                 else if (rating > 0 && rating < 0.20)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("POOR (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.20 && rating < 0.4)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("AVERAGE (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.4 && rating < 0.6)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("GOOD (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.6 && rating < 0.8)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("EXCELLENT (" + Math.round(rating * 100) + ")");
                 else if (rating >= 0.8)
                     albumRatingBox.find("span.deezer-albumrating-counter").text("OVERWHELMING (" + Math.round(rating * 100) + ")");
                 albumRatingBox.find("div.deezer-albumrating-meter-fill").width(rating * albumRatingBox.outerWidth());

                 albumCountBox.find("span.deezer-albumrating-counter").text(friendsTotal);
             }
         },
         updateTracks: function()
         {
             if ($("#page_naboo_album").length == 1 && core.variables.cache.history)
             {
                 var tracks = $("table.datagrid-table tbody tr.song");
                 for (var i = 0; i < tracks.length; i++)
                 {
                     var track = tracks.eq(i);
                     var trackid = track.attr("id").match(/naboo_datagrid_track_([\d]+)/)[1];

                     var ratingElement = track.find("span.deezer-album-track-rating");
                     var friendsCountElement = track.find("span.deezer-album-track-friendscount");
                     if (ratingElement.length == 0)
                     {
                         friendsCountElement = $('<span/>')
                         .addClass("deezer-album-track-friendscount deezer-icon-favorite")
                         .prependTo(track.find("td.track div.wrapper"));
                         ratingElement = $('<span/>')
                         .addClass("deezer-album-track-rating deezer-icon-play")
                         .prependTo(track.find("td.track div.wrapper"));
                     }

                     var friendsTotal = 0;
                     for (var j = 0; j < core.variables.cache.followings.length; j++)
                     {
                         if (core.variables.cache.followingsDetail[core.variables.cache.followings[j].id] && core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites)
                         {
                             var friendsTracks = core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites.filter(function(a) { return a.id == trackid; });
                             friendsTotal += friendsTracks.length;
                         }
                     }

                     var trackCount = core.variables.cache.history.filter(function(a) { return a.id == trackid; }).length;
                     ratingElement.text(trackCount);
                     if (trackCount == 0)
                         ratingElement.addClass("deezer-album-track-inactive");
                     else
                         ratingElement.removeClass("deezer-album-track-inactive");
                     friendsCountElement.text(friendsTotal);
                     if (friendsTotal == 0)
                         friendsCountElement.addClass("deezer-album-track-inactive");
                     else
                         friendsCountElement.removeClass("deezer-album-track-inactive");
                 }
             }
         }
     };

     var hearthis = {
         init: function()
         {
             GM_addStyle(" \
section.card-edito-track { display:none; } \
section.card-radio { display:none; } \
section.card-algo-playlist { display:none; } \
section.card-edito-playlist { display:none; } \
section.card-auto-promo { display:none; } \
section.card-sponsor { display:none; } \
");
         }
     };

     var profile = {
         init: function()
         {
             GM_addStyle(" \
#profile_page_tab section[data-target='playlists'] { display:none; } \
#profile_page_tab section[data-target='albums'] { display:none; } \
#profile_page_tab section[data-target='artists'] { display:none; } \
#profile_page_tab section[data-target='following'] { display:none; } \
#profile_page_tab section[data-target='follower'] { display:none; } \
");
         }
     };

     var cover = {
         init: function()
         {
             GM_addStyle(" \
span.sent-to-mobile { display:none; } \
span.deezer-profile-album-rating { position:absolute; right:10px; bottom:10px; background-color:#67a91f; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-profile-album-rating.deezer-profile-album-inactive { background-color:#666666; } \
span.deezer-profile-album-friendscount { position:absolute; right:10px; bottom:60px; background-color:#46a2ec; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-profile-album-friendscount.deezer-profile-album-inactive { background-color:#666666; } \
span.deezer-profile-album-friendsrating { position:absolute; right:10px; bottom:35px; background-color:#bcaf12; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-profile-album-friendsrating.deezer-profile-album-inactive { background-color:#666666; } \
span.deezer-profile-album-globalcount { position:absolute; right:10px; bottom:85px; background-color:#46a2ec; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-profile-album-genre { position:absolute; right:10px; bottom:191px; background-color:#ab1d81; width:164px; height:14px; overflow:hidden; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-profile-album-genre-item { opacity:0; position:absolute; right:10px; top:3; transition:opacity linear 500ms; } \
span.deezer-profile-album-genre-item-visible { opacity:1; } \
\
span.deezer-fan-album { position:absolute; right:10px; bottom:10px; background-color:#67a91f; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-fan-album.deezer-fan-inactive { background-color:#666666; } \
span.deezer-fan-artist { position:absolute; right:10px; bottom:35px; background-color:#67a91f; height:14px; font-size:12px; padding:2px 10px 2px 20px; color:#ffffff; font-family:'Open Sans',Arial,sans-serif; font-weight:bold; line-height:14px; text-align:right; vertical-align:top; letter-spacing:1px; } \
span.deezer-fan-artist.deezer-fan-inactive { background-color:#666666; } \
");

             if (!core.variables.cache.fans)
                 core.variables.cache.fans = {};
             if (!core.variables.cache.albumsDetail)
                 core.variables.cache.albumsDetail = {};

             setInterval(cover.album, settings.REFRESH_INTERVAL);
             setInterval(cover.fans, settings.REFRESH_INTERVAL);
             setInterval(cover.switchAlbumGenre, settings.REFRESH_INTERVAL / 2);
         },
         album: function()
         {
             if (core.variables.cache.albums && core.variables.cache.favorites && core.variables.cache.followings && core.variables.cache.followingsDetail)
             {
                 var albumElements = $("li[id^='album_']:not(.deezer-profile-album-hasrating), #naboo_tops_container li:not(.deezer-profile-album-hasrating), #discography_artist li:not(.deezer-profile-album-hasrating)");

                 var loadAlbum = function(id, name)
                 {
                     var createElement = function(id)
                     {
                         var figure = $("figure[data-id='" + id + "'], figure[data-reactid*='" + id + "']");
                         var albumItem = core.variables.cache.albumsDetail[id];
                         if (!albumItem) return;
                         var favoriteItems = core.variables.cache.favorites.filter(function(a) { return a.album.id == id; });
                         var rating = album.calculateRating(favoriteItems.length, albumItem.nb_tracks);

                         var friendsTotal = 0;
                         var friendsRating = 0.0;
                         var friendsRatingCount = 0;
                         for (var j = 0; j < core.variables.cache.followings.length; j++)
                         {
                             if (core.variables.cache.followingsDetail[core.variables.cache.followings[j].id] && core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].albums)
                             {
                                 var friendsAlbum = core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].albums.filter(function(a) { return a.id == id; });
                                 friendsTotal += friendsAlbum.length;

                                 if (core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites)
                                 {
                                     var friendsFavorites = core.variables.cache.followingsDetail[core.variables.cache.followings[j].id].favorites.filter(function(a) { return a.album.id == id; });
                                     if (friendsFavorites.length > 0)
                                     {
                                         friendsRating += album.calculateRating(friendsFavorites.length, albumItem.nb_tracks);
                                         friendsRatingCount++;
                                     }
                                 }
                             }
                         }

                         $('<span/>')
                         .addClass("deezer-profile-album-friendscount deezer-icon-social")
                         .addClass(friendsTotal == 0 ? "deezer-profile-album-inactive" : "")
                         .text(friendsTotal)
                         .appendTo(figure);
                         $('<span/>')
                         .addClass("deezer-profile-album-friendsrating deezer-icon-social")
                         .addClass(friendsRatingCount == 0 ? "deezer-profile-album-inactive" : "")
                         .text((friendsRatingCount > 0 ? Math.round(friendsRating / friendsRatingCount * 100) : 0) + "%")
                         .appendTo(figure);
                         $('<span/>')
                         .addClass("deezer-profile-album-rating deezer-icon-favorite")
                         .addClass(rating == 0 ? "deezer-profile-album-inactive" : "")
                         .text(Math.round(rating * 100) + "%")
                         .appendTo(figure);
                         $('<span/>')
                         .addClass("deezer-profile-album-globalcount deezer-icon-global")
                         .text(utils.numberWithCommas(albumItem.fans))
                         .appendTo(figure);
                         var genres = $('<span/>')
                         .addClass("deezer-profile-album-genre")
                         .appendTo(figure);

                         for (var j = 0; j < albumItem.genres.data.length; j++)
                         {
                             $('<span/>')
                             .addClass("deezer-profile-album-genre-item")
                             .addClass(j == 0 ? "deezer-profile-album-genre-item-visible" : "")
                             .text(albumItem.genres.data[j].name)
                             .appendTo(genres);
                         }
                     };

                     if (!core.variables.cache.albumsDetail[id])
                     {
                         core.getData("Loading album (" + name + ")...", "https://api.deezer.com/album/" + id, function(data)
                                      {
                                          core.variables.cache.albumsDetail[id] = data;
                                          createElement(id);
                                      });
                     }
                     else
                         createElement(id);
                 };
                 for (var i = 0; i < albumElements.length; i++)
                 {
                     var li = albumElements.eq(i);
                     li.addClass("deezer-profile-album-hasrating");

                     var figure = li.find("figure");
                     if (albumElements.eq(i).find("figure").data("id"))
                         loadAlbum(albumElements.eq(i).find("figure").data("id"), albumElements.eq(i).find("dt a").text());
                     else if (albumElements.eq(i).find("figure").data("reactid") && (d.location.href.indexOf("charts/news/genre") > -1 || d.location.href.indexOf("charts/album/genre") > -1 || d.location.href.indexOf("artist") > -1))
                         loadAlbum(albumElements.eq(i).find("figure").data("reactid").match(/[$]([\d]+)/)[1], albumElements.eq(i).find("h4 a").text());
                 }
             }
         },
         switchAlbumGenre: function()
         {
             var genres = $("span.deezer-profile-album-genre-item-visible");
             for (var i = 0; i < genres.length; i++)
             {
                 var genre = genres.eq(i);
                 genre.removeClass("deezer-profile-album-genre-item-visible");
                 if (genre.next("span.deezer-profile-album-genre-item").length == 0)
                     genre.parent().children().eq(0).addClass("deezer-profile-album-genre-item-visible");
                 else
                     genre.next("span.deezer-profile-album-genre-item").addClass("deezer-profile-album-genre-item-visible");
             }
         },
         fans: function()
         {
             if (core.variables.cache.albums && core.variables.cache.artists && core.variables.cache.fans)
             {
                 var fans = $("li[id^='user_']:not(.deezer-fan-hasinfo), #naboo_fan_list li:not(.deezer-fan-hasinfo)");

                 var loadFan = function(fanElement, id)
                 {
                     fanElement.addClass("deezer-fan-hasinfo");

                     var renderFan = function(id)
                     {
                         if (!core.variables.cache.fans[id].albums || !core.variables.cache.fans[id].artists)
                         {
                             fanElement.removeClass("deezer-fan-hasinfo");
                             return;
                         }
                         
                         var figure = fanElement.find("figure");
                         var mineAlbums = core.variables.cache.albums.map(function(a) { return a.id; });
                         var fanAlbums = core.variables.cache.fans[id].albums.map(function(a) { return a.id; });
                         var intersectAlbums = mineAlbums.filter(function(a) { return fanAlbums.indexOf(a) > -1; }).length;
                         var mineArtists = core.variables.cache.artists.map(function(a) { return a.id; });
                         var fanArtists = core.variables.cache.fans[id].artists.map(function(a) { return a.id; });
                         var intersectArtists = mineArtists.filter(function(a) { return fanArtists.indexOf(a) > -1; }).length;

                         $('<span/>')
                         .addClass("deezer-fan-album deezer-icon-album")
                         .addClass(intersectAlbums == 0 ? "deezer-fan-inactive" : "")
                         .text((intersectAlbums > 0 ? Math.round(intersectAlbums / mineAlbums.length * 100) : 0) + "%")
                         .appendTo(figure);
                         $('<span/>')
                         .addClass("deezer-fan-artist deezer-icon-artist")
                         .addClass(intersectArtists == 0 ? "deezer-fan-inactive" : "")
                         .text((intersectArtists > 0 ? Math.round(intersectArtists / mineArtists.length * 100) : 0) + "%")
                         .appendTo(figure);
                     };

                     if (!core.variables.cache.fans[id])
                     {
                         core.variables.cache.fans[id] = {
                             albums: [],
                             artists: []
                         };
                         core.getData("Loading fan albums (" + id + ")...", "https://api.deezer.com/user/" + id + "/albums", function(data)
                                      {
                                          core.variables.cache.fans[id].albums = data || [];
                                      });
                         core.getData("Loading fan artists (" + id + ")...", "https://api.deezer.com/user/" + id + "/artists", function(data)
                                                       {
                                                           core.variables.cache.fans[id].artists = data || [];
                                                           renderFan(id);
                                                       });
                     }
                     else
                         renderFan(id);
                 };

                 for (var i = 0; i < fans.length; i++)
                 {
                     var fanElement = fans.eq(i);
                     if (fanElement.find("figure").data("id") && (d.location.href.indexOf("following") > -1 || d.location.href.indexOf("followers") > -1))
                         loadFan(fanElement, fanElement.find("figure").data("id"));
                     else if (fans.eq(i).data("reactid") && d.location.href.indexOf("album") > -1)
                         loadFan(fanElement, fans.eq(i).data("reactid").match(/[$]([\d]+)/)[1]);
                 }
             }
         }
     };

     var utils = {
         numberWithCommas: function (number)
         {
             return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
         },
         isCrossDomain: function(iframeElement)
         {
             // check if passed argument is String or Iframe element
             iframeElement = typeof iframeElement === "string" ? form.find(id) : iframeElement;
             iframeElement = iframeElement instanceof $ ? iframeElement.get(0) : (iframeElement[0] || iframeElement);

             try
             {
                 iframeElement.contentWindow.document;
                 return false;
             }
             catch (e)
             {
                 return true;
             }
         },
         document: function (iframeElement)
         {
             if (iframeElement != null)
             {
                 try
                 {
                     iframeElement = iframeElement instanceof $ ? iframeElement.get(0) : (iframeElement[0] || iframeElement);
                     return utils.isCrossDomain(iframeElement) ? null : (iframeElement.contentDocument || iframeElement.contentWindow.document);
                 }
                 catch (e)
                 {
                     return null;
                 }
             }
             else
                 return null;
         },
         window: function (iframeElement)
         {
             if (iframeElement != null)
             {
                 try
                 {
                     iframeElement = iframeElement instanceof $ ? iframeElement.get(0) : (iframeElement[0] || iframeElement);
                     return utils.isCrossDomain(iframeElement) ? null : iframeElement.contentWindow;
                 }
                 catch (e)
                 {
                     return null;
                 }
             }
             else
                 return null;
         }
     };

     if (d.location.href.indexOf("appcache") == -1)
     {
         core.init();
     }
 })(window, document, jQuery, unsafeWindow);