Jump to content

User:PleaseStand/highlight-comments-dev.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by PleaseStand (talk | contribs) at 01:33, 21 April 2012 (Fix issues detected by JSHint). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/*global mediaWiki*/

/**
 * Highlights specific users' posts to discussion pages using a CSS class.
 *
 * Originally written by PleaseStand in 2010, updated for MediaWiki 1.17 in 2011
 * Rewrite completed in 2012
 *
 * Released to the public domain; see http://en.wikipedia.org/wiki/Template:PD-self
 */

mediaWiki.loader.using( 'mediawiki.util', function() {

	"use strict";

	// MediaWiki globals
	var $ = window.jQuery, mw = window.mediaWiki;

	// Default settings
	var settings = $.extend( {
		highlighterFunction: function( hc ) {
				// Default highlighter function
				hc.addColorForUsers( '#ff7', [mw.config.get( 'wgUserName' )] );
				hc.wrapComments();
				hc.addMenuItem();
		}
	}, window.highlightCommentsSettings );

	// Initialize other enclosed variables.
	var linkMap = new mw.Map(), classNumber = 0, undoLog = null;

	/**
	 * Give comments linking to any given page a specific CSS class.
	 * Essentially, we need to find the comment's container and wrap (except where unnecessary)
	 * everything inside except replies to that comment. We can filter the replies out in that
	 * they are inside other element types that have the effect of indenting the text.
	 * @see unwrapComments
	 */
	function wrapComments() {
		// Elements containing comments or indented text (replies to those comments)
		var commentTags = 'dd, li, p', indentTags = 'dl, ol, ul';

		undoLog = [];
		$( 'a' ).each( function() {
			// linkMap is a Map from linked page names to CSS class names.
			if ( linkMap.exists( this.title ) ) {
				var className = linkMap.get( this.title );
				$(this).closest( commentTags ).contents().not( indentTags ).each( function() {
					var undoEntry = {className: className};
					if ( this.nodeType === 1 ) {
						var $elem = $( this );
						if ( !$elem.hasClass( className ) ) {
							undoEntry.type = 'addClass';
							undoEntry.$elem = $elem.addClass( className );
						}
					} else {
						undoEntry.type = 'wrap';
						undoEntry.$elem = $( this ).wrap( $( '<span/>', {'class': className} ) );
					}
					undoLog.push( undoEntry );
				} );
			}
		} );
		addMenuItem( true );
	}

	/**
	 * Undo the actions performed by wrapComments() using the undo log saved by wrapComments().
	 */
	function unwrapComments() {
		for ( var i = 0; i < undoLog.length; ++i ) {
			var undoEntry = undoLog[i];
			if ( undoEntry.type === 'addClass' ) {
				undoEntry.$elem.removeClass( undoEntry.className );
			} else if ( undoEntry.type === 'wrap' ) {
				undoEntry.$elem.unwrap( undoEntry.$elem );
			}
		}
		undoLog = null;
		addMenuItem( true );
	}

	/**
	 * Add a group of users whose comments should be given the same CSS class.
	 * @param className The CSS class name to use
	 * @param users An array of usernames
	 */
	function addClassForUsers( className, users ) {
		var ns = mw.config.get( 'wgFormattedNamespaces' );
		for ( var i = 0; i < users.length; ++i ) {
			var userName = users[i];
			var userPage = ns[2] + ':' + userName, userTalkPage = ns[3] + ':' + userName;
			linkMap.set( userPage, className );
			linkMap.set( userTalkPage, className );
		}
	}

	/**
	 * Add a group of users whose comments should be highlighted in the same color.
	 * @param color The CSS background-color to use
	 * @param users An array of usernames
	 * @return The resulting CSSStyleSheet object
	 */
	function addColorForUsers( color, users ) {
		var className = 'highlighted-comment-' + classNumber++;
		addClassForUsers( className, users );
		return mw.util.addCSS( '.' + className + ' { background-color: ' + color + '; }' );
	}

	/**
	 * Adds or updates a "Highlight" or "Unhighlight" option in the content action menu.
	 * @param updateOnly Do nothing if the menu item does not already exist?
	 */
	function addMenuItem( updateOnly ) {

		var text, tooltip, $oldItem = $( '#ca-highlightcomments' );

		if ( updateOnly && !$oldItem.length ) {
			return;
		}

		if ( undoLog ) {
			text = 'Unhighlight';
			tooltip = 'Disable highlighting of your own comments on this page';
		} else {
			text = 'Highlight';
			tooltip = 'Enable highlighting of your own comments on this page';
		}

		var link = mw.util.addPortletLink(
			'p-cactions', '#', text, 'ca-highlightcomments', tooltip, null, $oldItem[0]
		);
		$oldItem.remove();

		$( link ).click(function() {
			if ( undoLog ) {
				unwrapComments();
			} else {
				wrapComments();
			}
		});

	}

	// Members exposed to custom highlighter functions
	var hc = {
		addClassForUsers: addClassForUsers,
		addColorForUsers: addColorForUsers,
		addMenuItem: addMenuItem,
		wrapComments: wrapComments
	};

	$( function() {
		settings.highlighterFunction( hc );
	} );

} );