MediaWiki:Gadget-metadata.js
From OpenWetWare
Jump to navigationJump to search
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.
/** * Metadata assessment script * Finds the WP 1.0/WikiProject assessment of every article you go to, then * displays that information in the article header. * @author Outriggr - created the script and used to maintain it * @author Pyrospirit - currently maintains and updates the script */ // Import stylesheet with custom classes for header colors importStylesheet('User:Pyrospirit/metadata.css'); var assessment = { autorun: true, showOldPeerReviews: false, /** * Starts the script object running. The main function of the script. If the * getMainType() function can find the assessment, it uses that assessment * for the page, parses it, and displays it in the header. Otherwise, it runs * ajaxMain(). */ init: function init () { this.callHooks('init_before'); var initialAssessment = this.checkArticle(); // checks for types visible from article page if (initialAssessment.exists) { this.currentAssessment = initialAssessment; var data = this.talkAssess(this.currentAssessment); this.update(data.newClass, data.slogan, data.info); } else this.ajaxMain(); // proceed to check the talk page this.callHooks('init_after'); }, /** * The main function when an AJAX request is needed to find the assessment. * Creates an AJAX request for the contents of a URL (defaults to the * first section of the article's talk page), then sends the request. After * getting the requested data back, it finds the assessment information in * the data, then uses and displays that assessment in the header. * @param {String} url - Optional: override the default URL for the request. * @param {Function} stateChange - Optional: override the default request callback * @param optionalArgument - Optional: passed to the stateChange function */ ajaxMain: function ajaxMain (url, stateChange, optionalArgument) { if (!url || !/^https?:\/\//i.test(url)) // optional url override url = wgServer + wgScript + '?title=Talk:' + encodeURIComponent(wgPageName) + '&action=raw§ion=0'; if (typeof stateChange !== 'function') { stateChange = this.stateChange; this.url = url; } request = sajax_init_object(); if (request) { var that = this; // store value of 'this' request.onreadystatechange = function () { stateChange.call(that, request, optionalArgument); } request.open('GET', url, true); request.send(null); } }, /** * This function is passed as a parameter to ajaxMain. It is called each time * this.request updates, and the code inside the conditional runs when the * data is available. */ stateChange: function stateChange (request) { if (request.readyState == 4 && request.status == 200) { this.text = request.responseText; this.request = request; var rating = this.getRating(this.text); this.currentAssessment = this.getAssessment(this.text, rating); var data = this.talkAssess(this.currentAssessment); this.update(data.newClass, data.slogan, data.info); this.callHooks('onCompletedRequest'); } }, /** * Checks for various objects on the article page that indicate a certain * assessment, such as a featured star or disambiguation page notice. If this * function can find the assessment, AJAX is not needed for this page. * @return {Object} assess - the assessment in an easily readable format * @static */ checkArticle: function checkArticle () { var assess = {}; assess.extra == ''; assess.exists = true; if (document.getElementById('disambig') || document.getElementById('disambig_disambigbox')) assess.rating = 'dab'; else if (document.getElementById('setindexbox')) assess.rating = 'setindex'; else if (document.getElementById('contentSub').innerHTML == 'Redirect page') assess.rating = 'redir'; else if (document.getElementById('ca-talk') && document.getElementById('ca-talk').className == 'new') // no talk page assess.rating = 'none'; else assess.exists = false; // none of the above, no assessment found return assess; }, /** * Searches the provided wikicode for the rating part of an assessment and * returns it as a string. * Note that a higher assessment takes priority, and less-used assessments * such as "list", "current", or "future" are used only if nothing else can * be found. * @param {String} text - some wikitext to be searched for assessment info * @return {String} rating - the article's current assessment */ getRating: function getRating (text) { this.callHooks('getRating_before'); var rating = 'none'; if (text.match(/\|\s*(class|currentstatus)\s*=\s*fa\b/i)) rating = 'fa'; else if (text.match(/\|\s*(class|currentstatus)\s*=\s*fl\b/i)) rating = 'fl'; else if (text.match(/\|\s*class\s*=\s*a\b/i)) { if (text.match(/\|\s*class\s*=\s*ga\b|\|\s*currentstatus\s*=\s*(ffa\/)?ga\b/i)) rating = 'a/ga'; // A-class articles that are also GA's else rating = 'a'; } else if (text.match(/\|\s*class\s*=\s*ga\b|\|\s*currentstatus\s*=\s*(ffa\/)?ga\b|\{\{\s*ga\s*\|/i) && !text.match(/\|\s*currentstatus\s*=\s*dga\b/i)) rating = 'ga'; else if (text.match(/\|\s*class\s*=\s*b\b/i)) rating = 'b'; else if (text.match(/\|\s*class\s*=\s*bplus\b/i)) rating = 'bplus'; // used by WP Math else if (text.match(/\|\s*class\s*=\s*c\b/i)) rating = 'c'; else if (text.match(/\|\s*class\s*=\s*start/i)) rating = 'start'; else if (text.match(/\|\s*class\s*=\s*stub/i)) rating = 'stub'; else if (text.match(/\|\s*class\s*=\s*list/i)) rating = 'list'; else if (text.match(/\|\s*class\s*=\s*(dab|disambig)/i)) rating = 'dab'; else if (text.match(/\|\s*class\s*=\s*cur(rent)?/i)) rating = 'cur'; else if (text.match(/\|\s*class\s*=\s*future/i)) rating = 'future'; this.callHooks('getRating_after'); return rating; }, /** * Searches the provided wikicode for data on the article's current and past * featured or good status and returns an object that contains this data * along with some miscellaneous other bits of information. * @param {String} text - some wikitext to be searched for assessment info * @return {Object} assess - the assessment data for the page */ getAssessment: function getAssessment (text, rating) { this.callHooks('getAssessment_before'); var assess = {rating: rating, pageLink: [null, null], extra: [], activeReview: null}; var actionNumber = 0, pageLinkFlag = false, tempMatch, articleName; // Current nominations (FAC, FLC, or GAN) if ((assess.reg = text.match(/\{\{\s*featured[ _]article[ _]candidates\s*(?:[\|\}]\s*([^\|\}]*))?[^\}]*?\}\}/i))) { assess.extra.push('fac'); if (assess.reg[1] && (articleName = this.decodeEntities(assess.reg[1].trim()))) assess.pageLink[0] = 'Wikipedia:Featured_article_candidates\/' + articleName; } else if ((assess.reg = text.match(/\{\{\s*featured[ _]list[ _]candidates\s*(?:[\|\}]\s*([^\|\}]*))?[^\}]*?\}\}/i))) { assess.extra.push('flc'); if (assess.reg[1] && (articleName = this.decodeEntities(assess.reg[1].trim()))) assess.pageLink[0] = 'Wikipedia:Featured_list_candidates\/' + articleName; } else if ((assess.reg = text.match(/\{\{\s*ga ?nominee\s*[\|\}][^\}]*\}\}/i))) { assess.extra.push('gan'); tempMatch = assess.reg[0].match(/\|\s*page\s*=\s*(\d+).*\|\s*status\s*=\s*\w+\b/i); if (tempMatch) assess.pageLink[0] = 'Talk:' + this.encodePageName(wgPageName) + '\/GA' + tempMatch[1]; } // Current reviews of a status (FAR, FLRC, or GAR) else if ((assess.reg = text.match(/\{\{\s*featured[ _]article[ _]review\s*(?:[\|\}]\s*([^\|\}]*))?[^\}]*?\}\}/i))) { assess.extra.push('far'); if (assess.reg[1] && (articleName = this.decodeEntities(assess.reg[1].trim()))) assess.pageLink[0] = 'Wikipedia:Featured_article_review\/' + articleName; } else if ((assess.reg = text.match(/\{\{\s*featured[ _]list[ _]removal[ _]candidates\s*(?:[\|\}]\s*([^\|\}]*))?[^\}]*?\}\}/i))) { assess.extra.push('flrc'); if (assess.reg[1] && (articleName = this.decodeEntities(assess.reg[1].trim()))) assess.pageLink[0] = 'Wikipedia:Featured_list_removal_candidates\/' + articleName; } else if ((assess.reg = text.match(/\{\{\s*gar\/link\s*[\|\}][^\}]*\}\}/i))) { assess.extra.push('gar'); tempMatch = assess.reg[0].match(/\|\s*GARpage\s*=\s*(\d+).*\|/i); if (tempMatch) assess.pageLink[0] = 'Wikipedia:Good_article_reassessment\/' + this.encodePageName(wgPageName) + '\/' + tempMatch[1]; } // Former statuses (FFA, FFL, or DGA) if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*ffa\b/i))) { tempMatch = text.match(/\|\s*action(\d+)\s*=\s*far\b/gi); actionNumber = tempMatch[tempMatch.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('ffa'); } else if ((assess.reg = text.match(/\|\s*action(\d+)\s*=\s*far\b/gi)) // This checks if the last FAR entry in ArticleHistory resulted in removal. && text.match(RegExp( '\\|\\s*action' + assess.reg[assess.reg.length - 1].match(/\d+/) + 'result\\s*=\\s*removed\\b', 'i' )) && assess.rating.search(/f[al]/i) == -1) { actionNumber = assess.reg[assess.reg.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('ffa'); } else if ((assess.reg = text.match(/\{\{\s*formerfa2?\b/i))) { assess.extra.push('ffa'); } else if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*ffl\b/i))) { assess.extra.push('ffl'); } else if ((assess.reg = text.match(/\{\{\s*ffl\s*[\|\}]/i))) { assess.extra.push('ffl'); } else if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*dga\b/i))) { tempMatch = text.match(/\|\s*action(\d+)\s*=\s*gar\b/gi); actionNumber = tempMatch[tempMatch.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('dga'); } else if ((assess.reg = text.match(/\{\{\s*d(elisted)?ga\s*[\|\}]/i))) { assess.extra.push('dga'); } // Former nominations (former FAC, FLC, or GAN) else if ((assess.reg = text.match(/\|\s*action(\d+)\s*=\s*fac\b/gi)) && assess.rating.search(/f[al]/i) == -1) { actionNumber = assess.reg[assess.reg.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('ffac'); } else if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*ffac\b/i))) { assess.extra.push('ffac'); } else if ((assess.reg = text.match(/\{\{\s*fac?(failed|(\-|[ _]\()?contested\)?)\s*[\|\}]/i))) { assess.extra.push('ffac'); } else if ((assess.reg = text.match(/\|\s*action(\d+)\s*=\s*flc\b/gi)) && assess.rating.search(/f[al]/i) == -1) { actionNumber = assess.reg[assess.reg.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('fflc'); } else if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*fflc\b/i))) { assess.extra.push('fflc'); } else if ((assess.reg = text.match(/\|\s*action(\d+)\s*=\s*gan\b/gi)) && assess.rating.search(/f[al]|(a\/)?ga/i) == -1) { actionNumber = assess.reg[assess.reg.length - 1].match(/\d+/); pageLinkFlag = true; assess.extra.push('fgan'); } else if ((assess.reg = text.match(/\|\s*currentstatus\s*=\s*fgan\b/i))) { assess.extra.push('fgan'); } else if ((assess.reg = text.match(/\{\{\s*f(ailed ?)?ga\s*[\|\}]/i))) { assess.extra.push('fgan'); } // Looks for currently active peer reviews var peerReview; if ((peerReview = text.match(/\{\{\s*peer[_ ]?review\s*\|\s*archive\s*=\s*(\d+)\b/i))) { assess.review = 'Wikipedia:Peer_review/' + wgPageName + '/archive' + peerReview[1]; assess.activeReview = true; } else if (this.showOldPeerReviews) { // TODO: Add code for old peer reviews } else assess.review = null; // Scans for the link associated with an action in ArticleHistory if (pageLinkFlag) { var linkPattern = RegExp('\\|\\s*action' + actionNumber + 'link\\s*=\\s*([^\\n\\|]+)\\s*\\|'); var linkMatch = text.match(linkPattern); assess.pageLink[1] = linkMatch ? this.decodeEntities(linkMatch[1]) : null; } assess.exists = true; this.callHooks('getAssessment_after'); return assess; }, /** * Parses an assessment object into the HTML and CSS code needed to update * the article header. If it doesn't recognize a part of the information * given, it will simply ignore it and mark as unassessed. * @param {Object} assess - assessment information for this article * @return {String} newClass - the CSS class corresponding to its assessment * @return {String} slogan - HTML giving (with a link) the main assessment * @return {String} info - HTML giving (with a link) additional information */ talkAssess: function talkAssess (assess) { this.callHooks('talkAssess_before'); var path = wgArticlePath.replace('$1', ''); var assessLink = path + 'Wikipedia:Version_1.0_Editorial_Team/Assessment'; if (typeof assess.extra === 'undefined') assess.extra = ''; var extra = assess.extra, rating = assess.rating; var pageLink = assess.pageLink ? [this.encodePageName(assess.pageLink[0]), this.encodePageName(assess.pageLink[1])] : [null, null]; var peerReview = this.encodePageName(assess.review); var info = this.getExtraInfo(extra, pageLink); var peerReviewText = this.addPeerReview(peerReview, assess.activeReview); if (peerReviewText) info.push(peerReviewText); var newClass, slogan; if (rating == 'a' || rating == 'a/ga') { newClass = 'assess-a-text'; slogan = 'An <a href="' + assessLink + '">A-class</a> article'; if (rating == 'a/ga') { info.push('Also a <a href="' + path + 'Wikipedia:Good_Articles">good article</a>.'); } } else if (rating == 'ga') { newClass = 'assess-ga-text'; slogan = 'A <a href="' + path + 'Wikipedia:Good_Articles">good article</a>' } else if (rating == 'b') { newClass = 'assess-b-text'; slogan = 'A <a href="' + assessLink + '">B-class</a> article'; } else if (rating == 'bplus') { newClass = 'assess-bplus-text'; slogan = 'A <a href="' + path + 'Wikipedia:WikiProject_Mathematics/Wikipedia_1.0' + '/Grading_scheme">B-plus-class</a> article'; } else if (rating == 'c') { newClass = 'assess-c-text'; slogan = 'A <a href="' + assessLink + '">C-class</a> article'; } else if (rating == 'start') { newClass = 'assess-start-text'; slogan = 'A <a href="' + assessLink + '">start-class</a> article'; } else if (rating == 'stub') { newClass = 'assess-stub-text'; slogan = 'A <a href="' + assessLink + '">stub-class</a> article'; } else if (rating == 'list') { newClass = 'assess-list-text'; slogan = 'A <a href="' + path + 'Wikipedia:Lists">list-class</a> article'; } else if (rating == 'dab') { newClass = 'assess-dab-text'; slogan = 'A <a href="' + path + 'Wikipedia:Disambiguation">disambiguation page</a>'; } else if (rating == 'setindex') { newClass = 'assess-setindex-text'; slogan = 'A <a href="' + path + 'Wikipedia:Disambiguation#Set_index_articles">' + 'set index article</a>'; } else if (rating == 'redir') { newClass = 'assess-redir-text'; slogan = 'A <a href="' + path + 'Help:Redirect">redirect page</a>'; } else if (rating == 'fl') { newClass = 'assess-fl-text'; slogan = 'A <a href="' + path + 'Wikipedia:Featured_lists">featured list</a>'; } else if (rating == 'fa') { newClass = 'assess-fa-text'; slogan = 'A <a href="' + path + 'Wikipedia:Featured_articles">featured article</a>'; } else if (rating == 'cur') { newClass = 'assess-cur-text'; slogan = 'A <a href="' + path + 'Portal:Current_events">current-class</a> article'; } else if (rating == 'future') { newClass = 'assess-future-text'; slogan = 'A <a href="' + path + 'Category:Future-Class_articles">future-class</a>' + ' article'; } else { newClass = 'assess-unassessed-text'; slogan = 'An <a href="' + assessLink + '">unassessed</a> article'; } this.callHooks('talkAssess_after'); return {newClass: newClass, slogan: slogan, info: info}; }, /** * Creates an info string based on the assessment info and a page link. */ getExtraInfo: function getExtraInfo (extra, pageLink) { var info = []; var page = this.encodePageName(wgPageName); // Current nominations and reviews if (extra.indexOf('fac') != -1) { info.push(this.makeInfoString('Currently a', pageLink[0], 'Wikipedia:Featured_article_candidates/' + page, 'featured article candidate', null)); } else if (extra.indexOf('flc') != -1) { info.push(this.makeInfoString('Currently a', pageLink[0], 'Wikipedia:Featured_list_candidates/' + page, 'featured list candidate', null)); } else if (extra.indexOf('gan') != -1) { info.push(this.makeInfoString('Currently a', pageLink[0], 'Wikipedia:Good_article_nominations', 'good article nominee', null)); } else if (extra.indexOf('far') != -1) { info.push(this.makeInfoString('Currently undergoing', pageLink[0], 'Wikipedia:Featured_article_review/' + page, 'review', 'of its featured status')); } else if (extra.indexOf('flrc') != -1) { info.push(this.makeInfoString('Currently a', pageLink[0], 'Wikipedia:Featured_list_removal_candidates/' + page, 'candidate', 'for removal as a featured list')); } else if (extra.indexOf('gar') != -1) { info.push(this.makeInfoString('Currently undergoing a', pageLink[0], 'Wikipedia:Good_article_reassessment', 'good article reassessment', null)); } // Past statuses and nominations if (extra.indexOf('ffa') != -1) { info.push(this.makeInfoString('A', pageLink[1], 'Wikipedia:Featured_article_review/' + page, 'former', 'featured article')); } else if (extra.indexOf('ffl') != -1) { info.push(this.makeInfoString('A', pageLink[1], 'Wikipedia:Featured_list_removal_candidates/' + page, 'former', 'featured list')); } else if (extra.indexOf('dga') != -1) { info.push(this.makeInfoString('A', pageLink[1], 'Wikipedia:Good_article_reassessment', 'delisted', 'good article')); } else if (extra.indexOf('ffac') != -1) { info.push(this.makeInfoString('A former', pageLink[1], 'Wikipedia:Featured_article_candidates/' + page, 'featured article candidate', null)); } else if (extra.indexOf('fflc') != -1) { info.push(this.makeInfoString('A former', pageLink[1], 'Wikipedia:Featured_list_candidates/' + page, 'featured list candidate', null)); } else if (extra.indexOf('fgan') != -1) { info.push(this.makeInfoString('A former', pageLink[1], 'Wikipedia:Good_article_nominations', 'good article nominee', null)); } return info; }, /** * Creates the peer review text from an info string, if a peer review was detected earlier. */ addPeerReview: function addPeerReview (peerReview, activeReview) { var reviewText = null, path = wgArticlePath.replace('$1', ''); if (peerReview) { reviewText = (activeReview ? 'Currently being <a href="' + path + peerReview + '">peer reviewed</a>.' : 'Previously <a href="' + path + peerReview + '">peer reviewed</a>.'); reviewText = '<span class="assess-info-review">' + reviewText + '</span>'; } return reviewText; }, /** * Updates article header with new assessment information by giving it a new * class (for style information such as color) and altering the tagline below * it to state the assessment found. * @param {String} newClass - the CSS class name added to the article header * @param {String} slogan - italicized text prepended to the tagline, showing * the article's main assessment * @param {String} info - additional assessment info appended to the tagline * @static */ update: function update (newClass, slogan, info) { var firstHeading = document.getElementsByTagName('h1')[0]; var siteSub = '<span class="assess-article-rating">' + slogan + '<\/span>'; siteSub += ' from Wikipedia, the free encyclopedia'; if (info && info.length > 0) { siteSub += '<span class="assess-info-all">. ' + (typeof info.join === 'undefined' ? info.toString() : info.join(' ') ) + '<\/span>'; } firstHeading.className += ' ' + (typeof newClass.join === 'undefined' ? newClass.toString() : newClass.join(' ') ); // add newClass as additional class(es) document.getElementById('siteSub').innerHTML = siteSub; }, /** * Creates a string formatted for the 'info' parameter in the update method. * @param start - text at the beginning of the string, before the link * @param pageLink - a link to the target page * @param defLink - the backup page link if !pageLink * @param linkText - the text of the link * @param end - text after the link * @return {String} output - the info string * @static */ makeInfoString: function makeInfoString (start, pageLink, defLink, linkText, end) { var output; // path is usually just '/wiki/', but it's different on secure.wikimedia.org var path = wgArticlePath.replace('$1', ''); var page = pageLink ? path + pageLink : (defLink ? path + defLink : null); start = start ? start.toString() + ' ' : ''; linkText = linkText ? linkText.toString() : ''; end = end ? ' ' + end.toString() + '.' : '.'; output = start + (page ? '<a href="' + page + '"' + (linkText ? '>' : ' \/>') : '') + linkText + ((page && linkText) ? '<\/a>' : '') + end; return output; }, /** * Encodes the URL of a Wikipedia page for use in the talkAssess method. * @param {String} inputText - the unencoded full page name * @return {String} outputText - the encoded page name * @static */ encodePageName: function encodePageName (inputText) { if (!inputText) return null; var outputText = encodeURIComponent(inputText); while (outputText != null && outputText.match(/(\%20|\%2F)/i)) { outputText = outputText.replace(/\%20/i, '_'); // unescape spaces for readability outputText = outputText.replace(/\%2F/i, '\/'); // %2F must be unescaped } return outputText; }, callHooks: function callHooks (hook) { for (funct in this[hook]) { this[hook][funct].call(this); } }, addHook: function addHook (hook, funct) { if (typeof this[hook] === 'undefined') this[hook] = []; this[hook][this[hook].length] = funct; return this; }, /** * Decodes all HTML entities in the string provided. */ decodeEntities: function decodeEntities (str) { var t = document.createElement("textarea"); t.innerHTML = str; return t.value; } }; // Implement Array.indexOf for older browsers that don't have it if (!Array.prototype.indexOf) { Array.prototype.indexOf = function indexOf (elt, from) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; } // Implement String.trim for browsers that don't have it if (!String.prototype.trim) { String.prototype.trim = function trim () { var str = this.replace(/^\s\s*/, ''), ws = /\s/, i = str.length; while (ws.test(str.charAt(--i))); return str.slice(0, i + 1); } } // Backwards compatibility MetadataScript = MetadataObject = assessment /** * Initializes the script on page load */ if (wgNamespaceNumber == 0 && (wgAction == 'view' || wgAction == 'purge') && document.location.href.search(/\?(.+\&)?printable=[^&]/i) == -1 && wgPageName != 'Main_Page') { addOnloadHook(function () { if (assessment.autorun) assessment.init(); }); }