MediaWiki:FileComments.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/**
 * (c) 2011 by Magnus Manske
 * Released under GPL V2 or above
*/

$.fn.exists = function() {
    return this.length > 0;
};

/**
 * Check typeof of variables.
 * Note: Do not use for variables that may be undeclared (ie. implied global)
 * this only works for local variables and arguments
 * (Otherwise a ReferenceError is thrown when the function is called)
 */
function isDef( o ) {
    return typeof o !== "undefined";
}

$.fn.tagName = function() {
    return this.get(0).tagName;
};

Object.size = function(obj) {
    var size = 0, key;
    for ( key in obj ) {
        if ( obj.hasOwnProperty( key ) ) size++;
    }
    return size;
};

var filecomments = {

	interface_texts : {
		'heading' : 'Comments and favourites' ,
		'comments' : 'comments' ,
		'favourites' : 'favourites' ,
		'rem_fav' : 'Remove favourite' ,
		'set_fav' : 'Set favourite' ,
		'loading_comments' : 'Loading comments from talk page...' ,
		'add_comment' : 'Add a comment' ,
		'edit_comments' : 'Edit comments' ,
		'my_favs' : 'My favourite files' ,
		'my_comments' : 'My file comments' ,
		'summary_new_fav' : 'New favourite of ' ,
		'summary_rem_fav' : 'Removing favourite' ,
		'talk_to_user' : 'talk' ,
		'add_a_comment' : 'Add a comment, ' ,
		'summary_new_comment' : 'New comment : ' ,
		'alert_no_edit_token' : 'Could not acquire edit token, operation aborted' ,
		'alert_edit_fail' : 'The edit operation has failed' ,
		'user_subpage_favourites' : 'My_favourite_files' ,
		'user_subpage_comments' : 'My_file_comments'
	} ,
	
	it : function ( key ) {
		var ret = filecomments.interface_texts[key];
		if ( !isDef( ret ) ) ret = "???";
		return ret;
	} ,
	
	initInterfaceTexts : function () {
		// For future use
	} ,

	init : function () {
		if ( !mw.user.isAnon() ) this.user_name = mw.user.getName();
		else this.user_name = 'anonymous user'; // This should never be used, but...

		filecomments.user_favs_page = 'User:' + filecomments.user_name + '/' + filecomments.it('user_subpage_favourites');
		filecomments.user_comments_page = 'User:' + filecomments.user_name + '/' + filecomments.it('user_subpage_comments');
		
		if ( mw.config.get('wgAction') !== 'view' ) return; // Only on views!

		// Redirect to category of my comments:
		if ( filecomments.user_comments_page == mw.config.get('wgPageName') ) {
			window.location = mw.util.getUrl( 'Category:File comments by ' + this.user_name );
			return;
		}

		// Show interface texts only on file pages
		if ( mw.config.get( 'wgNamespaceNumber' ) !== 6 ) return; // Nevermind...

		this.initInterfaceTexts();
		
		this.addCommentsBox();
	} ,
	
	addCommentsBox : function () {
		var cn = '';
		var ib = $('#filehistory');
		$('#filecomments_container').remove();
		var h = "<div id='filecomments_container' class='" + cn + "'>";
		h += "<h2>" + filecomments.it('heading') + "</h2>";
		h += "<div id='filecomments_top'></div>";
		h += "<div id='filecomments_main'></div>";
		h += "<div id='filecomments_bottom'></div>";
		h += "</div>";
		ib.before( h );
		filecomments.parseTalkPage();
	} ,
	
	parseTalkPage : function () {
	
		filecomments.talk_page = 'File talk:' + mw.config.get('wgTitle');
		filecomments.loadBeforeParsing = 1; // Future parallel queries
		
		// plain text
		$.getJSON(
			mw.util.wikiScript( 'api' ), {
				'format': 'json',
				'action': 'query',
				'titles': filecomments.talk_page,
				'prop' : 'revisions' ,
				'rvprop' : 'content'
			}, function( data ) {
				$.each( data.query.pages , function ( k , v ) {
					if ( isDef( v.missing ) ) filecomments.talk_page_text = '';
					else {
						$.each( v.revisions , function ( k2 , v2 ) {
							filecomments.talk_page_text = v2['*'];
						} );
					}
				} );
				filecomments.renderCommentBox();
			}
		);
		
	} ,
	
	renderCommentBox : function () {
		filecomments.loadBeforeParsing--;
		if ( 0 < filecomments.loadBeforeParsing ) return;
		
		var text = filecomments.talk_page_text;
		
		// Parse comments
		var comments = text.match( /\{\{filecommentstart\|.+?\{\{filecommentend\}\}/gi );
		if ( !comments ) comments = [];
		
		// Parse favourites
		var favourites = text.match( /\{\{filecommentfavourite\|.+?\}\}/gi );
		if ( !favourites ) favourites = [];

		filecomments.isMyFavourite = false;
		$.each ( favourites , function ( k , v ) {
			var v2 = v.match( /^\{\{filecommentfavourite\|(.+?)\}\}$/i );
			if ( v2[1] != filecomments.user_name ) return;
			filecomments.isMyFavourite = true;
			filecomments.myFavouriteTag = v2[0];
		} );
		
		
		// Top
		var h = '';
		h += comments.length + " " + filecomments.it('comments') + " | ";
		h += favourites.length + "  " + filecomments.it('favourites');
		if ( !mw.user.isAnon() ) {
			if ( filecomments.isMyFavourite ) h += " | <a href='#' onclick='filecomments.setFavourite(false);return false'>" + filecomments.it('rem_fav') + "</a>";
			else h += " | <a href='#' onclick='filecomments.setFavourite(true);return false'>" + filecomments.it('set_fav') + "</a>";
			h += " | <a href='" + mw.util.getUrl( filecomments.user_favs_page  ) + "'>" + filecomments.it('my_favs') + "</a>";
			h += " | <a href='" + mw.util.getUrl( filecomments.user_comments_page  ) + "'>" + filecomments.it('my_comments') + "</a>";
		}
		h += "<br/>";
		
		$('#filecomments_top').html( h  );
		
		
		// Main
		filecomments.comment_render_counter = 1;
		filecomments.comments = {};
		$.each( comments , function ( k , v ) {
			filecomments.parseComment( v );
		} );
		h = '';
		$('#filecomments_main').html( h  );
		$('#filecomments_main').html( "<i>" + filecomments.it('loading_comments') + "</i>" );

		// Bottom
		h = '';
		if ( !mw.user.isAnon() ) {
			h += "<a href='#' onclick='filecomments.addComment();return false'>" + filecomments.it('add_comment') + "</a> | ";
			h += "<a href='" + mw.util.getUrl( filecomments.talk_page ) + "'>" + filecomments.it('edit_comments') + "</a>";
		}
		$('#filecomments_bottom').html( h  );
		filecomments.renderComments();
	} ,
	
	setFavourite : function ( what ) {
		if ( filecomments.isMyFavourite == what ) {
			// This should never happen
			return;
		}
		
		// Edit talk page
		var t = filecomments.talk_page_text;
		if ( what ) { // Set favourite
			t += "\n" + '{{filecommentfavourite|' + filecomments.user_name + '}}';
			filecomments.editTalkPage( 'text' , t , filecomments.it('summary_new_fav') + filecomments.user_name );
		} else { // Remove favourite
			t = t.replace( filecomments.myFavouriteTag , '' );
			filecomments.editTalkPage( 'text' , t , filecomments.it('summary_rem_fav') );
		}
		
		// Edit favs page
		$.getJSON(
			mw.util.wikiScript( 'api' ), {
				'format': 'json',
				'action': 'query',
				'titles': filecomments.user_favs_page,
				'prop' : 'revisions' ,
				'rvprop' : 'content'
			}, function( data ) {
				// @todo Check for 'error' in data before assuming everything is alright.
				var t = "<gallery>\n</gallery>"; // If page is blank
				$.each( data.query.pages , function ( k , v ) {
					if ( ! isDef( v.missing ) ) {
						$.each( v.revisions , function ( k2 , v2 ) {
							t = v2['*'];
						} );
					}
				} );

				var fn = "\nFile:" + mw.config.get('wgTitle');
				t = t.replace( fn , '' ); // Remove if exists
				if ( what ) t = t.replace( "\n</gallery>" , fn + "\n</gallery>" );
				
				filecomments.editPage( filecomments.user_favs_page , 'text' , t , mw.config.get('wgTitle') , false );

			}
		);
		
		
	} ,
	
	renderComments : function () {
		filecomments.comment_render_counter--;
		if ( filecomments.comment_render_counter > 0 ) return;
		
		var keys = [];
		$.each( filecomments.comments , function ( k , v ) { keys.push( k ); } );
		keys.sort();
		
		var h = '';
		$.each( keys , function ( kk , k ) {
			v = filecomments.comments[k];
			h += "<div class='filecomments_comment'>";
	
			h += "<div>";
			h += "<a href='" + mw.util.getUrl( 'User:'+v.user ) + "'><b>" + v.user + "</b></a>";
			h += " (<a href='" + mw.util.getUrl( 'User talk:'+v.user ) + "'>" + filecomments.it('talk_to_user') + "</a>)";
			h += " " + v.time;
			h += "</div>";
			
			if ( isDef( v.rendered ) ) h += "<div>" + v.rendered + "</div>";
			else h += "<div>" + v.text + "</div>";
			
			h += "</div>";
		} );

		$('#filecomments_main').html( h );
	} ,
	
	parseComment : function ( comment ) {
		var user = comment.match( /\|(.+?)\|(.+?)\}\}/ );
		var time = user[1];
		user = user[2];
		
		var text = comment.match( /\}\}(.+?)\{\{filecommentend\}\}$/ );
		text = text[1];

		var begin = comment.match( /^(.+?\}\})/ );
		var end = comment.match( /\}\}.+?(\{\{filecommentend\}\})$/ );
		
		filecomments.comments[time+"/"+user] = {
			'user' : user ,
			'time' : time ,
			'text' : text ,
			'begin' : begin[1] ,
			'end' : end[1]
		};
		
		if ( !filecomments.containsWikiText( text ) ) return;
		
		filecomments.comment_render_counter++;
		$.getJSON(
			mw.util.wikiScript( 'api' ), {
				'format': 'json',
				'action': 'parse',
				'text' : text
			}, function( data ) {
				$.each( data.parse.text , function ( k , v ) {
					filecomments.comments[time + '/' + user].rendered = v;
					filecomments.renderComments();
				} );
			}
		);
		
	} ,
	
	containsWikiText : function ( text ) {
		if ( text.match( /\[\[/ ) ) return true;
		if ( text.match( /\{\{/ ) ) return true;
		if ( text.match( /''/ ) ) return true;
		if ( text.match( /[<>]/ ) ) return true;
		return false;
	} ,
	
	twoDigits : function ( v ) {
		if ( v < 10 ) return "0" + v;
		return "" + v;
	} ,

	
	addComment : function () {
		var comment = prompt( filecomments.it('add_a_comment') + filecomments.user_name  );
		if ( !comment || comment === '' || comment === null ) return;
		
		var ct = new Date();
		var t = ct.getFullYear() + "-";
		t += filecomments.twoDigits( ct.getMonth()+1 ) + "-";
		t += filecomments.twoDigits( ct.getDate() ) + " ";
		t += filecomments.twoDigits( ct.getHours() ) + ":";
		t += filecomments.twoDigits( ct.getMinutes() ) + ":";
		t += filecomments.twoDigits( ct.getSeconds() );
		
		var line = "\n" + '{{filecommentstart|' + t + "|" + filecomments.user_name + '}}' + comment + '{{filecommentend}}';
		
		filecomments.editTalkPage( 'appendtext' , line , filecomments.it('summary_new_comment') + comment );
		
		line = "\n* [[:File:" + mw.config.get('wgTitle') + "|64px|noframe|]] " + mw.config.get('wgTitle') + " (" + t + ") : " + comment;
		filecomments.editPage( filecomments.user_comments_page , 'appendtext' , line , filecomments.it('summary_new_comment') + comment , 0 );
		
	} ,
	
	editTalkPage : function ( mode , text , summary ) {
		filecomments.editPage( filecomments.talk_page , mode , text , summary , true );
	} ,

	editPage : function ( page , mode , text , summary , update_box ) {
	
		
		$('#filecomments_container').css( 'background-color' , '#DDDDDD' );
		$.getJSON(
			mw.util.wikiScript( 'api' ), {
				'format': 'json',
				'action': 'query',
				'titles': page,
				'prop' : 'info' ,
				'intoken' : 'edit'
			}, function( data ) {
			
				var token = '';
				$.each( data.query.pages , function ( k , v ) {
					token = v.edittoken;
				} );
				if ( token === '' ) {
					alert( filecomments.it('alert_no_edit_token') );
					$('#filecomments_container').css( 'background-color' , '' );
					return;
				}
				
				var o = {
					'format': 'json',
					'action': 'edit',
					'title': page,
					'token' : token ,
					'summary' : summary
				};
				o[mode] = text;
				if ( !mw.user.isAnon() ) o.minor = '';

				$.post(
					mw.util.wikiScript( 'api' ), o,
					function( data ) {
						$('#filecomments_container').css( 'background-color' , '' );
						if ( data.edit.result != 'Success' ) {
							// @todo: just load the "json" module because JSON is not cross-browser supported
							alert( filecomments.it('alert_edit_fail') + "\n" + JSON.stringify( data ) );
						} else {
							if ( update_box ) filecomments.parseTalkPage();
						}
					}
				);
				
			
			}
		);

	} ,
	
	the_end_of_the_object : '' // To avoid worrying about the final comma...

};


$( function() {
	filecomments.thumb_size = 128;
	mw.util.addCSS(
		'#filecomments_container { padding:2px; }'
		+ '.filecomments_floatright { float:right; max-width:500px; border:1px solid #EEEEEE; margin-left:5px; margin-bottom:5px; }'
		+ '#filecomments_top { border-bottom:1px solid #EEEEEE; padding-bottom:2px; margin-bottom:5px; }'
		+ '.filecomments_comment { margin-top:3px; margin-bottom:3px; border-left:5px solid #62A9FF; padding-left:2px }'
		+ '.filecomments_comment:hover { background-color:#DBEBFF; }'
		+ '#filecomments_bottom { margin-top:5px; }'
		+ '.filecomments_thumb_container { width:'+filecomments.thumb_size+'px; height:'+filecomments.thumb_size+'px; margin:5px; padding:2px; border:1px solid #EEEEEE; display:inline-block; text-align:center; }'
	);
	filecomments.init();
});