User:Tucoxn/common.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
The accompanying .css page for this skin can be added at User:Tucoxn/common.css. |
/**
* Enhaced POTY - convenient voting!
* [[Help:EnhancedPOTY.js]]
*
* Features:
* Eligiblity check, assists to cast votes (from gallery and from vote-page) and vote removal (autodetection & saving in local storage or, if not present from contribs)
* Gallery-resize and -randomization based on the user name (seeded random)
* Statistics: Some tiny charts (someone has to run genStats() regulary and provide a page to save the data in)
* MyPOTY- a control center for each user (what (s)he has voted for, eligibility info & why I can vote here or not, language)
*
* Thanks to all translators!
*
* <nowiki>
* jshint validation, please
*
* @rev 1 (2012-06-01)
* @author
* [[User:Rillke]], 2012
* @license
* GPL v.3
*/
/*global jQuery:false, mediaWiki:false, Geo:false, importStylesheet:false, importScript:false, wpAvailableLanguages:false, GallerySlide:false*/
/*jshint curly:false */
(function($, mw, undefined) {
'use strict';
if (window.POTY || 'view' !== mw.config.get('wgAction')) return;
var poty;
// A bunch of helper functions
function isNumber(n) {
return !isNaN(parseInt(n, 10)) && isFinite(n);
}
function firstItem(o) { for (var i in o) { if (o.hasOwnProperty(i)) { return o[i]; } } }
function firstItemName(o) { for (var i in o) { if (o.hasOwnProperty(i)) { return i; } } }
function isCanvasSupported() { var elem = document.createElement('canvas'); return !!(elem.getContext && elem.getContext('2d'));}
/********************************
**
** Translation
**
********************************/
mw.messages.set({
'poty-poty-year': "POTY $1",
'poty-poty-full-year': "Picture of the Year $1",
'poty-poty-full-commons': "POTY - Commons Picture of the Year",
'poty-welcome-banner': "Welcome $1! Loading POTY $2.",
'poty-slideshow': "Slideshow",
'poty-fullscreen': "Full screen",
'poty-fullscreen-close': "Close full screen",
'poty-report-error-h1': "POTY-App experienced an error",
'poty-report-error': "The data that will be saved and publicly visible if you send the report: Your username, a timestamp, what the App did immediately before and \"$1\"",
'poty-report-error-send': "Send report",
'poty-report-error-reset': "Reset and Reload",
'poty-report-error-cancel': "Cancel",
'poty-ineligible-blocked': "Your account is ineligible because it is blocked on Commons. You were blocked by $1 because $2 with an expiry of $3.",
'poty-ineligible-nosul': "Your account is ineligible because it is not attached to SUL.",
'poty-ineligible-suleditcount': "Your account is ineligible because you do not have $1 or more edits on any attached SUL account or on Commons.",
'poty-ineligible-dateeditcount': "Your account is ineligible because you do not have $1 or more edits on any attached SUL account or on Commons before $2.",
'poty-eligible': "You are eligible to vote because you have $1 edits on $2 before $3!",
'poty-anonymous-no-vote-msg': "You are currently not logged in. Only registered users can vote in POTY.",
'poty-vote-add': "Vote",
'poty-vote-remove': "Remove vote",
'poty-vote-stats': "Statistics",
'poty-voting-vote': "Saving your vote",
'poty-voting-remove-vote': "Removing your vote",
'poty-voting-app-error': "Application ERROR",
'poty-voting-edit-error': "Edit ERROR",
'poty-vote-nothing-to-remove': "Can\'t find your vote",
'poty-vote-already-there': "Already voted this image",
'poty-vote-multiple-possible': "You may vote for more than one picture",
'poty-vote-single-only': "You can vote for only ONE picture",
'poty-stats-chart-desc': "Vote count compared to the average vote count per picture: ",
'poty-stats-votelist': "Votelist",
'poty-stats-close-click': "Click to close",
'poty-my-poty-link': "My POTY",
'poty-my-poty-link-tooltip': "All about your participation in POTY $1",
'poty-my-poty-h1': "POTY $1 and You",
'poty-my-poty-app-version': "POTY-App-Version $1",
'poty-my-poty-language': "Language",
'poty-my-poty-eligibility': "Eligibility",
'poty-my-poty-votes': "Votes",
'poty-my-poty-state': "POTY - State",
'poty-my-poty-data': "Data saved in your browser",
'poty-my-poty-state-RX': "Round $1 is running.",
'poty-my-poty-state-novote': "There is no voting at the moment.",
'poty-my-poty-state-g-RX': "You are on a gallery made for round $1.",
'poty-my-poty-action-language-saveoncommons': "Save on Commons",
'poty-my-poty-action-eligibility-recheck': "Check again",
'poty-my-poty-action-votes-recheck': "Update - Requery",
'poty-my-poty-action-data-remove': "Remove",
'poty-my-poty-action-data-remove-warn': "After removing this data POTY-App won\'t know what you have voted for."
});
poty = window.POTY = {
/********************************
**
** Configuration
**
********************************/
version: '0.4.3.0',
// The key to be used to store the data in the user's browser
storageKey: '2012POTY',
// POTY <year>
year: 2012,
// Will be retrieved from the template $('#potyVotingState')
state: 'R1',
// Required e.g. for digging the contribs to a specific date
startDate: '2013-01-16T00:00:00Z',
// Eligibility requirements
required: {
minEditCount: 75 + 1,
editsBefore: '2013-01-01T00:00:00Z',
editsDaysAgo: 6000,
registeredBefore: '2013-01-01T00:00:00Z',
includeDeleted: false
},
votingFormat: '\n# [[User:%UserName%|%UserName%]]',
// Everything with null in it will be initialized later
formattedVote: null,
voteRegExp: null,
votingSummaryAdd: '+1 POTY vote - eligible on %wiki% with %edits% edits - Vote through [[%VoteFrom%]] - [[Help:EnhancedPOTY.js|POTY App]]',
votingSummaryRemove: '-1 POTY removing vote - Vote through [[%VoteFrom%]] - [[Help:EnhancedPOTY.js|POTY App]]',
// These sizes will be offered in the galleries
availableImageSizes: [{w: 1280, h: 1024}, {w: 1024, h: 768}, {w: 750, h: 750}, {w: 600, h: 500}, {w: 400, h: 400}, {w: 335, h: 250}, {w: 300, h: 300}, {w: 250, h: 250}, {w: 200, h: 200}, {w: 180, h: 180}, {w: 150, h: 150}, {w: 120, h: 120}],
availableImageSizesWide: [{w: 1920, h: 600}, {w: 1600, h: 350}, {w: 1280, h: 400}, {w: 800, h: 175}, {w: 600, h: 130}, {w: 400, h: 100}, {w: 300, h: 75}, {w: 200, h: 50}, {w: 100, h: 50}],
wide: false,
username: mw.config.get('wgUserName') || (window.Geo?Geo.IP:'') || 'anonymous',
userlanguage: mw.config.get('wgUserLanguage'),
ratelimit: -1 === $.inArray('autoconfirmed', mw.config.get('wgUserGroups')) ? 8 : -1,
// Whether the current gallery was made for R1 or the Final
galleryType: /\d{4}\/Final(?:ists)?/.test(mw.config.get('wgPageName')) ? 'R2' : 'R1',
votingPageRegExp: /^Commons:Picture of the Year\/\d{4}\/(R1|Final(?:ists)?)\/([^\/]{5,})$/,
galleryRegExp: /^Commons:Picture of the Year\/\d{4}\/(?:Galleries\/([^\/]+)|(Finalists))/,
// Style & CSS
stylesheet: 'MediaWiki:EnhancedPOTY.css',
logo: '//upload.wikimedia.org/wikipedia/commons/9/91/POTY_Logo_For_Banner.png',
// When thumbs are resized to small sizes, text would overflow otherwise
iconOnlyWidthThreshold: 250,
// Variables for internal use
translationLoaded: false,
// Everything that does not need DOM-ready
init: function () {
// Variable set up
poty.formattedVote = poty.getFormattedVote();
poty.voteRegExp = poty.getVoteRegExp();
poty.availableImageSizes.reverse();
// Enqueue tasks
poty.addTask('loadTranslation');
poty.addTask('splash');
poty.addTask('loadComponents');
poty.addTask('launch');
poty.nextTask();
},
/**************************
*
* The next bunch of Helpers
*
**************************/
// For cross-browser-compatibility
borderRadius: function (r) {
return {
'-webkit-border-radius': r,
'-moz-border-radius': r,
'-o-border-radius': r,
'-ms-border-radius': r,
'border-radius': r
};
},
/** Helper - i18n and language
@param {<dummy>} params The first param is the message-name; as many arguments of type string or number as the message to create requires
@return {String} the parsed message (HTML special chars should be escaped by parse)
**/
getMessage: function (params) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = 'poty-' + args[0];
args[args.length] = poty.year;
return mw.message.apply(this, args).parse();
},
/** Helper
@param {String} stTimestamp MediaWiki-Timestamp of format YYYY-MM-DDThh:mm:ssZ
@return {Object} JavaScript Date-Object
**/
getDateFromMWDate: function( stTimestamp ) {
var regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
var m1 = stTimestamp.match(regex);
return new Date(m1[1], m1[2]-1, m1[3], m1[4], m1[5], m1[6]); // Wer hat sich diesen Unsinn ausgedacht?
},
////////////////////////////
///////// Storage //////////
////////////////////////////
saveData: function () {
var r;
// Catch jStorage errors. Odd Safari error occured on Mac.
// something undefined in '_storage.__jstorage_meta.CRC32'
try {
var needsTTL = !poty.dataExists();
r = $.jStorage.set(poty.storageKey, poty.data);
if (needsTTL) {
// Expires in 50d
$.jStorage.setTTL(poty.storageKey, 4320000000);
}
} catch (ex) {
r = ex;
}
return r;
},
getData: function () {
var d = $.jStorage.get(poty.storageKey);
return $.extend(true, d, {
R1: {
votes: {}
},
R2: {
votes: {}
},
novote: {
votes: {}
},
local: {
}
});
},
deleteData: function() {
try {
return $.jStorage.deleteKey(poty.storageKey);
} catch (ex) {
return ex;
}
},
dataExists: function() {
return !!$.jStorage.get(poty.storageKey);
},
getVotingPage: function (p, r) {
return 'Commons:Picture of the Year/' + poty.year + '/' + ((r || poty.galleryType) === 'R1' ? 'R1' : 'Finalists') + '/' + p;
},
getVotingPageURL: function (p, r) {
return mw.util.wikiScript('index') + '?' + $.param({ title: poty.getVotingPage(p, r).replace(/ /g, '_') });
},
getFormattedVote: function () {
return poty.votingFormat.replace(/%UserName%/g, poty.username);
},
getVoteRegExp: function () {
return new RegExp(poty.mdEscapeSpecial($.escapeRE(poty.votingFormat.replace(/%UserName%/g, poty.username))), 'g');
},
/** Helper-Escape some special characters that $.escapeRE does not
@param {String} string String that's special chars should be escaped
@return {String} Escaped string
**/
mdEscapeSpecial: function(string) {
var specials = ['t', 'n', 'v', '0', 'f'];
$.each(specials, function(i, s) {
var rx = new RegExp('\\'+s, 'g');
string = string.replace(rx, '\\'+s);
});
return string;
},
getFileNameFromPageName: function(pagename) {
if ('string' !== typeof pagename) pagename = mw.config.get('wgPageName');
return pagename.replace(/_/g, ' ').match(poty.votingPageRegExp)[2];
},
getGalleryTypeFromPageName: function(pagename) {
if ('string' !== typeof pagename) pagename = mw.config.get('wgPageName');
return pagename.replace(/_/g, ' ').match(poty.votingPageRegExp)[1] === 'R1' ? 'R1' : 'R2';
},
getGalleryNameFromPageName: function(pagename) {
if ('string' !== typeof pagename) pagename = mw.config.get('wgPageName');
var m = pagename.replace(/_/g, ' ').match(poty.galleryRegExp);
return m[1] || m[2];
},
/********************************
**
** Startup
**
********************************/
loadComponents: function () {
// TODO: Replace jquery.form with an API call // use [[MediaWiki:Gadget-SettingsManager.js]]
mw.loader.using(['mediawiki.util', 'ext.gadget.math.seedrandom', 'ext.gadget.libAPI', 'ext.gadget.libUtil', 'ext.gadget.jquery.fullscreen', 'jquery.jStorage', 'jquery.form', 'jquery.spinner', 'jquery.ui', 'jquery.client'], poty.nextTask);
},
relaunch: function() {
poty.tasks = [];
setTimeout(poty.launch, 200);
},
launch: function () {
poty.data = poty.getData();
// delete test data, to be removed soon
$.jStorage.deleteKey('2011POTY');
$.jStorage.deleteKey('2012POTY_test');
poty.dataExist = poty.dataExists();
poty.wide = !!$('#potyWide').length;
if (poty.wide) {
poty.availableImageSizes = poty.availableImageSizesWide.reverse();
}
var msgToShow = 'poty-full-commons';
switch ($('#potyVotingState').text()) {
case 'duringR1':
poty.state = 'R1';
if ('R1' === poty.galleryType) msgToShow = 'vote-multiple-possible';
break;
case 'duringR2':
poty.state = 'R2';
if ('R2' === poty.galleryType) msgToShow = 'vote-single-only';
break;
default:
poty.state = 'novote';
break;
}
poty.showBannerMessage([msgToShow]);
if (poty.isVotingPage) {
poty.galleryType = poty.getGalleryTypeFromPageName();
}
if (mw.user.isAnon()) {
return poty.showAnonWarning();
}
poty.addTask('checkLocal');
poty.addTask('checkLanguage');
if (!poty.data.eligible && !poty.data.ineligible) {
poty.currentEditGroup = -1;
poty.queriesRunning = 0;
poty.addTask('checkSUL');
poty.addTask('getDbList');
if (poty.required.includeDeleted) {
poty.addTask('checkLocalContribs');
poty.addTask('checkGlobalContribs');
} else {
poty.addTask('checkLocalExistingContribs');
poty.addTask('checkGlobalExistingContribs');
}
}
poty.addTask('showEligible');
poty.nextTask();
},
/********************************
**
** UI: Gallery, styling
**
********************************/
splash: function () {
$(function() {
poty.$gallery = $('#potyEasyVoteEnhanced').find('ul.gallery');
poty.$gallery.$oldParent = poty.$gallery.parent();
var l = poty.$gallery.find('li.gallerybox').length;
if (l < 2 || l > 250) {
if ($('#potyEasyVoteEnhancedButton').length){
poty.isVotingPage = true;
document.title = poty.getFileNameFromPageName() + ' - ' + poty.getMessage('poty-full-commons');
} else {
poty.log('Startup aborted', l);
return;
}
} else {
document.title = poty.getGalleryNameFromPageName() + ' - ' + poty.getMessage('poty-full-commons');
}
mw.loader.using(['ext.gadget.jquery.blockUI', 'jquery.ui'], function () {
poty.secureCall('showSplash');
});
});
},
showSplash: function () {
var $loadBanner = poty.$loadBanner = $('<div>').hide().text(poty.getMessage('welcome-banner', poty.username));
var $loadImage = poty.$loadImage = $('<img>', { src: poty.logo });
var $loadBannerOuter = poty.$loadBannerOuter = $('<div>', { style: 'font-size:2em; padding:0.5em; min-height:1.15em' })
.append($loadImage, $loadBanner);
poty.secureCall('showLoader');
setTimeout(function () {
$loadBanner.hide().fadeIn(1000);
}, 100);
poty.nextTask();
importStylesheet(poty.stylesheet);
},
showLoader: function () {
if (poty.loaderShown) return;
poty.loaderShown = true;
if (poty.loaderHideTimeout) clearTimeout(poty.loaderHideTimeout);
if (poty.$loadBanner.parent().hasClass('ui-effects-wrapper')) poty.$loadBanner.parent().css('height', 'auto');
var css = poty.borderRadius('10px');
$.blockUI({
message: poty.$loadBannerOuter,
css: $.extend(css, {
border: 'none',
padding: '15px',
backgroundColor: '#000',
'-ms-filter': "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)",
opacity: 0.7,
color: '#fff'
})
});
},
hideLoader: function () {
poty.loaderHideTimeout = setTimeout(function() {
poty.$loadBanner.stop().clearQueue().removeAttr('style');
$.unblockUI({ fadeOut: 1500, onUnblock: function() { if (poty.$loadBanner.parent().hasClass('ui-effects-wrapper')) poty.$loadBanner.parent().css('height', 'auto'); } });
poty.loaderShown = false;
}, 200);
},
showBannerMessage: function (msg) {
poty.$loadBanner.hide('clip', {}, 1500, function () {
$(this).text(poty.getMessage.apply(this, msg));
}).show('clip', {}, 1000).delay(1500);
},
showImmediateBannerMessage: function (msg) {
if (poty.$loadBanner.parent().hasClass('ui-effects-wrapper')) poty.$loadBanner.unwrap(poty.$loadBanner.parent());
poty.$loadBanner.stop().clearQueue().removeAttr('style').text(poty.getMessage.apply(this, msg)).show();
},
mdCreatejIcon: function (iconClass) {
return $('<span>', { 'class': 'ui-icon ' + iconClass + ' md-inline-icon', text: ' ' });
},
createNotifyArea: function(textNode, icon, state) {
return $('<div>', { 'class': 'ui-widget' }).append(
$('<div>', { 'class': state + ' ui-corner-all', style: 'margin-top:5px; padding:0.7em;' }).append($('<p>').append(
this.mdCreatejIcon(icon).css('margin-right', '.3em'), textNode
))
);
},
showAnonWarning: function () {
var dlgBtns = {};
var $logInNode = $('#pt-login');
dlgBtns[$logInNode.text() || 'LogIn'] = function() {
var logInLink = ($logInNode.find('a').length ? $logInNode.find('a').attr('href') : $logInNode.attr('href')) || mw.util.wikiScript() + '?' + $.param({ title: 'Special:UserLogin', returnto: mw.config.get('wgPageName') });
window.location = logInLink;
};
$('<div>').text(poty.getMessage('anonymous-no-vote-msg')).dialog({
title: poty.getMessage('poty-full-commons'),
buttons: dlgBtns,
modal: true,
open: function() {
// Look out for http://bugs.jqueryui.com/ticket/6830 / jQuery UI 1.9 / fixed in 1.10.0
var $buttons = $(this).parent().find('.ui-dialog-buttonpane button');
$buttons.eq(0).button({ icons: { primary: 'ui-icon-key' } }).addClass('ui-button-green');
poty.hideLoader();
},
close: function() {
poty.hideLoader();
}
});
return;
},
setUpGallery: function () {
var $gallery = poty.$gallery;
var $imgs = $gallery.find('div.thumb a.image img:first-child');
// Support for videos
$imgs = $imgs.add($gallery.find('div.thumb video:first-child')).add($gallery.find('div.thumb img.playerPoster')).add($gallery.find('div.thumb div.PopUpMediaTransform img'));
// Remove styling from gallery (important on panorama pages)
$gallery.removeAttr('style');
// Current thumb size and set up vars we need (perhaps, if the user wants them) later
var currentThumbSize = poty.newSize = 5;
var currentThumbSizeWidth = $imgs.attr('width');
poty.resizeRunning = false;
$imgs.each(function(i, el) {
var $el = $(el),
w = $el.attr('width');
currentThumbSizeWidth = Math.max(currentThumbSizeWidth, w);
});
var lastDiff = Infinity;
$.each(poty.availableImageSizes, function(i, s) {
var currentDiff = Math.abs(s.w-currentThumbSizeWidth);
if (currentDiff < lastDiff) {
poty.newSize = currentThumbSize = i;
lastDiff = currentDiff;
} else {
return false;
}
});
poty.images = {};
poty.arrImgs = [];
$imgs.each(function(i, el) {
var $el = $(el),
h = $el.height(),
w = $el.width(),
src = $el.attr('src') || $el.attr('poster'),
fileName = mw.libs.commons.titleFromImgSrc(src),
imgInfo = poty.images[fileName] = { el: $el, sizes: {}, name: fileName };
imgInfo.sizes[currentThumbSize] = { url: src, h: h, w: w };
poty.arrImgs.push('File:' + fileName);
$el.data('potyInfo', imgInfo);
$el.parents('li.gallerybox').addClass('poty-element').css('width', currentThumbSizeWidth+25).children('div').css('width', currentThumbSizeWidth+25).children('div.thumb').css('width', currentThumbSizeWidth+20);
});
// Resize-slider and input
poty.viewContainer = $('<div>', { id: 'potyViewContainer' });
var $sizeContainer = $('<div>', { id: 'potyResizeContainer', 'class': 'poty-element' }).appendTo(poty.viewContainer);
var $sizeInput = $('<input>', {
type: 'text',
size: 5,
id: 'sizeInput',
'class': 'numbersOnly',
value: poty.availableImageSizes[currentThumbSize].w
}).appendTo($sizeContainer)
.bind('input change keyup', function() {
// IE hack: Otherwise change does not fire
var valNew = this.value.replace(/[^0-9]/g,'');
if (valNew !== this.value) this.value = valNew;
})
.keyup(function(e) {
if (13 === e.keyCode-0) $(this).triggerHandler('change');
})
.change(function() {
var tVal = this.value;
if (!tVal) return;
var lastDiff = Infinity;
var nearestVal = currentThumbSize;
$.each(poty.availableImageSizes, function(i, s) {
var thisdiff = Math.abs(s.w - tVal);
if (lastDiff > thisdiff) {
nearestVal = i;
} else {
return false;
}
lastDiff = thisdiff;
});
this.value = poty.availableImageSizes[nearestVal].w;
$sizeSlider.slider('option', 'value', nearestVal);
});
var $sizeSlider = $('<div>', { css: { 'display': 'inline-block', width: '250px', margin: '10px' } })
.slider({
max: poty.availableImageSizes.length-1,
animate: true,
change: function(e, ui) {
$sizeInput.val( poty.availableImageSizes[ui.value].w );
poty.secureCall('resizeThumbs', ui.value);
},
slide: function(e, ui) {
$sizeInput.val( poty.availableImageSizes[ui.value].w );
},
value: currentThumbSize
})
.appendTo($sizeContainer.append(' px<br/>'));
var $oldSizeContainer = $('span.thumb-size-bar');
$('<label>', { 'for': 'sizeInput', text: $oldSizeContainer.find('.thumb-size-text').text() + ' ' }).prependTo($sizeContainer);
$oldSizeContainer.after(poty.viewContainer).hide();
if ($.isNumeric(poty.data.lastThumbSize) && (poty.data.lastThumbSize-0) !== currentThumbSize && !poty.wide) {
$sizeSlider.slider('option', 'value', poty.data.lastThumbSize);
} else {
poty.nextTask();
}
},
installSlideshow: function () {
var $buttons = $('<span>').attr('id', 'potyViewButtons'),
$fullscreen,
$slideshow;
var startSlideshow = function (o, cont, screenread) {
if (cont) o.cont = cont;
if (screenread) {
o.readFromScreen = true;
o.readFromScreenSmallImages = true;
o.autoPlay = true;
o.remoteUse = true;
}
o.start();
};
var loadSlideshowAndStart = function (cont, screenread) {
if ('object' === typeof window.GallerySlide) {
if ($.isFunction(GallerySlide.toggleVisibility)) {
GallerySlide.toggleVisibility();
} else {
startSlideshow(window.GallerySlide, cont, screenread);
}
} else {
$(document).bind('slideshow', function (e, st, o) {
// If the code requires debugging, you can uncomment the following line
poty.log('Slideshow> ' + st);
if ('codeLoaded' === st && o) {
startSlideshow(o, cont, screenread);
}
});
poty.log('loading Slideshow');
importScript('MediaWiki:GallerySlideshow.js');
importStylesheet('MediaWiki:Gadget-GallerySlideshow.css');
}
};
if ($.FullScreenSupported) {
var _fullScreen = function(prevent) {
if (false !== prevent) $(document.documentElement).requestFullScreen();
var $el = $fullscreen;
$el.unbind('click', _fullScreen).click(_closeFullScreen);
$el.button({ label: poty.getMessage('fullscreen-close'), icons: { primary: 'ui-icon-newwin' } });
};
var _closeFullScreen = function(prevent) {
if (false !== prevent) $.FullScreen.cancelFullScreen();
var $el = $fullscreen;
$el.unbind('click', _closeFullScreen).click(_fullScreen);
$el.button({ label: poty.getMessage('fullscreen'), icons: { primary: 'ui-icon-extlink' } });
};
$(document).fullScreenChange(function() {
if ($.FullScreen.isFullScreen()) {
_fullScreen(false);
} else {
_closeFullScreen(false);
}
});
$fullscreen = $('<button type="button" role="button" id="potyFullscreenButton">').text(poty.getMessage('fullscreen'))
.button({ icons: { primary: 'ui-icon-extlink' } })
.click(_fullScreen)
.appendTo($buttons);
}
$slideshow = $('<button type="button" role="button" id="potySlideshowButton">').text(poty.getMessage('slideshow'))
.button({ icons: { primary: 'ui-icon-play' } })
.click(function() {
loadSlideshowAndStart(0, true);
})
.appendTo($buttons);
if ($fullscreen) $buttons.buttonset();
$buttons.appendTo(poty.viewContainer);
poty.nextTask();
},
imgsToQuery: [],
resizeThumbs: function (newSize) {
poty.imageSizeInCache = newSize in firstItem(poty.images).sizes;
poty._resizeThumbs(newSize);
},
_resizeThumbs: function (newSize) {
if (poty.resizeRunning) return;
poty.resizeRunning = true;
poty.newSize = newSize;
if (!poty.wide) poty.data.lastThumbSize = newSize;
if (poty.imageSizeInCache) {
return poty.secureCall('resizeThumbsCb');
}
if (0 === poty.imgsToQuery.length) poty.imgsToQuery = poty.arrImgs;
var toQuery = poty.imgsToQuery.slice(0,50);
poty.imgsToQuery = poty.imgsToQuery.slice(50);
poty.queryAPI({
action: 'query',
prop: 'imageinfo',
iiprop: 'url',
iiurlwidth: poty.availableImageSizes[newSize].w,
iiurlheight: poty.availableImageSizes[newSize].h,
titles: toQuery.join('|'),
requestid: newSize
}, 'resizeThumbsCb', undefined, 'POST');
if (!poty.loaderShown) {
poty.showImmediateBannerMessage(['poty-year']);
poty.showLoader();
}
},
resizeThumbsCb: function (r) {
var newSize = r ? r.requestid : poty.newSize;
var wasReady = true;
if (poty.imgsToQuery.length) {
// If there are images to query in the pipe,
// immediately query the remaining ones to speed-up execution
wasReady = poty.resizeRunning = false;
poty._resizeThumbs(poty.newSize);
}
var doResize = function(img) {
// Prepare for resizing
var newFullW = poty.availableImageSizes[newSize].w,
newFullH = poty.availableImageSizes[newSize].h,
$galleryBox = img.el.parents('li.gallerybox');
// Resize the gallerybox
var $vDiv = $galleryBox.css('width', newFullW+25).children('div').css('width', newFullW+25).children('div.thumb').css('width', newFullW+20).children('div');
if (!(newSize in img.sizes)) return;
var newW = img.sizes[newSize].w;
var newH = img.sizes[newSize].h;
// Video support
var $player = $galleryBox.find('div.mwPlayerContainer');
if ($player.length) {
$galleryBox.find('div.mwPlayerContainer').css('width', newW).css('height', newH);
}
var $miniPreview = $galleryBox.find('div.PopUpMediaTransform');
$miniPreview.removeAttr('style');
img.el.attr('src', img.sizes[newSize].url);
img.el.css('width', newW).attr('width', newW);
img.el.css('height', newH).attr('height', newH);
$vDiv.css('margin', Math.round(newFullH+20-newH)/2 + 'px auto');
};
if (r) {
var pgs = r.query.pages;
$.each(pgs, function(idx, pg) {
if (!pg.imageinfo) return;
var ii = pg.imageinfo[0],
img = poty.images[pg.title.replace('File:', '')];
// Save result
img.sizes[newSize] = { url: ii.thumburl, h: ii.thumbheight, w: ii.thumbwidth };
doResize(img);
});
} else if (poty.imageSizeInCache) {
$.each(poty.images, function(i, img) {
doResize(img);
});
}
if (wasReady) {
poty.resizeRunning = false;
poty.saveData();
if (poty.availableImageSizes[poty.newSize].w < poty.iconOnlyWidthThreshold) {
poty.buttons.$text.hide();
} else {
poty.buttons.$text.show();
}
if (poty.tasks.length) {
poty.nextTask();
} else {
poty.hideLoader();
}
}
},
setUpButtons: function() {
var statsLabel = poty.getMessage('vote-stats'),
$voteButtonText = $('<span>', { 'class': 'poty-vote-button-text' }),
$statusButtonText = $('<span>', { 'class': 'poty-vote-button-text', text: statsLabel });
if (poty.isVotingPage) {
var fileName = poty.getFileNameFromPageName(),
label = poty.data[poty.galleryType].votes[fileName] ? poty.getMessage('vote-remove') : poty.getMessage('vote-add'),
iconClass = poty.data[poty.galleryType].votes[fileName] ? 'poty-ui-icon-vote-minus' : 'poty-ui-icon-vote-plus',
$container = $('#potyEasyVoteEnhancedButton'),
$vbt = $voteButtonText.clone().text(label);
poty.buttons.$text = poty.buttons.$text.add($vbt);
$('<button>', { title: label })
.prepend($vbt)
.prepend($('<span>', { 'class': iconClass }))
.button({ disabled: poty.state !== poty.galleryType || !poty.data.eligible }).appendTo($container).data('potyImgName', fileName).click(poty.voteThroughVotingpage);
} else {
$.each(poty.images, function(i, img) {
var label = poty.data[poty.galleryType].votes[i] ? poty.getMessage('vote-remove') : poty.getMessage('vote-add'),
$galleryBox = img.el.parents('li.gallerybox'),
$galleryText = $galleryBox.find('div.gallerytext'),
oldText = $galleryText.text(''),
$vbt = $voteButtonText.clone().text(label),
$sbt = $statusButtonText.clone(),
$votingButton = $('<button>', { css: { 'float': 'left' }, title: label })
.prepend($vbt)
.prepend($('<span>', { 'class': poty.data[poty.galleryType].votes[i] ? 'poty-ui-icon-vote-minus' : 'poty-ui-icon-vote-plus' }))
.button({ disabled: poty.state !== poty.galleryType || !poty.data.eligible }).appendTo($galleryText).data('potyImgName', i).click(poty.voteThroughGallery),
$statsButton = $('<button>', { css: { 'float': 'right' }, title: statsLabel })
.prepend($sbt)
.prepend($('<span>', { 'class': 'poty-ui-icon-vote-stats' }))
.button().appendTo($galleryText).data('potyImgName', i).click(poty.showStats);
// jQuery UI destroys the reference to $vbt
poty.buttons.$text = poty.buttons.$text.add($votingButton.find('.poty-vote-button-text')).add($statsButton.find('.poty-vote-button-text'));
});
if (poty.availableImageSizes[poty.newSize].w < poty.iconOnlyWidthThreshold) poty.buttons.$text.hide();
}
// Enforcing single vote
if ('R2' === poty.state && 0 !== poty.getVoteCount(poty.data.R2.votes)) {
$('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', true);
}
poty.nextTask();
},
detachGallery: function() {
var h = poty.$gallery.height();
poty.$gallery.$placeholder = $('<div>').text('Doing sophisticated magic!').height(h).appendTo(poty.$gallery.$oldParent);
poty.$gallery.detach();
poty.nextTask();
},
attachGallery: function() {
poty.$gallery.$placeholder.remove();
poty.$gallery.appendTo(poty.$gallery.$oldParent);
poty.nextTask();
},
enqueueGalleryShuffle: function() {
if (Math.seedrandom) {
poty.secureCall('shuffleGallery');
} else {
$(document).bind('scriptLoaded.POTYListenerR', function(e, st) {
if ('SeedRandom' === st) {
$(document).unbind('scriptLoaded.POTYListenerR');
poty.secureCall('shuffleGallery');
}
});
}
poty.nextTask();
},
// seeded "randomization" based on the user-name
shuffleGallery: function() {
// Initialize the generator with the user-name
Math.seedrandom(poty.username);
// Detaching from DOM to speed-up large gallery-shuffle
// if we would just know $li.length ...
poty.$gallery.detach().each(function (i, ul) {
var $ul = $(ul),
$li = $ul.children('li.gallerybox');
while ($li.length) {
$ul.append($li.splice(Math.floor(Math.seededrandom() * $li.length), 1)[0]);
}
});
if (!poty.willAttachLater) poty.$gallery.appendTo(poty.$gallery.$oldParent);
poty.saveData();
},
checkLanguage: function() {
// Only change the language automatically for new users who haven't set their language yet
var browserLang = navigator.userLanguage || navigator.language || navigator.browserLanguage;
if (!poty.dataExist && poty.data.local.editcount < 5 && browserLang !== poty.userlanguage && 'en' === poty.userlanguage && browserLang in wpAvailableLanguages) {
poty.secureCall('changeLangTo', browserLang);
} else {
poty.nextTask();
}
},
/********************************
**
** My POTY
**
********************************/
installMyPOTY: function() {
var $p = mw.util.addPortletLink('p-personal', '#', poty.getMessage('my-poty-link'), 'pt-poty', poty.getMessage('my-poty-link-tooltip'), '', document.getElementById('pt-logout'));
if ($p) {
$p = $($p);
$p.click(poty.myPOTY);
}
poty.nextTask();
},
changeLangTo: function(lcId, cb) {
var _savedPrefs = function() {
if ($.isFunction(cb)) {
cb();
} else {
poty.userlanguage = lcId;
poty.secureCall('loadTranslation');
}
};
var _gotPrefs = function(r) {
var $r = $(r);
$r.find('#mw-input-wplanguage').val(lcId);
$r.find('#mw-prefs-form').ajaxSubmit(_savedPrefs);
};
$.get(mw.util.getUrl('Special:Preferences'), '', _gotPrefs);
},
myPOTY: function(e) {
if (e) e.preventDefault();
var $d = $('<div>');
$('<div>', { css: { 'float': 'right' } }).append($('<img>', { src: poty.logo, css: { width: '80px', height: 'auto' } })).appendTo($d);
$('<div>', { css: { 'font-size': 'smaller' }, text: poty.getMessage('poty-full-commons') + '; ' + poty.getMessage('my-poty-app-version', poty.version) }).appendTo($d);
var $l = $('<div>', { id: 'myPotyLang' }).append($('<h2>', { text: poty.getMessage('my-poty-language') })).appendTo($d);
var $s = $('<div>', { id: 'myPotyState' }).append($('<h2>', { text: poty.getMessage('my-poty-state') })).appendTo($d);
var $e = $('<div>', { id: 'myPotyEligibility' }).append($('<h2>', { text: poty.getMessage('my-poty-eligibility') })).appendTo($d);
var $v = $('<div>', { id: 'myPotyVotes' }).append($('<h2>', { text: poty.getMessage('my-poty-votes') })).appendTo($d);
var $a = $('<div>', { id: 'myPotyData' }).append($('<h2>', { text: poty.getMessage('my-poty-data') })).appendTo($d);
// Language
var $ls;
$('<button>', { text: poty.getMessage('my-poty-action-language-saveoncommons'), style: 'float:right;' })
.button({ icons: { primary: 'ui-icon-disk' }}).click(function() {
var $btn = $(this);
$btn.unbind('click');
var _done = function() {
$btn.unblock();
poty.reloadPage();
};
$btn.block({ message: $.createSpinner() });
poty.changeLangTo($ls.val(), _done);
}).appendTo($l);
$ls = $('<select>', { size: 1 }).appendTo($l);
$.each(wpAvailableLanguages, function(s, l) {
$ls.append($('<option>', { 'value': s, text: l }));
});
$ls.val(poty.userlanguage);
// State
var statetext;
switch (poty.state) {
case 'R1':
case 'R2':
statetext = poty.getMessage('my-poty-state-RX', poty.state.slice(1));
break;
case 'novote':
statetext = poty.getMessage('my-poty-state-novote');
break;
}
$('<p>', { text: statetext + ' ' + poty.getMessage('my-poty-state-g-RX', poty.galleryType.slice(1)) }).appendTo($s);
// Eligibility
$('<button>', { text: poty.getMessage('my-poty-action-eligibility-recheck'), style: 'float:right;' })
.button({ icons: { primary: 'ui-icon-search' }}).click(function() {
delete poty.data.ineligible;
delete poty.data.eligible;
poty.saveData();
poty.reloadPage();
}).appendTo($e);
if (poty.data.eligible) {
$('<p>', { text: poty.getMessage('eligible', poty.data.eligible.edits, poty.data.eligible.on.name, poty.getDateFromMWDate(poty.required.editsBefore).toLocaleString()) }).appendTo($e);
} else {
var reason = firstItemName(poty.data.ineligible),
item = firstItem(poty.data.ineligible),
args = ['ineligible-' + reason];
/*jshint onecase:true*/
switch (reason) {
case 'blocked':
args.push(item.by, item.reason, item.exp);
break;
default:
args.push(poty.required.minEditCount, poty.getDateFromMWDate(poty.required.editsBefore).toLocaleString());
break;
}
$('<p>', { text: poty.getMessage.apply(this, args) }).appendTo($e);
}
// Votes
$('<button>', { text: poty.getMessage('my-poty-action-votes-recheck'), style: 'float:right;' })
.button({ icons: { primary: 'ui-icon-search' }}).click(function() {
var $btn = $(this);
$btn.unbind('click');
$btn.block({ message: $.createSpinner() });
// TODO: Find a cleaner way
poty.tasks = [];
poty.addTask(function() {
$d.dialog('close');
poty.myPOTY();
});
poty.contribsDigger();
}).appendTo($v);
var listVotes = function(r) {
$('<h3>', { text: r }).appendTo($v);
var $vl = $('<ul>', { css: { 'max-height': '150px', 'overflow': 'auto' } }).appendTo($v);
$.each(poty.data[r].votes, function(f, bool) {
if (bool) $('<li>').append($('<a>', { href: mw.util.getUrl('File:' + f), text: f }), ' ', $('<a>', { href: poty.getVotingPageURL(f, r), text: '(vp)' })).appendTo($vl);
});
};
listVotes('R1');
listVotes('R2');
// Data
var $dd = $('<div>', { css: { 'max-height': '100px', overflow: 'scroll', background: '#fff', border: '1px dotted gray' }, text: $.toJSON(poty.data) }).appendTo($a);
poty.createNotifyArea($('<span>', { text: poty.getMessage('my-poty-action-data-remove-warn') }), 'ui-icon-alert', 'ui-state-highlight').appendTo($a);
$('<button>', { text: poty.getMessage('my-poty-action-data-remove') }).appendTo($a).button({ icons: { primary: 'ui-icon-trash' }}).click(function() {
poty.deleteData();
poty.data = poty.getData();
$dd.text($.toJSON(poty.data));
});
$d.dialog({
title: poty.getMessage('my-poty-h1'),
modal: true,
height: 'auto',
width: Math.min($(window).width(), 700),
close: function() {
$(this).remove();
}
});
},
// Contibs digger
diggerRunning: false,
digMap: {},
uccontinue: '',
contribsDigger: function() {
if (poty.diggerRunning) poty.log('abort new instance: digger is running');
poty.diggerRunning = true;
poty.uccontinue = '',
poty.data.R1.votes = {};
poty.data.R2.votes = {};
poty.secureCall('digContribs');
},
digContribs: function() {
poty.queryAPI({
action: 'query',
rawcontinue: '',
list: 'usercontribs',
uclimit: 'max',
ucend: poty.startDate,
uccontinue: poty.uccontinue,
ucuser: poty.username,
ucnamespace: 4,
ucprop: 'title'
}, 'digContribsCb', undefined, 'POST');
},
digContribsCb: function(r) {
var uc = r.query.usercontribs;
if (0 === uc.length) return poty.digVotingPages();
$.each(uc, function(i, c){
if (poty.votingPageRegExp.test(c.title)) poty.digMap[c.title] = true;
});
if (r['query-continue']) {
poty.uccontinue = r['query-continue'].usercontribs.ucstart;
poty.digContribs();
} else {
return poty.secureCall('digVotingPages');
}
},
digVotingPages: function() {
var toQuery = [];
$.each(poty.digMap, function(p, b) {
if (b) {
toQuery.push(p);
poty.digMap[p] = false;
if (toQuery > 20) return false;
}
});
if (toQuery.length) {
poty.queryAPI({
action: 'query',
prop: 'revisions',
rvprop: 'content',
redirects: true,
titles: toQuery.join('|')
}, 'digVotingPagesCb', undefined, 'POST');
} else {
poty.diggerRunning = false;
poty.saveData();
poty.nextTask();
}
},
digVotingPagesCb: function(r) {
var pgs = r.query.pages;
$.each(pgs, function(ids, p) {
// Page moved, deleted, whatever that should never happen in usercontribs ...
if (!p.revisions) return;
var content = p.revisions[0]['*'];
var title = p.title;
poty.voteRegExp.lastIndex = 0;
if (poty.voteRegExp.test(content)) {
var r, t, m = title.match(poty.votingPageRegExp);
r = m[1].replace('Finalists', 'R2');
t = m[2].replace('File:', '');
poty.data[r].votes[t] = true;
}
});
poty.digVotingPages();
},
/********************************
**
** Statistics
**
********************************/
showStats: function() {
var $b = $(this),
$gb = $b.parents('li.gallerybox'),
fileName = $b.data('potyImgName'),
$chart = $('<div>').text('Loading chart');
// Incompatible browsers
var incompat = {
'camino': 2 // Blacklisted due to a report by [[User:Kersti Nebelsiek]] on [[Special:Permalink/72839941#Results?]] (was version 1.6 but let's be sure)
};
var clnt = $.client.profile();
if ((!isCanvasSupported() && 'msie' !== clnt.name) || ((clnt.name in incompat) && (incompat[clnt.name] >= clnt.versionNumber))) {
window.location = poty.getVotingPageURL(fileName);
return;
}
var sparklineLoaded = false,
statsLoaded = false;
if ($.fn.sparkline) sparklineLoaded = true;
if (!sparklineLoaded) importScript('User:Rillke/jquery.sparkline.js');
var isReady = function(e, st) {
if (sparklineLoaded && statsLoaded) poty.secureCall('showStatsCb', $chart, $gb, fileName);
};
$(document).bind('scriptLoaded.POTYListener', function(e, st) {
if ('jquery.sparkline' === st) {
$(document).unbind('scriptLoaded.POTYListener');
sparklineLoaded = true;
isReady();
}
});
poty.secureCall('loadStats', function() {
statsLoaded = true;
isReady();
});
var w = $gb.width();
w = Math.min(w < 350 ? Math.round((400/w)*40) : 30, 90);
$gb.block({
message: $('<div>', { style: 'font-size:smaller' })
.text(poty.getMessage('stats-chart-desc'))
.append($chart)
.append($('<a>', { href: poty.getVotingPageURL(fileName), text: poty.getMessage('stats-votelist') })),
css: {
width: w + '%'
}
});
$gb.find('.blockUI').css({'cursor': 'default'}).attr('title', poty.getMessage('stats-close-click')).click(function() { $gb.unblock(); });
setTimeout(function() {
$gb.unblock();
}, 10000);
},
showStatsCb: function($chart, $gb, fileName) {
var diffAvgVots = [],
imageVots = [],
maxVoteCount = 0,
maxDiff = 0;
imageVots = poty.statistics[fileName];
$.each(imageVots, function(i, votecount) {
var totalVotes = 0,
voteCont = 0;
$.each(poty.statistics, function(fName, votes) {
if ('number' === typeof votes[i]) {
if (votes[i] > maxVoteCount) maxVoteCount = votes[i];
totalVotes += votes[i];
voteCont++;
}
});
var diff = votecount-Math.round(totalVotes/voteCont);
if (Math.abs(diff) > maxDiff) maxDiff = Math.abs(diff);
diffAvgVots.push(diff);
});
// height: diff of max equals 60px
var h = Math.round(maxDiff*60/maxVoteCount) + 5;
setTimeout(function() {
$chart.sparkline(diffAvgVots, { height: h + 'px', width: '80%', type: 'bar', fillColor: false });
}, 500);
},
genStats: function() {
poty.genericVoteRegExp = new RegExp(poty.mdEscapeSpecial($.escapeRE(poty.votingFormat)).replace(/%UserName%/g, '[^\\|\\[\\]]+'), 'g');
mw.loader.using(['jquery.json'], function() {
poty.gapfrom = '';
poty.secureCall('loadStats', poty.updateStats);
});
},
loadStats: function(cb) {
if (poty.statistics) return cb();
$.ajax({
url: mw.util.wikiScript(),
dataType: 'script',
data: {
title: 'User:Rillke/POTYStats' + poty.year + poty.galleryType + '.js',
action: 'raw',
ctype: 'text/javascript',
// Disallow caching
maxage: 0,
smaxage: 0
},
cache: false,
complete: function() {
if (!poty.statistics) poty.statistics = {};
if (!poty.statisticsDates) poty.statisticsDates = [new Date()];
return cb();
}
});
},
updateStats: function() {
poty.queryAPI({
action: 'query',
rawcontinue: '',
generator: 'allpages',
gapnamespace: 4,
gapfilterredir: 'nonredirects',
gaplimit: 100,
gapprefix: 'Picture of the Year/' + poty.year + '/' + (poty.galleryType === 'R1' ? 'R1' : 'Finalists') + '/',
gapfrom: poty.gapfrom,
prop: 'revisions',
rvprop: 'content'
}, 'updateStatsCB', undefined, 'POST');
},
updateStatsCB: function(r) {
var pgs = r.query.pages;
$.each(pgs, function(ids, pg) {
var c = pg.revisions[0]['*'],
t = pg.title,
f = poty.getFileNameFromPageName(t),
m = c.match(poty.genericVoteRegExp),
l = 0;
if (m) l = m.length;
if (!(f in poty.statistics)) poty.statistics[f] = [0];
poty.statistics[f].push(l);
});
if (!r['query-continue']) {
poty.statisticsDates.push(new Date());
mw.libs.commons.api.editPage({
editType: 'text',
title: 'User:Rillke/POTYStats' + poty.year + poty.galleryType + '.js',
text: 'POTY.statistics=' + $.toJSON(poty.statistics) + ';\n\n' +
'POTY.statisticsDates=' + $.toJSON(poty.statisticsDates) + ';\n\n' +
"$(document).triggerHandler(\'statsLoaded\', [\'POTYStats" + poty.year + "\', POTY.statistics]);",
summary: 'updating voting statistics',
recreate: false,
minor: true,
watchlist: 'nochange'
});
} else {
poty.gapfrom = r['query-continue'].allpages.gapcontinue;
poty.secureCall('updateStats');
}
},
/********************************
**
** Voting
**
********************************/
voteR2Listener: function() {
poty.nextTask();
// Will be executed when there is time to
var __handleStorageChange = function() {
var oldVotecount = poty.getVoteCount(poty.data.R2.votes);
$.jStorage.reInit();
poty.data = poty.getData();
var newVoteCount = poty.getVoteCount(poty.data.R2.votes);
if (0 === newVoteCount) {
var $minusButtons = $('.poty-ui-icon-vote-minus');
var addMsg = poty.getMessage('vote-add');
$minusButtons.parents('button').attr('title', addMsg).find('.poty-vote-button-text').text(addMsg);
$minusButtons.removeClass('poty-ui-icon-vote-minus').addClass('poty-ui-icon-vote-plus');
$('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', false);
} else {
$('.poty-ui-icon-vote-minus, .poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', true);
// Reload page only if user removed vote (prevents reload-loops between tabs)
if (oldVotecount !== newVoteCount) poty.reloadPage();
}
};
var t = 0;
$.jStorage.listenKeyChange(poty.storageKey, function(e) {
clearTimeout(t);
t = setTimeout(__handleStorageChange, 750);
});
},
voteBlockImg: function($el, msg) {
if ('R1' === poty.galleryType) {
var w = $el.width();
w = Math.min(w < 350 ? Math.round((400/w)*40) : 30, 90);
$el.block({
message: poty.getMessage(msg),
css: {
width: w + '%'
}
});
} else {
poty.secureCall('showLoader');
poty.secureCall('showImmediateBannerMessage', [msg]);
}
},
voteUnblockImg: function($el) {
if ('R1' === poty.galleryType) {
setTimeout(function () {
$el.unblock();
}, 1000);
} else {
poty.secureCall('hideLoader');
}
},
voteMessageAndUnblock: function($el, msg) {
if ('R1' === poty.galleryType) {
poty.voteBlockImg($el, msg);
setTimeout(function () {
poty.voteUnblockImg($el);
}, 2000);
} else {
setTimeout(function () {
poty.secureCall('hideLoader');
}, 2000);
poty.secureCall('showImmediateBannerMessage', [msg]);
}
},
voteSetButtonPlus: function($b) {
$b.find('.poty-ui-icon-vote-minus').removeClass('poty-ui-icon-vote-minus').addClass('poty-ui-icon-vote-plus');
var addMsg = poty.getMessage('vote-add');
$b.attr('title', addMsg).find('.poty-vote-button-text').text(addMsg);
},
voteSetButtonMinus: function($b) {
$b.find('.poty-ui-icon-vote-plus').removeClass('poty-ui-icon-vote-plus').addClass('poty-ui-icon-vote-minus');
var rmMsg = poty.getMessage('vote-remove');
$b.attr('title', rmMsg).find('.poty-vote-button-text').text(rmMsg);
},
voteThroughGallery: function() {
var $b = $(this),
fileName = $b.data('potyImgName'),
$img = poty.images[fileName].el,
$galleryBox = $img.parents('li.gallerybox');
poty.secureCall('vote', fileName, $galleryBox, $b);
},
voteThroughVotingpage: function() {
var $b = $(this),
fileName = poty.getFileNameFromPageName();
poty.secureCall('vote', fileName, $b.parent(), $b, poty.reloadPage);
},
getVoteCount: function(votelist) {
var c = 0;
$.each(votelist, function(i, b) {
if (b) c++;
});
return c;
},
vote: function(fileName, $toBlock, $b, readyCb) {
var votingPage = poty.getVotingPage(fileName),
addText = poty.formattedVote,
removeRegExp = poty.voteRegExp,
basetimestamp = '',
starttimestamp = '',
wikitext = '',
remove = poty.data[poty.galleryType].votes[fileName];
if (remove) {
poty.voteBlockImg($toBlock, 'voting-remove-vote');
} else {
poty.voteBlockImg($toBlock, 'voting-vote');
}
var _goToVotingPage = function() {
window.location = poty.getVotingPageURL(fileName);
};
var __addVoteOk = function(r) {
poty.voteSetButtonMinus($b);
if ('R1' === poty.state && 0 === poty.getVoteCount(poty.data.R1.votes)) {
poty.voteMessageAndUnblock($toBlock, 'vote-multiple-possible');
} else if ('R2' === poty.state) {
// Enforcing single vote
poty.voteMessageAndUnblock($toBlock, 'vote-single-only');
$('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', true);
} else {
poty.voteUnblockImg($toBlock);
}
poty.data[poty.galleryType].votes[fileName] = true;
if ('undefined' !== typeof r.edit['new']) {
poty.autoreport('++created ' + votingPage);
}
poty.saveData();
if ($.isFunction(readyCb)) readyCb();
};
var __addVoteErr = function(t, r, q) {
poty.voteMessageAndUnblock($toBlock, 'voting-edit-error');
poty.fail('voteadd ' + votingPage + '; ' + t, _goToVotingPage);
};
var _addVote = function() {
mw.libs.commons.api.editPage({
cb: __addVoteOk,
errCb: __addVoteErr,
editType: 'appendtext',
title: votingPage,
text: addText,
summary: poty.votingSummaryAdd
.replace('%wiki%', poty.data.eligible.on.name)
.replace('%edits%', poty.data.eligible.edits)
.replace('%VoteFrom%', mw.config.get('wgPageName').replace(/_/g, ' ')),
recreate: false,
minor: true,
redirect: true,
watchlist: 'nochange'
});
};
var __removeVoteOk = function(r) {
poty.voteSetButtonPlus($b);
poty.voteUnblockImg($toBlock);
poty.data[poty.galleryType].votes[fileName] = false;
poty.saveData();
if ('R2' === poty.state && 0 === poty.getVoteCount(poty.data.R2.votes)) {
// Enforcing single vote
$('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', false);
}
if ($.isFunction(readyCb)) readyCb();
};
var __removeVoteErr = function(t, r, q) {
poty.voteMessageAndUnblock($toBlock, 'voting-edit-error');
poty.fail('voteremove ' + votingPage + '; ' + t, _goToVotingPage);
};
var _removeVote = function() {
var newText = wikitext.replace(removeRegExp, '');
mw.libs.commons.api.editPage({
cb: __removeVoteOk,
errCb: __removeVoteErr,
editType: 'text',
title: votingPage,
text: newText,
summary: poty.votingSummaryRemove
.replace('%VoteFrom%', mw.config.get('wgPageName').replace(/_/g, ' ')),
recreate: false,
minor: true,
redirect: true,
watchlist: 'nochange'
});
};
var _gotWikitext = function(r) {
try {
var p = firstItem(r.query.pages),
rv;
if (p.revisions) {
rv = p.revisions[0];
starttimestamp = p.starttimestamp;
basetimestamp = r.timestamp;
wikitext = rv['*'];
} else {
wikitext = '';
}
// Cave:
// http://stackoverflow.com/questions/1520800/why-regexp-with-global-flag-in-javascript-give-wrong-results
removeRegExp.lastIndex = 0;
var contains = removeRegExp.test(wikitext);
if (remove) {
if (contains) {
_removeVote();
} else {
poty.voteMessageAndUnblock($toBlock, 'vote-nothing-to-remove');
poty.voteSetButtonPlus($b);
poty.data[poty.galleryType].votes[fileName] = false;
}
} else {
if (contains) {
poty.voteMessageAndUnblock($toBlock, 'vote-already-there');
poty.voteSetButtonMinus($b);
poty.data[poty.galleryType].votes[fileName] = true;
} else {
_addVote();
}
}
} catch (ex) {
poty.voteMessageAndUnblock($toBlock, 'voting-app-error');
poty.fail(ex + ' at ' + votingPage, _goToVotingPage);
}
poty.saveData();
};
poty.queryAPI({
action: 'query',
prop: 'info|revisions',
intoken: 'edit',
rvprop: 'timestamp|content',
rvlimit: 1,
titles: votingPage,
redirects: 1
}, _gotWikitext, null, 'POST');
poty.saveData();
},
/********************************
**
** Eligiblity check
**
********************************/
showIneligible: function () {
poty.saveData();
poty.log('ineligible');
poty.secureCall('continueEligible');
},
showEligible: function () {
poty.saveData();
poty.log('Eligible!');
poty.secureCall('continueEligible');
},
continueEligible: function () {
// Just task scheduling
poty.tasks = [];
poty.buttons = {
$text: $()
};
if (!poty.isVotingPage) {
poty.addTask('detachGallery');
poty.addTask('setUpGallery');
}
poty.addTask('installMyPOTY');
if (!poty.isVotingPage) poty.addTask('installSlideshow');
if (!poty.dataExist && poty.data.local.editcount > 0) poty.addTask('contribsDigger');
poty.addTask('setUpButtons');
if ('R2' === poty.state && 'R2' === poty.galleryType) poty.addTask('voteR2Listener');
if (!poty.isVotingPage) {
poty.addTask('enqueueGalleryShuffle');
poty.willAttachLater = true;
poty.addTask('attachGallery');
}
poty.addTask('hideLoader');
poty.nextTask();
},
checkLocal: function () {
poty.queryAPI({
action: 'query',
meta: 'userinfo',
uiprop: 'blockinfo|ratelimits|editcount|registrationdate|preferencestoken'
}, 'checkLocalCb');
},
checkLocalCb: function (r) {
poty.log('userinfo', r);
var ui = r.query.userinfo;
poty.username = ui.name;
if (ui.ratelimits.ip) {
var l = ui.ratelimits.edit.ip;
if (l) {
poty.ratelimit = Math.floor(l.hits / (l.seconds / 60));
}
}
if (ui.blockexpiry) {
poty.data.ineligible = {
blocked: {
by: ui.blockedby,
reason: ui.blockreason,
exp: ui.blockexpiry
}
};
return poty.secureCall('showIneligible');
} else {
if (poty.data.ineligible && poty.data.ineligible.blocked) poty.data.ineligible = null;
}
if (poty.data.local.editcount && poty.data.local.id !== ui.id) {
poty.log('Wrong user data. Ereasing...');
poty.deleteData();
poty.data = {};
poty.saveData();
return poty.secureCall('relaunch');
}
poty.data.local = {
id: ui.id,
editcount: ui.editcount,
registrationdate: ui.registrationdate
};
poty.preferencestoken = ui.preferencestoken;
poty.nextTask();
if ('string' === typeof ui.anon && !mw.user.isAnon()) {
return poty.showAnonWarning();
}
},
checkSUL: function () {
poty.queryAPI({
action: 'query',
meta: 'globaluserinfo',
guiprop: 'merged',
guiuser: poty.username
}, 'checkSULCb');
},
checkSULCb: function (r) {
var sul = r.query.globaluserinfo,
sulmap = {},
sularr = [];
if (typeof sul.missing !== 'undefined') {
poty.data.sulmissing = true;
return poty.nextTask();
}
poty.data.sul = {
creationTime: sul.registration,
id: sul.id
};
$.each(sul.merged, function (i, account) {
// Map the number of edits to wikis and also add the edit-numbers to an array
if (account.wiki === 'commonswiki') return;
if (!sulmap[account.editcount]) sulmap[account.editcount] = [];
sulmap[account.editcount].push(account);
sularr.push(account.editcount - 0);
});
var numsort = function (n1, n2) {
return n1 - n2;
};
sularr.sort(numsort);
poty.sulInfo = [];
var seenEditCount = -1;
var i = sularr.length - 1;
for (; i !== -1; i--) {
var editcount = sularr[i];
if (editcount === seenEditCount) continue;
if (editcount < poty.required.minEditCount) continue;
poty.sulInfo.push(sulmap[editcount]);
seenEditCount = editcount;
}
if (0 === poty.sulInfo.length && poty.data.local.editcount < poty.required.minEditCount) {
poty.data.ineligible = {
suleditcount: true
};
return poty.secureCall('showIneligible');
}
poty.nextTask();
},
getDbList: function () {
poty.queryAPI({
action: 'sitematrix'
}, 'getDbListCb');
},
getDbListCb: function (r) {
poty.sitematrix = {};
$.each(r.sitematrix, function (i, sites) {
if (!isNumber(i)) return;
$.each(sites.site, function (x, s) {
poty.sitematrix[s.dbname] = {
api: s.url.replace('http://', '//') + '/w/api.php',
langcode: sites.code,
typecode: s.code
};
});
});
$.each(r.sitematrix.specials, function (i, s) {
poty.sitematrix[s.dbname] = {
api: s.url.replace('http://', document.location.protocol + '//') + '/w/api.php',
specialcode: s.code
};
});
poty.nextTask();
},
checkLocalExistingContribs: function () {
poty.queryAPI({
action: 'query',
list: 'usercontribs',
ucuser: poty.username,
ucstart: poty.required.editsBefore,
uclimit: poty.required.minEditCount,
ucprop: '',
requestid: 'commonswiki'
}, 'checkLocalExistingContribsCb');
},
checkLocalExistingContribsCb: function (r) {
var uc = r.query.usercontribs;
if (uc.length < poty.required.minEditCount) {
// Not enough edits
return poty.nextTask();
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: uc.length + '+'
};
poty.showEligible();
},
checkLocalContribs: function () {
poty.queryAPI({
action: 'userdailycontribs',
user: poty.username,
daysago: poty.required.editsDaysAgo,
basetimestamp: poty.required.editsBefore,
requestid: 'commonswiki'
}, 'checkLocalContribsCb');
},
checkLocalContribsCb: function (r) {
var uc = r.userdailycontribs;
// First check the registration date
if (parseInt(uc.registration, 10) > parseInt(poty.required.registeredBefore.replace(/\D/g, ''), 10)) {
// Registered later
return poty.nextTask();
}
if (parseInt(uc.timeFrameEdits, 10) < poty.required.minEditCount) {
// Not enough edits
return poty.nextTask();
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: uc.timeFrameEdits,
registration: uc.registration
};
poty.showEligible();
},
checkGlobalExistingContribs: function () {
if (poty.data.sulmissing) {
poty.data.ineligible = {
nosul: true
};
return poty.secureCall('showIneligible');
}
// Next group of contribs-count
poty.currentEditGroup++;
if (!poty.sulInfo[poty.currentEditGroup]) {
poty.data.ineligible = {
dateeditcount: true
};
return poty.secureCall('showIneligible');
}
$.each(poty.sulInfo[poty.currentEditGroup], function (i, s) {
if (!(s.wiki in poty.sitematrix)) throw new Error('There are contributions in CentralAuth for an unknown wiki.');
var url = poty.sitematrix[s.wiki].api;
poty.queryAPI({
action: 'query',
list: 'usercontribs',
ucuser: poty.username,
ucstart: poty.required.editsBefore,
uclimit: poty.required.minEditCount,
ucprop: '',
requestid: s.wiki
}, 'checkGlobalExistingContribsCb', url);
poty.queriesRunning++;
});
},
checkGlobalExistingContribsCb: function (r) {
poty.queriesRunning--;
var uc = r.query.usercontribs;
if (poty.data.eligible) return;
if (uc.length < poty.required.minEditCount) {
// Not enough edits
if (0 === poty.queriesRunning) poty.secureCall('checkGlobalExistingContribs');
return;
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: uc.length + '+'
};
poty.showEligible();
},
checkGlobalContribs: function () {
if (poty.data.sulmissing) {
poty.data.ineligible = {
nosul: true
};
return poty.secureCall('showIneligible');
}
// Next group of contribs-count
poty.currentEditGroup++;
if (!poty.sulInfo[poty.currentEditGroup]) {
poty.data.ineligible = {
dateeditcount: true
};
return poty.secureCall('showIneligible');
}
$.each(poty.sulInfo[poty.currentEditGroup], function (i, s) {
if (!(s.wiki in poty.sitematrix)) throw new Error('There are contributions in CentralAuth for an unknown wiki.');
var url = poty.sitematrix[s.wiki].api;
poty.queryAPI({
action: 'userdailycontribs',
user: poty.username,
daysago: poty.required.editsDaysAgo,
basetimestamp: poty.required.editsBefore,
requestid: s.wiki
}, 'checkGlobalContribsCb', url);
poty.queriesRunning++;
});
},
checkGlobalContribsCb: function (r) {
poty.queriesRunning--;
var uc = r.userdailycontribs;
if (poty.data.eligible) return;
// First check the registration date
if (parseInt(uc.registration, 10) > parseInt(poty.required.registeredBefore.replace(/\D/g, ''), 10)) {
// Registered later
if (0 === poty.queriesRunning) poty.secureCall('checkGlobalContribs');
return;
}
if (parseInt(uc.timeFrameEdits, 10) < poty.required.minEditCount) {
// Not enough edits
if (0 === poty.queriesRunning) poty.secureCall('checkGlobalContribs');
return;
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: uc.timeFrameEdits,
registration: uc.registration
};
poty.showEligible();
},
loadTranslation: function() {
var l = poty.userlanguage;
switch (l) {
case 'nb':
l = 'no';
break;
case 'zh-hans':
case 'zh-cn':
case 'zh-sg':
case 'zh-my':
l = 'zh-hans';
break;
default:
l = l.split('-')[0];
}
if (poty.userlanguage !== 'en') {
poty.translationLoaded = true;
$.ajax({
url: mw.util.wikiScript(),
dataType: 'script',
data: {
title: 'MediaWiki:EnhancedPOTY.js/' + l + '.js',
action: 'raw',
ctype: 'text/javascript',
// Allow caching for 1/2 day
maxage: 43200,
smaxage: 43200
},
cache: true,
success: poty.nextTask,
error: poty.nextTask
});
} else {
poty.nextTask();
}
},
/**
** Does a MediaWiki API request and passes the result to the supplied callback.
**/
queryAPI: function (params, callback, url, method) {
mw.libs.commons.api.query(params,
{
cache: false,
url: url,
method: method,
cb: function(r) {
poty.secureCall(callback, r);
},
// r-result, query, text
errCb: function(t, r, q) {
poty.fail(t);
}
});
},
fail: function (err, cb) {
poty.log('error', err);
if (typeof err === 'object') {
var stErr = err.message + ' \n\n ' + err.name;
if (err.lineNumber) stErr += ' @line' + err.lineNumber;
err = stErr;
}
var $dlg = $('<div>'),
dlgBtns = {};
if (((poty.data.eligible && !poty.data.eligible.on) || -1 !== err.indexOf(' \'on\'')) && $.toJSON) err = err + ' e:' + $.toJSON(poty.data.eligible) + ';\n i:' + $.toJSON(poty.data.ineligible);
dlgBtns[poty.getMessage('report-error-send')] = function() {
$dlg.parent().block();
poty.autoreport(err, function() {
$dlg.dialog('close');
$dlg.remove();
if ($.isFunction(cb)) cb();
});
};
dlgBtns[poty.getMessage('report-error-reset')] = function() {
// Delete saved data as this could be a problem
poty.deleteData();
// Purge the page and reloadPage
poty.purgePage();
$dlg.parent().block();
};
dlgBtns[poty.getMessage('report-error-cancel')] = function() {
$dlg.dialog('close');
};
try {
poty.hideLoader();
} catch (ex) {}
poty.createNotifyArea($('<span>', { text: poty.getMessage('report-error', err) }), 'ui-icon-alert', 'ui-state-error').appendTo($dlg);
$dlg.dialog({
title: poty.getMessage('report-error-h1'),
buttons: dlgBtns,
modal: true,
height: 'auto',
width: Math.min($(window).width(), 500),
open: function() {
// Look out for http://bugs.jqueryui.com/ticket/6830 / jQuery UI 1.9
var $buttons = $(this).parent().find('.ui-dialog-buttonpane button');
$buttons.eq(0).button({ icons: { primary: 'ui-icon-circle-check' } });
$buttons.eq(1).button({ icons: { primary: 'ui-icon-wrench' } });
$buttons.eq(2).button({ icons: { primary: 'ui-icon-circle-close' } });
},
close: function() {
poty.hideLoader();
}
});
},
autoreport: function (errText, cb) {
var randomId = Math.round(Math.random()*1099511627776);
var currentTask = $.isFunction(poty.currentTask) ? (poty.currentTask.name ? poty.currentTask.name : 'inline') : poty.currentTask;
var toSend = '\n== Autoreport by POTY ' + randomId + ' ==\n' + errText +
'\n++++\n:Task: ' + currentTask + '\n:NextTask: ' + poty.tasks[0] + '\n:LastTask: ' + poty.tasks[poty.tasks.length - 1] +
'\n:Page: ' + (mw.config.get('wgPageName')) + '\n:Skin: ' + mw.user.options.get('skin') +
'\n:[{{fullurl:Special:Contributions|target={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Contribs before error]';
mw.libs.commons.api.editPage({
cb: cb,
errCb: cb,
editType: 'appendtext',
title: 'MediaWiki talk:EnhancedPOTY.js/auto-reports',
text: toSend,
summary: '[[#Autoreport by POTY ' + randomId + '|Reporting a POTY-App error.]] Random ID=' + randomId,
minor: true,
watchlist: 'nochange'
});
},
/**
** Method to catch errors and report where they occurred
**/
secureCall: function (fn) {
var o = poty;
try {
o.currentTask = arguments[0];
if ($.isFunction(fn)) {
if (fn.name) poty.log(fn);
return fn.apply(o, Array.prototype.slice.call(arguments, 1)); // arguments is not of type array so we can't just write arguments.slice
} else if ('string' === typeof fn) {
poty.log(fn);
return o[fn].apply(o, Array.prototype.slice.call(arguments, 1)); // arguments is not of type array so we can't just write arguments.slice
} else {
poty.log('This is not a function!');
}
} catch (ex) {
poty.log('failure at ' + fn);
o.fail(ex);
}
},
/**
** Simple task queue. addTask() adds a new task to the queue, nextTask() executes
** the next scheduled task. Tasks are specified as method names to call.
**/
tasks: [],
// list of pending tasks
currentTask: '',
// current task, for error reporting
oldTask: '',
// task called before
addTask: function (task) {
poty.tasks.push(task);
},
nextTask: function () {
var task = poty.currentTask = poty.tasks.shift();
return ($.isArray(task) ? poty.secureCall.apply(poty, task) : poty.secureCall(task)); // Ja da guckste ...
},
lastTask: function () {
var task = poty.currentTask = poty.tasks[poty.tasks.length - 1];
poty.tasks = [];
return ($.isArray(task) ? poty.secureCall.apply(poty, task) : poty.secureCall(task));
},
log: function (key, val) {
if (window.console && $.isFunction(window.console.log)) window.console.log('POTY> ' + key, val/*, this*/);
},
reloadPage: function () {
window.location = window.location;
},
purgePage: function (page) {
window.location = mw.util.wikiScript('index') + '?' + $.param({ title: mw.config.get('wgPageName') || page.replace(/ /g, '_'), action: 'purge' });
}
};
poty.secureCall('init');
})(jQuery, mediaWiki);
// </nowiki>