MediaWiki:Gadget-metadata.js

From Wikipedia, the free encyclopedia

Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: hold down the Ctrl key and click the Refresh or Reload button. Firefox: hold down the Shift key while clicking Reload (or press Ctrl-Shift-R). Google Chrome and Safari users can just click the Reload button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/**
 * 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
 */

if (typeof(unsafeWindow) != 'undefined')
{
	addOnloadHook = unsafeWindow.addOnloadHook;
	console = unsafeWindow.console;
	importStylesheet = unsafeWindow.importStylesheet;
	mw = unsafeWindow.mw;
	sajax_init_object = unsafeWindow.sajax_init_object;
}

// Import stylesheet with custom classes for header colors
importStylesheet('User:Pyrospirit/metadata.css');

var assessment = {
    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 () {
        if( !$('#siteSub').length ) return; //This skin is not compatible with the tool
        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 = mw.config.get('wgServer') + mw.config.get('wgScript') + '?title=Talk:' + encodeURIComponent(mw.config.get('wgPageName'))
            + '&action=raw&section=0';
        if (typeof stateChange !== 'function') {
            stateChange = this.stateChange;
            this.url = url;
        }
        var 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 ($('#disambig').length || $('#disambig_disambigbox').length
                || $('#disambigbox').length)
            assess.rating = 'dab';
        else if ($('#setindexbox').length)
            assess.rating = 'setindex';
        else if ($('#contentSub').length
                && $('#contentSub').text() == 'Redirect page')
            assess.rating = 'redir';
        else if ($('#ca-talk').length
                && $('#ca-talk').hasClass('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*sl/i))
            rating = 'sl'; // used by WP Plants
        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($.trim(assess.reg[1]))))
                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($.trim(assess.reg[1]))))
                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(mw.config.get('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($.trim(assess.reg[1]))))
                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($.trim(assess.reg[1]))))
                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] = this.getGARLink(this.encodePageName(mw.config.get('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/' + mw.config.get('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 = mw.config.get('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 == 'sl') {
            newClass = 'assess-sl-text';
            slogan = 'A <a href="' + assessLink + '">stub-class</a> list';
        } 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(mw.config.get('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('<span id="assess-gar-link">Currently undergoing a', pageLink[0],
                'Wikipedia:Good_article_reassessment', 'good article reassessment', '<\/span>', true));
        }
        // 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;
    },
    /**
     * Get the correct link for Good Article reassessments. These things require an
     * additional AJAX request to determine whether it's a community or individual
     * reassessment. The trick is to assume it's a community reassessment, then
     * switch the link once the request returns if it's actually not.
     */
    getGARLink: function getGARLink (articleName, reviewNumber) {
        var communityTitle = 'Wikipedia:Good_article_reassessment\/' + articleName + '\/' + reviewNumber,
            individualTitle = 'Talk:' + articleName + '\/GA' + reviewNumber,
            url = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '\/api.php?action=query&titles='
                + communityTitle + '|' + individualTitle + '&prop=info&format=json';
        this.ajaxMain(url, this.getGARLinkCallback, [communityTitle, individualTitle]);
        return communityTitle;
    },
    /**
     * Now we have the information back from the API and need to figure out if the
     * link needs to be changed.
     */
    getGARLinkCallback: function getGARLinkCallback (request, altTitles) {
        if (request.readyState == 4 && request.status == 200) {
            var text = request.responseText;
            if (JSON && JSON.parse) {
                var query = JSON.parse(text)['query'];
            }
            else return;
            var communityTitleNorm = altTitles[0],
                individualTitleNorm = altTitles[1];
            var len = query['normalized'].length;
            for (var j = 0; j < len; j++) {
                switch (query['normalized'][j]['from']) {
                    case altTitles[0]:
                        communityTitleNorm = query['normalized'][j]['to'];
                        break;
                    case altTitles[1]:
                        individualTitleNorm = query['normalized'][j]['to'];
                        break;
                }
            }
            var noCommunityAssessment = false;
            for (var i = -1; i >= -2; i--) {
                if (query['pages'][i] && typeof query['pages'][i]['missing'] === 'string') {
                    if (query['pages'][i]['title'] == individualTitleNorm) {
                        // No individual assessment, no need to change anything.
                        return;
                    }
                    else if (query['pages'][i]['title'] == communityTitleNorm) {
                        // There's no community assessment, so flag it.
                        noCommunityAssessment = true;
                    }
                }
            }
			var garLink = $('#assess-gar-link a:first');
			if (noCommunityAssessment && garLink.length)
			{
				// There's an individual assessment but no community assessment. Switch the link.
				garLink.attr('href', mw.config.get('wgArticlePath').replace('$1', '') + altTitles[1]);
			}
        }
    },
    /**
     * 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 = mw.config.get('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 = $('h1:first');
		var siteSub = '<span class="assess-article-rating">' + slogan + '<\/span> 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.addClass((typeof newClass.join === 'undefined' ? newClass.toString() : newClass.join(' '))); // add newClass as additional class(es)
		$('#siteSub').html(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, noEndSpace) {
        var output;
        // path is usually just '/wiki/', but it's different on secure.wikimedia.org
        var path = mw.config.get('wgArticlePath').replace('$1', '');
        var page = pageLink ? path + pageLink : (defLink ? path + defLink : null);
        start = start ? start.toString() + ' ' : '';
        linkText = linkText ? linkText.toString() : '';
        end = (end ? (noEndSpace ? '' : ' ') + 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|\%253A)/i)) {
            outputText = outputText.replace(/\%20/i, '_'); // unescape spaces for readability
            outputText = outputText.replace(/\%2F/i, '\/'); // %2F must be unescaped
			outputText = outputText.replace(/\%253A/i, ':'); // "%253A" for special cases such as [[Metroid: Other M]]
        }
        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;
    };
}

/**
 * Initializes the script on page load
 */
if (mw.config.get('wgNamespaceNumber') == 0 && (mw.config.get('wgAction') == 'view' || mw.config.get('wgAction') == 'purge') && !mw.util.getParamValue('printable') && mw.config.get('wgPageName') != 'Main_Page' )
{
	$(document).ready(function()
	{
		assessment.init();
	});
}