Welcome to the DFO World Wiki. With many major updates since the release of DFO, many items are missing. Visit Item Database Project to learn more.
Please remember to click "show preview" before saving the page.
Thanks for the updated logo snafuPop!
MediaWiki:Gadget-EnhancedPOTY.js
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/** * 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 (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.4.6', // The key to be used to store the data in the user's browser storageKey: '2014-POTY-2', // POTY <year> year: 2014, // Will be retrieved from the template $('#potyVotingState') state: 'R1', // Required e.g. for digging the contribs to a specific date startDate: '2015-01-17T20:05:00Z', appName: 'POTY-App', // Eligibility requirements required: { minEditCount: 75, editsBefore: '2015-01-01T00:00:00Z', editsDaysAgo: 7000, registeredBefore: '2015-01-01T00:00:00Z', includeDeleted: false }, // Max votes maxVotes: { 'R1': '~', 'R2': 3, 'novote': 0 }, roundCount: 2, votingFormat: '\n# [[User:%UserName%|%UserName%]]', // This avoids disclosing user names in AbuseFilter logs addVoteTemplate: '\n# [[User:{{subst:REVISIONUSER}}|]]', // 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?Geo.IP:'') || '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'), // Style & CSS stylesheet: 'MediaWiki:Gadget-EnhancedPOTY.css', logo: '//upload.wikimedia.org/wikipedia/commons/thumb/d/d4/POTY_barnstar.svg/150px-POTY_barnstar.svg.png', // 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 () { // 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($.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) { 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 () { // TODO: Replace jquery.form with an API call // use [[MediaWiki:Gadget-SettingsManager.js]] mw.loader.using(['mediawiki.util', 'ext.gadget.libAPI', 'ext.gadget.libUtil', 'ext.gadget.jquery.fullscreen', 'ext.gadget.math.seedrandom', 'ext.gadget.SettingsManager', 'jquery.jStorage', 'jquery.spinner', 'jquery.ui.dialog', 'jquery.ui.slider', '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'); 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('checkLocalExistingContribs'); poty.addTask('checkGlobalExistingContribs'); } } 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.effects.clip', 'jquery.effects.blind', 'mediawiki.jqueryMsg'], 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.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'), 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); 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) .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 = 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(); } }, 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).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); } 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.unbind('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) { $(elem).css(oldCSS); } 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); } }); 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); }, _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); 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.$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 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.$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.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(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.unbind('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', 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($.escapeRE(poty.votingFormat)).replace(/%UserName%/g, '[^\\|\\[\\]]+'), 'g'); mw.loader.using(['json'], 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', 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.dateAxisRenderer', 'ext.gadget.jquery.jqplot.canvasRenderer', 'ext.gadget.jquery.jqplot.highlighter'], 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.addVoteTemplate || 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' }); }; 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(); }, checkLocalExistingContribs: function () { poty.queryAPI({ action: 'query', list: 'usercontribs', ucuser: poty.username, ucstart: poty.required.editsBefore, uclimit: poty.required.minEditCount, ucprop: '', requestid: poty.wiki }, '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: 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(); }, 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: 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, 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: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: 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 = window.location; }, 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>