// ==UserScript==
// @name BOINCstatsBadges
// @namespace http://www.cryotest.com/
// @description Add badge stats to the boincstats user page.
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @include http://boincstats.com/*/stats/-1/user/detail/*
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_log
// @grant GM_openInTab
// @version 2.1
// @icon http://s20.postimage.org/v41hivk09/bbadges.png
// @require https://greasyfork.org/scripts/2855-gm-config/code/GM_config.js?version=9272
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @downloadURL none
// ==/UserScript==
// Change Log
//----------------------------
// Version 1.0
// -Initial release.
//
// Version 1.01
// -Added updater.
// -Added icon.
//
// Version 1.02
// -Removed the updater as it's dead. :-(
//
// Version 1.03
// -Tried another updater.
//
// Version 1.04
// -Updater tweak.
//
// Version 1.05
// -Added NumberFields and Radioactive.
// -Improved error handling.
//
// Version 1.06
// -Added WUProp.
// -Fixed an error with GPUGrid tooltips.
//
// Version 1.07
// -Added Collatz Conjecture but left it disabled until the server is back up.
// -Added default GPUGrid badge for no publications.
//
// Version 2.0
// -Enabled Collatz Conjecture.
// -Added Wildlife@Home.
// -Changed things so that badges graphics are loaded after the AJAX call returns,
// to prevent them not rendering if the tab is selected before the HTML finishes rendering.
// -Added failure to connect message.
// -Added delay configuration.
//
// Version 2.01
// -Fixed a bug in delay prefs.
//
// Version 2.1
// -Added Milkway@Home.
// -Added NFS@Home.
// -Added theSkyNet POGS.
// -Added Odd Weird Search to Yoyo@home.
// -Added % RAC to Collatz Conjecture.
// -Fixed breakage caused by GM 2.0 update security changes.
// -Fixed Wildlife@Home breakage after image src format change.
// -Fixed issue with badges not loading if the HTML wasn't already displayed.
//
(function()
{
// Avoid problems due to different versions of jQuery in this script and the target page.
this.$ = this.jQuery = jQuery.noConflict(true);
//********************************************************************************
// Borrowed from GM_config Extender.
//********************************************************************************
// ========================================================[ localization ]===
// --------------------------------------------------------[ translations ]---
GM_config.trans = {
en: {
'ButtonSave': 'Save',
'ButtonSaveTip': 'Save options and close window',
'ButtonCancel': 'Cancel',
'ButtonCancelTip': 'Close window (reject changes)',
'ResetLinkName': 'Reset to defaults',
'ResetLinkTip': 'Reset settings to shipped defaults',
'ConfirmOverwriteFromClipboard': 'Sure to overwrite your settings from Clipboard?',
'SettingsSaved': 'Settings saved.',
'SaveAborted': 'Aborted.',
'PromptSettingsPaste': 'Please paste your settings here:',
'ConfirmOverwriteFromPaste': 'Sure to overwrite your settings with the entered data?'
},
de: {
'ButtonSave': 'Speichern',
'ButtonSaveTip': 'Änderungen speichern und Fenster schließen',
'ButtonCancel': 'Abbrechen',
'ButtonCancelTip': 'Fenster schließen (Änderungen verwerfen)',
'ResetLinkName': 'Zurücksetzen',
'ResetLinkTip': 'Alle Werte auf Defaults zurücksetzen',
'ConfirmOverwriteFromClipboard': 'Sollen die Einstellungen wirklich mit den Daten vom Clipboard überschrieben werden?',
'SettingsSaved': 'Einstellungen gespeichert.',
'SaveAborted': 'Aktion abgebrochen.',
'PromptSettingsPaste': 'Bitte Einstellungen hier hineinkopieren:',
'ConfirmOverwriteFromPaste': 'Sicher, dass die Einstellungen mit den kopierten Daten überschrieben werden sollen?'
},
nl: {
'ButtonSave': 'Opslaan',
'ButtonSaveTip': 'Instellingen opslaan en sluit venster',
'ButtonCancel': 'Annuleren',
'ButtonCancelTip': 'Sluit venster (wist wijzigingen)',
'ResetLinkName': 'Standaardinstellingen herstellen',
'ResetLinkTip': 'Herstelt alle instellingen naar de standaardwaarden',
'ConfirmOverwriteFromClipboard': 'Weet u zeker dat u de instellen vanaf het clipboard wil overschrijven?',
'SettingsSaved': 'Instellingen opgeslagen.',
'SaveAborted': 'Afgebroken.',
'PromptSettingsPaste': 'Plak uw instellingen hier:',
'ConfirmOverwriteFromPaste': 'Weet u zeker dat u de instellingen wilt overschrijven met de ingevoerde data?'
},
br: {},
cz: {},
cn: {},
es: {},
fi: {},
fr: {},
ro: {},
ru: {},
se: {},
tw: {},
useLang: 'en',
fallBack: true
};
/* -------------------------------------------------[ adding translations ]---
* can be used to overwrite existing translations and/or add new ones
* string lang: 2 char language code
* object trans: translations to add in the format {'code':'translation','code2':'trans2', ...) */
GM_config.setTranslations = function(lang, trans){
for(attrname in trans){
GM_config.trans[lang][attrname] = trans[attrname];
}
}
/* ---------------------------------------------------[ init localization ]---
* string lang: language to translate into
* boolean fallback: return original (true) or empty string (false) on NoFound? */
GM_config.initLocalization = function(lang, fallback){
GM_config.trans.useLang = lang;
GM_config.trans.fallback = fallback;
}
/* -------------------------------------------------[ translate something ]---
* string term: term to translate */
GM_config.lang = function(term){
if(typeof (GM_config.trans[GM_config.trans.useLang]) == 'undefined' || !GM_config.trans[GM_config.trans.useLang][term]){
if(!GM_config.trans['en'][term]){
if(GM_config.trans.fallback)
return term;
return '';
}
return GM_config.trans['en'][term];
}
return GM_config.trans[GM_config.trans.useLang][term];
}
/* ----------------------------------------------------[ localize Buttons ]---
* uses setup default language for translation - see initLocalization() */
GM_config.localizeButtons = function(){
if(cf = this.frame.contentWindow.document.getElementById('buttons_holder')){
cf.childNodes[0].innerHTML = this.lang('ButtonSave');
cf.childNodes[0].setAttribute('title', this.lang('ButtonSaveTip'));
cf.childNodes[1].innerHTML = this.lang('ButtonCancel');
cf.childNodes[1].setAttribute('title', this.lang('ButtonCancelTip'));
cf.childNodes[2].childNodes[0].innerHTML = this.lang('ResetLinkName');
cf.childNodes[2].childNodes[0].setAttribute('title', this.lang('ResetLinkTip'));
}
}
/* =========================================[ Resize configuration window ]===
* int width: new width
* int height: new height */
GM_config.resizeFrame = function(wid, hei){
if(fid = this.frame.id){
this.frame.style.width = wid;
this.frame.style.height = hei;
}
}
/* ====================================[ Add a border to the config frame ]===
* object spec { width (5px), style (ridge), color (#eae9e8) }
*/
GM_config.addBorder = function(){
if(fid = this.frame.id){
spec = arguments[0] || {};
this.frame.style.borderWidth = (spec.width || '5px');
this.frame.style.borderStyle = (spec.style || 'ridge');
this.frame.style.borderColor = (spec.color || '#999999');
}
}
/* -------------------------------------------------[ Sections to Tabs ]---
* Convert sections to tabbed pages
*/
var sectionTabs = 0; // holds the number of tabs we have
GM_config.toggleSection = function(e){ // onClick handler for the tabs
if((typeof e) == 'number')
var objNum = e;
else
var objNum = /\_(\d+)\_/.exec(e.target.id)[1], tobj;
for(var i = 0; i < sectionTabs; i++){
tobj = GM_config.frame.contentWindow.document.getElementById('GM_config_section_' + i + '_tab');
tdat = GM_config.frame.contentWindow.document.getElementById('GM_config_section_' + i);
tdat.setAttribute('className', 'section_header tab'); // does not work
if(i == objNum){ // Activate
// tab
// if (tobj.style.cssText.match(/font-weight/) )
// tobj.setAttribute('style',tobj.style.cssText.replace(/font-weight:[^\;]*/,'font-weight: bold !important'));
// else
// tobj.setAttribute('style',tobj.style.cssText + 'font-weight: bold !important;');
tobj.setAttribute('selected', true);
// content
if(tdat.style.cssText.match(/display:/))
tdat.setAttribute('style', tdat.style.cssText.replace(/display:[^\;]*/, 'display:table !important'));
else
tdat.setAttribute('style', tdat.style.cssText + 'display:table !important;');
}else{ // DeActivate
// tab
// if (tobj.style.cssText.match(/font-weight/) )
// tobj.setAttribute('style',tobj.style.cssText.replace(/font-weight:[^\;]*/,'font-weight: normal !important'));
// else
// tobj.setAttribute('style',tobj.style.cssText + 'font-weight: normal !important;');
tobj.setAttribute('selected', false);
// content
if(tdat.style.cssText.match(/display:/))
tdat.setAttribute('style', tdat.style.cssText.replace(/display:[^\;]*/, 'display:none !important'));
else
tdat.setAttribute('style', tdat.style.cssText + 'display:none !important;');
}
}
}
GM_config.sections2tabs = function(){
var divs = this.frame.contentWindow.document.getElementsByTagName('div');
var rows = [];
for(var i = 0; i < divs.length; i++){
if(divs[i].id.indexOf('GM_config_section_') == 0 && divs[i].id.indexOf('GM_config_section_header_') != 0){
rows.push(divs[i]);
}
}
if(rows.length < 1)
return;
var anch = document.createElement('div');
anch.style.cssText = 'border-bottom: 3px solid #cccccc;';
anch.id = 'GM_config_tab_holder';
sectionTabs = rows.length;
// Tabs.
var tab_container = document.createElement('div');
tab_container.setAttribute('class', "tab-container");
for(var i = 0; i < sectionTabs; i++){
var tab = document.createElement('div');
tab.setAttribute('class', "tab");
tab.id = 'GM_config_section_' + i + '_tab';
tab.addEventListener('click', GM_config.toggleSection, false);
tab.innerHTML = GM_config.frame.contentWindow.document.getElementById('GM_config_section_header_' + i).innerHTML;
tab_container.appendChild(tab);
}
anch.appendChild(tab_container);
// Config. pages.
for(var i = 0; i < sectionTabs; i++){
anch.appendChild(rows[i]);
rows[i].style.marginLeft = "auto";
rows[i].style.marginRight = "auto";
}
this.frame.contentWindow.document.getElementById('GM_config_wrapper').insertBefore(anch, this.frame.contentWindow.document.getElementById('GM_config_buttons_holder'));
this.frame.contentWindow.document.getElementById('GM_config_section_0_tab').setAttribute('selected', true);
this.toggleSection(0);
}
//********************************************************************************
var BBadges = {
idAsteroids: GM_getValue('asteroids'),
idBitcoinUtopia: GM_getValue('bitcoinutopia'),
idCollatz: GM_getValue('collatz'),
idConvector: GM_getValue('convector'),
idEnigma: GM_getValue('enigma'),
idGPUGrid: GM_getValue('gpugrid'),
idMilkyway: GM_getValue('milkyway'),
idNFS: GM_getValue('nfs'),
idNumberFields: GM_getValue('numberfields'),
idOProject: GM_getValue('oproject'),
idPOGS: GM_getValue('pogs'),
idPrimeGrid: GM_getValue('primegrid'),
idRadioactive: GM_getValue('radioactive'),
idWCG: GM_getValue('wcg'),
idWildlife: GM_getValue('wildlife'),
idWUProp: GM_getValue('wuprop'),
idYoyo: GM_getValue('yoyo'),
delayAJAX: GM_getValue('stats_timeout'),
delayTabLoad: GM_getValue('tab_load_delay'),
userPage: "show_user.php?userid=",
asteroids: {
name: "Asteroids@home",
root: "http://asteroidsathome.net/boinc/",
badges: [],
alt: []
},
bitcoinUtopia: {
name: "Bitcoin Utopia",
root: "http://www.bitcoinutopia.net/bitcoinutopia/",
badges: [],
alt: []
},
collatz: {
name: "Collatz Conjecture",
root: "http://boinc.thesonntags.com/collatz/",
badges: [],
alt: []
},
convector: {
name: "Convector",
root: "http://convector.fsv.cvut.cz/",
badges: [],
alt: []
},
enigma: {
name: "Enigma@home",
root: "http://www.enigmaathome.net/",
badges: '',
alt: ''
},
gpugrid: {
name: "GPUGRID.net",
root: "http://www.gpugrid.net/",
badges: [],
alt: [],
ranks: [],
citations: [],
topics: []
},
milkyway: {
name: "MilkyWay@Home",
root: "http://milkyway.cs.rpi.edu/milkyway/",
badges: [],
alt: []
},
nfs: {
name: "NFS@Home",
root: "http://escatter11.fullerton.edu/nfs/",
badges: [],
alt: []
},
numberfields: {
name: "NumberFields@home",
root: "http://numberfields.asu.edu/NumberFields/",
badges: [],
alt: []
},
oproject: {
name: "OProject@Home",
root: "http://oproject.info/",
badges: [],
alt: []
},
pogs: {
name: "theSkyNet POGS",
root: "http://pogs.theskynet.org/pogs/",
trophies: "http://www.theskynet.org/profiles/{id}/trophies.json",
trophy: "http://www.theskynet.org/trophies/{id}.json",
badges: [],
alt: []
},
primegrid: {
name: "PrimeGrid",
root: "http://www.primegrid.com/",
badges: [],
alt: []
},
radioactive: {
name: "Radioactive@home",
root: "http://radioactiveathome.org/boinc/",
badges: [],
alt: []
},
wcg: {
name: "World Community Grid",
root: "http://www.worldcommunitygrid.org/stat/viewMemberInfo.do?userName=",
badges: [],
alt: []
},
wildlife: {
name: "Wildlife@Home",
root: "http://volunteer.cs.und.edu/wildlife/",
badges: [],
alt: []
},
wuprop: {
name: "WUProp@home",
root: "http://wuprop.boinc-af.org/",
badges: [],
alt: []
},
yoyo: {
name: "Yoyo@home",
root: "http://www.rechenkraft.net/yoyo/",
badges: [],
alt: []
},
// Configuration management.
config: function(){
var configStyle = "\
.config_var {text-align: center; padding-top: 5px;} \
.field_label {padding-left: 5px;} \
.reset {display: none;} \
input {width: 50px;} \
#GM_config_field_wcg {width: 100px;} \
.config_var {width: 245px; text-align: left !important; margin: 0 auto 4px !important;} \
.field_label {width: 130px; float: left; margin-top: 4px;} \
#GM_config_asteroids_field_label {float: left;} \
/* Tabbed */\
#GM_config .section_header_holder{margin-top: 0;}\
.section_header[selected=\"true\"] {\
position: relative !important;\
color: #000000 !important;\
top: 1px !important;\
}\
#GM_config_tab_holder {\
margin-left:5px !important;\
margin-right:5px !important;\
border-bottom: 1px solid #B2A293 !important;\
}\
.tab-container {\
background: url(\"http://boincstats.com/css/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png\") repeat-x scroll 55% 55% transparent;\
border-radius: 5px 5px 5px 5px;\
height: 30px;\
margin-bottom: 1px;\
margin-left: 4px;\
width: 98%;\
}\
.tab {\
-moz-user-select: -moz-none;\
background: url(\"images/ui-bg_glass_85_dfeffc_1x400.png\") repeat-x scroll 50% 50% #DFEFFC;\
border-radius: 5px 5px 0 0;\
color: #2E6E9E;\
cursor: pointer;\
display:inline-block;\
font-size: 11px;\
font-weight: bold;\
height: 25px;\
line-height: 25px;\
margin-right: 4px;\
margin-top: 3px;\
padding-left:10px;\
padding-right:10px;\
user-select: none;\
text-align: center;\
white-space: nowrap;\
}\
.tab[selected=\"true\"] {\
background: url(\"images/ui-bg_inset-hard_100_f5f8f9_1x100.png\") repeat-x scroll 50% 50% #F5F8F9;\
color: #E17009;\
margin-top: 4px;\
padding-bottom: 1px;\
}\
#GM_config_section_0_tab { margin-left:4px !important; }\
";
GM_config.init('BoincStats Badges',
/* Fields object */
{
'asteroids': {
'section': ['Project IDs', 'Enter your user ID for each project, found on your account details page.
For World Community Grid, enter your user NAME.'],
'label': 'Asteroids@home:',
'type': 'text',
'default': ''
},
'bitcoinutopia': {
'label': 'Bitcoin Utopia:',
'type': 'text',
'default': ''
},
'collatz': {
'label': 'Collatz Conjecture:',
'type': 'text',
'default': ''
},
'convector': {
'label': 'Convector:',
'type': 'text',
'default': ''
},
'enigma': {
'label': 'Enigma@home:',
'type': 'text',
'default': ''
},
'gpugrid': {
'label': 'GPUGRID.net:',
'type': 'text',
'default': ''
},
'milkyway': {
'label': 'MilkyWay@Home:',
'type': 'text',
'default': ''
},
'nfs': {
'label': 'NFS@Home:',
'type': 'text',
'default': ''
},
'numberfields': {
'label': 'NumberFields@home:',
'type': 'text',
'default': ''
},
'oproject': {
'label': 'OProject@Home:',
'type': 'text',
'default': ''
},
'pogs': {
'label': 'theSkyNet POGS:',
'type': 'text',
'default': ''
},
'primegrid': {
'label': 'PrimeGrid:',
'type': 'text',
'default': ''
},
'radioactive': {
'label': 'Radioactive@home:',
'type': 'text',
'default': ''
},
'wildlife': {
'label': 'Wildlife@Home:',
'type': 'text',
'default': ''
},
'wcg': {
'label': 'World Community Grid:',
'type': 'text',
'default': ''
},
'wuprop': {
'label': 'WUProp@Home:',
'type': 'text',
'default': ''
},
'yoyo': {
'label': 'Yoyo@home:',
'type': 'text',
'default': ''
},
'stats_timeout': {
'section': ['Delays', 'You can adjust the maximum periods that the script will wait for AJAX badge stats and for the tab to be rendered.'],
'label': 'AJAX Timeout (ms):',
'type': 'text',
'default': '2000'
},
'tab_load_delay': {
'label': 'Tab Load Delay (ms)',
'type': 'text',
'default': '1500'
}
}, configStyle, {
open: function(){
GM_config.addBorder(); // add a fancy border
GM_config.resizeFrame('420px', '695px'); // resize the config window
GM_config.center();
GM_config.sections2tabs();
},
save: function(){
GM_setValue('asteroids', GM_config.get('asteroids'));
GM_setValue('bitcoinutopia', GM_config.get('bitcoinutopia'));
GM_setValue('collatz', GM_config.get('collatz'));
GM_setValue('convector', GM_config.get('convector'));
GM_setValue('enigma', GM_config.get('enigma'));
GM_setValue('gpugrid', GM_config.get('gpugrid'));
GM_setValue('milkyway', GM_config.get('milkyway'));
GM_setValue('nfs', GM_config.get('nfs'));
GM_setValue('numberfields', GM_config.get('numberfields'));
GM_setValue('oproject', GM_config.get('oproject'));
GM_setValue('pogs', GM_config.get('pogs'));
GM_setValue('primegrid', GM_config.get('primegrid'));
GM_setValue('radioactive', GM_config.get('radioactive'));
GM_setValue('wcg', GM_config.get('wcg'));
GM_setValue('wildlife', GM_config.get('wildlife'));
GM_setValue('wuprop', GM_config.get('wuprop'));
GM_setValue('yoyo', GM_config.get('yoyo'));
GM_setValue('stats_timeout', GM_config.get('stats_timeout'));
GM_setValue('tab_load_delay', GM_config.get('tab_load_delay'));
location.reload(); // reload the page when configuration was changed
}
});
// Register the menu item.
GM_registerMenuCommand("BoincStats Badges", function(){
GM_config.open()
}, 'B');
// Open prefs on first run.
if(!GM_getValue('hasrun')){
GM_config.open();
GM_setValue('hasrun', true)
}
},
// Language support.
locale: 'en',
br: {
"Badges": "Emblemas",
"Project name": "Nome do projeto",
"Unable To Connect": "Não é possível conectar ao projeto."
},
cz: {
"Badges": "Odznaky",
"Project name": "Název projektu",
"Unable To Connect": "Nelze se připojit k projektu."
},
cn: {
"Badges": "徽章",
"Project name": "项目忿称",
"Unable To Connect": "无法连接到项目。"
},
de: {
"Badges": "Abzeichen",
"Project name": "Projektname",
"Unable To Connect": "Es kann keine Verbindung zum Projekt."
},
en: {
"Badges": "Badges",
"Project name": "Project name",
"Unable To Connect": "Unable to connect to project."
},
es: {
"Badges": "Insignias",
"Project name": "Nombre del proyecto",
"Unable To Connect": "No se puede conectar al proyecto."
},
fi: {
"Badges": "Merkit",
"Project name": "Hankkeen nimi",
"Unable To Connect": "Ei voida yhdistää hankkeeseen."
},
fr: {
"Badges": "Emblem",
"Project name": "Nom du projet",
"Unable To Connect": "Impossible de se connecter au projet."
},
nl: {
"Badges": "Badges",
"Project name": "Naam van het project",
"Unable To Connect": "Kan geen verbinding maken met project."
},
ro: {
"Badges": "Insigne",
"Project name": "Proiectul nume",
"Unable To Connect": "Nu se poate conecta la proiect."
},
ru: {
"Badges": "Значки",
"Project name": "название проекта",
"Unable To Connect": "пе удаетѿѿ подключитьѿѿ к проекту."
},
se: {
"Badges": "Emblem",
"Project name": "Projektnamn",
"Unable To Connect": "Kan inte ansluta till projektet."
},
tw: {
"Badges": "徽章",
"Project name": "項目忿稱",
"Unable To Connect": "无法连接到项目。"
},
getLocale: function(){
var loc = document.URL.split('/')[3];
if(typeof loc != 'undefined'){
this.locale = loc;
}
},
initLang: function(){
GM_config.setTranslations('br', BBadges.br);
GM_config.setTranslations('cz', BBadges.cz);
GM_config.setTranslations('cn', BBadges.cn);
GM_config.setTranslations('en', BBadges.en);
GM_config.setTranslations('es', BBadges.es);
GM_config.setTranslations('fi', BBadges.fi);
GM_config.setTranslations('de', BBadges.de);
GM_config.setTranslations('fr', BBadges.fr);
GM_config.setTranslations('nl', BBadges.nl);
GM_config.setTranslations('ro', BBadges.ro);
GM_config.setTranslations('ru', BBadges.ru);
GM_config.setTranslations('se', BBadges.se);
GM_config.setTranslations('tw', BBadges.tw);
GM_config.initLocalization(BBadges.locale, true);
}
};
// Init language and config. settings.
BBadges.config();
BBadges.getLocale();
BBadges.initLang();
// Insert a new tab in the header.
$("#main .ui-tabs ul").append("