User:Lectrician1/MediaWiki:Gadget-addMe-WishlistSurvey.js
Note: After publishing, 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 / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// <nowiki>
/**
* Fork of [[MediaWiki:Gadget-addMe.js]] for use in the [[Community Wishlist Survey]].
* This gadget will be default-on until voting ends on December 10.
* Please direct inquiries/concerns to [[Talk:Community Wishlist Survey]] or [[User talk:MusikAnimal (WMF)]].
*/
/* global RJSON */
/* eslint-disable no-implicit-globals, one-var, vars-on-top, no-jquery/no-global-selector */
/* eslint-disable no-jquery/no-trim, new-cap, no-useless-concat */
/* eslint-disable no-jquery/no-parse-html-literal, no-multi-str */
/* eslint-disable no-console */
/*
* Common utilities for both the endorse & the join gadget
*/
var gadgetUtilities = function () {
// A reference to the object
var that = this;
/*
* The interface messages or strings are maintained in interfaceMessagesPath & config values eg,
* section-header, the section where the comments are added etc are maintained in configPath
*/
this.interfaceMessagesPath = 'Community Wishlist Survey/AddMe/InterfaceText';
this.configPath = 'Community Wishlist Survey/AddMe/Config';
// The time taken for the page to scroll to the feedback speech bubble (milliseconds)
this.feedbackScrollTime = 2000;
// The time taken for the feedback speech bubble to disappear (milliseconds)
this.feedbackDisappearDelay = 10000;
/*
* This function is used to set a cookie to show the speech bubble
* on page reload
*/
this.setFeedbackCookie = function () {
mw.storage.set('showWishlistSurveyThanks', true);
};
/*
* This function is used to check if a has been set by the above function
* to show the speech bubble on page reload
*/
this.checkFeedbackCookie = function () {
if (mw.storage.get('showWishlistSurveyThanks')) {
mw.storage.remove('showWishlistSurveyThanks');
return true;
} else {
return false;
}
};
/*
* To display an error message when an error occurs
* in the gadget
*/
this.showErrorMessage = function (gadget, type) {
var errorAttr = '[localize=error-' + type + ']';
var gadgetID = '.' + gadget;
$(gadgetID + ' ' + errorAttr).show();
};
/*
* To remove the error message displayed by the above function
*/
this.removeErrorMessage = function (gadget) {
var gadgetID = '.' + gadget;
$(gadgetID + ' [localize^="error-"]').hide();
};
/*
* To detect the type of grant. IEG,PEG etc
*/
this.grantType = function (config) {
var grant = mw.config.get('wgTitle').split('/')[0].replace(/ /g, '_');
if (grant in config) {
return config[grant];
} else {
return config.default;
}
};
/*
* To detect the users default language
*/
this.userLanguage = function () {
return mw.config.get('wgUserLanguage');
};
/*
* To detect the language of the page
*/
this.contentLanguage = function () {
return mw.config.get('wgContentLanguage');
};
/*
* To remove extra spaces & cleanup the comment string
*/
this.cleanupText = function (text) {
text = $.trim(text) + ' ';
var indexOf = text.indexOf('[[User:Lectrician1|Lectrician1]] ([[User talk:Lectrician1|talk]]) 05:26, 6 February 2022 (UTC)');
if (indexOf === -1) {
return text;
} else {
return text.slice(0, indexOf) + text.slice(indexOf + 4);
}
};
/*
* The config files which can be translated with the help of the
* translation tool generates the dict with the values having a
* lot of space in the key value pairs. This function strips the
* whitespace.
*/
this.stripWhiteSpace = function (dict) {
for (var key in dict) {
// Temp fix for section header
if (key === 'section-header') {
dict['section-header-read'] = dict[key].replace(/ /g, '_');
dict['section-header-write'] = dict[key];
}
dict[key] = typeof (dict[key]) === 'object' ? that.stripWhiteSpace(dict[key]) : $.trim(dict[key]);
}
return dict;
};
/*
* The function creates the markup for the link to a
* user's user page
*/
this.addToInfobox = function (username) {
return username;
};
/*
* To localize the gadget's interface messages based on the user's language setting
*/
this.localizeGadget = function (gadgetClass, localizeDict) {
$(gadgetClass + ' [localize]').each(function () {
var localizeValue = localizeDict[$(this).attr('localize')];
if ($(this).attr('value')) {
$(this).attr('value', localizeValue);
} else if ($(this).attr('placeholder')) {
$(this).attr('placeholder', localizeValue);
} else if ($(this).attr('data-placeholder')) {
$(this).attr('data-placeholder', localizeValue);
} else {
$(this).html(localizeValue);
}
});
};
/*
* This function show the feedback speech bubble after an
* endorsement has been made or after joining a project
*/
this.showFeedback = function (config) {
var $li = $('#' + config['section-header-read']).parent().next().find('li').eq(-1);
var $speechBubble = $li.append($('<div class="grantsSpeechBubbleContainer"></div>').html('<div class="grantsSpeechBubble">\
<span>Thank you for participating in the survey!</span></div><div class="grantsSpeechBubbleArrowDown"></div>' )).find('.grantsSpeechBubbleContainer');
var width = $li.css('display', 'inline-block').width();
$li.css('display', '');
$li.css('position', 'relative');
$speechBubble.css('left', width / 2 + 'px');
$('body, html').animate({ scrollTop: $li[0].offsetTop }, that.feedbackScrollTime);
setTimeout(function () {
$speechBubble.hide();
}, that.feedbackDisappearDelay);
};
};
/*
* The Endorse Gadget
*/
var endorseGadget = function () {
/* Variables */
var util = new gadgetUtilities();
var dialog = null;
var api = new mw.Api();
var that = this;
var supportTemplates = '{{\\s*(support|s|yes|vote+|Sì|賛成|موافق|strong support|weak support)\\s*}}';
// Define page information variables to be set by Initialize and used by addEndorsement
var sectionCount = 0;
var sectionFound = false;
var wikitext;
this.Initialize = function () {
api.get({
format: 'json',
action: 'parse',
prop: 'sections',
page: that.title,
uselang: 'en'
}).then(function (result) {
var sections = result.parse.sections;
for (var section in sections) {
if (sections[section].level > 3) {
continue;
}
if ($.trim(sections[section].anchor) === that.config['section-header-read']) {
sectionFound = true;
break;
}
sectionCount++;
}
if (sectionFound) {
api.get({
format: 'json',
action: 'parse',
prop: 'wikitext',
page: that.title,
section: sectionCount
}).then(function (result) {
wikitext = result.parse.wikitext['*'];
// Check if they've already voted.
var regexp = new RegExp(
supportTemplates + '.*?' +
mw.config.get('wgUserName').replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') +
'.*?\\(UTC\\)'
);
if (regexp.test(wikitext)) {
$('.wp-addme-button').css({
"background-color": "#c8ccd1",
"color": "#ffffff",
"border-color": "#c8ccd1",
"cursor": "default",
});
}
// They have not voted yet
else {
// Prepare dialog to be shown
Dialog();
$('.wp-addme-button').off();
$('.wp-addme-button').on('click', function (e) {
e.preventDefault();
// Checking if the user is logged in
if (!mw.config.get('wgUserName')) {
util.showErrorMessage('gadget', 'login');
return;
}
// Show dialog
Dialog();
});
}
});
}
});
};
/*
* This function creates the dialog box for the gadget.
* It is also where all the dialog related interactions are defined.
*/
var createDialog = function () {
dialog = $("<div id='devEndorseDialog'></div>").html(
'<div class="mw-ui-vform">\
<div class="error grantsHide" localize="error-save">An error occurred</div>\
<div class="error grantsHide" localize="error-login">An error occurred</div>\
</div>\
<div localize="message-description" class="messageDescription">Explaining your endorsement improves process</div>' + '\
<textarea rows="5" cols="10" placeholder="Add your comment" id="devEndorseComment" class="" localize="placeholder-comment" style="color: #333;"></textarea>\
<span localize="message-signature" class="messageSignature">Your signature will be added automatically</span>\
<div class="gadgetControls">\
<span localize="button-cancel" class="mw-ui-button cancel mw-ui-quiet">Cancel</span>\
<input type="submit" localize="button-submit" class="mw-ui-button mw-ui-progressive addme-support" localize="button" value="Support"></input>\
</div>'
).dialog({
dialogClass: 'grantsGadget endorseGadget',
autoOpen: false,
title: '<span localize="title">Support proposal</span>',
width: '495px',
modal: true,
closeOnEscape: true,
resizable: false,
draggable: false,
close: function () {
$('#devEndorseComment').val('');
}
});
$('.addme-support').on('click', function () {
that.addEndorsement(util.cleanupText($('#devEndorseComment').val()));
});
$('.endorseGadget .cancel').on('click', function () {
dialog.dialog('close');
});
util.localizeGadget('.endorseGadget', that.interfaceMessages);
};
var Dialog = function () {
if (dialog === null) {
createDialog();
} else {
dialog.dialog('open');
}
};
/*
* The main function to add the feedback/endorsement to the page. It first checks if the page has an endorsement section.
* If it dosent it creates a new section called Endorsements and appends the feedback/endorsement comment to that section,
* else it appends the feedback/endorsement comment to existing Endorsements section.
* The name of the endorsement section is defined in the config.
*/
this.addEndorsement = function (text) {
// Remove support template for comment if present.
text = text.replace(new RegExp(supportTemplates), '').trim();
var endorseComment = '\n* {{support}}' + (text.length ? ' ' + text : '') + ' [[User:Lectrician1|Lectrician1]] ([[User talk:Lectrician1|talk]]) 05:26, 6 February 2022 (UTC)' + '\n';
var proposalUrl = 'https://meta.wikimedia.org' + (new mw.Title(that.title)).getUrl();
var summary = 'Support proposal' + (text.length > 0 ? ': ' + text : '').trim();
if (sectionFound) {
var endorsementSection = wikitext + endorseComment;
api.post({
action: 'edit',
title: that.title,
text: endorsementSection,
summary: summary,
section: sectionCount,
// 'watchlist':'watch',
token: mw.user.tokens.get('csrfToken')
}).then(function () {
console.log('Successfully added endorsement');
window.location = proposalUrl;
util.setFeedbackCookie();
}, function () {
util.showErrorMessage('endorseGadget', 'save');
});
} else {
api.get({
format: 'json',
action: 'parse',
prop: 'wikitext',
page: that.title,
section: sectionCount
}).then(function (result) {
var wikitext = result.parse.wikitext['*'] + '\n=== ' + that.config['section-header-write'] + ' ===\n';
api.post({
action: 'edit',
title: that.title,
text: wikitext + endorseComment,
summary: summary,
section: sectionCount,
// 'watchlist': 'watch',
token: mw.user.tokens.get('csrfToken')
}).then(function () {
console.log('Successfully added endorsement');
window.location = proposalUrl;
util.setFeedbackCookie();
}, function () {
util.showErrorMessage('endorseGadget', 'save');
});
});
}
}, function () {
util.showErrorMessage('endorseGadget', 'save');
};
};
/* End of functions */
$(function () {
if (!$('.wp-addme-button').length) {
// Ignore this page
return;
}
mw.loader.using(['jquery.ui', 'mediawiki.api', 'mediawiki.ui', 'jquery.chosen'], function () {
/*
* Fix mw.config.get('wgPageContentLanguage') == 'en') checking with a better solution,
* either when pages can be tagged with arbitary language or when we set langauge markers later on.
*
*/
if (mw.config.get('wgPageContentLanguage') === 'en') {
var endorse = new endorseGadget();
var util = new gadgetUtilities();
var api = new mw.Api();
var interfaceMessagesFullPath = util.interfaceMessagesPath;
var configFullPath = util.configPath;
/*
* To detect if we have the gadget translations and config in the desired languages.
* Currently page language is English always. So the config returned is in en. The InterfaceMessages is
* in the user's language
*/
api.get({ action: 'query', titles: interfaceMessagesFullPath + '/' + util.userLanguage() + '|' + configFullPath + '/' + util.userLanguage(), format: 'json' }).then(function (data) {
for (var id in data.query.pages) {
if (data.query.pages[id].title === util.interfaceMessagesPath + '/' + util.userLanguage() && id > 0) {
interfaceMessagesFullPath = util.interfaceMessagesPath + '/' + util.userLanguage();
}
if (data.query.pages[id].title === util.configPath + '/' + util.contentLanguage() && id > 0) {
configFullPath = util.configPath + '/' + util.userLanguage();
}
}
var interfaceMessagesUrl = 'https://meta.wikimedia.org/w/index.php?title=' + interfaceMessagesFullPath + '&action=raw';
var configUrl = 'https://meta.wikimedia.org/w/index.php?title=' + configFullPath + '&action=raw';
// Get the config for the detected language
$.when($.get(interfaceMessagesUrl), $.get(configUrl)).then(function (interfaceStr, configStr) {
var interfaceData = RJSON.parse(interfaceStr[0]),
configData = RJSON.parse(configStr[0]);
endorse.config = util.stripWhiteSpace(util.grantType(configData.endorse));
endorse.interfaceMessages = util.stripWhiteSpace(util.grantType(interfaceData.endorse));
// hack - proper entity encoding is hard in wikitext
proposal = decodeURIComponent($('.wp-addme-button').attr('data-addme-proposal').replace(/\+/g, '_'));
endorse.section = $('.wp-addme-button').attr('data-addme-section')
endorse.title = mw.config.get('wgTitle').split('/')[0] + '/' + proposal;
// Initialize gadget now that we have config, interfaceMessages, section, and title
endorse.Initialize();
if (util.checkFeedbackCookie()) {
util.showFeedback(endorse.config, endorse.interfaceMessages);
}
});
});
} else {
$('.wp-addme-button').hide();
}
});
});