/*
* Extended version of SiteBase.js for use in jQuery version of Special:GlobalWatchlist
*/
var GlobalWatchlistSiteBase = require( './SiteBase.js' );
/**
* Represents a specific site, including the display (used in jQuery display)
*
* @class GlobalWatchlistSiteDisplay
* @extends GlobalWatchlistSiteBase
*
* @constructor
* @param {GlobalWatchlistDebugger} globalWatchlistDebug Debugger instance to log to
* @param {GlobalWatchlistLinker} linker Linker instance to use
* @param {Object} config User configuration
* @param {mw.ForeignApi} api Instance of mw.ForeignApi for this site
* @param {GlobalWatchlistWatchlistUtils} watchlistUtils WatchlistUtils instance for this site
* @param {string} urlFragment string for which site this represents
*/
function GlobalWatchlistSiteDisplay(
globalWatchlistDebug,
linker,
config,
api,
watchlistUtils,
urlFragment
) {
GlobalWatchlistSiteDisplay.super.call(
this,
globalWatchlistDebug,
linker,
config,
api,
watchlistUtils,
urlFragment
);
// Actual output for this site
this.$feedDiv = '';
}
OO.inheritClass( GlobalWatchlistSiteDisplay, GlobalWatchlistSiteBase );
/**
* Make the links for a row in the watchlist
*
* @param {GlobalWatchlistEntryBase} entry Details of the list entry to create
* @return {jQuery} list item
*/
GlobalWatchlistSiteDisplay.prototype.makePageLink = function ( entry ) {
var pageTitle = encodeURIComponent( entry.title ).replace( /'/g, '%27' );
var $pageLink = $( '<a>' )
.attr( 'href', this.linker.linkQuery( 'title=' + pageTitle + '&redirect=no' ) )
.attr( 'target', '_blank' )
.text( entry.titleMsg || entry.title );
var that = this;
// Actually set up the $row to be returned
var $row = $( '<li>' );
$row.attr( 'data-site', encodeURIComponent( this.siteID ) );
$row.attr( 'data-title', encodeURIComponent( entry.title ) );
if ( entry.timestamp ) {
// entry.timestampTitle is either a string explaining grouped changes, or null to ignore
var $timestamp = $( '<span>' )
.text( entry.timestamp )
.attr( 'title', entry.timestampTitle );
$row.append( $timestamp )
.append( ' ' );
}
if ( entry.expiry ) {
var clockIcon = new OO.ui.IconWidget( {
classes: [ 'ext-globalwatchlist-expiry-icon' ],
icon: 'clock',
title: entry.expiry
} );
$row.append( clockIcon.$element )
.append( ' ' );
}
if ( entry.flags ) {
// New page / minor edit / bot flag
$row.append( $( '<b>' ).text( entry.flags ) )
.append( ' ' );
}
if ( entry.entryType === 'log' ) {
var logText = 'Log: ' + entry.logtype + '/' + entry.logaction + ': ';
$row.append( $( '<em>' ).text( logText ) )
.append( ' ' );
}
$row.append( $pageLink )
.append( ' (' );
if ( entry.entryType !== 'log' ) {
// No history link for log entries, T273691
var $historyLink = $( '<a>' )
.attr( 'href', this.linker.linkQuery( 'title=' + pageTitle + '&action=history' ) )
.attr( 'target', '_blank' )
.text( mw.msg( 'globalwatchlist-history' ) );
$row.append( $historyLink )
.append( ', ' );
}
// No diff links in fast mode, see T269728
if ( entry.entryType === 'edit' && entry.newPage === false && this.config.fastMode === false ) {
var $diffLink = $( '<a>' )
.attr( 'href', this.linker.linkQuery( 'diff=' + entry.toRev + '&oldid=' + entry.fromRev ) )
.attr( 'target', '_blank' )
.addClass( 'ext-globalwatchlist-diff' )
.text(
entry.editCount === 1 ? mw.msg( 'diff' ) : mw.msg( 'nchanges', entry.editCount )
);
$row.append( $diffLink )
.append( ', ' );
} else if ( entry.entryType === 'log' ) {
var $logPageLink = $( '<a>' )
.attr( 'href', this.linker.linkQuery( 'title=Special:Log&page=' + pageTitle ) )
.attr( 'target', '_blank' )
.text( mw.msg( 'globalwatchlist-log-page' ) );
$row.append( $logPageLink )
.append( ', ' );
var $logEntryLink = $( '<a>' )
.attr( 'href', this.linker.linkQuery( 'title=Special:Log&logid=' + entry.logid ) )
.attr( 'target', '_blank' )
.text( mw.msg( 'globalwatchlist-log-entry' ) );
$row.append( $logEntryLink )
.append( ', ' );
}
var $unwatchLink = $( '<a>' )
.addClass( 'ext-globalwatchlist-watchunwatch' )
.text( mw.msg( 'globalwatchlist-unwatch' ) )
.on( 'click', function () {
that.changeWatched( entry.title, 'unwatch' );
} );
$row.append( $unwatchLink )
.append( ', ' );
var $markAsReadLink = $( '<a>' )
.addClass( 'ext-globalwatchlist-markpageseen' )
.text( mw.msg( 'globalwatchlist-markpageseen' ) )
.on( 'click', function () {
that.markPageAsSeen( entry.title );
} );
$row.append( $markAsReadLink );
$row.append( ')' );
var $user = ( this.config.fastMode ? '' : entry.userDisplay );
var $comment = '';
if ( entry.commentDisplay ) {
// Need to process links in the parsed comments as raw HTML
$comment = $( '<span>' ).html( entry.commentDisplay );
}
if ( $user !== '' || $comment !== '' ) {
$row.append( ' (' )
.append( $user )
.append( $comment )
.append( ')' );
}
if ( entry.tagsDisplay ) {
// Need to process links in the parsed description as raw HTML
var $tags = $( '<em>' ).html( entry.tagsDisplay );
$row.append( ' ' )
.append( $tags );
}
return $row;
};
/* end GlobalWatchlistSiteDisplay.prototype.makePageLink */
/**
* Create the output for this.$feedDiv, either for success (via renderWatchlist) or
* failure (via renderApiFailure)
*
* @param {jQuery} $content Content to show
*/
GlobalWatchlistSiteDisplay.prototype.actuallyRenderWatchlist = function ( $content ) {
var headerTemplate = mw.template.get(
'ext.globalwatchlist.specialglobalwatchlist',
'templates/siteRowHeader.mustache'
);
var headerParams = {
'special-watchlist-url': this.linker.linkPage( 'Special:Watchlist' ),
'site-name': this.site,
'special-edit-watchlist-url': this.linker.linkPage( 'Special:EditWatchlist' ),
'edit-watchlist-msg': mw.msg( 'globalwatchlist-editwatchlist' )
};
// Get RTL/LTR direction for the site. We can't use String.prototype.startsWith, since
// that is unavailable in IE11, and doesn't take multiple values anyway. Use
// String.prototype.match with a list of the language codes that should be RTL
// this.siteID is based on the URL form of the wiki, and we assume that wikis that are
// meant to be RTL are in the form `⧼rtl language code⧽.*`, and any URL that does not
// match this is for an LTR wiki. See T274602 and T274313
var isRTL = this.siteID.match(
/^(ar|azb|ckb|dv|fa|glk|he|ks|lrc|mzn|nqo|pnb|ps|sd|ug|ur|yi)_/
);
// mw-content-ltr and -rtl classes are not enough to ensure that the text is formatted
// in the correct direction, so add a manual direction attribute. See T287649
// We still add those classes because they are also used by jQuery.makeCollapsible
// to know if the collapse button should be on the right or left.
this.$feedDiv = $( '<div>' )
.attr( 'id', 'ext-globalwatchlist-feed-site-' + this.siteID )
.attr( 'dir', isRTL ? 'rtl' : 'ltr' )
.addClass( 'ext-globalwatchlist-feed-site' )
.addClass( isRTL ? 'mw-content-rtl' : 'mw-content-ltr' )
.append(
headerTemplate.render( headerParams ),
$content
);
};
/**
* Alert on API failures
*/
GlobalWatchlistSiteDisplay.prototype.renderApiFailure = function () {
var $siteContent = $( '<p>' ).text(
mw.msg( 'globalwatchlist-fetch-site-failure' )
);
this.actuallyRenderWatchlist( $siteContent );
};
/**
* Display the watchlist
*
* @param {GlobalWatchlistEntryBase[]} summary What should be rendered
*/
GlobalWatchlistSiteDisplay.prototype.renderWatchlist = function ( summary ) {
var $ul = $( '<ul>' ),
that = this;
summary.forEach( function ( element ) {
$ul.append( that.makePageLink( element ) );
} );
var markSeenButton = new OO.ui.ButtonInputWidget( {
classes: [ 'ext-globalwatchlist-feed-markseen' ],
flags: [ 'destructive' ],
icon: 'check',
label: mw.msg( 'globalwatchlist-markseen' )
} ).on( 'click', function () {
that.markAllAsSeen();
} );
var $outputContent = $( '<div>' )
.addClass( 'ext-globalwatchlist-site' )
.append(
markSeenButton.$element,
$ul
)
.makeCollapsible();
this.actuallyRenderWatchlist( $outputContent );
};
/* end GlobalWatchlistSiteDisplay.prototype.renderWatchlist */
/**
* Update display after marking a site as seen
*/
GlobalWatchlistSiteDisplay.prototype.afterMarkAllAsSeen = function () {
this.debug( 'markSiteAsSeen - hiding site' );
if ( this.$feedDiv ) {
// Don't call .children() on the default empty string, T275078
$( this.$feedDiv.children()[ 1 ] ).hide();
}
// FIXME
// GlobalWatchlist.watchlists.checkChangesShown( true );
};
/* end GlobalWatchlistSiteDisplay.prototype.afterMarkAllAsSeen */
GlobalWatchlistSiteBase.prototype.afterMarkPageAsSeen = function ( pageTitle ) {
this.debug( 'markPageAsSeen - hiding page' );
if ( this.$feedDiv ) {
var dataTitle = encodeURIComponent( pageTitle );
$( this.$feedDiv.find( '[data-title="' + dataTitle + '"]' ) ).hide();
}
};
/**
* Update entry click handlers, text, and strikethrough for a specific title
*
* @param {string} pageTitle Title of the page that was unwatched/rewatched.
* @param {boolean} unwatched Whether the page was unwatched
*/
GlobalWatchlistSiteDisplay.prototype.processUpdateWatched = function ( pageTitle, unwatched ) {
this.debug(
'Processing after ' + ( unwatched ? 'unwatching' : 'rewatching' ) + ': ' + pageTitle
);
var encodedSite = encodeURIComponent( this.siteID );
var encodedTitle = encodeURIComponent( pageTitle );
var $entries = $( 'li[data-site="' + encodedSite + '"][data-title="' + encodedTitle + '"]' );
$entries[ unwatched ? 'addClass' : 'removeClass' ]( 'ext-globalwatchlist-strike' );
$entries.children( '.ext-globalwatchlist-expiry-icon' ).remove();
var $links = $entries.children( 'a.ext-globalwatchlist-watchunwatch' );
var newText = mw.msg( unwatched ? 'globalwatchlist-rewatch' : 'globalwatchlist-unwatch' );
var that = this;
$links.each( function () {
$( this ).off( 'click' );
$( this ).on( 'click', function () {
that.changeWatched( pageTitle, unwatched ? 'watch' : 'unwatch' );
} );
$( this ).text( newText );
} );
};
/* end GlobalWatchlistSiteDisplay.prototype.processUpdateWatched */
module.exports = GlobalWatchlistSiteDisplay;