MediaWiki:Gadget-EnhancedPOTY.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.
Documentation for this user script can be added at MediaWiki:Gadget-EnhancedPOTY. This user script seems to have an accompanying .css page at MediaWiki:Gadget-EnhancedPOTY.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-2016
* @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 (1===1) {
throw new Error('This script is currently not maintained. It could pose a security risk to the site or otherwise cause harm. Do not remove this line until you are know what you are doing.');
}*/
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': "$1 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-vote-info': "Info",
'poty-vote-next-in-set': "Next set image",
'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-limited': "You can vote for {{Plural:$1|ONE picture|$1 pictures}} in total",
'poty-stats-chart-desc': "Vote count compared to the average vote count per picture: ",
'poty-stats-votelist': "Votelist",
'poty-stats-comments': "Comments",
'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': "$1-Version $2",
'poty-my-poty-language': "Language",
'poty-my-poty-eligibility': "Eligibility",
'poty-my-poty-votes': "Votes",
'poty-my-poty-state': "Contest Status",
'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 language",
'poty-my-poty-action-eligibility-recheck': "Check again",
'poty-my-poty-action-votes-recheck': "Check again",
'poty-my-poty-action-data-remove': "Remove",
'poty-my-poty-action-data-remove-warn': "After removing this data $1 won\'t know what you have voted for."
});
poty = window.POTY = {
/********************************
**
** Configuration
**
********************************/
version: '0.6.10.0',
// The key to be used to store the data in the user's browser
storageKey: ((new Date()).getFullYear()-1).toString() + '-POTY2', // 'YEAR-POTY2'
// POTY <year>
year: (new Date()).getFullYear()-1,
// Will be retrieved from the template $('#potyVotingState')
// Keep this 'novote'. Anything using this must be after the state is loaded properly
state: 'novote',
// Required e.g. for digging the contribs to a specific date
// Also update [[MediaWiki:Gadget-POTYEnhancements.core.js]] which will override this
startDate: '2020-09-18T15:00:00Z',
appName: 'POTY-App',
// Eligibility requirements
required: {
minEditCount: 76,
editsBefore: (new Date()).getFullYear().toString() + '-01-01T00:00:00Z',
editsDaysAgo: 7000,
registeredBefore: (new Date()).getFullYear().toString() + '-01-01T00:00:00Z',
includeDeleted: false
},
// Max votes
maxVotes: {
'R1': '~',
'R2': 3,
'novote': 0
},
roundCount: 2,
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]]',
reportPage: 'MediaWiki talk:Gadget-EnhancedPOTY.js/auto-reports',
// These sizes will be offered in the galleries
availableImageSizes: [{w: 1776, h: 1000}, {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,
galleryBoxRenderer: $.noop,
listSelector: 'li.gallerybox',
containerSelector: '#potyEasyVoteEnhanced',
gallerySelector: 'ul.gallery',
setGallerySelector: '.poty-file-sets',
fancyFontClass: 'com-poty-fancy-font',
username: window.debugPOTYUserName || mw.config.get('wgUserName') || (window.Geo?('User from '+Geo.city):'') || 'anonymous',
userlanguage: mw.config.get('wgUserLanguage'),
ratelimit: -1 === $.inArray('autoconfirmed', mw.config.get('wgUserGroups')) ? 8 : -1,
// For which round is this gallery?
galleryType: (function() {
var m = mw.config.get('wgPageName').match(/\d{4}\/(R\d)/);
return (m && m[1]) || 'Rx';
}()),
votingPageRegExp: /^Commons:Picture of the Year\/\d{4}\/(R\d)\/v\/([^\/]{5,})$/,
galleryRegExp: /^Commons:Picture of the Year\/\d{4}\/R\d\/Gallery\/(.+)/,
pageNamespace: 4,
pageBase: 'Picture of the Year',
statsPage: 'User:Rillke/POTYStats',
useStats: false,
infoFromFileDescriptionPage: true,
wiki: mw.config.get('wgDBname'),
eligibilityTemplate: '{{Special:Contributions/$user|limit=$count|offset=$offset}}',
// Style & CSS
stylesheet: 'MediaWiki:Gadget-EnhancedPOTY.css',
logo: '//upload.wikimedia.org/wikipedia/commons/d/d4/POTY_barnstar.svg',
// When thumbs are resized to small sizes, text would overflow otherwise
iconOnlyWidthThreshold: 250,
fullScreenElement: {
'R1': document.documentElement,
'R2': '#content',
'novote': document.documentElement
},
// Global functions to be used
importScript: window.importScript,
importStylesheet: window.importStylesheet,
availableLanguages: window.wpAvailableLanguages || window.wgULSLanguages || {},
wikiScript: mw.util.wikiScript(),
// Variables merely for internal use
translationLoaded: false,
maxVotesR: 0,
totalVoters: 0,
voters: {},
// Everything that does not need DOM-ready
init: function () {
const match = mw.config.values.wgPageName
.replaceAll("_", " ")
.match(/Picture of the Year\/(\d{4})/);
if (!match || Number(match[1]) > 2022) {
return;
}
// Variable set up
poty.formattedVote = poty.getFormattedVote();
poty.voteRegExp = poty.getVoteRegExp();
poty.lastThumbSize = 'lastThumbSize' + poty.galleryType;
poty.availableImageSizes.reverse();
// Enqueue tasks
poty.addTask('loadTranslation');
poty.addTask('loadTranslationDone');
poty.addTask('splash');
poty.addTask('loadComponents');
poty.addTask('launch');
poty.nextTask();
},
/**************************
*
* The next bunch of Helpers
*
**************************/
/** 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})(?:\.\d{3})?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?
},
forEachRound: function(cb) {
for (var i = 1; i <= poty.roundCount; ++i) {
cb('R' + i);
}
},
////////////////////////////
///////// 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),
def = {
novote: {
votes: {}
},
local: {
}
};
poty.forEachRound(function(r) {
def[r] = {
votes: {}
};
});
return $.extend(true, d, def);
},
deleteData: function() {
try {
return $.jStorage.deleteKey(poty.storageKey);
} catch (ex) {
return ex;
}
},
dataExists: function() {
return !!$.jStorage.get(poty.storageKey);
},
getXPage: function(p, r, t) {
return mw.config.get('wgFormattedNamespaces')[poty.pageNamespace] + ':' + poty.pageBase + '/' + poty.year + '/' + (r || poty.galleryType) + '/' + t + '/' + p;
},
getVotingPage: function (p, r) {
return poty.getXPage(p, r, 'v');
},
getVotingPageURL: function (p, r) {
return mw.util.wikiScript('index') + '?' + $.param({ title: poty.getVotingPage(p, r).replace(/ /g, '_') });
},
getInfoPage: function (p, r) {
return poty.getXPage(p, r, 'i');
},
getCommentsPage: function (p, r) {
return poty.getXPage(p, r, 'c');
},
getCommentsPageURL: function (p, r) {
return mw.util.wikiScript('index') + '?' + $.param({ title: poty.getCommentsPage(p, r).replace(/ /g, '_') });
},
getFormattedVote: function () {
return poty.votingFormat.replace(/%UserName%/g, poty.username);
},
getVoteRegExp: function () {
return new RegExp(poty.mdEscapeSpecial(mw.RegExp.escape(poty.votingFormat.replace(/%UserName%/g, poty.username))), 'g');
},
/** Helper-Escape some special characters that mw.RegExp.escape 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) {
pagename = pagename || mw.config.get('wgPageName');
var m = pagename.replace(/_/g, ' ').match(poty.votingPageRegExp);
if (!m) {
poty.log("Invalid voting page " + pagename);
return '';
}
return m[2];
},
getGalleryTypeFromPageName: function(pagename) {
pagename = pagename || mw.config.get('wgPageName');
var m = pagename.replace(/_/g, ' ').match(poty.votingPageRegExp);
if (!m) {
poty.log("Invalid voting page " + pagename);
return '';
}
return m[1];
},
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] || m[3];
},
getStatsPage: function() {
return poty.statsPage + poty.year + poty.galleryType + '.js';
},
/********************************
**
** Startup
**
********************************/
loadComponents: function () {
mw.loader.using(['mediawiki.util', 'ext.gadget.libAPI', 'ext.gadget.libUtil', 'ext.gadget.jquery.fullscreen', 'ext.gadget.math.seedrandom', 'ext.gadget.SettingsManager', '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');
$.jStorage.deleteKey('2012POTY');
$.jStorage.deleteKey('2014-POTY');
$.jStorage.deleteKey('2014VVA-n');
$.jStorage.deleteKey('2013POTY_x');
$.jStorage.deleteKey('2014-POTY-2');
poty.dataExist = poty.dataExists();
poty.wide = !!$('#potyWide').length;
if (poty.wide) {
poty.availableImageSizes = poty.availableImageSizesWide.reverse();
}
poty.state = $('#potyVotingState').text().match(/during(R\d)/);
poty.state = ( poty.state ? poty.state[1] : 'novote' );
poty.maxVotesR = poty.maxVotes[poty.state];
var msgToShow = 'poty-full-commons';
switch (poty.maxVotesR) {
case '~':
if (poty.state === poty.galleryType) msgToShow = 'vote-multiple-possible';
break;
case 0:
// Show default message
break;
default:
if (poty.state === poty.galleryType) msgToShow = 'vote-limited';
break;
}
poty.showBannerMessage([msgToShow, poty.maxVotesR]);
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('checkLocalExistingContribsFast');
poty.addTask('checkGlobalExistingContribsFast');
}
}
poty.addTask('showEligible');
poty.nextTask();
},
/********************************
**
** UI: Gallery, styling
**
********************************/
splash: function () {
poty.$gallery = $(poty.containerSelector).find(poty.gallerySelector);
poty.$gallery.$oldParent = poty.$gallery.parent();
poty.$setGalleries = $(poty.setGallerySelector);
poty.$myPotyPanelContainer = $('#com-my-poty-panel-container');
poty.fancyFontClass = $('#potyFontSupported').text() ? poty.fancyFontClass : '';
var l = poty.$gallery.find(poty.listSelector).length;
if (l < 2 || l > 250) {
if ($('#potyEasyVoteEnhancedButton').length){
poty.isVotingPage = true;
document.title = poty.getFileNameFromPageName() + ' - ' + poty.getMessage('poty-full-commons');
} else if ($('#votesByDate').length) {
return poty.secureCall('advancedStats');
} else if (poty.$myPotyPanelContainer.length) {
return poty.secureCall('myPOTYPanelPage');
} else {
return poty.log('Startup aborted', l);
}
} else {
document.title = ($('#poty-gallery-heading').text() || poty.getGalleryNameFromPageName()) + ' - ' + poty.getMessage('poty-full-commons');
}
if (!mw.loader.getState('ext.gadget.POTYEnhancements.core')) {
poty.importStylesheet(poty.stylesheet);
}
poty.secureCall('showSplash');
},
showSplash: function() {
mw.loader.using(['ext.gadget.jquery.blockUI', 'jquery.client', 'jquery.ui', 'mediawiki.jqueryMsg', 'mediawiki.util'], 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();
},
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');
$.blockUI({
message: poty.$loadBannerOuter,
blockMsgClass: 'poty-splash-foreground blockMsg ' + poty.fancyFontClass
});
var clnt = $.client.profile();
if ('msie' === clnt.name && clnt.versionNumber >= 9) {
$('.poty-splash-foreground').addClass('ms-ie-9-nukefilter');
}
$('.blockOverlay').addClass('poty-splash-background');
},
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;
},
/********************************
**
** Sets
**
********************************/
parseSets: function(currentThumbSize, currentThumbSizeWidth) {
var sets = poty.setFiles = {},
$sets = poty.$setGalleries;
$sets.find('ul.gallery').each(function(i, ul) {
var set = { $ul: null, members: [] },
$ul = set.$ul = $(ul);
$ul.find('div.thumb a.image img:first-child').each(function(x, img) {
var $el = $(img),
// We cannot use .height() because this return 0 in webkit for detached elements
h = Number($el.attr('height')) || parseFloat($el.css('height')),
w = Number($el.attr('width')) || parseFloat($el.css('width')),
$gb = $el.parents(poty.listSelector),
src = $el.attr('src') || $el.attr('poster'),
fileName = mw.libs.commons.titleFromImgSrc(src),
imgInfo = poty.images[fileName] = { el: $el, sizes: {}, name: fileName, $gb: $gb };
imgInfo.sizes[currentThumbSize] = { url: src, h: h, w: w };
poty.arrImgs.push('File:' + fileName);
$el.data('potyInfo', imgInfo);
$gb.addClass('poty-element').css('width', currentThumbSizeWidth+25).children('div').css('width', currentThumbSizeWidth+25).children('div.thumb').css('width', currentThumbSizeWidth+20);
set.members.push({ name: fileName, $gb: $gb, $el: $el });
sets[fileName] = set;
});
});
setTimeout(function() {
// Do this the next time being idle ...
$sets.hide();
}, 5);
return sets;
},
setRenderer: function($gb, $el, src, fileName) {
// Turn sets into "image stacks"
var set = poty.setFiles[fileName];
if (!set) return;
$.each(set.members, function(i, setItem) {
if (setItem.name === fileName) {
set.coverImage = i;
set.currentIndex = i;
}/* else {
// Keep them hidden but they are curated by POTY app like any other candidate
}*/
});
// Add set-indicators
poty.images[fileName].set = set;
$gb.addClass('poty-set-candidate');
},
nextInSet: function() {
var $b = $(this),
$gb = $b.closest(poty.listSelector),
fileName = $b.data('potyImgName'),
img = poty.images[fileName],
set = img.set,
idx = set.currentIndex,
idxNew = (idx + 1) % set.members.length,
$currentTh = $gb.find('div.thumb'),
$currentCont= $currentTh.parent(),
$nextTh = set.members[idxNew].$gb.find('div.thumb');
// Transfer file currently shown to hidden set-store
$currentTh.prependTo(set.members[idx].$gb.children('div').first().empty());
$currentCont.prepend($nextTh);
set.currentIndex = idxNew;
},
resetSetPositions: function() {
$.each(poty.setFiles, function(fn, set) {
if (set.coverImage === set.currentIndex) return;
// First, transfer the file currently shown back to hidden set-store
var coverImg = set.members[set.coverImage],
$candidateTh = poty.images[coverImg.name].$gb.find('div.thumb'),
$candidateCont = $candidateTh.parent();
$candidateTh.prependTo(set.members[set.currentIndex].$gb.children('div').first());
// Then transfer the cover-image to its default position
coverImg.$gb.find('div.thumb').prependTo($candidateCont);
set.currentIndex = set.coverImage;
});
},
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') || $el[0].width || 0;
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.failedImages = {};
poty.arrImgs = [];
poty.secureCall('parseSets', currentThumbSize, currentThumbSizeWidth);
$imgs.each(function(i, el) {
var $el = $(el),
// We cannot use .height() because this return 0 in webkit for detached elements
h = Number($el.attr('height')) || parseFloat($el.css('height')),
w = Number($el.attr('width')) || parseFloat($el.css('width')),
$gb = $el.parents(poty.listSelector),
src = $el.attr('src') || $el.attr('poster'),
srcset = $el.attr('srcset'),
fileName = mw.libs.commons.titleFromImgSrc(src),
imgInfo = poty.images[fileName] = { el: $el, sizes: {}, name: fileName, $gb: $gb };
imgInfo.sizes[currentThumbSize] = { url: src, h: h, w: w, srcset: srcset };
poty.arrImgs.push('File:' + fileName);
$el.data('potyInfo', imgInfo);
$el.on('error', poty._imgLoadFailed);
$el.on('load', poty._imgLoadSucceeded);
$gb.addClass('poty-element').css('width', currentThumbSizeWidth+25).children('div').css('width', currentThumbSizeWidth+25).children('div.thumb').css('width', currentThumbSizeWidth+20);
try {
poty.galleryBoxRenderer($gb, $el, src);
poty.setRenderer($gb, $el, src, fileName);
} catch (ex) {
poty.log(ex);
}
});
// 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)
.on('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 = poty.$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();
var lastUsed = poty.data[poty.lastThumbSize];
if ($.isNumeric(lastUsed) && (lastUsed-0) !== currentThumbSize && !poty.wide) {
$sizeSlider.slider('option', 'value', lastUsed);
} else {
poty.nextTask();
}
},
_workOnLoadFailedImages: function () {
var count = 0,
imgName, data, oldSrc, oldSrcset;
function applyOldSrcAfterDelay($img, src, srcset) {
return setTimeout(function() {
if ($img.attr('src')) {
return;
}
$img.attr({
'src': src,
'srcset': srcset
});
}, 100);
}
for (imgName in poty.failedImages) {
if (poty.failedImages.hasOwnProperty(imgName)) {
poty.log('Retrying: ', imgName);
data = poty.failedImages[imgName];
oldSrc = data.el.attr('src');
oldSrcset = data.el.attr('srcset');
data.el.attr({
'src': '',
'srcset': ''
});
applyOldSrcAfterDelay(data.el, oldSrc, oldSrcset);
delete poty.failedImages[imgName];
if (count > 2) {
poty.failedImagesQueueTimeout =
setTimeout(poty._workOnLoadFailedImages, 2000);
return;
} else {
count++;
}
}
}
if (count === 0) {
clearTimeout(poty.failedImagesQueueTimeout);
}
},
_imgLoadFailed: function (e) {
var $img = $(e.target),
data = $img.data('potyInfo'),
$gb = data.$gb;
$gb.addClass('poty-load-error');
poty.failedImages[data.name] = data;
if (poty.failedImagesQueueTimeout) {
clearTimeout(poty.failedImagesQueueTimeout);
}
poty.failedImagesQueueTimeout =
setTimeout(poty._workOnLoadFailedImages, 2000);
},
_imgLoadSucceeded: function (e) {
var $img = $(e.target),
data = $img.data('potyInfo'),
$gb = data.$gb;
if (poty.failedImages[data.name]) {
$gb.removeClass('poty-load-error');
delete poty.failedImages[data.name];
}
},
installSlideshow: function () {
var $buttons = $('<span>').attr('id', 'potyViewButtons'),
slideshowVisiblityChanged = $.noop(),
$fullscreen,
$slideshow,
currentFullscreenElement;
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).on('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);
}
if ('visibility' === st && o) {
slideshowVisiblityChanged(o);
}
});
poty.log('loading Slideshow');
poty.importScript('MediaWiki:GallerySlideshow.js');
poty.importStylesheet('MediaWiki:Gadget-GallerySlideshow.css');
}
};
if ($.FullScreenSupported) {
var oldCSS = '';
var _goFullScreen = function(prevent, elem, css) {
var $elem;
if (false !== prevent) {
elem = elem || poty.fullScreenElement[poty.galleryType];
$elem = $(elem);
if (elem !== document.documentElement) {
oldCSS = oldCSS || {
overflow: $elem.css('overflow'),
margin: $elem.css('margin')
};
$elem.css(css || {
'overflow': 'auto',
'margin': 0
});
}
$elem.requestFullScreen();
currentFullscreenElement = elem;
}
var $el = $fullscreen;
$el.off('click', _fullScreen).click(_closeFullScreen);
$el.button({ label: poty.getMessage('fullscreen-close'), icons: { primary: 'ui-icon-newwin' } });
};
var _fullScreen = function(prevent) {
_goFullScreen(prevent);
};
var _closeFullScreen = function(prevent) {
var elem;
if (false !== prevent) {
$.FullScreen.cancelFullScreen();
}
elem = poty.fullScreenElement[poty.galleryType];
if (elem !== document.documentElement && oldCSS) {
$(elem).css(oldCSS);
}
var $el = $fullscreen;
$el.off('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);
}
});
slideshowVisiblityChanged = function( $elem ) {
if ($elem.css('display') === 'none' && $.FullScreen.isFullScreen()) {
_closeFullScreen();
}
};
$(mw).on('EmbedPlayerUpdateDependencies', function() {
if (currentFullscreenElement !== document.documentElement && $.FullScreen.isFullScreen()) {
_closeFullScreen();
}
});
$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() {
// Slideshow needs document to be fullscreen
// otherwise it's hidden
if ($fullscreen) {
var requiredElem = document.documentElement;
if (currentFullscreenElement !== requiredElem && $.FullScreen.isFullScreen()) {
_closeFullScreen();
}
if(!$.FullScreen.isFullScreen()) {
_goFullScreen(null, requiredElem, {
'overflow': 'hidden'
});
}
}
loadSlideshowAndStart(0, true);
})
.appendTo($buttons);
if ($fullscreen) $buttons.buttonset();
$buttons.appendTo(poty.viewContainer);
poty.nextTask();
},
imgsToQuery: [],
resizeThumbs: function (newSize) {
if (poty.resizeRunning) return;
poty.$sizeSlider.slider( 'option', 'disabled', true );
poty.resetSetPositions();
poty.imageSizeInCache = newSize in firstItem(poty.images).sizes;
poty._resizeThumbs(newSize);
},
_calcThumbDimensions: function (hDesired, wDesired) {
if (window.devicePixelRatio) {
var dpr = Math.max(window.devicePixelRatio, 1);
hDesired *= dpr;
wDesired *= dpr;
// Find the next size bucket
var bucket;
for (var i = 0; i < poty.availableImageSizes.length; i++) {
bucket = poty.availableImageSizes[i];
if (hDesired <= bucket.h && wDesired <= bucket.w) {
break;
}
}
hDesired = bucket.h;
wDesired = bucket.w;
}
return { h: hDesired, w: wDesired };
},
_getThumbDimensionsForSize: function (size) {
var bucket = poty.availableImageSizes[size],
dtd = bucket.deviceThumbDimensions;
if (!dtd) {
dtd = poty._calcThumbDimensions(bucket.h, bucket.w);
bucket.deviceThumbDimensions = dtd;
}
return dtd;
},
_resizeThumbs: function (newSize) {
if (poty.resizeRunning) return;
poty.resizeRunning = true;
poty.newSize = newSize;
if (!poty.wide) poty.data[poty.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);
var deviceThumbDimensions = poty._getThumbDimensionsForSize(newSize);
poty.queryAPI({
action: 'query',
prop: 'imageinfo',
iiprop: 'url',
iiurlwidth: deviceThumbDimensions.w,
iiurlheight: deviceThumbDimensions.h,
titles: toQuery.join('|'),
requestid: newSize
}, 'resizeThumbsCb', undefined, 'POST');
if (!poty.loaderShown) {
poty.showImmediateBannerMessage(['poty-year']);
poty.showLoader();
}
},
_limitThumbSizeTo: function (from, constraint) {
var wRatio = from.w / constraint.w,
hRatio = from.h / constraint.h,
ratio = 1;
if (wRatio <= 1 && hRatio <= 1) {
// ratio remains 1
} else if (wRatio > hRatio) {
// Constraint width
ratio = wRatio;
} else {
// Constraint height
ratio = hRatio;
}
return { w: Math.round( from.w / ratio ), h: Math.round( from.h / ratio ) };
},
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.$gb;
// 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 constraintSizes = poty._limitThumbSizeTo(
img.sizes[newSize], poty.availableImageSizes[newSize]);
var newW = constraintSizes.w;
var newH = constraintSizes.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.attr('srcset', img.sizes[newSize].srcset || '');
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.$sizeSlider.slider( 'option', 'disabled', false );
poty.saveData();
if (poty.availableImageSizes[poty.newSize].w < poty.iconOnlyWidthThreshold) {
if (poty.buttons) poty.buttons.$text.hide();
} else {
if (poty.buttons) poty.buttons.$text.show();
}
if (poty.tasks.length) {
poty.nextTask();
} else {
poty.hideLoader();
}
}
},
maybeDisableButtons: function() {
// Enforcing x vote count
if (isNumber(poty.maxVotesR) && poty.getCurrentVoteCount() >= poty.maxVotesR) {
poty.buttons.$button.find('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', true);
}
},
galleryButtonRenderer: function( statsLabel, setLabel, $voteButtonText, $statusButtonText, $setButtonText ) {
$.each(poty.images, function(i, img) {
var label = poty.data[poty.galleryType].votes[i] ? poty.getMessage('vote-remove') : poty.getMessage('vote-add'),
$galleryBox = img.$gb,
$galleryText = $galleryBox.find('div.gallerytext'),
oldText = $galleryText.text(''),
$vbt = $voteButtonText.clone().text(label),
$sbt = $statusButtonText.clone(),
$setbt = $setButtonText.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') + ' poty-icon md-inline-icon' }))
.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.useStats ? 'poty-ui-icon-vote-stats' : 'ui-icon ui-icon-info') + ' poty-icon md-inline-icon' }))
.button().appendTo($galleryText).data('potyImgName', i).click(poty.showStatsOrInfo),
$setButton = $();
if (img.set) {
$setButton = $('<button>', { css: { 'float': 'right' }, title: setLabel })
.prepend($setbt)
.prepend($('<span>', { 'class': 'ui-icon ui-icon-carat-2-n-s poty-icon md-inline-icon' }))
.button().appendTo($galleryText).data('potyImgName', i).click(poty.nextInSet);
}
// jQuery UI destroys the reference to $vbt
poty.buttons.$button = poty.buttons.$button.add($votingButton);
poty.buttons.$text = poty.buttons.$text
.add($votingButton.find('.poty-vote-button-text'))
.add($statsButton.find('.poty-vote-button-text'))
.add($setButton.find('.poty-vote-button-text'));
});
if (poty.availableImageSizes[poty.newSize].w < poty.iconOnlyWidthThreshold) poty.buttons.$text.hide();
},
setUpButtons: function() {
poty.buttons = {
$button: $(),
$text: $()
};
var statsLabel = poty.getMessage(poty.useStats ? 'vote-stats' : 'vote-info'),
setLabel = poty.getMessage('vote-next-in-set'),
$voteButtonText = $('<span>', { 'class': 'poty-vote-button-text' }),
$statusButtonText = $('<span>', { 'class': 'poty-vote-button-text', text: statsLabel }),
$setButtonText = $('<span>', { 'class': 'poty-vote-button-text', text: setLabel });
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),
$votingButton = $('<button>', { title: label })
.prepend($vbt)
.prepend($('<span>', { 'class': iconClass + ' poty-icon md-inline-icon' }))
.button({ disabled: poty.state !== poty.galleryType || !poty.data.eligible })
.appendTo($container).data('potyImgName', fileName).click(poty.voteThroughVotingpage);
poty.buttons.$button = poty.buttons.$button.add($votingButton);
poty.buttons.$text = poty.buttons.$text.add($vbt);
} else {
poty.secureCall( 'galleryButtonRenderer' , statsLabel, setLabel, $voteButtonText, $statusButtonText, $setButtonText );
}
poty.secureCall('maybeDisableButtons');
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();
},
// seeded "randomization" based on the user-name
shuffleElements: function($parants, childSelector) {
// Initialize the generator with the user-name
Math.seedrandom(poty.username);
// Detaching from DOM to speed-up large element-shuffle
// if we would just know $li.length ...
$parants.each(function (i, parent) {
var $par = $(parent),
$children = $par.children(childSelector);
while ($children.length) {
$par.append($children.splice(Math.floor(Math.seededrandom() * $children.length), 1)[0]);
}
});
},
shuffleGallery: function() {
poty.secureCall('shuffleElements', poty.$gallery.detach(), poty.listSelector);
if (!poty.willAttachLater) poty.$gallery.appendTo(poty.$gallery.$oldParent);
poty.nextTask();
},
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 poty.availableLanguages) {
poty.secureCall('changeLangTo', browserLang);
} else {
poty.nextTask();
}
},
/********************************
**
** My POTY
**
********************************/
installMyPOTY: function() {
// On the control-panel page, there should be no myPOTY button
if (!poty.$myPotyPanelContainer.length) {
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);
}
$('#com-my-poty-button').click(poty.myPOTY);
}
poty.nextTask();
},
changeLangTo: function(lcId, cb) {
mw.libs.settingsManager.switchPref( 'language', lcId, function() {
if ($.isFunction(cb)) {
cb();
} else {
poty.userlanguage = lcId;
poty.secureCall('loadTranslation');
}
} );
},
myPOTYPanelPage: function() {
document.title = $('h1').not('#firstHeading').text() + ' - ' + poty.getMessage('poty-full-commons');
poty.tasks = [];
poty.addTask('showSplash');
poty.addTask('loadComponents');
poty.addTask('launch');
poty.nextTask();
},
showMyPOTYPanelPage: function() {
poty.$myPotyPanelContainer.empty().append(poty.$myPOTY());
poty.nextTask();
},
myPOTY: function(e) {
var $d = poty.$myPOTY(e);
$d.dialog({
title: poty.getMessage('my-poty-h1'),
modal: true,
height: 'auto',
width: Math.min($(window).width(), 700),
close: function() {
$(this).remove();
}
});
},
$myPOTY: function(e) {
if (e) e.preventDefault();
var $d = $('<div>').attr('id', 'poty-mypoty').css({ 'max-width': 800 });
$('<div>', { css: { 'float': 'right' } }).append($('<img>', { src: poty.logo, css: { width: '80px', display: 'none' } })).appendTo($d);
$('<div>', { css: { 'font-size': 'smaller' }, text: poty.getMessage('poty-full-commons') + '; ' + poty.getMessage('my-poty-app-version', poty.appName, poty.version) }).appendTo($d);
var $l = $('<div>', { id: 'myPotyLang' }).append($('<h3>', { text: poty.getMessage('my-poty-language') })).appendTo($d);
var $s = $('<div>', { id: 'myPotyState' }).append($('<h3>', { text: poty.getMessage('my-poty-state') })).appendTo($d);
var $e = $('<div>', { id: 'myPotyEligibility' }).append($('<h3>', { text: poty.getMessage('my-poty-eligibility') })).appendTo($d);
var $v = $('<div>', { id: 'myPotyVotes' }).append($('<h3>', { text: poty.getMessage('my-poty-votes') })).appendTo($d);
var $a = $('<div>', { id: 'myPotyData' }).append($('<h3>', { 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.off('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(poty.availableLanguages, function(s, l) {
$ls.append($('<option>', { 'value': s, text: l }));
});
$ls.val(poty.userlanguage);
// State
var statetext;
if (/R\d/.test(poty.state)) {
statetext = poty.getMessage('my-poty-state-RX', poty.state.slice(1));
} else {
statetext = poty.getMessage('my-poty-state-novote');
}
$('<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.off('click');
$btn.block({ message: $.createSpinner() });
// TODO: Find a cleaner way
poty.tasks = [];
poty.addTask(function() {
try {
if (poty.$myPotyPanelContainer.length) {
poty.showMyPOTYPanelPage();
} else {
$d.dialog('close');
poty.myPOTY();
}
} catch(ex) {
poty.reloadPage();
}
});
poty.contribsDigger();
}).appendTo($v);
var listVotes = function(r) {
$('<h4>', { text: r }).appendTo($v);
var $vl = $('<ul>', { css: { 'max-height': '100px', 'margin-top': '2em', '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: '(vote page)' })).appendTo($vl);
});
};
poty.forEachRound(listVotes);
// Data
var $dd = $('<div>', { css: { 'max-height': '75px', overflow: 'auto', background: '#fff', border: '1px dotted gray' }, text: JSON.stringify(poty.data) }).appendTo($a);
poty.createNotifyArea($('<span>', { text: poty.getMessage('my-poty-action-data-remove-warn', poty.appName) }), '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(JSON.stringify(poty.data));
});
return $d;
},
// 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.forEachRound(function(r) {
poty.data[r].votes = {};
});
poty.secureCall('digContribs');
},
digContribs: function() {
var query = {
action: 'query',
list: 'usercontribs',
rawcontinue: 1,
uclimit: 'max',
ucend: poty.startDate,
ucuser: poty.username,
ucnamespace: poty.pageNamespace,
ucprop: 'title'
};
if (poty.uccontinue) query.uccontinue = poty.uccontinue;
poty.queryAPI(query, '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.length > 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];
t = m[2].replace('File:', '');
poty.data[r].votes[t] = true;
}
});
poty.digVotingPages();
},
/********************************
**
** Statistics
**
********************************/
showStatsOrInfo: function() {
if (poty.useStats) {
poty.showStats(this);
} else {
poty.showInfo(this);
}
},
getInfo: function(fn, cb) {
poty.infocache = poty.infocache || {};
var inf = poty.infocache[fn];
if (inf) return cb(inf);
if (poty.infoFromFileDescriptionPage) {
poty.queryAPI({
action: 'query',
prop: 'imageinfo',
iilimit: 1,
iiprop: 'timestamp|user|url|extmetadata',
iiextmetadatalanguage: poty.userlanguage,
redirects: 1,
titles: 'File:' + fn
}, function(r) {
var pg = firstItem(r.query.pages);
if (!pg) return cb("- File not found -", true);
var ii = pg.imageinfo[0];
if (!ii) return cb("- No imageinfo for that file -", true);
var imgDesc = ii.extmetadata.ImageDescription;
if (!imgDesc) return cb("- File has no machine-readable description -", true);
// Strip any markup
var desc = $('<a>').html(imgDesc.value).text(),
date = (ii.extmetadata.DateTime || {}).value,
$editLink = $('<a>')
.attr('href', mw.util.wikiScript() + '?' + $.param({
action: 'edit',
title: 'File:' + fn
}) + '#editform')
.attr('title', "Edit description")
.append(poty.mdCreatejIcon('ui-icon-pencil'));
if (date) date = '<hr />' + date;
return cb(desc + ' ' + $('<span>').append($editLink).html() + date, true);
});
} else {
$.get(mw.util.wikiScript() + '?' + $.param({
'action': 'render',
'title': poty.getInfoPage(fn)
})).done(function(r) {
if (r) poty.infocache[fn] = r;
cb(r, true);
}).fail(function() {
cb("- No information available -", true);
});
}
},
showInfo: function(b) {
var $b = $(b),
$gb = $b.closest(poty.listSelector),
fileName = $b.data('potyImgName'),
$info = $('<div>').css('min-height', '3em').text("Loading info"),
w, $blocked,
displayInfo, to;
displayInfo = function(i, repos) {
var newHeight, oldHeight,
parentHeight, newTop;
if ($blocked) oldHeight = $blocked.height();
$info.html(i);
if (!repos || !$blocked) return;
newHeight = $blocked.height();
$info.fadeTo(0, 0).fadeTo('fast', 1);
$blocked.css('overflow', 'hidden').height(oldHeight);
// Compute center position
parentHeight = $blocked.closest(poty.listSelector).height();
newTop = (parentHeight - newHeight)/2;
$blocked.animate({
top: newTop,
height: newHeight
}, 'fast');
};
poty.secureCall('getInfo', fileName, displayInfo);
w = $gb.width();
w = Math.min(w < 350 ? Math.round((400/w)*40) : 30, 90);
$gb.block({
message: $('<div>', { style: 'font-size:.8em; line-height:1.1em' })
.append($info)
.append('<hr/>')
.append($('<a>', { href: poty.getVotingPageURL(fileName), text: poty.getMessage('stats-votelist'), title: poty.getMessage('stats-votelist') }))
/*.append(' • ')
.append($('<a>', { href: poty.getCommentsPageURL(fileName), text: poty.getMessage('stats-comments'), title: poty.getMessage('stats-comments') }))*/,
css: {
width: w + '%',
'border-radius': '5px',
'border': '1px solid #ABE'
}
});
var startTimer = function() {
clearTimeout(to);
to = setTimeout(function() {
$gb.unblock();
}, 10000);
};
$blocked = $gb.find('.blockUI').css({'cursor': 'default'})
.attr('title', poty.getMessage('stats-close-click')).click(function() {
$gb.unblock();
clearTimeout(to);
}).hover(function() {
clearTimeout(to);
}, function() {
startTimer();
}).filter('.blockMsg');
startTimer();
},
showStats: function(b) {
var $b = $(b),
$gb = $b.closest(poty.listSelector),
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;
}
poty.secureCall('loadStats', function() {
mw.loader.using('ext.gadget.jquery.sparkline', function() {
poty.secureCall('showStatsCb', $chart, $gb, fileName);
});
});
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(mw.RegExp.escape(poty.votingFormat)).replace(/%UserName%/g, '[^\\|\\[\\]]+'), 'g');
mw.loader.using([], function() {
poty.gapfrom = '';
poty.secureCall('loadStats', poty.updateStats);
});
},
loadStats: function(cb) {
if (poty.statistics) return cb();
$.ajax({
url: mw.util.wikiScript(),
dataType: 'json',
data: {
title: poty.getStatsPage(),
action: 'raw',
ctype: 'text/javascript',
// Disallow caching
maxage: 0,
smaxage: 0
},
cache: false,
success: function(r) {
poty.statistics = r || [];
$.each(poty.statistics, function(i, d) {
d[0] = new Date(d[0]);
});
cb();
}
});
},
updateStats: function() {
poty.queryAPI({
action: 'query',
generator: 'allpages',
rawcontinue: 1,
gapnamespace: poty.pageNamespace,
gapfilterredir: 'nonredirects',
gaplimit: 100,
gapprefix: poty.pageBase + '/' + poty.year + '/' + poty.galleryType + '/v/',
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 (!f) return;
if (!m) return;
$.each( m, function( i, u ) {
if ( u in poty.voters ) {
return;
}
poty.voters[u] = 1;
l++;
} );
poty.totalVoters += l;
});
if (!r['query-continue']) {
poty.statistics.push([ new Date(), poty.totalVoters ]);
mw.libs.commons.api.editPage({
editType: 'text',
title: poty.getStatsPage(),
text: JSON.stringify(poty.statistics),
summary: 'updating voting statistics',
recreate: false,
minor: true,
watchlist: 'nochange'
});
} else {
poty.gapfrom = r['query-continue'].allpages.gapcontinue;
poty.secureCall('updateStats');
}
},
/*
* Loads heavy statistics plugIn
*/
advancedStats: function() {
mw.loader.using(['ext.gadget.jquery.jqplot']).then(function() {
poty.secureCall('loadStats', poty.showAdvancedStats);
});
},
showAdvancedStats: function() {
var $plot = $('#votesByDate').text('');
$plot.jqplot([poty.statistics], {
title: "Voters versus time",
axesDefaults: {
labelRenderer: $.jqplot.CanvasAxisLabelRenderer
},
gridPadding: { right: 35 },
axes:{
xaxis:{
renderer: $.jqplot.DateAxisRenderer,
label: "Time"
},
yaxis:{
label: "Voters",
pad: 0
}
},
series:[{ lineWidth: 1 }],
highlighter: {
show: true,
sizeAdjust: 7.5
},
cursor: {
show: true,
zoom:true,
showTooltip: false
}
});
// poty.statistics
// poty.statisticsDates
},
/********************************
**
** Voting
**
********************************/
lock: function() {
poty.locked = true;
},
unlock: function() {
poty.locked = false;
},
voteXWindowListener: function() {
poty.nextTask();
var oldCount;
// Will be executed when there is time to
var __handleStorageChange = function() {
$.jStorage.reInit();
poty.data = poty.getData();
var newCount = poty.getCurrentVoteCount();
if (oldCount !== newCount) {
oldCount = newCount;
if (poty.isVotingPage) {
poty.reloadPage();
} else {
// Insert a dummy function
poty.addTask($.noop);
// Recalc buttons
poty.setUpButtons();
}
}
};
var t = 0;
var _perhapsHandleStorageChange = function(key, action) {
clearTimeout(t);
if (poty.locked) return;
t = setTimeout(__handleStorageChange, 750);
};
oldCount = poty.getCurrentVoteCount();
$.jStorage.listenKeyChange(poty.storageKey, _perhapsHandleStorageChange);
},
voteBlockImg: function($el, msg) {
if (!isNumber(poty.maxVotesR) || poty.maxVotesR > 5) {
var w = $el.width();
w = Math.min(w < 350 ? Math.round((400/w)*40) : 30, 90);
$el.block({
message: poty.getMessage(msg, poty.maxVotesR),
css: {
width: w + '%'
}
});
} else {
poty.secureCall('showLoader');
poty.secureCall('showImmediateBannerMessage', [msg]);
}
},
voteUnblockImg: function($el) {
if (!isNumber(poty.maxVotesR) || poty.maxVotesR > 5) {
setTimeout(function () {
$el.unblock();
}, 1000);
} else {
poty.secureCall('hideLoader');
}
},
voteMessageAndUnblock: function($el, msg) {
if (!isNumber(poty.maxVotesR) || poty.maxVotesR > 5) {
poty.voteBlockImg($el, msg);
setTimeout(function () {
poty.voteUnblockImg($el);
}, 2000);
} else {
setTimeout(function () {
poty.secureCall('hideLoader');
}, 2000);
poty.secureCall('showImmediateBannerMessage', [msg, poty.maxVotesR]);
}
},
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'),
ii = poty.images[fileName],
$galleryBox = ii.$gb;
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;
votelist = votelist || {};
$.each(votelist, function(i, b) {
if (b) c++;
});
return c;
},
getCurrentVoteCount: function() {
return poty.getVoteCount((poty.data[poty.galleryType] || {}).votes);
},
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];
// Prevent reloading and other interferring actions
// could be also delegated to other tabs/windows so
// they could be locked
poty.lock();
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 ('~' === poty.maxVotesR && 0 === poty.getCurrentVoteCount()) {
poty.voteMessageAndUnblock($toBlock, 'vote-multiple-possible');
} else if (isNumber(poty.maxVotesR)) {
poty.voteMessageAndUnblock($toBlock, 'vote-limited');
if (poty.getCurrentVoteCount() + 1 >= poty.maxVotesR) {
// Enforcing x vote count
$('.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();
poty.unlock();
if ($.isFunction(readyCb)) readyCb();
};
var __addVoteErr = function(t, r, q) {
poty.voteMessageAndUnblock($toBlock, 'voting-edit-error');
poty.fail('voteadd ' + votingPage + '; ' + t, _goToVotingPage);
poty.unlock();
};
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',
assert: 'user'
});
};
var __removeVoteOk = function(r) {
poty.voteSetButtonPlus($b);
poty.voteUnblockImg($toBlock);
poty.data[poty.galleryType].votes[fileName] = false;
poty.saveData();
if (isNumber(poty.maxVotesR) && poty.getCurrentVoteCount() < poty.maxVotesR) {
// Enforcing x vote count
$('.poty-ui-icon-vote-plus').parents('button').button('option', 'disabled', false);
}
poty.unlock();
if ($.isFunction(readyCb)) readyCb();
};
var __removeVoteErr = function(t, r, q) {
poty.voteMessageAndUnblock($toBlock, 'voting-edit-error');
poty.fail('voteremove ' + votingPage + '; ' + t, _goToVotingPage);
poty.unlock();
};
var _removeVote = function() {
var newText = wikitext.replace(removeRegExp, '');
newText = newText || ' ';
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;
poty.unlock();
}
} else {
if (contains) {
poty.voteMessageAndUnblock($toBlock, 'vote-already-there');
poty.voteSetButtonMinus($b);
poty.data[poty.galleryType].votes[fileName] = true;
poty.unlock();
} 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 = [];
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 (poty.maxVotesR !== '~') poty.addTask('voteXWindowListener');
if (!poty.isVotingPage) {
poty.addTask('shuffleGallery');
poty.willAttachLater = true;
poty.addTask('attachGallery');
}
if (poty.$myPotyPanelContainer.length) {
poty.addTask('showMyPOTYPanelPage');
}
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 = window.debugPOTYUserName || 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 === poty.wiki) 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();
},
checkLocalExistingContribsFast: function () {
var text = poty.eligibilityTemplate
.replace('$user', poty.username)
.replace('$count', poty.required.minEditCount)
.replace('$offset',
poty.required.editsBefore.replace(/[\:\-TZ]/g, ''));
poty.queryAPI({
action: 'parse',
title: 'Commons:POTY',
text: text,
disablelimitreport: 1,
disabletidy: 1,
uselang: 'en',
requestid: poty.wiki
}, 'checkLocalExistingContribsFastCb');
},
checkLocalExistingContribsFastCb: function(r) {
var parsed = r.parse.text['*'],
countribsCount = $(parsed)
.find('.mw-contributions-list')
.addBack('.mw-contributions-list')
.find('li')
.length;
if (countribsCount < poty.required.minEditCount) {
// Not enough edits
return poty.nextTask();
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: countribsCount + '+'
};
poty.showEligible();
},
checkLocalExistingContribsAPI: function () {
// Too slow, see https://phabricator.wikimedia.org/T131065
poty.queryAPI({
action: 'query',
list: 'usercontribs',
ucuser: poty.username,
ucstart: poty.required.editsBefore,
uclimit: poty.required.minEditCount,
ucprop: '',
requestid: poty.wiki
}, 'checkLocalExistingContribsAPICb');
},
checkLocalExistingContribsAPICb: 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: poty.wiki
}, '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();
},
checkGlobalExistingContribsFast: function () {
if (poty.data.sulmissing) {
poty.data.ineligible = {
nosul: true
};
return poty.secureCall('showIneligible');
}
var text = poty.eligibilityTemplate
.replace('$user', poty.username)
.replace('$count', poty.required.minEditCount)
.replace('$offset',
poty.required.editsBefore.replace(/[\:\-TZ]/g, ''));
// 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: 'parse',
title: 'User:Rillke',
text: text,
disablelimitreport: 1,
disabletidy: 1,
uselang: 'en',
requestid: s.wiki
}, 'checkGlobalExistingContribsFastCb', url);
poty.queriesRunning++;
});
},
checkGlobalExistingContribsFastCb: function (r) {
poty.queriesRunning--;
if (poty.data.eligible) return;
var parsed = r.parse.text['*'],
countribsCount = $(parsed)
.find('.mw-contributions-list')
.addBack('.mw-contributions-list')
.find('li')
.length;
if (countribsCount < poty.required.minEditCount) {
// Not enough edits
if (0 === poty.queriesRunning) poty.secureCall('checkGlobalExistingContribsFast');
return;
}
poty.data.eligible = {
on: {
name: r.requestid,
details: poty.sitematrix[r.requestid]
},
edits: countribsCount + '+'
};
poty.showEligible();
},
checkGlobalExistingContribsAPI: 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
}, 'checkGlobalExistingContribsAPICb', url);
poty.queriesRunning++;
});
},
checkGlobalExistingContribsAPICb: 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('checkGlobalExistingContribsAPI');
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: poty.wikiScript,
dataType: 'script',
data: {
title: 'MediaWiki:Gadget-EnhancedPOTY.js/i18n/' + 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();
}
},
/**
** Overwriting this method allows
** changing translations after they have been loaded.
**/
loadTranslationDone: function() {
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,
// no credentials when doing a cross-origin request
withCredentials: !url,
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') {
try {
var stErr = err.message + ' \n\n ' + err.name;
if (err.lineNumber) stErr += ' @line' + err.lineNumber;
err = stErr;
} catch (ex) {
err = '';
}
}
var $dlg = $('<div>'),
dlgBtns = {};
if (((poty.data.eligible && !poty.data.eligible.on) || -1 !== err.indexOf(' \'on\'')) && JSON.stringify) err = err + ' e:' + JSON.stringify(poty.data.eligible) + ';\n i:' + JSON.stringify(poty.data.ineligible);
dlgBtns[poty.getMessage('report-error-send')] = function() {
$dlg.parent().block();
poty.autoreport(err, function() {
poty.unlock();
$dlg.dialog('close');
$dlg.remove();
if ($.isFunction(cb)) cb();
});
};
dlgBtns[poty.getMessage('report-error-reset')] = function() {
poty.unlock();
// 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() {
poty.unlock();
$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', poty.appName),
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 intelliVote ' + randomId + ' ==\n' + errText +
'\n++++\n:Task: ' + currentTask + '\n:NextTask: ' + poty.tasks[0] + '\n:LastTask: ' + poty.tasks[poty.tasks.length - 1] +
'\n:APP Version: ' + poty.version +
'\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 event]';
mw.libs.commons.api.editPage({
cb: cb,
errCb: cb,
editType: 'appendtext',
title: poty.reportPage,
text: toSend,
summary: '[[#Autoreport by intelliVote ' + randomId + '|Reporting a intelliVote-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);
// arguments is not of type array so we can't just write arguments.slice
return fn.apply(o, Array.prototype.slice.call(arguments, 1));
} else if ('string' === typeof fn) {
poty.log(fn);
return o[fn].apply(o, Array.prototype.slice.call(arguments, 1));
} 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 () {
if (!poty.locked) window.location.reload(false);
},
purgePage: function (page) {
window.location = mw.util.wikiScript('index') + '?' + $.param({ title: mw.config.get('wgPageName') || page.replace(/ /g, '_'), action: 'purge' });
}
};
// Now, merge configuration
// (yes, you can use this script not only for POTY and not only on Commons)
window.potyconfig = window.potyconfig || {};
$.extend(true, poty, window.potyconfig);
// Stuff that should be overwritten and not extended.
var noExtend = ['availableImageSizes', 'availableImageSizesWide'];
$.each(noExtend, function(i, k) {
if (k in window.potyconfig) poty[k] = window.potyconfig[k];
});
// And go!
poty.secureCall('init');
})(jQuery, mediaWiki);
// </nowiki>