ESLint: Autofix no-var rule

Leave rule off for now as manual fixes are required.

Also temporarily disable prefer-const rule as that
will also require some manual fixes.

Change-Id: I8c3478f26f51287acb943bd38c9c1020c06b9f39
This commit is contained in:
Ed Sanders 2024-05-24 13:20:50 +01:00
parent 9f68ee30af
commit ca5157156a
36 changed files with 512 additions and 510 deletions

View file

@ -4,6 +4,7 @@
# Build
/vendor/
/coverage/
/docs/
# Language files written automatically by TranslateWiki
/i18n/**/*.json

View file

@ -12,6 +12,7 @@
"rules": {
"no-implicit-globals": "off",
"no-var": "off",
"prefer-const": "off",
"max-len": "off",
"prefer-arrow-callback": "error",
"implicit-arrow-linebreak": "error",

View file

@ -64,12 +64,12 @@ function getLatestRevId( pageName ) {
* @return {jQuery.Promise} Promise which resolves with a CommentDetails object, or rejects with an error
*/
CommentController.prototype.getTranscludedFromSource = function () {
var pageName = mw.config.get( 'wgRelevantPageName' ),
const pageName = mw.config.get( 'wgRelevantPageName' ),
oldId = mw.config.get( 'wgCurRevisionId' ),
threadItem = this.getThreadItem();
function followTransclusion( recursionLimit, code, data ) {
var errorData;
let errorData;
if ( recursionLimit > 0 && code === 'comment-is-transcluded' ) {
errorData = data.errors[ 0 ].data;
if ( errorData.follow && typeof errorData.transcludedFrom === 'string' ) {
@ -85,7 +85,7 @@ CommentController.prototype.getTranscludedFromSource = function () {
// Arbitrary limit of 10 steps, which should be more than anyone could ever need
// (there are reasonable use cases for at least 2)
var promise = controller.checkThreadItemOnPage( pageName, oldId, threadItem )
const promise = controller.checkThreadItemOnPage( pageName, oldId, threadItem )
.catch( followTransclusion.bind( null, 10 ) );
return promise;
@ -114,7 +114,7 @@ CommentController.static.initType = 'page';
* @param {boolean} [suppressNotifications] Don't notify the user if recovering auto-save
*/
CommentController.prototype.setup = function ( mode, hideErrors, suppressNotifications ) {
var threadItem = this.getThreadItem();
const threadItem = this.getThreadItem();
if ( mode === undefined ) {
mode = mw.user.options.get( 'discussiontools-editmode' ) ||
@ -168,7 +168,7 @@ CommentController.prototype.setup = function ( mode, hideErrors, suppressNotific
// we wrap this loading message in another element.
$( '<span>' ).text( mw.msg( 'discussiontools-replywidget-loading' ) )
);
var scrollPaddingCollapsed = OO.copy( scrollPadding );
const scrollPaddingCollapsed = OO.copy( scrollPadding );
// We don't know exactly how tall the widge will be, but leave room for one line
// of preview in source mode (~270px). Visual mode is ~250px.
scrollPaddingCollapsed.bottom += 270;
@ -230,8 +230,8 @@ CommentController.prototype.onVisibilityChange = function () {
CommentController.prototype.startPoll = function ( nextDelay ) {
nextDelay = nextDelay || 5000;
var threadItemId = this.threadItem.id;
var subscribableHeadingId = this.threadItem.getSubscribableHeading().id;
const threadItemId = this.threadItem.id;
const subscribableHeadingId = this.threadItem.getSubscribableHeading().id;
this.pollApiRequest = controller.getApi().get( {
action: 'discussiontoolscompare',
@ -245,18 +245,18 @@ CommentController.prototype.startPoll = function ( nextDelay ) {
cmt.author !== mw.user.getName();
}
var result = OO.getProp( response, 'discussiontoolscompare' ) || {};
var addedComments = result.addedcomments.filter( relevantCommentFilter );
var removedComments = result.removedcomments.filter( relevantCommentFilter );
const result = OO.getProp( response, 'discussiontoolscompare' ) || {};
const addedComments = result.addedcomments.filter( relevantCommentFilter );
const removedComments = result.removedcomments.filter( relevantCommentFilter );
if ( addedComments.length || removedComments.length ) {
this.updateNewCommentsWarning( addedComments, removedComments );
}
// Parent comment was deleted
var isParentRemoved = result.removedcomments.some( ( cmt ) => cmt.id === threadItemId );
const isParentRemoved = result.removedcomments.some( ( cmt ) => cmt.id === threadItemId );
// Parent comment was deleted then added back (e.g. reverted vandalism)
var isParentAdded = result.addedcomments.some( ( cmt ) => cmt.id === threadItemId );
const isParentAdded = result.addedcomments.some( ( cmt ) => cmt.id === threadItemId );
if ( isParentAdded ) {
this.setParentRemoved( false );
@ -410,12 +410,12 @@ CommentController.prototype.onReplyWidgetTeardown = function ( mode ) {
* @return {Object.<string,string>} API query data
*/
CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
var threadItem = this.getThreadItem();
var replyWidget = this.replyWidget;
var sameNameComments = this.threadItemSet.findCommentsByName( threadItem.name );
const threadItem = this.getThreadItem();
const replyWidget = this.replyWidget;
const sameNameComments = this.threadItemSet.findCommentsByName( threadItem.name );
var mode = replyWidget.getMode();
var tags = [
const mode = replyWidget.getMode();
const tags = [
'discussiontools',
'discussiontools-reply',
'discussiontools-' + mode
@ -425,7 +425,7 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
tags.push( 'discussiontools-source-enhanced' );
}
var data = {
const data = {
action: 'discussiontoolsedit',
paction: 'addcomment',
page: pageName,
@ -448,7 +448,7 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
data.html = replyWidget.getValue();
}
var captchaInput = replyWidget.captchaInput;
const captchaInput = replyWidget.captchaInput;
if ( captchaInput ) {
data.captchaid = captchaInput.getCaptchaId();
data.captchaword = captchaInput.getCaptchaWord();
@ -470,11 +470,11 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
* @return {jQuery.Promise} Promise which resolves when the save is complete
*/
CommentController.prototype.save = function ( pageName ) {
var replyWidget = this.replyWidget,
const replyWidget = this.replyWidget,
threadItem = this.getThreadItem();
return this.replyWidget.checkboxesPromise.then( ( checkboxes ) => {
var data = this.getApiQuery( pageName, checkboxes );
const data = this.getApiQuery( pageName, checkboxes );
if (
// We're saving the first comment on a page that previously didn't exist.
@ -491,7 +491,7 @@ CommentController.prototype.save = function ( pageName ) {
// This means that we might need to redirect to an opaque URL,
// so we must set up query parameters we want ahead of time.
data.returnto = pageName;
var params = new URLSearchParams();
const params = new URLSearchParams();
params.set( 'dtrepliedto', this.getThreadItem().id );
params.set( 'dttempusercreated', '1' );
data.returntoquery = params.toString();
@ -499,9 +499,9 @@ CommentController.prototype.save = function ( pageName ) {
// No timeout. Huge talk pages can take a long time to save, and falsely reporting an error
// could result in duplicate messages if the user retries. (T249071)
var defaults = OO.copy( controller.getApi().defaults );
const defaults = OO.copy( controller.getApi().defaults );
defaults.ajax.timeout = 0;
var noTimeoutApi = new mw.Api( defaults );
const noTimeoutApi = new mw.Api( defaults );
return mw.libs.ve.targetSaver.postContent(
data, { api: noTimeoutApi }
@ -541,7 +541,7 @@ CommentController.prototype.updateNewCommentsWarning = function ( addedComments,
this.newComments.push.apply( this.newComments, addedComments );
// Delete any comments which have since been deleted (e.g. posted then reverted)
var removedCommentIds = removedComments.filter( ( cmt ) => cmt.id );
const removedCommentIds = removedComments.filter( ( cmt ) => cmt.id );
this.newComments = this.newComments.filter(
// If comment ID is not in removedCommentIds, keep it
( cmt ) => removedCommentIds.indexOf( cmt.id ) === -1
@ -571,14 +571,14 @@ CommentController.prototype.setParentRemoved = function ( parentRemoved ) {
* @return {jQuery.Promise} Promise which resolves when switch is complete
*/
CommentController.prototype.switchToWikitext = function () {
var oldWidget = this.replyWidget,
const oldWidget = this.replyWidget,
target = oldWidget.replyBodyWidget.target,
oldShowAdvanced = oldWidget.showAdvanced,
oldEditSummary = oldWidget.getEditSummary(),
previewDeferred = $.Deferred();
// TODO: We may need to pass oldid/etag when editing is supported
var wikitextPromise = target.getWikitextFragment( target.getSurface().getModel().getDocument() );
const wikitextPromise = target.getWikitextFragment( target.getSurface().getModel().getDocument() );
this.replyWidgetPromise = this.createReplyWidget(
oldWidget.commentDetails,
{ mode: 'source' }
@ -633,7 +633,7 @@ CommentController.prototype.doIndentReplacements = function ( wikitext, indent )
* @param {Node} rootNode Node potentially containing definition lists (modified in-place)
*/
CommentController.prototype.undoIndentReplacements = function ( rootNode ) {
var children = Array.prototype.slice.call( rootNode.childNodes );
const children = Array.prototype.slice.call( rootNode.childNodes );
// There may be multiple lists when some lines are template generated
children.forEach( ( child ) => {
if ( child.nodeType === Node.ELEMENT_NODE ) {
@ -671,7 +671,7 @@ CommentController.prototype.getUnsupportedNodeSelectors = function () {
* @return {jQuery.Promise} Promise which resolves when switch is complete
*/
CommentController.prototype.switchToVisual = function () {
var oldWidget = this.replyWidget,
let oldWidget = this.replyWidget,
oldShowAdvanced = oldWidget.showAdvanced,
oldEditSummary = oldWidget.getEditSummary(),
wikitext = oldWidget.getValue();
@ -684,7 +684,7 @@ CommentController.prototype.switchToVisual = function () {
'$1<span data-dtsignatureforswitching="1"></span>$2'
);
var parsePromise;
let parsePromise;
if ( wikitext ) {
wikitext = this.doIndentReplacements( wikitext, dtConf.replyIndentation === 'invisible' ? ':' : '*' );
@ -705,17 +705,17 @@ CommentController.prototype.switchToVisual = function () {
);
return $.when( parsePromise, this.replyWidgetPromise ).then( ( html, replyWidget ) => {
var unsupportedSelectors = this.getUnsupportedNodeSelectors();
const unsupportedSelectors = this.getUnsupportedNodeSelectors();
var doc;
let doc;
if ( html ) {
doc = replyWidget.replyBodyWidget.target.parseDocument( html );
// Remove RESTBase IDs (T253584)
mw.libs.ve.stripRestbaseIds( doc );
// Check for tables, headings, images, templates
for ( var type in unsupportedSelectors ) {
for ( const type in unsupportedSelectors ) {
if ( doc.querySelector( unsupportedSelectors[ type ] ) ) {
var $msg = $( '<div>' ).html(
const $msg = $( '<div>' ).html(
mw.message(
'discussiontools-error-noswitchtove',
// The following messages are used here:

View file

@ -51,8 +51,8 @@ OO.inheritClass( CommentItem, ThreadItem );
* @return {string} Comment timestamp in standard format
*/
CommentItem.prototype.getTimestampString = function () {
var dtConfig = require( './config.json' );
var switchTime = moment.utc( dtConfig.switchTime );
const dtConfig = require( './config.json' );
const switchTime = moment.utc( dtConfig.switchTime );
if ( this.timestamp < switchTime ) {
return this.timestamp.utc().toISOString();
} else {
@ -65,7 +65,7 @@ CommentItem.prototype.getTimestampString = function () {
* @return {HeadingItem} Closest ancestor which is a HeadingItem
*/
CommentItem.prototype.getHeading = function () {
var parent = this;
let parent = this;
while ( parent && parent.type !== 'heading' ) {
parent = parent.parent;
}
@ -76,7 +76,7 @@ CommentItem.prototype.getHeading = function () {
* @return {HeadingItem|null} Closest heading that can be used for topic subscriptions
*/
CommentItem.prototype.getSubscribableHeading = function () {
var heading = this.getHeading();
let heading = this.getHeading();
while ( heading && heading.type === 'heading' && !heading.isSubscribable() ) {
heading = heading.parent;
}

View file

@ -22,11 +22,11 @@ function HeadingItem( range, headingLevel ) {
OO.inheritClass( HeadingItem, ThreadItem );
HeadingItem.prototype.getLinkableTitle = function () {
var title = '';
let title = '';
// If this comment is in 0th section, there's no section title for the edit summary
if ( !this.placeholderHeading ) {
var headline = this.range.startContainer;
var id = headline.getAttribute( 'id' );
const headline = this.range.startContainer;
const id = headline.getAttribute( 'id' );
if ( id ) {
// Replace underscores with spaces to undo Sanitizer::escapeIdInternal().
// This assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ],

View file

@ -40,7 +40,7 @@ LedeSectionDialog.prototype.getSetupProcess = function ( data ) {
// Enable collapsible content (T323639), which is normally not handled on mobile (T111565).
// It's safe to do this twice if that changes (makeCollapsible() checks if each element was
// already handled). Using the same approach as in 'mediawiki.page.ready' in MediaWiki core.
var $collapsible = this.contentLayout.$element.find( '.mw-collapsible' );
const $collapsible = this.contentLayout.$element.find( '.mw-collapsible' );
if ( $collapsible.length ) {
// This module is also preloaded in PageHooks to avoid visual jumps when things collapse.
mw.loader.using( 'jquery.makeCollapsible' ).then( () => {

View file

@ -67,12 +67,12 @@ NewTopicController.static.suppressedEditNotices = [
* @inheritdoc
*/
NewTopicController.prototype.setup = function ( mode ) {
var rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
const rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
// Insert directly after the page content on already existing pages
// (.mw-parser-output is missing on non-existent pages)
var $parserOutput = this.$pageContainer.find( '.mw-parser-output' ).first();
var $mobileAddTopicWrapper = this.$pageContainer.find( '.ext-discussiontools-init-new-topic' );
const $parserOutput = this.$pageContainer.find( '.mw-parser-output' ).first();
const $mobileAddTopicWrapper = this.$pageContainer.find( '.ext-discussiontools-init-new-topic' );
if ( $parserOutput.length ) {
$parserOutput.after( this.container.$element );
} else if ( $mobileAddTopicWrapper.length ) {
@ -109,19 +109,19 @@ NewTopicController.prototype.setupReplyWidget = function ( replyWidget, data ) {
NewTopicController.super.prototype.setupReplyWidget.apply( this, arguments );
this.$notices.empty();
for ( var noticeName in this.replyWidget.commentDetails.notices ) {
for ( const noticeName in this.replyWidget.commentDetails.notices ) {
if ( this.constructor.static.suppressedEditNotices.indexOf( noticeName ) !== -1 ) {
continue;
}
var noticeItem = this.replyWidget.commentDetails.notices[ noticeName ];
var $noticeElement = $( '<div>' )
const noticeItem = this.replyWidget.commentDetails.notices[ noticeName ];
const $noticeElement = $( '<div>' )
.addClass( 'ext-discussiontools-ui-replyWidget-notice' )
.html( typeof noticeItem === 'string' ? noticeItem : noticeItem.message );
this.$notices.append( $noticeElement );
}
mw.hook( 'wikipage.content' ).fire( this.$notices );
var title = this.replyWidget.storage.get( 'title' );
const title = this.replyWidget.storage.get( 'title' );
if ( title && !this.sectionTitle.getValue() ) {
// Don't overwrite if the user has already typed something in while the widget was loading.
// TODO This should happen immediately rather than waiting for the reply widget to load,
@ -130,7 +130,7 @@ NewTopicController.prototype.setupReplyWidget = function ( replyWidget, data ) {
this.prevTitleText = title;
if ( this.replyWidget.storage.get( 'summary' ) === null ) {
var generatedSummary = this.generateSummary( title );
const generatedSummary = this.generateSummary( title );
this.replyWidget.editSummaryInput.setValue( generatedSummary );
}
}
@ -180,8 +180,8 @@ NewTopicController.prototype.onReplyWidgetClearStorage = function () {
NewTopicController.prototype.storeEditSummary = function () {
if ( this.replyWidget ) {
var currentSummary = this.replyWidget.editSummaryInput.getValue();
var generatedSummary = this.generateSummary( this.sectionTitle.getValue() );
const currentSummary = this.replyWidget.editSummaryInput.getValue();
const generatedSummary = this.generateSummary( this.sectionTitle.getValue() );
if ( currentSummary === generatedSummary ) {
// Do not store generated summaries (T315730)
this.replyWidget.storage.remove( 'summary' );
@ -202,7 +202,7 @@ NewTopicController.prototype.onReplyWidgetTeardown = function ( abandoned ) {
this.container.$element.detach();
if ( mw.config.get( 'wgDiscussionToolsStartNewTopicTool' ) ) {
var url = new URL( location.href );
const url = new URL( location.href );
url.searchParams.delete( 'action' );
url.searchParams.delete( 'veaction' );
url.searchParams.delete( 'section' );
@ -244,11 +244,11 @@ NewTopicController.prototype.getUnsupportedNodeSelectors = function () {
* @inheritdoc
*/
NewTopicController.prototype.getApiQuery = function ( pageName, checkboxes ) {
var data = NewTopicController.super.prototype.getApiQuery.call( this, pageName, checkboxes );
let data = NewTopicController.super.prototype.getApiQuery.call( this, pageName, checkboxes );
// Rebuild the tags array and remove the reply tag
var tags = ( data.dttags || '' ).split( ',' );
var replyTag = tags.indexOf( 'discussiontools-reply' );
const tags = ( data.dttags || '' ).split( ',' );
const replyTag = tags.indexOf( 'discussiontools-reply' );
if ( replyTag !== -1 ) {
tags.splice( replyTag, 1 );
}
@ -290,16 +290,16 @@ NewTopicController.prototype.generateSummary = function ( titleText ) {
* @private
*/
NewTopicController.prototype.onSectionTitleChange = function () {
var titleText = this.sectionTitle.getValue();
var prevTitleText = this.prevTitleText;
const titleText = this.sectionTitle.getValue();
const prevTitleText = this.prevTitleText;
if ( prevTitleText !== titleText ) {
this.replyWidget.storage.set( 'title', titleText );
var generatedSummary = this.generateSummary( titleText );
var generatedPrevSummary = this.generateSummary( prevTitleText );
const generatedSummary = this.generateSummary( titleText );
const generatedPrevSummary = this.generateSummary( prevTitleText );
var currentSummary = this.replyWidget.editSummaryInput.getValue();
const currentSummary = this.replyWidget.editSummaryInput.getValue();
// Fill in edit summary if it was not modified by the user yet
if ( currentSummary === generatedPrevSummary ) {
@ -318,13 +318,13 @@ NewTopicController.prototype.onSectionTitleChange = function () {
* @private
*/
NewTopicController.prototype.onBodyFocus = function () {
var offsetBefore = this.replyWidget.$element.offset().top;
var rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
var scrollBefore = rootScrollable.scrollTop;
const offsetBefore = this.replyWidget.$element.offset().top;
const rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
const scrollBefore = rootScrollable.scrollTop;
this.checkSectionTitleValidity();
var offsetChange = this.replyWidget.$element.offset().top - offsetBefore;
const offsetChange = this.replyWidget.$element.offset().top - offsetBefore;
// Ensure the rest of the widget doesn't move when the validation
// message is triggered by a focus. (T275923)
// Browsers sometimes also scroll in response to focus events,

View file

@ -44,7 +44,7 @@ Parser.prototype.parse = function ( rootNode, title ) {
this.rootNode = rootNode;
this.title = title;
var result = this.buildThreadItems();
const result = this.buildThreadItems();
this.buildThreads( result );
this.computeIdsAndNames( result );
@ -90,12 +90,12 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
return '(' + array.map( mw.util.escapeRegExp ).join( '|' ) + ')';
}
var s = '';
var raw = false;
let s = '';
let raw = false;
// Adapted from Language::sprintfDate()
for ( var p = 0; p < format.length; p++ ) {
var num = false;
var code = format[ p ];
for ( let p = 0; p < format.length; p++ ) {
let num = false;
let code = format[ p ];
if ( code === 'x' && p < format.length - 1 ) {
code += format[ ++p ];
}
@ -182,7 +182,7 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
case '"':
// Quoted literal
if ( p < format.length - 1 ) {
var endQuote = format.indexOf( '"', p + 1 );
const endQuote = format.indexOf( '"', p + 1 );
if ( endQuote === -1 ) {
// No terminating quote, assume literal "
s += '"';
@ -213,10 +213,10 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
s += '[\\u200E\\u200F]?';
}
var tzRegexp = regexpAlternateGroup( Object.keys( tzAbbrs ) );
const tzRegexp = regexpAlternateGroup( Object.keys( tzAbbrs ) );
// Hard-coded parentheses and space like in Parser::pstPass2
// Ignore some invisible Unicode characters that often sneak into copy-pasted timestamps (T245784)
var regexp = s + ' [\\u200E\\u200F]?\\(' + tzRegexp + '\\)';
const regexp = s + ' [\\u200E\\u200F]?\\(' + tzRegexp + '\\)';
return regexp;
};
@ -235,9 +235,9 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
* @return {TimestampParser} Timestamp parser function
*/
Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits, localTimezone, tzAbbrs ) {
var matchingGroups = [];
for ( var p = 0; p < format.length; p++ ) {
var code = format[ p ];
const matchingGroups = [];
for ( let p = 0; p < format.length; p++ ) {
let code = format[ p ];
if ( code === 'x' && p < format.length - 1 ) {
code += format[ ++p ];
}
@ -275,7 +275,7 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
case '"':
// Quoted literal
if ( p < format.length - 1 ) {
var endQuote = format.indexOf( '"', p + 1 );
const endQuote = format.indexOf( '"', p + 1 );
if ( endQuote !== -1 ) {
p = endQuote;
}
@ -312,16 +312,16 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
* - {string|null} warning Warning message if the input wasn't correctly formed
*/
return ( match ) => {
var
let
year = 0,
monthIdx = 0,
day = 0,
hour = 0,
minute = 0;
for ( var i = 0; i < matchingGroups.length; i++ ) {
var code2 = matchingGroups[ i ];
var text = match[ i + 1 ];
for ( let i = 0; i < matchingGroups.length; i++ ) {
const code2 = matchingGroups[ i ];
const text = match[ i + 1 ];
switch ( code2 ) {
case 'xg':
@ -378,11 +378,11 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
}
}
// The last matching group is the timezone abbreviation
var tzAbbr = tzAbbrs[ match[ match.length - 1 ] ];
const tzAbbr = tzAbbrs[ match[ match.length - 1 ] ];
// Most of the time, the timezone abbreviation is not necessary to parse the date, since we
// can assume all times are in the wiki's local timezone.
var date = moment.tz( [ year, monthIdx, day, hour, minute ], localTimezone );
let date = moment.tz( [ year, monthIdx, day, hour, minute ], localTimezone );
// But during the "fall back" at the end of DST, some times will happen twice. Per the docs,
// "Moment Timezone handles this by always using the earlier instance of a duplicated hour."
@ -390,7 +390,7 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
// Since the timezone abbreviation disambiguates the DST/non-DST times, we can detect when
// that behavior was incorrect...
var dateWarning = null;
let dateWarning = null;
if ( date.zoneAbbr() !== tzAbbr ) {
// ...and force the correct parsing. I can't find proper documentation for this feature,
// but this pull request explains it: https://github.com/moment/moment-timezone/pull/101
@ -466,7 +466,7 @@ Parser.prototype.getLocalTimestampParsers = function () {
*/
function acceptOnlyNodesAllowingComments( node ) {
if ( node instanceof HTMLElement ) {
var tagName = node.tagName.toLowerCase();
const tagName = node.tagName.toLowerCase();
// The table of contents has a heading that gets erroneously detected as a section
if ( node.id === 'toc' ) {
return NodeFilter.FILTER_REJECT;
@ -493,7 +493,7 @@ function acceptOnlyNodesAllowingComments( node ) {
return NodeFilter.FILTER_REJECT;
}
}
var parentNode = node.parentNode;
const parentNode = node.parentNode;
// Don't detect comments within headings (but don't reject the headings themselves)
if ( parentNode instanceof HTMLElement && parentNode.tagName.match( /^h([1-6])$/i ) ) {
return NodeFilter.FILTER_REJECT;
@ -515,7 +515,7 @@ function acceptOnlyNodesAllowingComments( node ) {
* - {Object} range Range-like object covering the timestamp
*/
Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
var matchData, i,
let matchData, i,
nodeText = '',
offset = 0,
// Searched nodes (reverse order)
@ -562,12 +562,12 @@ Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
if ( matchData ) {
var timestampLength = matchData[ 0 ].length;
// Bytes at the end of the last node which aren't part of the match
var tailLength = nodeText.length - timestampLength - matchData.index;
const tailLength = nodeText.length - timestampLength - matchData.index;
// We are moving right to left, but we start to the right of the end of
// the timestamp if there is trailing garbage, so that is a negative offset.
var count = -tailLength;
var endContainer = nodes[ 0 ];
var endOffset = endContainer.nodeValue.length - tailLength;
const endContainer = nodes[ 0 ];
const endOffset = endContainer.nodeValue.length - tailLength;
var startContainer, startOffset;
// eslint-disable-next-line no-loop-func
@ -584,7 +584,7 @@ Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
return false;
} );
var range = {
const range = {
startContainer: startContainer,
startOffset: startOffset,
endContainer: endContainer,
@ -613,12 +613,12 @@ Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
* - {string|null} displayName Display name (link text if link target was in the user namespace)
*/
Parser.prototype.getUsernameFromLink = function ( link ) {
var title;
let title;
// Selflink: use title of current page
if ( link.classList.contains( 'mw-selflink' ) ) {
title = this.title;
} else {
var titleString = utils.getTitleFromUrl( link.href ) || '';
const titleString = utils.getTitleFromUrl( link.href ) || '';
// Performance optimization, skip strings that obviously don't contain a namespace
if ( !titleString || titleString.indexOf( ':' ) === -1 ) {
return null;
@ -629,11 +629,11 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
return null;
}
var username;
var displayName = null;
var namespaceId = title.getNamespaceId();
var mainText = title.getMainText();
var namespaceIds = mw.config.get( 'wgNamespaceIds' );
let username;
let displayName = null;
const namespaceId = title.getNamespaceId();
const mainText = title.getMainText();
const namespaceIds = mw.config.get( 'wgNamespaceIds' );
if (
namespaceId === namespaceIds.user ||
@ -645,17 +645,17 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
}
if ( namespaceId === namespaceIds.user ) {
// Use regex trim for consistency with PHP implementation
var text = link.textContent.replace( /^[\s]+/, '' ).replace( /[\s]+$/, '' );
const text = link.textContent.replace( /^[\s]+/, '' ).replace( /[\s]+$/, '' );
// Record the display name if it has been customised beyond changing case
if ( text && text.toLowerCase() !== username.toLowerCase() ) {
displayName = text;
}
}
} else if ( namespaceId === namespaceIds.special ) {
var parts = mainText.split( '/' );
const parts = mainText.split( '/' );
if ( parts.length === 2 && parts[ 0 ] === this.data.specialContributionsName ) {
// Normalize the username: users may link to their contributions with an unnormalized name
var userpage = mw.Title.makeTitle( namespaceIds.user, parts[ 1 ] );
const userpage = mw.Title.makeTitle( namespaceIds.user, parts[ 1 ] );
if ( !userpage ) {
return null;
}
@ -692,10 +692,10 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
* - {string|null} username Username, null for unsigned comments
*/
Parser.prototype.findSignature = function ( timestampNode, until ) {
var sigUsername = null;
var sigDisplayName = null;
var length = 0;
var lastLinkNode = timestampNode;
let sigUsername = null;
let sigDisplayName = null;
let length = 0;
let lastLinkNode = timestampNode;
utils.linearWalkBackwards(
timestampNode,
@ -724,7 +724,7 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
// Handle links nested in formatting elements.
if ( event === 'leave' && node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'a' ) {
if ( !node.classList.contains( 'ext-discussiontools-init-timestamplink' ) ) {
var user = this.getUsernameFromLink( node );
const user = this.getUsernameFromLink( node );
if ( user ) {
// Accept the first link to the user namespace, then only accept links to that user
if ( sigUsername === null ) {
@ -744,13 +744,13 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
}
);
var range = {
const range = {
startContainer: lastLinkNode.parentNode,
startOffset: utils.childIndexOf( lastLinkNode ),
endContainer: timestampNode.parentNode,
endOffset: utils.childIndexOf( timestampNode ) + 1
};
var nativeRange = ThreadItem.prototype.getRange.call( { range: range } );
const nativeRange = ThreadItem.prototype.getRange.call( { range: range } );
// Expand the range so that it covers sibling nodes.
// This will include any wrapping formatting elements as part of the signature.
@ -761,7 +761,7 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
// "« Saper // dyskusja »"
//
// TODO Not sure if this is actually good, might be better to just use the range...
var sigNodes = utils.getCoveredSiblings( nativeRange ).reverse();
const sigNodes = utils.getCoveredSiblings( nativeRange ).reverse();
return {
nodes: sigNodes,
@ -784,9 +784,9 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
* @return {Node}
*/
Parser.prototype.nextInterestingLeafNode = function ( node ) {
var rootNode = this.rootNode;
const rootNode = this.rootNode;
var treeWalker = rootNode.ownerDocument.createTreeWalker(
const treeWalker = rootNode.ownerDocument.createTreeWalker(
rootNode,
// eslint-disable-next-line no-bitwise
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
@ -827,14 +827,14 @@ Parser.prototype.nextInterestingLeafNode = function ( node ) {
* @return {Object} Range-like object
*/
function adjustSigRange( sigNodes, match, node ) {
var firstSigNode = sigNodes[ sigNodes.length - 1 ];
var lastSigNode = sigNodes[ 0 ];
const firstSigNode = sigNodes[ sigNodes.length - 1 ];
const lastSigNode = sigNodes[ 0 ];
// TODO Document why this needs to be so complicated
var lastSigNodeOffset = lastSigNode === node ?
const lastSigNodeOffset = lastSigNode === node ?
match.matchData.index + match.matchData[ 0 ].length - match.offset :
utils.childIndexOf( lastSigNode ) + 1;
var sigRange = {
const sigRange = {
startContainer: firstSigNode.parentNode,
startOffset: utils.childIndexOf( firstSigNode ),
endContainer: lastSigNode === node ? node : lastSigNode.parentNode,
@ -847,13 +847,13 @@ function adjustSigRange( sigNodes, match, node ) {
* @return {ThreadItemSet}
*/
Parser.prototype.buildThreadItems = function () {
var result = new ThreadItemSet();
const result = new ThreadItemSet();
var
const
dfParsers = this.getLocalTimestampParsers(),
timestampRegexps = this.getLocalTimestampRegexps();
var treeWalker = this.rootNode.ownerDocument.createTreeWalker(
const treeWalker = this.rootNode.ownerDocument.createTreeWalker(
this.rootNode,
// eslint-disable-next-line no-bitwise
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
@ -861,14 +861,14 @@ Parser.prototype.buildThreadItems = function () {
false
);
var curComment, range;
var curCommentEnd = null;
let curComment, range;
let curCommentEnd = null;
var node;
let node;
while ( ( node = treeWalker.nextNode() ) ) {
var match;
if ( node.tagName && ( match = node.tagName.match( /^h([1-6])$/i ) ) ) {
var headingNode = utils.getHeadlineNode( node );
const headingNode = utils.getHeadlineNode( node );
range = {
startContainer: headingNode,
startOffset: 0,
@ -880,9 +880,9 @@ Parser.prototype.buildThreadItems = function () {
result.addThreadItem( curComment );
curCommentEnd = node;
} else if ( node.nodeType === Node.TEXT_NODE && ( match = this.findTimestamp( node, timestampRegexps ) ) ) {
var warnings = [];
var foundSignature = this.findSignature( node, curCommentEnd );
var author = foundSignature.username;
const warnings = [];
const foundSignature = this.findSignature( node, curCommentEnd );
const author = foundSignature.username;
if ( !author ) {
// Ignore timestamps for which we couldn't find a signature. It's probably not a real
@ -897,7 +897,7 @@ Parser.prototype.buildThreadItems = function () {
timestampRanges.push( match.range );
// Everything from the last comment up to here is the next comment
var startNode = this.nextInterestingLeafNode( curCommentEnd );
const startNode = this.nextInterestingLeafNode( curCommentEnd );
var endNode = foundSignature.nodes[ 0 ];
// Skip to the end of the "paragraph". This only looks at tag names and can be fooled by CSS, but
@ -914,7 +914,7 @@ Parser.prototype.buildThreadItems = function () {
endNode,
// eslint-disable-next-line no-loop-func
( event, n ) => {
var match2, foundSignature2;
let match2, foundSignature2;
if ( utils.isBlockElement( n ) || utils.isCommentSeparator( n ) ) {
// Stop when entering or leaving a block node
return true;
@ -940,7 +940,7 @@ Parser.prototype.buildThreadItems = function () {
}
);
var length = endNode.nodeType === Node.TEXT_NODE ?
const length = endNode.nodeType === Node.TEXT_NODE ?
endNode.textContent.replace( /[\t\n\f\r ]+$/, '' ).length :
endNode.childNodes.length;
range = {
@ -950,19 +950,19 @@ Parser.prototype.buildThreadItems = function () {
endOffset: length
};
var startLevel = utils.getIndentLevel( startNode, this.rootNode ) + 1;
var endLevel = utils.getIndentLevel( node, this.rootNode ) + 1;
const startLevel = utils.getIndentLevel( startNode, this.rootNode ) + 1;
const endLevel = utils.getIndentLevel( node, this.rootNode ) + 1;
if ( startLevel !== endLevel ) {
warnings.push( 'Comment starts and ends with different indentation' );
}
// Should this use the indent level of `startNode` or `node`?
var level = Math.min( startLevel, endLevel );
const level = Math.min( startLevel, endLevel );
var parserResult = dfParsers[ match.parserIndex ]( match.matchData );
const parserResult = dfParsers[ match.parserIndex ]( match.matchData );
if ( !parserResult ) {
continue;
}
var dateTime = parserResult.date;
const dateTime = parserResult.date;
if ( parserResult.warning ) {
warnings.push( parserResult.warning );
}
@ -989,7 +989,7 @@ Parser.prototype.buildThreadItems = function () {
endContainer: this.rootNode,
endOffset: 0
};
var fakeHeading = new HeadingItem( range, null );
const fakeHeading = new HeadingItem( range, null );
fakeHeading.rootNode = this.rootNode;
result.addThreadItem( fakeHeading );
}
@ -1019,7 +1019,7 @@ Parser.prototype.truncateForId = function ( text ) {
* @return {string}
*/
Parser.prototype.computeId = function ( threadItem, previousItems ) {
var id, headline;
let id, headline;
if ( threadItem instanceof HeadingItem && threadItem.placeholderHeading ) {
// The range points to the root note, using it like below results in silly values
@ -1035,7 +1035,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
// If there would be multiple comments with the same ID (i.e. the user left multiple comments
// in one edit, or within a minute), append sequential numbers
var threadItemParent = threadItem.parent;
const threadItemParent = threadItem.parent;
if ( threadItemParent instanceof HeadingItem && !threadItemParent.placeholderHeading ) {
headline = threadItemParent.range.startContainer;
id += '-' + this.truncateForId( headline.getAttribute( 'id' ) || '' );
@ -1048,7 +1048,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
// (e.g. dozens of threads titled "question" on [[Wikipedia:Help desk]]: https://w.wiki/fbN),
// include the oldest timestamp in the thread (i.e. date the thread was started) in the
// heading ID.
var oldestComment = threadItem.getOldestReply();
const oldestComment = threadItem.getOldestReply();
if ( oldestComment ) {
id += '-' + oldestComment.getTimestampString();
}
@ -1058,7 +1058,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
// Well, that's tough
threadItem.warnings.push( 'Duplicate comment ID' );
// Finally, disambiguate by adding sequential numbers, to allow replying to both comments
var number = 1;
let number = 1;
while ( previousItems.findCommentById( id + '-' + number ) ) {
number++;
}
@ -1078,7 +1078,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
* @return {string}
*/
Parser.prototype.computeName = function ( threadItem ) {
var name, mainComment;
let name, mainComment;
if ( threadItem instanceof HeadingItem ) {
name = 'h-';
@ -1102,10 +1102,10 @@ Parser.prototype.computeName = function ( threadItem ) {
* @param {ThreadItemSet} result
*/
Parser.prototype.buildThreads = function ( result ) {
var lastHeading = null;
var replies = [];
let lastHeading = null;
const replies = [];
var i, threadItem;
let i, threadItem;
for ( i = 0; i < result.threadItems.length; i++ ) {
threadItem = result.threadItems[ i ];
@ -1122,7 +1122,7 @@ Parser.prototype.buildThreads = function ( result ) {
// New root (thread)
// Attach as a sub-thread to preceding higher-level heading.
// Any replies will appear in the tree twice, under the main-thread and the sub-thread.
var maybeParent = lastHeading;
let maybeParent = lastHeading;
while ( maybeParent && maybeParent.headingLevel >= threadItem.headingLevel ) {
maybeParent = maybeParent.parent;
}
@ -1153,14 +1153,14 @@ Parser.prototype.buildThreads = function ( result ) {
* @param {ThreadItemSet} result
*/
Parser.prototype.computeIdsAndNames = function ( result ) {
var i, threadItem;
let i, threadItem;
for ( i = 0; i < result.threadItems.length; i++ ) {
threadItem = result.threadItems[ i ];
var name = this.computeName( threadItem );
const name = this.computeName( threadItem );
threadItem.name = name;
var id = this.computeId( threadItem, result );
const id = this.computeId( threadItem, result );
threadItem.id = id;
result.updateIdAndNameMaps( threadItem );
@ -1172,17 +1172,17 @@ Parser.prototype.computeIdsAndNames = function ( result ) {
* @return {CommentItem|null}
*/
Parser.prototype.getThreadStartComment = function ( threadItem ) {
var oldest = null;
let oldest = null;
if ( threadItem instanceof CommentItem ) {
oldest = threadItem;
}
// Check all replies. This can't just use the first comment because threads are often summarized
// at the top when the discussion is closed.
for ( var i = 0; i < threadItem.replies.length; i++ ) {
var comment = threadItem.replies[ i ];
for ( let i = 0; i < threadItem.replies.length; i++ ) {
const comment = threadItem.replies[ i ];
// Don't include sub-threads to avoid changing the ID when threads are "merged".
if ( comment instanceof CommentItem ) {
var oldestInReplies = this.getThreadStartComment( comment );
const oldestInReplies = this.getThreadStartComment( comment );
if ( !oldest || oldestInReplies.timestamp.isBefore( oldest.timestamp ) ) {
oldest = oldestInReplies;
}

View file

@ -7,7 +7,7 @@ var featuresEnabled = mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) || {};
function tryInfuse( $element ) {
if ( $element.length ) {
var element = null;
let element = null;
// $.data() might have already been cleared by jQuery if the elements were removed, ignore
// TODO: We should keep references to the OO.ui.ButtonWidget objects instead of infusing again,
// which would avoid this issue too
@ -34,8 +34,8 @@ function ReplyLinksController( $pageContainer ) {
this.$replyLinkSets = $pageContainer.find( '.ext-discussiontools-init-replylink-buttons[ data-mw-thread-id ]:not( :empty )' );
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
var $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
const $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
$replyLink.on( 'click keypress', this.onReplyLinkClickHandler );
if ( replyButton ) {
replyButton.on( 'click', this.onReplyButtonClickHandler, [ replyButton ] );
@ -49,7 +49,7 @@ function ReplyLinksController( $pageContainer ) {
// "Add topic" link in the skin interface
if ( featuresEnabled.newtopictool ) {
// eslint-disable-next-line no-jquery/no-global-selector
var $addSectionTab = $( '#ca-addsection' );
const $addSectionTab = $( '#ca-addsection' );
if ( $addSectionTab.length ) {
this.$addSectionLink = $addSectionTab.find( 'a' );
this.$addSectionLink.on( 'click keypress', this.onAddSectionLinkClickHandler );
@ -85,7 +85,7 @@ ReplyLinksController.prototype.onReplyLinkClick = function ( e ) {
// Browser plugins (such as Google Translate) may add extra tags inside
// the link, so find the containing link tag with the data we need.
var $linkSet = $( e.target ).closest( '[data-mw-thread-id]' );
const $linkSet = $( e.target ).closest( '[data-mw-thread-id]' );
if ( !$linkSet.length ) {
return;
}
@ -93,7 +93,7 @@ ReplyLinksController.prototype.onReplyLinkClick = function ( e ) {
};
ReplyLinksController.prototype.onReplyButtonClick = function ( button ) {
var $linkSet = button.$element.closest( '[data-mw-thread-id]' );
const $linkSet = button.$element.closest( '[data-mw-thread-id]' );
this.emit( 'link-click', $linkSet.data( 'mw-thread-id' ), $linkSet );
};
@ -117,12 +117,12 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
}
// Check query parameters to see if this is really a new topic link
var href = e.currentTarget.href;
const href = e.currentTarget.href;
if ( !href ) {
return;
}
var data = this.parseNewTopicLink( href );
const data = this.parseNewTopicLink( href );
if ( !data ) {
return;
}
@ -142,9 +142,9 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
* @return {Object|null} `null` if not a new topic link, parameters otherwise
*/
ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
var searchParams = new URL( href ).searchParams;
const searchParams = new URL( href ).searchParams;
var title = mw.Title.newFromText( utils.getTitleFromUrl( href ) || '' );
let title = mw.Title.newFromText( utils.getTitleFromUrl( href ) || '' );
if ( !title ) {
return null;
}
@ -156,7 +156,7 @@ ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
title.getMainText().split( '/' )[ 0 ] === parserData.specialNewSectionName
) {
// Get the real title from the subpage parameter
var param = title.getMainText().slice( parserData.specialNewSectionName.length + 1 );
const param = title.getMainText().slice( parserData.specialNewSectionName.length + 1 );
title = mw.Title.newFromText( param );
if ( !title ) {
return null;
@ -181,7 +181,7 @@ ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
return null;
}
var data = {};
const data = {};
if ( searchParams.get( 'editintro' ) ) {
data.editintro = searchParams.get( 'editintro' );
}
@ -221,7 +221,7 @@ ReplyLinksController.prototype.isActivationEvent = function ( e ) {
ReplyLinksController.prototype.focusLink = function ( $linkSet ) {
if ( $linkSet.is( this.$replyLinkSets ) ) {
var button = tryInfuse( $linkSet.find( '.ext-discussiontools-init-replybutton' ) );
const button = tryInfuse( $linkSet.find( '.ext-discussiontools-init-replybutton' ) );
// Focus whichever is visible, the link or the button
if ( button ) {
button.focus();
@ -233,8 +233,8 @@ ReplyLinksController.prototype.focusLink = function ( $linkSet ) {
ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
this.$activeLink = $linkSet;
var isNewTopic = false;
var activeButton;
let isNewTopic = false;
let activeButton;
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
this.$activeLink.addClass( 'ext-discussiontools-init-replylink-active' );
activeButton = tryInfuse( this.$activeLink.find( '.ext-discussiontools-init-replybutton' ) );
@ -248,8 +248,8 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
$( '#ca-view' ).removeClass( 'selected' );
}
var title = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
var pageTitleMsg = mw.message( 'pagetitle',
const title = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
const pageTitleMsg = mw.message( 'pagetitle',
mw.msg(
isNewTopic ?
'discussiontools-pagetitle-newtopic' :
@ -269,8 +269,8 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
$( document.body ).addClass( 'ext-discussiontools-init-replylink-open' );
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
var $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
const $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
$replyLink.attr( 'tabindex', -1 );
if ( !replyButton ) {
return;
@ -289,7 +289,7 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
};
ReplyLinksController.prototype.clearActiveLink = function () {
var activeButton;
let activeButton;
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
this.$activeLink.removeClass( 'ext-discussiontools-init-replylink-active' );
activeButton = tryInfuse( this.$activeLink.find( '.ext-discussiontools-init-replybutton' ) );
@ -308,9 +308,9 @@ ReplyLinksController.prototype.clearActiveLink = function () {
$( document.body ).removeClass( 'ext-discussiontools-init-replylink-open' );
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
var $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
const $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
$replyLink.attr( 'tabindex', 0 );
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
if ( !replyButton ) {
return;
}
@ -336,11 +336,11 @@ ReplyLinksController.prototype.teardown = function () {
}
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
if ( replyButton ) {
replyButton.off( 'click', this.onReplyButtonClickHandler );
}
var $replyLink = $( this ).find( '.ext-discussiontools-init-replylink-reply' );
const $replyLink = $( this ).find( '.ext-discussiontools-init-replylink-reply' );
$replyLink.off( 'click keypress', this.onReplyLinkClickHandler );
} );

View file

@ -61,9 +61,9 @@ ThreadItem.static.newFromJSON = function ( json, rootNode ) {
// by an older version of our PHP code. Code below must be able to handle that.
// See ThreadItem::jsonSerialize() in PHP.
var hash = typeof json === 'string' ? JSON.parse( json ) : json;
const hash = typeof json === 'string' ? JSON.parse( json ) : json;
var item;
let item;
switch ( hash.type ) {
case 'comment':
// Late require to avoid circular dependency
@ -102,9 +102,9 @@ ThreadItem.static.newFromJSON = function ( json, rootNode ) {
item.rootNode = rootNode;
var idEscaped = $.escapeSelector( item.id );
var startMarker = document.getElementById( item.id );
var endMarker = document.querySelector( '[data-mw-comment-end="' + idEscaped + '"]' );
const idEscaped = $.escapeSelector( item.id );
const startMarker = document.getElementById( item.id );
const endMarker = document.querySelector( '[data-mw-comment-end="' + idEscaped + '"]' );
item.range = {
// Start range after startMarker, because it produces funny results from getBoundingClientRect
@ -125,10 +125,10 @@ ThreadItem.prototype.calculateThreadSummary = function () {
if ( this.authors ) {
return;
}
var authors = {};
var commentCount = 0;
var oldestReply = null;
var latestReply = null;
const authors = {};
let commentCount = 0;
let oldestReply = null;
let latestReply = null;
function threadScan( comment ) {
if ( comment.type === 'comment' ) {
authors[ comment.author ] = authors[ comment.author ] || {
@ -214,7 +214,7 @@ ThreadItem.prototype.getOldestReply = function () {
* @return {ThreadItem[]} Thread items
*/
ThreadItem.prototype.getThreadItemsBelow = function () {
var threadItems = [];
const threadItems = [];
function getReplies( comment ) {
threadItems.push( comment );
comment.replies.forEach( getReplies );
@ -231,8 +231,8 @@ ThreadItem.prototype.getThreadItemsBelow = function () {
* @return {Range}
*/
ThreadItem.prototype.getRange = function () {
var doc = this.range.startContainer.ownerDocument;
var nativeRange = doc.createRange();
const doc = this.range.startContainer.ownerDocument;
const nativeRange = doc.createRange();
nativeRange.setStart( this.range.startContainer, this.range.startOffset );
nativeRange.setEnd( this.range.endContainer, this.range.endOffset );
return nativeRange;

View file

@ -26,10 +26,10 @@ OO.initClass( ThreadItemSet );
* @return {ThreadItemSet}
*/
ThreadItemSet.static.newFromJSON = function ( threads, rootNode, parser ) {
var result = new ThreadItemSet();
const result = new ThreadItemSet();
function infuse( itemHash, parent ) {
var item = ThreadItem.static.newFromJSON( itemHash, rootNode );
const item = ThreadItem.static.newFromJSON( itemHash, rootNode );
result.addThreadItem( item );

View file

@ -56,7 +56,7 @@ function getApi() {
* @return {jQuery.Promise}
*/
function getPageData( pageName, oldId, apiParams ) {
var api = getApi();
const api = getApi();
apiParams = apiParams || {};
pageDataCache[ pageName ] = pageDataCache[ pageName ] || {};
@ -64,7 +64,7 @@ function getPageData( pageName, oldId, apiParams ) {
return pageDataCache[ pageName ][ oldId ];
}
var lintPromise, transcludedFromPromise;
let lintPromise, transcludedFromPromise;
if ( oldId ) {
lintPromise = api.get( {
action: 'query',
@ -84,13 +84,13 @@ function getPageData( pageName, oldId, apiParams ) {
transcludedFromPromise = $.Deferred().resolve( {} ).promise();
}
var veMetadataPromise = api.get( Object.assign( {
const veMetadataPromise = api.get( Object.assign( {
action: 'visualeditor',
paction: 'metadata',
page: pageName
}, apiParams ) ).then( ( response ) => OO.getProp( response, 'visualeditor' ) || [] );
var promise = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
const promise = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
.then( ( linterrors, transcludedfrom, metadata ) => ( {
linterrors: linterrors,
transcludedfrom: transcludedfrom,
@ -119,9 +119,9 @@ function getPageData( pageName, oldId, apiParams ) {
* Rejects with error data if the comment is transcluded, or there are lint errors on the page.
*/
function checkThreadItemOnPage( pageName, oldId, threadItem ) {
var isNewTopic = threadItem.id === utils.NEW_TOPIC_COMMENT_ID;
var defaultMode = mw.user.options.get( 'discussiontools-editmode' ) || mw.config.get( 'wgDiscussionToolsFallbackEditMode' );
var apiParams = null;
const isNewTopic = threadItem.id === utils.NEW_TOPIC_COMMENT_ID;
const defaultMode = mw.user.options.get( 'discussiontools-editmode' ) || mw.config.get( 'wgDiscussionToolsFallbackEditMode' );
let apiParams = null;
if ( isNewTopic ) {
apiParams = {
section: 'new',
@ -134,7 +134,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
return getPageData( pageName, oldId, apiParams )
.then( ( response ) => {
var metadata = response.metadata,
const metadata = response.metadata,
lintErrors = response.linterrors,
transcludedFrom = response.transcludedfrom;
@ -144,7 +144,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
// or if a thread item's parent changes.
// Data by name might be combined from two or more thread items, which would only allow us to
// treat them both as transcluded from unknown source, unless we check ID first.
var isTranscludedFrom = transcludedFrom[ threadItem.id ];
let isTranscludedFrom = transcludedFrom[ threadItem.id ];
if ( isTranscludedFrom === undefined ) {
isTranscludedFrom = transcludedFrom[ threadItem.name ];
}
@ -158,11 +158,11 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
mw.message( 'discussiontools-error-comment-disappeared-reload' ).parse()
} ] } ).promise();
} else if ( isTranscludedFrom ) {
var mwTitle = isTranscludedFrom === true ? null : mw.Title.newFromText( isTranscludedFrom );
const mwTitle = isTranscludedFrom === true ? null : mw.Title.newFromText( isTranscludedFrom );
// If this refers to a template rather than a subpage, we never want to edit it
var follow = mwTitle && mwTitle.getNamespaceId() !== mw.config.get( 'wgNamespaceIds' ).template;
const follow = mwTitle && mwTitle.getNamespaceId() !== mw.config.get( 'wgNamespaceIds' ).template;
var transcludedErrMsg;
let transcludedErrMsg;
if ( follow ) {
transcludedErrMsg = mw.message(
'discussiontools-error-comment-is-transcluded-title',
@ -192,7 +192,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
if ( lintErrors.length ) {
// We currently only request the first error
var lintType = lintErrors[ 0 ].category;
const lintType = lintErrors[ 0 ].category;
return $.Deferred().reject( 'lint', { errors: [ {
code: 'lint',
@ -227,7 +227,7 @@ function getCheckboxesPromise( pageName, oldId ) {
pageName,
oldId
).then( ( pageData ) => {
var data = pageData.metadata,
const data = pageData.metadata,
checkboxesDef = {};
mw.messages.set( data.checkboxesMessages );
@ -251,7 +251,7 @@ function getCheckboxesPromise( pageName, oldId ) {
* @return {string[]}
*/
function getReplyWidgetModules() {
var veConf = mw.config.get( 'wgVisualEditorConfig' ),
let veConf = mw.config.get( 'wgVisualEditorConfig' ),
modules = [ 'ext.discussionTools.ReplyWidget' ]
.concat( veConf.pluginModules.filter( mw.loader.getState ) );
@ -279,7 +279,7 @@ function getReplyWidgetModules() {
* @param {boolean} [state.tempUserCreated] Whether a temp user was just created
*/
function init( $container, state ) {
var
let
activeCommentId = null,
activeController = null,
// Loads later to avoid circular dependency
@ -315,9 +315,9 @@ function init( $container, state ) {
);
} );
var parser = new Parser( require( './parser/data.json' ) );
const parser = new Parser( require( './parser/data.json' ) );
var commentNodes = $pageContainer[ 0 ].querySelectorAll( '[data-mw-thread-id]' );
const commentNodes = $pageContainer[ 0 ].querySelectorAll( '[data-mw-thread-id]' );
pageThreads = ThreadItemSet.static.newFromJSON( mw.config.get( 'wgDiscussionToolsPageThreads' ) || [], $pageContainer[ 0 ], parser );
if ( featuresEnabled.topicsubscription ) {
@ -350,7 +350,7 @@ function init( $container, state ) {
* @param {MemoryStorage} [storage] Storage object for autosave
*/
function setupController( comment, $link, mode, hideErrors, suppressNotifications, storage ) {
var commentController, $addSectionLink;
let commentController, $addSectionLink;
if ( !storage ) {
storage = new MemoryStorage( mw.storage, 'mw-ext-DiscussionTools-reply/' + comment.id, STORAGE_EXPIRY );
@ -404,7 +404,7 @@ function init( $container, state ) {
}
function newTopicComment( data ) {
var comment = new HeadingItem( {}, 2 );
const comment = new HeadingItem( {}, 2 );
comment.id = utils.NEW_TOPIC_COMMENT_ID;
comment.isNewTopic = true;
Object.assign( comment, data );
@ -424,7 +424,7 @@ function init( $container, state ) {
return;
}
var teardownPromise = $.Deferred().resolve();
let teardownPromise = $.Deferred().resolve();
if ( commentId === utils.NEW_TOPIC_COMMENT_ID ) {
// If this is a new topic link, and a reply widget is open, attempt to close it first.
if ( activeController ) {
@ -447,7 +447,7 @@ function init( $container, state ) {
if ( activeController ) {
return;
}
var comment;
let comment;
if ( commentId !== utils.NEW_TOPIC_COMMENT_ID ) {
comment = pageThreads.findCommentById( commentId );
} else {
@ -462,7 +462,7 @@ function init( $container, state ) {
} );
} );
var mobilePromise = OO.ui.isMobile() && mw.loader.getState( 'mobile.init' ) ?
const mobilePromise = OO.ui.isMobile() && mw.loader.getState( 'mobile.init' ) ?
mw.loader.using( 'mobile.init' ) :
$.Deferred().resolve().promise();
@ -529,7 +529,7 @@ function init( $container, state ) {
}
} );
var dismissableNotificationPromise = null;
let dismissableNotificationPromise = null;
// Page-level handlers only need to be setup once
if ( !pageHandlersSetup ) {
$( window ).on( 'popstate', () => {
@ -556,9 +556,9 @@ function init( $container, state ) {
}
if ( state.firstLoad ) {
mobilePromise.then( () => {
var findCommentQuery;
var isHeading = false;
var highlightResult = highlighter.highlightTargetComment( pageThreads );
let findCommentQuery;
let isHeading = false;
const highlightResult = highlighter.highlightTargetComment( pageThreads );
// Hash contains a non-replaced space (should be underscore), maybe due to
// manual creation or a broken third party tool. Just replace the spaces
@ -568,7 +568,7 @@ function init( $container, state ) {
// element, but the fixed hash does, to avoid affects on other apps which
// may use fragments with spaces.
if ( location.hash && !mw.util.getTargetFromFragment() && location.hash.indexOf( '%20' ) !== -1 ) {
var fixedHash = location.hash.slice( 1 ).replace( /%20/g, '_' );
const fixedHash = location.hash.slice( 1 ).replace( /%20/g, '_' );
if ( mw.util.getTargetFromFragment( fixedHash ) ) {
location.hash = fixedHash;
}
@ -580,8 +580,8 @@ function init( $container, state ) {
// Not a DT comment
highlightResult.highlighted.length === 0 && highlightResult.requested.length === 0
) {
var fragment = location.hash.slice( 1 );
var ignorePatterns = [
const fragment = location.hash.slice( 1 );
const ignorePatterns = [
// A leading '/' or '!/' usually means a application route, e.g. /media, or /editor.
// We can't rule out a heading title (T349498), but they are unlikely
/^!?\//,
@ -611,8 +611,8 @@ function init( $container, state ) {
}
if ( findCommentQuery ) {
// TODO: Support multiple commentIds being requested and not all being found
var dtConf = require( './config.json' );
var findCommentRequest = dtConf.enablePermalinksFrontend ?
const dtConf = require( './config.json' );
const findCommentRequest = dtConf.enablePermalinksFrontend ?
getApi().get( Object.assign( {
action: 'discussiontoolsfindcomment'
}, findCommentQuery ) ) :
@ -621,14 +621,14 @@ function init( $container, state ) {
findCommentRequest,
mw.loader.using( 'mediawiki.notification' )
).then( ( results ) => {
var result = results[ 0 ];
var titles = [];
const result = results[ 0 ];
let titles = [];
if ( result.discussiontoolsfindcomment ) {
titles = result.discussiontoolsfindcomment.map( ( threadItemData ) => {
// Only show items that appear on the current revision of their page
// and are not transcluded from another page
if ( threadItemData.couldredirect ) {
var title = mw.Title.newFromText(
const title = mw.Title.newFromText(
threadItemData.title + '#' +
mw.util.escapeIdForLink( threadItemData.id )
);
@ -638,8 +638,8 @@ function init( $container, state ) {
} ).filter( ( url ) => url );
}
if ( titles.length ) {
var $list = $( '<ul>' );
var $notification = $( '<div>' ).append(
const $list = $( '<ul>' );
const $notification = $( '<div>' ).append(
$( '<p>' ).text( mw.message(
isHeading ?
'discussiontools-target-heading-found-moved' :
@ -696,7 +696,7 @@ function updatePageContents( $container, data ) {
// eslint-disable-next-line no-jquery/no-global-selector
if ( $( '#catlinks' ).length ) {
var $categories = $( $.parseHTML( data.parse.categorieshtml ) );
const $categories = $( $.parseHTML( data.parse.categorieshtml ) );
mw.hook( 'wikipage.categories' ).fire( $categories );
// eslint-disable-next-line no-jquery/no-global-selector
$( '#catlinks' ).replaceWith( $categories );
@ -732,23 +732,23 @@ function updatePageContents( $container, data ) {
// TODO: Upstream this to core/skins, triggered by a hook (wikipage.content?)
// eslint-disable-next-line no-jquery/no-global-selector
$( '#t-permalink' ).add( '#coll-download-as-rl' ).find( 'a' ).each( ( i, link ) => {
var permalinkUrl = new URL( link.href );
const permalinkUrl = new URL( link.href );
permalinkUrl.searchParams.set( 'oldid', data.parse.revid );
$( link ).attr( 'href', permalinkUrl.toString() );
} );
var url = new URL( location.href );
const url = new URL( location.href );
url.searchParams.delete( 'oldid' );
// If there are any other query parameters left, re-use that URL object.
// Otherwise use the canonical style view url (T44553, T102363).
var keys = [];
const keys = [];
url.searchParams.forEach( ( val, key ) => {
keys.push( key );
} );
if ( !keys.length || ( keys.length === 1 && keys[ 0 ] === 'title' ) ) {
var viewUrl = new URL( mw.util.getUrl( mw.config.get( 'wgRelevantPageName' ) ), document.baseURI );
const viewUrl = new URL( mw.util.getUrl( mw.config.get( 'wgRelevantPageName' ) ), document.baseURI );
viewUrl.hash = location.hash;
history.pushState( null, '', viewUrl );
} else {
@ -809,7 +809,7 @@ function update( data, threadItem, pageName, replyWidget ) {
replyWidget.unbindBeforeUnloadHandler();
replyWidget.clearStorage();
replyWidget.setPending( true );
var params = { dtrepliedto: threadItem.id };
const params = { dtrepliedto: threadItem.id };
if ( data.tempusercreated ) {
params.dttempusercreated = '1';
}
@ -826,7 +826,7 @@ function update( data, threadItem, pageName, replyWidget ) {
mw.dt.initState.tempUserCreated = data.tempusercreated;
// Update page state
var pageUpdated = $.Deferred();
const pageUpdated = $.Deferred();
if ( pageName === mw.config.get( 'wgRelevantPageName' ) ) {
// We can use the result from the VisualEditor API
updatePageContents( $pageContainer, {
@ -854,7 +854,7 @@ function update( data, threadItem, pageName, replyWidget ) {
} else {
// We saved to another page, we must purge and then fetch the current page
var api = getApi();
const api = getApi();
api.post( {
action: 'purge',
titles: mw.config.get( 'wgRelevantPageName' )
@ -871,7 +871,7 @@ function update( data, threadItem, pageName, replyWidget ) {
// User logged in if module loaded.
if ( mw.loader.getState( 'mediawiki.page.watch.ajax' ) === 'ready' ) {
var watch = require( 'mediawiki.page.watch.ajax' );
const watch = require( 'mediawiki.page.watch.ajax' );
watch.updateWatchLink(
mw.Title.newFromText( pageName ),

View file

@ -6,24 +6,24 @@ var updaters = [];
var isRtl = $( 'html' ).attr( 'dir' ) === 'rtl';
function markTimestamp( parser, node, match ) {
var dfParsers = parser.getLocalTimestampParsers();
const dfParsers = parser.getLocalTimestampParsers();
var newNode = node.splitText( match.matchData.index );
const newNode = node.splitText( match.matchData.index );
newNode.splitText( match.matchData[ 0 ].length );
var wrapper = document.createElement( 'span' );
const wrapper = document.createElement( 'span' );
wrapper.className = 'ext-discussiontools-debughighlighter-timestamp';
// We might need to actually port all the date formatting code from MediaWiki's PHP code
// if we want to support displaying dates in all the formats available in user preferences
// (which include formats in several non-Gregorian calendars).
var date = dfParsers[ match.parserIndex ]( match.matchData ).date;
const date = dfParsers[ match.parserIndex ]( match.matchData ).date;
wrapper.title = date.format() + ' / ' + date.fromNow();
wrapper.appendChild( newNode );
node.parentNode.insertBefore( wrapper, node.nextSibling );
}
function markSignature( sigNodes ) {
var
const
where = sigNodes[ 0 ],
wrapper = document.createElement( 'span' );
wrapper.className = 'ext-discussiontools-debughighlighter-signature';
@ -37,7 +37,7 @@ function fixFakeFirstHeadingRect( rect, comment ) {
// If the page has comments before the first section heading, they are connected to a "fake"
// heading with an empty range. Visualize the page title as the heading for that section.
if ( rect.x === 0 && rect.y === 0 && comment.type === 'heading' ) {
var node = document.getElementsByClassName( 'firstHeading' )[ 0 ];
const node = document.getElementsByClassName( 'firstHeading' )[ 0 ];
return node.getBoundingClientRect();
}
return rect;
@ -45,9 +45,9 @@ function fixFakeFirstHeadingRect( rect, comment ) {
function calculateSizes() {
// eslint-disable-next-line no-jquery/no-global-selector
var $content = $( '#mw-content-text' );
var $test = $( '<dd>' ).appendTo( $( '<dl>' ).appendTo( $content ) );
var rect = $content[ 0 ].getBoundingClientRect();
const $content = $( '#mw-content-text' );
const $test = $( '<dd>' ).appendTo( $( '<dl>' ).appendTo( $content ) );
const rect = $content[ 0 ].getBoundingClientRect();
initialOffset = isRtl ? document.body.scrollWidth - rect.left - rect.width : rect.left;
indentWidth = parseFloat( $test.css( isRtl ? 'margin-right' : 'margin-left' ) ) +
@ -57,33 +57,33 @@ function calculateSizes() {
}
function markComment( comment ) {
var marker = document.createElement( 'div' );
const marker = document.createElement( 'div' );
marker.className = 'ext-discussiontools-debughighlighter-comment';
if ( !firstMarker ) {
firstMarker = marker;
}
var marker2 = null;
let marker2 = null;
if ( comment.parent ) {
marker2 = document.createElement( 'div' );
marker2.className = 'ext-discussiontools-debughighlighter-comment-ruler';
}
var markerWarnings = null;
let markerWarnings = null;
if ( comment.warnings && comment.warnings.length ) {
markerWarnings = document.createElement( 'div' );
markerWarnings.className = 'ext-discussiontools-debughighlighter-comment-warnings';
markerWarnings.innerText = comment.warnings.join( '\n' );
}
var update = function () {
var rect = fixFakeFirstHeadingRect(
const update = function () {
const rect = fixFakeFirstHeadingRect(
comment.getRange().getBoundingClientRect(),
comment
);
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
marker.style.top = ( rect.top + scrollTop ) + 'px';
marker.style.height = ( rect.height ) + 'px';
@ -91,7 +91,7 @@ function markComment( comment ) {
marker.style.width = ( rect.width ) + 'px';
if ( marker2 ) {
var parentRect = comment.parent.getRange().getBoundingClientRect();
let parentRect = comment.parent.getRange().getBoundingClientRect();
parentRect = fixFakeFirstHeadingRect( parentRect, comment.parent );
if ( comment.parent.level === 0 ) {
// Twiddle so that it looks nice

View file

@ -11,7 +11,7 @@ require( './CommentTarget.js' );
* @param {Object} [config] Configuration options
*/
function CommentTargetWidget( replyWidget, config ) {
var excludeCommands = [
let excludeCommands = [
'blockquoteWrap', // T258194
// Disable to allow Tab/Shift+Tab to move focus out of the widget (T172694)
'indent',
@ -75,7 +75,7 @@ CommentTargetWidget.prototype.createTarget = function () {
* @inheritdoc
*/
CommentTargetWidget.prototype.setDocument = function ( docOrHtml ) {
var mode = this.target.getDefaultMode(),
const mode = this.target.getDefaultMode(),
doc = ( mode === 'visual' && typeof docOrHtml === 'string' ) ?
this.target.parseDocument( docOrHtml ) :
docOrHtml,

View file

@ -41,7 +41,7 @@ CeMWPingNode.static.getDescription = function ( model ) {
* @inheritdoc
*/
CeMWPingNode.prototype.initialize = function () {
var model = this.getModel(),
const model = this.getModel(),
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
user = model.getAttribute( 'user' ),
@ -51,7 +51,7 @@ CeMWPingNode.prototype.initialize = function () {
CeMWPingNode.super.prototype.initialize.call( this );
// DOM changes
var $link = $( '<a>' )
const $link = $( '<a>' )
.addClass( 'ext-discussiontools-ce-mwPingNode' )
.attr( {
href: title.getUrl(),

View file

@ -40,7 +40,7 @@ DmMWPingNode.static.matchFunction = function () {
DmMWPingNode.static.disallowedAnnotationTypes = [ 'link' ];
DmMWPingNode.static.toDomElements = function ( dataElement, doc, converter ) {
var domElements,
let domElements,
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, dataElement.attributes.user );

View file

@ -15,9 +15,9 @@ DtUiMWSignatureContextItem.static.label =
// Get the formatted, localized, platform-specific shortcut key for the given command
DtUiMWSignatureContextItem.prototype.getShortcutKey = function ( commandName ) {
// Adapted from ve.ui.CommandHelpDialog.prototype.initialize
var commandInfo = ve.ui.commandHelpRegistry.lookup( commandName );
var triggerList = ve.ui.triggerRegistry.lookup( commandInfo.trigger );
var $shortcut = $( '<kbd>' ).addClass( 've-ui-commandHelpDialog-shortcut' ).append(
const commandInfo = ve.ui.commandHelpRegistry.lookup( commandName );
const triggerList = ve.ui.triggerRegistry.lookup( commandInfo.trigger );
const $shortcut = $( '<kbd>' ).addClass( 've-ui-commandHelpDialog-shortcut' ).append(
triggerList[ 0 ].getMessage( true ).map( ve.ui.CommandHelpDialog.static.buildKeyNode )
).find( 'kbd + kbd' ).before( '+' ).end();
return $shortcut;

View file

@ -37,7 +37,7 @@ function MWUsernameCompletionAction() {
}
} );
// On user talk pages, always list the "owner" of the talk page
var relevantUserName = mw.config.get( 'wgRelevantUserName' );
const relevantUserName = mw.config.get( 'wgRelevantUserName' );
if (
relevantUserName &&
relevantUserName !== mw.user.getName() &&
@ -67,7 +67,7 @@ MWUsernameCompletionAction.static.methods.push( 'insertAndOpen' );
/* Methods */
MWUsernameCompletionAction.prototype.insertAndOpen = function () {
var inserted = false,
let inserted = false,
surfaceModel = this.surface.getModel(),
fragment = surfaceModel.getFragment();
@ -106,12 +106,12 @@ MWUsernameCompletionAction.prototype.getSequenceLength = function () {
};
MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
var title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, input ),
const title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, input ),
validatedInput = title ? input : '',
action = this;
this.api.abort(); // Abort all unfinished API requests
var apiPromise;
let apiPromise;
if ( input.length > 0 && !this.searchedPrefixes[ input ] ) {
apiPromise = this.api.get( {
action: 'query',
@ -123,7 +123,7 @@ MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
// blocked users and still probably have some suggestions left
aulimit: this.constructor.static.defaultLimit * 2
} ).then( ( response ) => {
var suggestions = response.query.allusers.filter(
const suggestions = response.query.allusers.filter(
// API doesn't return IPs
( user ) => !hasUser( action.localUsers, user.name ) &&
!hasUser( action.remoteUsers, user.name ) &&
@ -167,7 +167,7 @@ MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
* @inheritdoc
*/
MWUsernameCompletionAction.prototype.compareSuggestionToInput = function ( suggestion, normalizedInput ) {
var normalizedSuggestion = suggestion.username.toLowerCase(),
const normalizedSuggestion = suggestion.username.toLowerCase(),
normalizedSearchIndex = normalizedSuggestion + ' ' +
suggestion.displayNames
.map( ( displayName ) => displayName.toLowerCase() ).join( ' ' );
@ -197,13 +197,13 @@ MWUsernameCompletionAction.prototype.getMenuItemForSuggestion = function ( sugge
MWUsernameCompletionAction.prototype.getHeaderLabel = function ( input, suggestions ) {
if ( suggestions === undefined ) {
var $query = $( '<span>' ).text( input );
const $query = $( '<span>' ).text( input );
return mw.message( 'discussiontools-replywidget-mention-tool-header', $query ).parseDom();
}
};
MWUsernameCompletionAction.prototype.insertCompletion = function ( word, range ) {
var prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
const prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
title = mw.Title.newFromText( word, mw.config.get( 'wgNamespaceIds' ).user );
@ -213,7 +213,7 @@ MWUsernameCompletionAction.prototype.insertCompletion = function ( word, range )
return MWUsernameCompletionAction.super.prototype.insertCompletion.call( this, word, range );
}
var fragment = this.surface.getModel().getLinearFragment( range, true );
const fragment = this.surface.getModel().getLinearFragment( range, true );
fragment.removeContent().insertContent( [
{ type: 'mwPing', attributes: { user: word } },
{ type: '/mwPing' }

View file

@ -1,6 +1,6 @@
// Adapted from ve.ui.MWWikitextDataTransferHandlerFactory
function importRegistry( parent, child ) {
var name;
let name;
// Copy existing items
for ( name in parent.registry ) {
child.register( parent.registry[ name ] );

View file

@ -22,13 +22,13 @@ if ( debug & DEBUG_HIGHLIGHT ) {
comments.forEach( ( comment ) => {
comment.signatureRanges.forEach( ( signatureRange ) => {
var node = signatureRange.endContainer;
var match = parser.findTimestamp( node, timestampRegexps );
const node = signatureRange.endContainer;
const match = parser.findTimestamp( node, timestampRegexps );
if ( !match ) {
return;
}
var signature = parser.findSignature( node ).nodes;
var emptySignature = signature.length === 1 && signature[ 0 ] === node;
const signature = parser.findSignature( node ).nodes;
const emptySignature = signature.length === 1 && signature[ 0 ] === node;
// Note that additional content may follow the timestamp (e.g. in some voting formats), but we
// don't care about it. The code below doesn't mark that due to now the text nodes are sliced,
// but we might need to take care to use the matched range of node in other cases.
@ -43,7 +43,7 @@ if ( debug & DEBUG_HIGHLIGHT ) {
// eslint-disable-next-line no-bitwise
if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
threads.forEach( ( thread ) => {
var firstComment = thread.replies[ 0 ];
const firstComment = thread.replies[ 0 ];
if ( firstComment && firstComment.type === 'comment' ) {
// eslint-disable-next-line no-bitwise
@ -52,7 +52,7 @@ if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
return;
}
var firstVote = firstComment.level === 1 ?
const firstVote = firstComment.level === 1 ?
// In permissive mode, the first vote is the replies to the OP
firstComment.replies[ 0 ] :
firstComment;
@ -61,15 +61,15 @@ if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
return;
}
var lastReply;
var level = firstVote.level;
let lastReply;
const level = firstVote.level;
firstVote.parent.replies.forEach( ( reply ) => {
if ( reply.type === 'comment' && reply.level <= level ) {
lastReply = reply;
}
} );
var listItem = modifier.addSiblingListItem(
const listItem = modifier.addSiblingListItem(
utils.closestElement( lastReply.range.endContainer, [ 'li', 'dd', 'p' ] )
);
if ( listItem && listItem.tagName.toLowerCase() === 'li' ) {

View file

@ -63,7 +63,7 @@ mw.dt.init = function ( $container ) {
// If it's a full page live preview, (re)initialize to support highlighting comments (T309423)
// FIXME This really should not depend on implementation details of 2 different live previews
// FIXME VisualEditor (2017WTE) preview can't be supported, because it messes with `id` attributes
var livePreviewSelectors = '#wikiPreview, .ext-WikiEditor-realtimepreview-preview';
const livePreviewSelectors = '#wikiPreview, .ext-WikiEditor-realtimepreview-preview';
if ( $container.parent().is( livePreviewSelectors ) ) {
reallyInit( $container );
return;
@ -94,7 +94,7 @@ if ( mw.config.get( 'wgAction' ) === 'history' ) {
// TODO: Remove this code after a few weeks.
mw.requestIdleCallback( () => {
try {
for ( var key in localStorage ) {
for ( const key in localStorage ) {
if ( key.startsWith( 'reply/' ) ) {
localStorage.removeItem( key );
localStorage.removeItem( '_EXPIRY_' + key );

View file

@ -31,7 +31,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
this.pending = false;
this.isTornDown = false;
this.commentController = commentController;
var threadItem = commentController.getThreadItem();
const threadItem = commentController.getThreadItem();
this.commentDetails = commentDetails;
this.isNewTopic = !!threadItem.isNewTopic;
this.pageName = commentDetails.pageName;
@ -39,7 +39,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
// pageExists can only be false for the new comment tool, so we
// don't need to worry about transcluded replies.
this.pageExists = mw.config.get( 'wgRelevantArticleId', 0 ) !== 0;
var contextNode = utils.closestElement( threadItem.range.endContainer, [ 'dl', 'ul', 'ol' ] );
const contextNode = utils.closestElement( threadItem.range.endContainer, [ 'dl', 'ul', 'ol' ] );
this.context = contextNode ? contextNode.tagName.toLowerCase() : 'dl';
this.storage = commentController.storage;
// eslint-disable-next-line no-jquery/no-global-selector
@ -51,7 +51,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
this.$window = $( this.getElementWindow() );
this.onWindowScrollThrottled = OO.ui.throttle( this.onWindowScroll.bind( this ), 100 );
var inputConfig = Object.assign(
const inputConfig = Object.assign(
{
placeholder: this.isNewTopic ?
mw.msg( 'discussiontools-replywidget-placeholder-newtopic' ) :
@ -172,7 +172,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
mw.message( 'discussiontools-replywidget-transcluded', this.pageName ).parseDom()
) );
}
var $footerLinks = $( '<ul>' ).addClass( 'ext-discussiontools-ui-replyWidget-footer-links' );
const $footerLinks = $( '<ul>' ).addClass( 'ext-discussiontools-ui-replyWidget-footer-links' );
if ( mw.user.isNamed() ) {
$footerLinks.append(
$( '<li>' ).append(
@ -268,10 +268,10 @@ function ReplyWidget( commentController, commentDetails, config ) {
}
if ( mw.user.isAnon() ) {
var msg = this.commentDetails.wouldAutoCreate ?
const msg = this.commentDetails.wouldAutoCreate ?
'discussiontools-replywidget-autocreate-warning' :
'discussiontools-replywidget-anon-warning';
var returnTo = {
const returnTo = {
returntoquery: window.location.search.slice( 1 ),
returnto: mw.config.get( 'wgPageName' )
};
@ -310,7 +310,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
this.editSummaryField.$body.append( this.$checkboxes );
// bind logging:
for ( var name in checkboxes.checkboxesByName ) {
for ( const name in checkboxes.checkboxesByName ) {
checkboxes.checkboxesByName[ name ].$element.off( '.dtReply' ).on( 'click.dtReply', trackCheckbox.bind( this, name ) );
}
}
@ -436,9 +436,9 @@ ReplyWidget.prototype.clearStorage = function () {
* Handle window scroll events
*/
ReplyWidget.prototype.onWindowScroll = function () {
var rect = this.$element[ 0 ].getBoundingClientRect();
var viewportHeight = window.visualViewport ? visualViewport.height : this.viewportScrollContainer.clientHeight;
var floating = rect.bottom < 0 ? 'top' :
const rect = this.$element[ 0 ].getBoundingClientRect();
const viewportHeight = window.visualViewport ? visualViewport.height : this.viewportScrollContainer.clientHeight;
const floating = rect.bottom < 0 ? 'top' :
( rect.top > viewportHeight ? 'bottom' : null );
if ( floating !== this.floating ) {
@ -479,7 +479,7 @@ ReplyWidget.prototype.saveEditMode = function ( mode ) {
};
ReplyWidget.prototype.onAdvancedToggleClick = function () {
var showAdvanced = !this.showAdvanced;
const showAdvanced = !this.showAdvanced;
mw.track( 'visualEditorFeatureUse', {
feature: 'dtReply',
action: 'advanced-' + ( showAdvanced ? 'show' : 'hide' )
@ -490,20 +490,20 @@ ReplyWidget.prototype.onAdvancedToggleClick = function () {
this.toggleAdvanced( showAdvanced );
if ( showAdvanced ) {
var summary = this.editSummaryInput.getValue();
const summary = this.editSummaryInput.getValue();
// If the current summary has not been edited yet, select the text following the autocomment to
// make it easier to change. Otherwise, move cursor to end.
var selectFromIndex = summary.length;
let selectFromIndex = summary.length;
if ( this.isNewTopic ) {
var titleText = this.commentController.sectionTitle.getValue();
const titleText = this.commentController.sectionTitle.getValue();
if ( summary === this.commentController.generateSummary( titleText ) ) {
selectFromIndex = titleText.length + '/* '.length + ' */ '.length;
}
} else {
// Same as summary.endsWith( defaultReplyTrail )
var defaultReplyTrail = '*/ ' + mw.msg( 'discussiontools-defaultsummary-reply' );
var endCommentIndex = summary.indexOf( defaultReplyTrail );
const defaultReplyTrail = '*/ ' + mw.msg( 'discussiontools-defaultsummary-reply' );
const endCommentIndex = summary.indexOf( defaultReplyTrail );
if ( endCommentIndex + defaultReplyTrail.length === summary.length ) {
selectFromIndex = endCommentIndex + 3;
}
@ -537,7 +537,7 @@ ReplyWidget.prototype.getEditSummary = function () {
};
ReplyWidget.prototype.onModeTabSelectChoose = function ( option ) {
var mode = option.getData();
const mode = option.getData();
if ( mode === this.getMode() ) {
return;
@ -560,7 +560,7 @@ ReplyWidget.prototype.switch = function ( mode ) {
return $.Deferred().reject().promise();
}
var promise;
let promise;
this.setPending( true );
switch ( mode ) {
case 'source':
@ -609,7 +609,7 @@ ReplyWidget.prototype.setup = function ( data ) {
}
this.saveEditMode( this.getMode() );
var summary = this.storage.get( 'summary' ) || data.editSummary;
let summary = this.storage.get( 'summary' ) || data.editSummary;
if ( !summary ) {
if ( this.isNewTopic ) {
@ -617,7 +617,7 @@ ReplyWidget.prototype.setup = function ( data ) {
// in NewTopicController#onSectionTitleChange
summary = '';
} else {
var title = this.commentController.getThreadItem().getHeading().getLinkableTitle();
const title = this.commentController.getThreadItem().getHeading().getLinkableTitle();
summary = ( title ? '/* ' + title + ' */ ' : '' ) +
mw.msg( 'discussiontools-defaultsummary-reply' );
}
@ -663,10 +663,10 @@ ReplyWidget.prototype.afterSetup = function () {
* @return {string} Form token
*/
ReplyWidget.prototype.getFormToken = function () {
var formToken = this.storage.get( 'formToken' );
let formToken = this.storage.get( 'formToken' );
if ( !formToken ) {
// See ApiBase::PARAM_MAX_CHARS in ApiDiscussionToolsEdit.php
var maxLength = 16;
const maxLength = 16;
formToken = Math.random().toString( 36 ).slice( 2, maxLength + 2 );
this.storage.set( 'formToken', formToken );
}
@ -680,7 +680,7 @@ ReplyWidget.prototype.getFormToken = function () {
* @return {jQuery.Promise} Resolves if widget was torn down, rejects if it wasn't
*/
ReplyWidget.prototype.tryTeardown = function () {
var promise;
let promise;
if ( !this.isEmpty() || ( this.isNewTopic && this.commentController.sectionTitle.getValue() ) ) {
promise = OO.ui.getWindowManager().openWindow( this.isNewTopic ? 'abandontopic' : 'abandoncomment' )
@ -815,7 +815,7 @@ ReplyWidget.prototype.preparePreview = function ( wikitext ) {
wikitext = wikitext !== undefined ? wikitext : this.getValue();
wikitext = utils.htmlTrim( wikitext );
var title = this.isNewTopic && this.commentController.sectionTitle.getValue();
const title = this.isNewTopic && this.commentController.sectionTitle.getValue();
if ( this.previewWikitext === wikitext && this.previewTitle === title ) {
return $.Deferred().resolve().promise();
@ -828,7 +828,7 @@ ReplyWidget.prototype.preparePreview = function ( wikitext ) {
this.previewRequest = null;
}
var parsePromise;
let parsePromise;
if ( !wikitext ) {
parsePromise = $.Deferred().resolve( null ).promise();
} else {
@ -1029,7 +1029,7 @@ ReplyWidget.prototype.onNewCommentsCloseClick = function () {
* @return {OO.ui.MessageWidget} Message widget
*/
ReplyWidget.prototype.createErrorMessage = function ( message ) {
var errorMessage = new OO.ui.MessageWidget( {
const errorMessage = new OO.ui.MessageWidget( {
type: 'error',
label: message,
classes: [ 'ext-discussiontools-ui-replyWidget-error' ]
@ -1058,12 +1058,12 @@ ReplyWidget.prototype.onReplyClick = function () {
mw.track( 'editAttemptStep', { action: 'saveIntent' } );
// TODO: When editing a transcluded page, VE API returning the page HTML is a waste, since we won't use it
var pageName = this.pageName;
const pageName = this.pageName;
mw.track( 'editAttemptStep', { action: 'saveAttempt' } );
this.commentController.save( pageName ).fail( ( code, data ) => {
// Compare to ve.init.mw.ArticleTargetEvents.js in VisualEditor.
var typeMap = {
const typeMap = {
badtoken: 'userBadToken',
assertanonfailed: 'userNewUser',
assertuserfailed: 'userNewUser',

View file

@ -15,7 +15,7 @@ function ReplyWidgetPlain() {
ReplyWidgetPlain.super.apply( this, arguments );
if ( OO.ui.isMobile() ) {
var toolFactory = new OO.ui.ToolFactory(),
const toolFactory = new OO.ui.ToolFactory(),
toolGroupFactory = new OO.ui.ToolGroupFactory();
toolFactory.register( mw.libs.ve.MWEditModeVisualTool );
@ -56,7 +56,7 @@ OO.inheritClass( ReplyWidgetPlain, require( './dt.ui.ReplyWidget.js' ) );
* @inheritdoc
*/
ReplyWidgetPlain.prototype.createReplyBodyWidget = function ( config ) {
var textInput = new OO.ui.MultilineTextInputWidget( Object.assign( {
const textInput = new OO.ui.MultilineTextInputWidget( Object.assign( {
rows: 3,
// TODO: Fix upstream to support a value meaning no max limit (e.g. Infinity)
maxRows: 999,
@ -123,7 +123,7 @@ ReplyWidgetPlain.prototype.onInputChange = function () {
// Parent method
ReplyWidgetPlain.super.prototype.onInputChange.apply( this, arguments );
var wikitext = this.getValue();
const wikitext = this.getValue();
this.storage.set( 'body', wikitext );
};
@ -131,7 +131,7 @@ ReplyWidgetPlain.prototype.onInputChange = function () {
* @inheritdoc
*/
ReplyWidgetPlain.prototype.setup = function ( data ) {
var autosaveValue = this.storage.get( 'body' );
const autosaveValue = this.storage.get( 'body' );
data = data || {};

View file

@ -73,7 +73,7 @@ ReplyWidgetVisual.prototype.clear = function ( preserveStorage ) {
* @inheritdoc
*/
ReplyWidgetVisual.prototype.isEmpty = function () {
var surface = this.replyBodyWidget.target.getSurface();
const surface = this.replyBodyWidget.target.getSurface();
return !( surface && surface.getModel().getDocument().data.hasContent() );
};
@ -90,11 +90,11 @@ ReplyWidgetVisual.prototype.getMode = function () {
* @inheritdoc
*/
ReplyWidgetVisual.prototype.setup = function ( data, suppressNotifications ) {
var target = this.replyBodyWidget.target;
const target = this.replyBodyWidget.target;
data = data || {};
var htmlOrDoc;
let htmlOrDoc;
if ( this.storage.get( 'saveable' ) ) {
htmlOrDoc = this.storage.get( 've-dochtml' );
target.recovered = true;
@ -152,7 +152,7 @@ ReplyWidgetVisual.prototype.teardown = function () {
* @inheritdoc
*/
ReplyWidgetVisual.prototype.focus = function () {
var targetWidget = this.replyBodyWidget;
const targetWidget = this.replyBodyWidget;
setTimeout( () => {
// Check surface still exists after timeout
if ( targetWidget.getSurface() ) {

View file

@ -12,8 +12,8 @@ var
* @param {ThreadItem|ThreadItem[]} items Thread item(s) to highlight
*/
function Highlight( items ) {
var highlightNodes = [];
var ranges = [];
const highlightNodes = [];
const ranges = [];
this.topmostElement = null;
@ -22,12 +22,12 @@ function Highlight( items ) {
this.rootNode = items[ 0 ] ? items[ 0 ].rootNode : null;
items.forEach( ( item ) => {
var $highlight = $( '<div>' ).addClass( 'ext-discussiontools-init-highlight' );
const $highlight = $( '<div>' ).addClass( 'ext-discussiontools-init-highlight' );
// We insert the highlight in the DOM near the thead item, so that it remains positioned correctly
// when it shifts (e.g. collapsing the table of contents), and disappears when it is hidden (e.g.
// opening visual editor).
var range = item.getRange();
const range = item.getRange();
// Support: Firefox
// The highlight node must be inserted after the start marker node (data-mw-comment-start), not
// before, otherwise Node#getBoundingClientRect() returns wrong results.
@ -35,7 +35,7 @@ function Highlight( items ) {
// If the item is a top-level comment wrapped in a frame, highlight outside that frame
if ( item.level === 1 ) {
var coveredSiblings = utils.getFullyCoveredSiblings( item, item.rootNode );
const coveredSiblings = utils.getFullyCoveredSiblings( item, item.rootNode );
if ( coveredSiblings ) {
range.setStartBefore( coveredSiblings[ 0 ] );
range.setEndAfter( coveredSiblings[ coveredSiblings.length - 1 ] );
@ -86,13 +86,13 @@ Highlight.prototype.update = function () {
width: '',
height: ''
} );
var rootRect = this.rootNode.getBoundingClientRect();
const rootRect = this.rootNode.getBoundingClientRect();
this.topmostElement = null;
var topmostTop = Infinity;
let topmostTop = Infinity;
this.ranges.forEach( ( range, i ) => {
var $element = this.$element.eq( i );
var baseRect = $element[ 0 ].getBoundingClientRect();
var rect = RangeFix.getBoundingClientRect( range );
const $element = this.$element.eq( i );
const baseRect = $element[ 0 ].getBoundingClientRect();
const rect = RangeFix.getBoundingClientRect( range );
// rect may be null if the range is in a detached or hidden node
if ( rect ) {
// Draw the highlight over the full width of the page, except for very short comments
@ -104,9 +104,9 @@ Highlight.prototype.update = function () {
//
// It seems difficult to distinguish the floating boxes from comments that are just
// very short or very deeply indented, and this seems to work well enough in practice.
var useFullWidth = rect.width > rootRect.width / 3;
const useFullWidth = rect.width > rootRect.width / 3;
var headingTopAdj = 0;
let headingTopAdj = 0;
if (
featuresEnabled.visualenhancements &&
$element.closest( '.ext-discussiontools-init-section' ).length
@ -116,10 +116,10 @@ Highlight.prototype.update = function () {
headingTopAdj = 10;
}
var top = rect.top - baseRect.top;
var width = rect.width;
var height = rect.height;
var left, right;
const top = rect.top - baseRect.top;
let width = rect.width;
const height = rect.height;
let left, right;
if ( $element.css( 'direction' ) === 'ltr' ) {
left = rect.left - baseRect.left;
if ( useFullWidth ) {
@ -131,7 +131,7 @@ Highlight.prototype.update = function () {
width = rootRect.width - ( ( rootRect.left + rootRect.width ) - ( rect.left + rect.width ) );
}
}
var padding = 5;
const padding = 5;
$element.css( {
'margin-top': top - padding + headingTopAdj,
'margin-left': left !== undefined ? left - padding : '',
@ -179,11 +179,11 @@ function highlightTargetComment( threadItemSet, noScroll ) {
highlightedTarget = null;
}
var targetElement = mw.util.getTargetFromFragment();
const targetElement = mw.util.getTargetFromFragment();
if ( targetElement && targetElement.hasAttribute( 'data-mw-comment-start' ) ) {
var threadItemId = targetElement.getAttribute( 'id' );
var threadItem = threadItemSet.findCommentById( targetElement.getAttribute( 'id' ) );
const threadItemId = targetElement.getAttribute( 'id' );
const threadItem = threadItemSet.findCommentById( targetElement.getAttribute( 'id' ) );
if ( threadItem ) {
highlightedTarget = new Highlight( threadItem );
highlightedTarget.$element.addClass( 'ext-discussiontools-init-targetcomment' );
@ -203,7 +203,7 @@ function highlightTargetComment( threadItemSet, noScroll ) {
};
}
var url = new URL( location.href );
const url = new URL( location.href );
return highlightNewComments(
threadItemSet,
noScroll,
@ -225,11 +225,11 @@ function highlightTargetComment( threadItemSet, noScroll ) {
* @param {string} threadItemId Thread item ID (NEW_TOPIC_COMMENT_ID for the a new topic)
*/
function highlightPublishedComment( threadItemSet, threadItemId ) {
var highlightComments = [];
const highlightComments = [];
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
// Highlight the last comment on the page
var lastComment = threadItemSet.threadItems[ threadItemSet.threadItems.length - 1 ];
const lastComment = threadItemSet.threadItems[ threadItemSet.threadItems.length - 1 ];
lastHighlightedPublishedComment = lastComment;
highlightComments.push( lastComment );
@ -244,15 +244,15 @@ function highlightPublishedComment( threadItemSet, threadItemId ) {
lastHighlightedPublishedComment = lastComment.parent;
// Change URL to point to this section, like the old section=new wikitext editor does.
// This also expands collapsed sections on mobile (T301840).
var sectionTitle = lastHighlightedPublishedComment.getLinkableTitle();
var urlFragment = mw.util.escapeIdForLink( sectionTitle );
const sectionTitle = lastHighlightedPublishedComment.getLinkableTitle();
const urlFragment = mw.util.escapeIdForLink( sectionTitle );
// Navigate to fragment without scrolling
location.hash = '#' + urlFragment + '-DoesNotExist-DiscussionToolsHack';
history.replaceState( null, '', '#' + urlFragment );
}
} else {
// Find the comment we replied to, then highlight the last reply
var repliedToComment = threadItemSet.threadItemsById[ threadItemId ];
const repliedToComment = threadItemSet.threadItemsById[ threadItemId ];
highlightComments.push( repliedToComment.replies[ repliedToComment.replies.length - 1 ] );
lastHighlightedPublishedComment = highlightComments[ 0 ];
}
@ -260,7 +260,7 @@ function highlightPublishedComment( threadItemSet, threadItemId ) {
// We may have changed the location hash on mobile, so wait for that to cause
// the section to expand before drawing the highlight.
setTimeout( () => {
var highlight = new Highlight( highlightComments );
const highlight = new Highlight( highlightComments );
highlight.$element.addClass( 'ext-discussiontools-init-publishedcomment' );
// Show a highlight with the same timing as the post-edit message (mediawiki.action.view.postEdit):
@ -314,12 +314,12 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
options = options || {};
if ( options.newCommentsSinceId ) {
var newCommentsSince = threadItemSet.findCommentById( options.newCommentsSinceId );
const newCommentsSince = threadItemSet.findCommentById( options.newCommentsSinceId );
if ( newCommentsSince && newCommentsSince instanceof CommentItem ) {
var sinceTimestamp = newCommentsSince.timestamp;
var threadItems;
const sinceTimestamp = newCommentsSince.timestamp;
let threadItems;
if ( options.inThread ) {
var heading = newCommentsSince.getSubscribableHeading() || newCommentsSince.getHeading();
const heading = newCommentsSince.getSubscribableHeading() || newCommentsSince.getHeading();
threadItems = heading.getThreadItemsBelow();
} else {
threadItems = threadItemSet.getCommentItems();
@ -332,8 +332,8 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
if ( options.sinceThread ) {
// Check that we are in a thread that is newer than `sinceTimestamp`.
// Thread age is determined by looking at getOldestReply.
var itemHeading = threadItem.getSubscribableHeading() || threadItem.getHeading();
var oldestReply = itemHeading.getOldestReply();
const itemHeading = threadItem.getSubscribableHeading() || threadItem.getHeading();
const oldestReply = itemHeading.getOldestReply();
if ( !( oldestReply && oldestReply.timestamp >= sinceTimestamp ) ) {
return;
}
@ -344,7 +344,7 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
}
}
var comments;
let comments;
if ( newCommentIds.length ) {
comments = newCommentIds
.map( ( id ) => threadItemSet.findCommentById( id ) )
@ -372,9 +372,9 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
* @param {ThreadItemSet} threadItemSet
*/
function clearHighlightTargetComment( threadItemSet ) {
var url = new URL( location.href );
const url = new URL( location.href );
var targetElement = mw.util.getTargetFromFragment();
const targetElement = mw.util.getTargetFromFragment();
if ( targetElement && targetElement.hasAttribute( 'data-mw-comment-start' ) ) {
// Clear the hash from the URL, triggering the 'hashchange' event and updating the :target

View file

@ -8,7 +8,7 @@ var isIos = /ipad|iphone|ipod/i.test( navigator.userAgent );
$( document.body ).toggleClass( 'ext-discussiontools-init-ios', isIos );
function onViewportChange() {
var isKeyboardOpen;
let isKeyboardOpen;
if ( isIos ) {
isKeyboardOpen = visualViewport.height < viewportScrollContainer.clientHeight;
@ -32,20 +32,20 @@ function init( $container ) {
if ( !viewportScrollContainer && window.visualViewport ) {
viewportScrollContainer = OO.ui.Element.static.getClosestScrollableContainer( document.body );
initialClientHeight = viewportScrollContainer.clientHeight;
var onViewportChangeThrottled = OO.ui.throttle( onViewportChange, 100 );
const onViewportChangeThrottled = OO.ui.throttle( onViewportChange, 100 );
$( visualViewport ).on( 'resize', onViewportChangeThrottled );
}
// Mobile overflow menu
var $ledeContent = $container.find( '.mf-section-0' ).children( ':not( .ext-discussiontools-emptystate )' )
const $ledeContent = $container.find( '.mf-section-0' ).children( ':not( .ext-discussiontools-emptystate )' )
// On non-existent pages MobileFrontend wrapping isn't there
.add( $container.find( '.mw-talkpageheader' ) );
var $ledeButton = $container.find( '.ext-discussiontools-init-lede-button' );
const $ledeButton = $container.find( '.ext-discussiontools-init-lede-button' );
if ( $ledeButton.length ) {
var windowManager = OO.ui.getWindowManager();
const windowManager = OO.ui.getWindowManager();
if ( !ledeSectionDialog ) {
var LedeSectionDialog = require( './LedeSectionDialog.js' );
const LedeSectionDialog = require( './LedeSectionDialog.js' );
ledeSectionDialog = new LedeSectionDialog();
windowManager.addWindows( [ ledeSectionDialog ] );
}
@ -61,7 +61,7 @@ function init( $container ) {
}
// eslint-disable-next-line no-jquery/no-global-selector
var $newTopicWrapper = $( '.ext-discussiontools-init-new-topic' );
const $newTopicWrapper = $( '.ext-discussiontools-init-new-topic' );
if (
!newTopicButton &&
@ -72,16 +72,16 @@ function init( $container ) {
newTopicButton = OO.ui.infuse( $( '.ext-discussiontools-init-new-topic-button' ) );
// For compatibility with MobileWebUIActionsTracking logging (T295490)
newTopicButton.$element.attr( 'data-event-name', 'talkpage.add-topic' );
var $scrollContainer = $( OO.ui.Element.static.getClosestScrollableContainer( document.body ) );
var $scrollListener = $scrollContainer.is( 'html, body' ) ? $( OO.ui.Element.static.getWindow( $scrollContainer[ 0 ] ) ) : $scrollContainer;
var lastScrollTop = $scrollContainer.scrollTop();
var wasScrollDown = null;
var $body = $( document.body );
const $scrollContainer = $( OO.ui.Element.static.getClosestScrollableContainer( document.body ) );
const $scrollListener = $scrollContainer.is( 'html, body' ) ? $( OO.ui.Element.static.getWindow( $scrollContainer[ 0 ] ) ) : $scrollContainer;
let lastScrollTop = $scrollContainer.scrollTop();
let wasScrollDown = null;
const $body = $( document.body );
// This block of code is only run once, so we don't need to remove this listener ever
$scrollListener[ 0 ].addEventListener( 'scroll', OO.ui.throttle( () => {
// Round negative values up to 0 to ignore iOS scroll bouncing (T323400)
var scrollTop = Math.max( $scrollContainer.scrollTop(), 0 );
var isScrollDown = scrollTop > lastScrollTop;
const scrollTop = Math.max( $scrollContainer.scrollTop(), 0 );
const isScrollDown = scrollTop > lastScrollTop;
if ( isScrollDown !== wasScrollDown ) {
if ( !isScrollDown ) {
$newTopicWrapper.css( 'transition', 'none' );
@ -97,7 +97,7 @@ function init( $container ) {
} );
}
var observer = new IntersectionObserver(
const observer = new IntersectionObserver(
( ( entries ) => {
$newTopicWrapper.toggleClass( 'ext-discussiontools-init-new-topic-pinned', entries[ 0 ].intersectionRatio === 1 );
} ),

View file

@ -24,7 +24,7 @@ function sanitizeWikitextLinebreaks( wikitext ) {
* @param {HTMLElement} linkNode Reply link
*/
function addReplyLink( comment, linkNode ) {
var target = comment.range.endContainer;
const target = comment.range.endContainer;
// Insert the link before trailing whitespace.
// In the MediaWiki parser output, <ul>/<dl> nodes are preceded by a newline. Normally it isn't
@ -52,7 +52,7 @@ function addReplyLink( comment, linkNode ) {
* @return {HTMLElement}
*/
function addListItem( comment, replyIndentation ) {
var listTypeMap = {
const listTypeMap = {
li: 'ul',
dd: 'dl'
};
@ -62,13 +62,13 @@ function addListItem( comment, replyIndentation ) {
// (or in other words, all replies, and replies to replies, and so on)
// 3. Add comment with level of the given comment plus 1
var curComment = comment;
let curComment = comment;
while ( curComment.replies.length ) {
curComment = curComment.replies[ curComment.replies.length - 1 ];
}
// Tag names for lists and items we're going to insert
var itemType;
let itemType;
if ( replyIndentation === 'invisible' ) {
itemType = 'dd';
} else if ( replyIndentation === 'bullet' ) {
@ -76,16 +76,16 @@ function addListItem( comment, replyIndentation ) {
} else {
throw new Error( "Invalid reply indentation syntax '" + replyIndentation + "'" );
}
var listType = listTypeMap[ itemType ];
const listType = listTypeMap[ itemType ];
var desiredLevel = comment.level + 1;
var target = curComment.range.endContainer;
const desiredLevel = comment.level + 1;
let target = curComment.range.endContainer;
// target is a text node or an inline element at the end of a "paragraph" (not necessarily paragraph node).
// First, we need to find a block-level parent that we can mess with.
// If we can't find a surrounding list item or paragraph (e.g. maybe we're inside a table cell
// or something), take the parent node and hope for the best.
var parent = utils.closestElement( target, [ 'li', 'dd', 'p' ] ) || target.parentNode;
let parent = utils.closestElement( target, [ 'li', 'dd', 'p' ] ) || target.parentNode;
while ( target.parentNode !== parent ) {
target = target.parentNode;
}
@ -95,9 +95,9 @@ function addListItem( comment, replyIndentation ) {
// If the comment is fully covered by some wrapper element, insert replies outside that wrapper.
// This will often just be a paragraph node (<p>), but it can be a <div> or <table> that serves
// as some kind of a fancy frame, which are often used for barnstars and announcements.
var excludedWrapper = utils.closestElement( target, [ 'section' ] ) ||
const excludedWrapper = utils.closestElement( target, [ 'section' ] ) ||
curComment.rootNode;
var covered = utils.getFullyCoveredSiblings( curComment, excludedWrapper );
const covered = utils.getFullyCoveredSiblings( curComment, excludedWrapper );
if ( curComment.level === 1 && covered ) {
target = covered[ covered.length - 1 ];
parent = target.parentNode;
@ -105,7 +105,7 @@ function addListItem( comment, replyIndentation ) {
// If the comment is in a transclusion, insert replies after the transclusion. (T313100)
// This method should never be called in cases where that would be a bad idea.
var transclusionNode = utils.getTranscludedFromElement( target );
let transclusionNode = utils.getTranscludedFromElement( target );
if ( transclusionNode ) {
while (
transclusionNode.nextSibling &&
@ -142,9 +142,9 @@ function addListItem( comment, replyIndentation ) {
// Instead of just using curComment.level, consider indentation of lists within the
// comment (T252702)
var curLevel = utils.getIndentLevel( target, curComment.rootNode ) + 1;
let curLevel = utils.getIndentLevel( target, curComment.rootNode ) + 1;
var item, list;
let item, list;
if ( desiredLevel === 1 ) {
// Special handling for top-level comments
item = target.ownerDocument.createElement( 'div' );
@ -160,7 +160,7 @@ function addListItem( comment, replyIndentation ) {
// which appear at the end of the line in wikitext outside the paragraph,
// but we usually shouldn't insert replies between the paragraph and such comments. (T257651)
// Skip over comments and whitespace, but only update target when skipping past comments.
var pointer = target;
let pointer = target;
while (
pointer.nextSibling && (
utils.isRenderingTransparentNode( pointer.nextSibling ) ||
@ -196,7 +196,7 @@ function addListItem( comment, replyIndentation ) {
} else {
// Split the ancestor nodes after the target to decrease nesting.
var newNode;
let newNode;
do {
if ( !target || !parent ) {
throw new Error( 'Can not decrease nesting any more' );
@ -310,7 +310,7 @@ function removeAddedListItem( node ) {
* @param {DocumentFragment|null} fragment Containing document fragment if list has no parent
*/
function unwrapList( list, fragment ) {
var doc = list.ownerDocument,
let doc = list.ownerDocument,
container = fragment || list.parentNode,
referenceNode = list;
@ -330,11 +330,11 @@ function unwrapList( list, fragment ) {
return;
}
var insertBefore;
let insertBefore;
while ( list.firstChild ) {
if ( list.firstChild.nodeType === Node.ELEMENT_NODE ) {
// Move <dd> contents to <p>
var p = doc.createElement( 'p' );
let p = doc.createElement( 'p' );
while ( list.firstChild.firstChild ) {
// If contents is a block element, place outside the paragraph
// and start a new paragraph after
@ -375,7 +375,7 @@ function unwrapList( list, fragment ) {
* @return {HTMLElement}
*/
function addSiblingListItem( previousItem ) {
var listItem = previousItem.ownerDocument.createElement( previousItem.tagName );
const listItem = previousItem.ownerDocument.createElement( previousItem.tagName );
previousItem.parentNode.insertBefore( listItem, previousItem.nextSibling );
return listItem;
}

View file

@ -8,14 +8,14 @@ function init( $container, pageThreads ) {
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-editing-core' ] ).then( () => {
$container.find( '.ext-discussiontools-init-section-overflowMenuButton' ).each( ( i, button ) => {
// Comment ellipsis
var $threadMarker = $( button ).closest( '[data-mw-thread-id]' );
let $threadMarker = $( button ).closest( '[data-mw-thread-id]' );
if ( !$threadMarker.length ) {
// Heading ellipsis
$threadMarker = $( button ).closest( '.ext-discussiontools-init-section' ).find( '[data-mw-thread-id]' );
}
var threadItem = pageThreads.findCommentById( $threadMarker.data( 'mw-thread-id' ) );
const threadItem = pageThreads.findCommentById( $threadMarker.data( 'mw-thread-id' ) );
var buttonMenu = OO.ui.infuse( button, {
const buttonMenu = OO.ui.infuse( button, {
$overlay: true,
menu: {
classes: [ 'ext-discussiontools-init-section-overflowMenu' ],
@ -24,13 +24,13 @@ function init( $container, pageThreads ) {
} );
mw.loader.using( buttonMenu.getData().resourceLoaderModules || [] ).then( () => {
var itemConfigs = buttonMenu.getData().itemConfigs;
const itemConfigs = buttonMenu.getData().itemConfigs;
if ( !itemConfigs ) {
// We should never have missing itemConfigs, but if this happens, hide the empty menu
buttonMenu.toggle( false );
return;
}
var overflowMenuItemWidgets = itemConfigs.map( ( itemConfig ) => new OO.ui.MenuOptionWidget( itemConfig ) );
const overflowMenuItemWidgets = itemConfigs.map( ( itemConfig ) => new OO.ui.MenuOptionWidget( itemConfig ) );
buttonMenu.getMenu().addItems( overflowMenuItemWidgets );
buttonMenu.getMenu().items.forEach( ( menuItem ) => {
mw.hook( 'discussionToolsOverflowMenuOnAddItem' ).fire( menuItem.getData().id, menuItem, threadItem );

View file

@ -5,10 +5,10 @@ function init( $pageContainer ) {
}
function copyLink( link ) {
var $win = $( window );
var scrollTop = $win.scrollTop();
const $win = $( window );
const scrollTop = $win.scrollTop();
var $tmpInput = $( '<input>' )
const $tmpInput = $( '<input>' )
.val( link )
.addClass( 'noime' )
.css( {
@ -18,7 +18,7 @@ function copyLink( link ) {
.appendTo( 'body' )
.trigger( 'focus' );
$tmpInput[ 0 ].setSelectionRange( 0, link.length );
var copied;
let copied;
try {
copied = document.execCommand( 'copy' );
} catch ( err ) {

View file

@ -74,7 +74,7 @@ function updateSubscribeButton( button, state ) {
* @return {jQuery.Promise} Promise which resolves after change of state
*/
function changeSubscription( title, commentName, subscribe, isNewTopics ) {
var promise = api.postWithToken( 'csrf', {
const promise = api.postWithToken( 'csrf', {
action: 'discussiontoolssubscribe',
page: title,
commentname: commentName,
@ -148,19 +148,19 @@ function initTopicSubscriptions( $container, threadItemSet ) {
$container.find( '.ext-discussiontools-init-section-subscribeButton' ).each( ( i, element ) => {
// These attributes will be lost when infusing
// TODO: Could also be fixed by subclassing ButtonWidget in PHP
var subscribedStateTemp = getSubscribedStateFromElement( element );
const subscribedStateTemp = getSubscribedStateFromElement( element );
var id = $( element ).closest( '.ext-discussiontools-init-section' )
const id = $( element ).closest( '.ext-discussiontools-init-section' )
.find( '[data-mw-comment-start]' ).attr( 'id' );
var headingItem = threadItemSet.findCommentById( id );
const headingItem = threadItemSet.findCommentById( id );
if ( !( headingItem instanceof HeadingItem ) ) {
// This should never happen
return;
}
var name = headingItem.name;
var button = OO.ui.infuse( element );
const name = headingItem.name;
const button = OO.ui.infuse( element );
buttonsByName[ name ] = button;
// Restore data attribute
@ -168,11 +168,11 @@ function initTopicSubscriptions( $container, threadItemSet ) {
button.$element[ 0 ].setAttribute( 'data-mw-subscribed', String( subscribedStateTemp ) );
}
var title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
const title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
button.on( 'click', () => {
// Get latest subscribedState
var subscribedState = getSubscribedStateFromElement( button.$element[ 0 ] );
const subscribedState = getSubscribedStateFromElement( button.$element[ 0 ] );
button.setDisabled( true );
changeSubscription( title, name, !subscribedState )
@ -187,18 +187,18 @@ function initTopicSubscriptions( $container, threadItemSet ) {
// Subscription links (no visual enhancements)
$container.find( '.ext-discussiontools-init-section-subscribe-link' ).each( ( i, link ) => {
var $link = $( link );
var id = $link.closest( '.ext-discussiontools-init-section' )
const $link = $( link );
const id = $link.closest( '.ext-discussiontools-init-section' )
.find( '[data-mw-comment-start]' ).attr( 'id' );
var headingItem = threadItemSet.findCommentById( id );
const headingItem = threadItemSet.findCommentById( id );
if ( !( headingItem instanceof HeadingItem ) ) {
// This should never happen
return;
}
var itemName = headingItem.name;
var title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
const itemName = headingItem.name;
const title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
linksByName[ itemName ] = link;
@ -215,7 +215,7 @@ function initTopicSubscriptions( $container, threadItemSet ) {
e.preventDefault();
// Get latest subscribedState
var subscribedState = getSubscribedStateFromElement( $link[ 0 ] );
const subscribedState = getSubscribedStateFromElement( $link[ 0 ] );
$link.addClass( 'ext-discussiontools-init-section-subscribe-link-pending' );
changeSubscription( title, itemName, !subscribedState )
@ -241,7 +241,7 @@ function initTopicSubscriptions( $container, threadItemSet ) {
* page actions like live-preview can still reach this point.
*/
function initNewTopicsSubscription() {
var $button, $label, $icon;
let $button, $label, $icon;
initApi();
@ -252,7 +252,7 @@ function initNewTopicsSubscription() {
$icon = $button.find( '.minerva-icon' );
// HACK: We can't set data-mw-subscribed intially in Minerva, so work it out from the icon
// eslint-disable-next-line no-jquery/no-class-state
var initialState = $icon.hasClass( 'minerva-icon--bell' ) ? STATE_SUBSCRIBED : STATE_UNSUBSCRIBED;
const initialState = $icon.hasClass( 'minerva-icon--bell' ) ? STATE_SUBSCRIBED : STATE_UNSUBSCRIBED;
$button.attr( 'data-mw-subscribed', String( initialState ) );
} else {
// eslint-disable-next-line no-jquery/no-global-selector
@ -261,13 +261,13 @@ function initNewTopicsSubscription() {
$icon = $( [] );
}
var titleObj = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
var name = utils.getNewTopicsSubscriptionId( titleObj );
const titleObj = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
const name = utils.getNewTopicsSubscriptionId( titleObj );
$button.off( '.mw-dt-topicsubscriptions' ).on( 'click.mw-dt-topicsubscriptions', ( e ) => {
e.preventDefault();
// Get latest subscribedState
var subscribedState = getSubscribedStateFromElement( $button[ 0 ] );
const subscribedState = getSubscribedStateFromElement( $button[ 0 ] );
changeSubscription( titleObj.getPrefixedText(), name, !subscribedState, true )
.then( ( result ) => {
@ -284,9 +284,9 @@ function initSpecialTopicSubscriptions() {
// Unsubscribe links on special page
// eslint-disable-next-line no-jquery/no-global-selector
$( '.ext-discussiontools-special-unsubscribe-button' ).each( ( i, element ) => {
var button = OO.ui.infuse( element );
var data = button.getData();
var subscribedState = STATE_SUBSCRIBED;
const button = OO.ui.infuse( element );
const data = button.getData();
let subscribedState = STATE_SUBSCRIBED;
button.on( 'click', () => {
button.setDisabled( true );
@ -311,7 +311,7 @@ function initSpecialTopicSubscriptions() {
* Show the first time popup for auto topic subscriptions, if required
*/
function maybeShowFirstTimeAutoTopicSubPopup() {
var lastHighlightComment = require( './highlighter.js' ).getLastHighlightedPublishedComment();
const lastHighlightComment = require( './highlighter.js' ).getLastHighlightedPublishedComment();
if ( !lastHighlightComment || seenAutoTopicSubPopup ) {
return;
@ -321,7 +321,7 @@ function maybeShowFirstTimeAutoTopicSubPopup() {
mw.user.options.set( 'discussiontools-seenautotopicsubpopup', '1' );
api.saveOption( 'discussiontools-seenautotopicsubpopup', '1' );
var $popupContent, popup;
let $popupContent, popup;
function close() {
popup.$element.removeClass( 'ext-discussiontools-autotopicsubpopup-fadein' );
@ -420,13 +420,13 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
// If the topic is already marked as auto-subscribed, there's nothing to do.
// (Except maybe show the first-time popup.)
// If the topic is marked as having never been subscribed, check if they are auto-subscribed now.
var topicsToCheck = [];
var pendingLinks = [];
var pendingButtons = [];
for ( var headingName in headingsToUpdate ) {
var link = linksByName[ headingName ];
var button = buttonsByName[ headingName ];
var subscribedState = getSubscribedStateFromElement( link || button.$element[ 0 ] );
const topicsToCheck = [];
const pendingLinks = [];
const pendingButtons = [];
for ( const headingName in headingsToUpdate ) {
const link = linksByName[ headingName ];
const button = buttonsByName[ headingName ];
const subscribedState = getSubscribedStateFromElement( link || button.$element[ 0 ] );
if ( subscribedState === STATE_AUTOSUBSCRIBED ) {
maybeShowFirstTimeAutoTopicSubPopup();
@ -458,7 +458,7 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
// updateSubscriptionStates() method is only called if we're really expecting one to be there.
// (There are certainly neater ways to implement this, involving push notifications or at
// least long-polling or something. But this is the simplest one!)
var wait = $.Deferred();
const wait = $.Deferred();
setTimeout( wait.resolve, 5000 );
return wait.then( () => api.get( {
action: 'discussiontoolsgetsubscriptions',
@ -468,8 +468,8 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
return response;
} ).then( ( response ) => {
// Update state of each topic for which there is a subscription
for ( var subItemName in response.subscriptions ) {
var state = response.subscriptions[ subItemName ];
for ( const subItemName in response.subscriptions ) {
const state = response.subscriptions[ subItemName ];
if ( linksByName[ subItemName ] ) {
updateSubscribeLink( linksByName[ subItemName ], state );
}
@ -496,8 +496,8 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
* @param {string} [threadItemId] Just-posted comment ID (or NEW_TOPIC_COMMENT_ID)
*/
function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId ) {
var recentComments = [];
var headingsToUpdate = {};
const recentComments = [];
const headingsToUpdate = {};
if ( threadItemId ) {
// Edited by using the reply tool or new topic tool. Only check the edited topic.
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
@ -507,7 +507,7 @@ function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId )
}
} else if ( mw.config.get( 'wgPostEdit' ) ) {
// Edited by using wikitext editor. Check topics with their own comments within last minute.
for ( var i = 0; i < threadItemSet.threadItems.length; i++ ) {
for ( let i = 0; i < threadItemSet.threadItems.length; i++ ) {
if (
threadItemSet.threadItems[ i ] instanceof CommentItem &&
threadItemSet.threadItems[ i ].author === mw.user.getName() &&
@ -518,7 +518,7 @@ function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId )
}
}
recentComments.forEach( ( recentComment ) => {
var headingItem = recentComment.getSubscribableHeading();
const headingItem = recentComment.getSubscribableHeading();
if ( headingItem ) {
// Use names as object keys to deduplicate if there are multiple comments in a topic.
headingsToUpdate[ headingItem.name ] = headingItem;

View file

@ -21,7 +21,7 @@ var solTransparentLinkRegexp = /(?:^|\s)mw:PageProp\/(?:Category|redirect|Langua
* @return {boolean} Node is considered a rendering-transparent node in Parsoid
*/
function isRenderingTransparentNode( node ) {
var nextSibling = node.nextSibling;
const nextSibling = node.nextSibling;
return (
node.nodeType === Node.COMMENT_NODE ||
node.nodeType === Node.ELEMENT_NODE && (
@ -117,7 +117,7 @@ function isCommentSeparator( node ) {
return false;
}
var tagName = node.tagName.toLowerCase();
const tagName = node.tagName.toLowerCase();
if ( tagName === 'br' || tagName === 'hr' ) {
return true;
}
@ -130,7 +130,7 @@ function isCommentSeparator( node ) {
return true;
}
var classList = node.classList;
const classList = node.classList;
if (
// Anything marked as not containing comments
classList.contains( 'mw-notalk' ) ||
@ -174,7 +174,7 @@ function isCommentContent( node ) {
* @return {number} Index in parentNode's childNode list
*/
function childIndexOf( child ) {
var i = 0;
let i = 0;
while ( ( child = child.previousSibling ) ) {
i++;
}
@ -219,7 +219,7 @@ function getTranscludedFromElement( node ) {
node.getAttribute( 'about' ) &&
/^#mwt\d+$/.test( node.getAttribute( 'about' ) )
) {
var about = node.getAttribute( 'about' );
const about = node.getAttribute( 'about' );
// 2.
while (
@ -252,7 +252,7 @@ function getTranscludedFromElement( node ) {
*/
function getHeadlineNode( heading ) {
// This code assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ]
var headline = heading;
let headline = heading;
if ( headline.hasAttribute( 'data-mw-comment-start' ) ) {
// JS only: Support output from the PHP CommentFormatter
@ -292,12 +292,12 @@ function htmlTrim( str ) {
* @return {number}
*/
function getIndentLevel( node, rootNode ) {
var indent = 0;
let indent = 0;
while ( node ) {
if ( node === rootNode ) {
break;
}
var tagName = node instanceof HTMLElement ? node.tagName.toLowerCase() : null;
const tagName = node instanceof HTMLElement ? node.tagName.toLowerCase() : null;
if ( tagName === 'li' || tagName === 'dd' ) {
indent++;
}
@ -313,11 +313,11 @@ function getIndentLevel( node, rootNode ) {
* @return {Node[]}
*/
function getCoveredSiblings( range ) {
var ancestor = range.commonAncestorContainer;
const ancestor = range.commonAncestorContainer;
var siblings = ancestor.childNodes;
var start = 0;
var end = siblings.length - 1;
const siblings = ancestor.childNodes;
let start = 0;
let end = siblings.length - 1;
// Find first of the siblings that contains the item
if ( ancestor === range.startContainer ) {
@ -350,20 +350,20 @@ function getCoveredSiblings( range ) {
* @return {Node[]|null}
*/
function getFullyCoveredSiblings( item, excludedAncestorNode ) {
var siblings = getCoveredSiblings( item.getRange() );
let siblings = getCoveredSiblings( item.getRange() );
function makeRange( sibs ) {
var range = sibs[ 0 ].ownerDocument.createRange();
const range = sibs[ 0 ].ownerDocument.createRange();
range.setStartBefore( sibs[ 0 ] );
range.setEndAfter( sibs[ sibs.length - 1 ] );
return range;
}
var matches = compareRanges( makeRange( siblings ), item.getRange() ) === 'equal';
const matches = compareRanges( makeRange( siblings ), item.getRange() ) === 'equal';
if ( matches ) {
// If these are all of the children (or the only child), go up one more level
var parent;
let parent;
while (
( parent = siblings[ 0 ].parentNode ) &&
parent !== excludedAncestorNode &&
@ -387,18 +387,18 @@ function getTitleFromUrl( url ) {
if ( !url ) {
return null;
}
var parsedUrl = new URL( url );
const parsedUrl = new URL( url );
if ( parsedUrl.searchParams.get( 'title' ) ) {
return parsedUrl.searchParams.get( 'title' );
}
// wgArticlePath is site config so is trusted
// eslint-disable-next-line security/detect-non-literal-regexp
var articlePathRegexp = new RegExp(
const articlePathRegexp = new RegExp(
mw.util.escapeRegExp( mw.config.get( 'wgArticlePath' ) )
.replace( '\\$1', '(.*)' )
);
var match;
let match;
if ( ( match = parsedUrl.pathname.match( articlePathRegexp ) ) ) {
return decodeURIComponent( match[ 1 ] );
}
@ -420,7 +420,7 @@ function getTitleFromUrl( url ) {
* @return {any} Final return value of the callback
*/
function linearWalk( node, callback ) {
var
let
result = null,
withinNode = node.parentNode,
beforeNode = node;
@ -449,7 +449,7 @@ function linearWalk( node, callback ) {
* @inheritdoc #linearWalk
*/
function linearWalkBackwards( node, callback ) {
var
let
result = null,
withinNode = node.parentNode,
beforeNode = node;
@ -521,10 +521,10 @@ function getRangeLastNode( range ) {
function compareRanges( a, b ) {
// Compare the positions of: start of A to start of B, start of A to end of B, and so on.
// Watch out, the constant names are the opposite of what they should be.
var startToStart = a.compareBoundaryPoints( Range.START_TO_START, b );
var startToEnd = a.compareBoundaryPoints( Range.END_TO_START, b );
var endToStart = a.compareBoundaryPoints( Range.START_TO_END, b );
var endToEnd = a.compareBoundaryPoints( Range.END_TO_END, b );
let startToStart = a.compareBoundaryPoints( Range.START_TO_START, b );
const startToEnd = a.compareBoundaryPoints( Range.END_TO_START, b );
const endToStart = a.compareBoundaryPoints( Range.START_TO_END, b );
let endToEnd = a.compareBoundaryPoints( Range.END_TO_END, b );
// Check for almost equal ranges (boundary points only differing by uninteresting nodes)
if (
@ -580,15 +580,15 @@ function compareRangesAlmostEqualBoundaries( a, b, boundary ) {
// This code is awful, but several attempts to rewrite it made it even worse.
// You're welcome to give it a try.
var from = boundary === 'end' ? getRangeLastNode( a ) : getRangeFirstNode( a );
var to = boundary === 'end' ? getRangeLastNode( b ) : getRangeFirstNode( b );
const from = boundary === 'end' ? getRangeLastNode( a ) : getRangeFirstNode( a );
const to = boundary === 'end' ? getRangeLastNode( b ) : getRangeFirstNode( b );
var skipNode = null;
let skipNode = null;
if ( boundary === 'end' ) {
skipNode = from;
}
var foundContent = false;
let foundContent = false;
linearWalk(
from,
( event, n ) => {

View file

@ -5,10 +5,10 @@ var
QUnit.module( 'mw.dt.ThreadItem', QUnit.newMwEnvironment() );
QUnit.test( '#getAuthorsBelow/#getThreadItemsBelow', ( assert ) => {
var cases = require( '../cases/authors.json' );
const cases = require( '../cases/authors.json' );
function newFromJSON( json ) {
var item;
let item;
if ( json.type === 'heading' ) {
item = new HeadingItem();
} else {
@ -22,7 +22,7 @@ QUnit.test( '#getAuthorsBelow/#getThreadItemsBelow', ( assert ) => {
}
cases.forEach( ( caseItem ) => {
var threadItem = newFromJSON( caseItem.thread ),
const threadItem = newFromJSON( caseItem.thread ),
authors = threadItem.getAuthorsBelow();
assert.deepEqual(

View file

@ -6,36 +6,36 @@ var
QUnit.module( 'mw.dt.modifier', QUnit.newMwEnvironment() );
require( '../cases/modified.json' ).forEach( ( caseItem ) => {
var testName = '#addListItem/#removeAddedListItem (' + caseItem.name + ')';
const testName = '#addListItem/#removeAddedListItem (' + caseItem.name + ')';
// This should be one test with many cases, rather than multiple tests, but the cases are large
// enough that processing all of them at once causes timeouts in Karma test runner.
var skipTests = require( '../skip.json' )[ 'cases/modified.json' ];
const skipTests = require( '../skip.json' )[ 'cases/modified.json' ];
if ( skipTests.indexOf( caseItem.name ) !== -1 ) {
QUnit.skip( testName );
return;
}
QUnit.test( testName, ( assert ) => {
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
config = require( caseItem.config ),
data = require( caseItem.data );
testUtils.overrideMwConfig( config );
var expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
var reverseExpectedHtml = testUtils.getThreadContainer( dom ).innerHTML;
const expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
const reverseExpectedHtml = testUtils.getThreadContainer( dom ).innerHTML;
var container = testUtils.getThreadContainer( dom );
var title = mw.Title.newFromText( caseItem.title );
var threadItemSet = new Parser( data ).parse( container, title );
var comments = threadItemSet.getCommentItems();
const container = testUtils.getThreadContainer( dom );
const title = mw.Title.newFromText( caseItem.title );
const threadItemSet = new Parser( data ).parse( container, title );
const comments = threadItemSet.getCommentItems();
// Add a reply to every comment. Note that this inserts *all* of the replies, unlike the real
// thing, which only deals with one at a time. This isn't ideal but resetting everything after
// every reply would be super slow.
var nodes = [];
const nodes = [];
comments.forEach( ( comment ) => {
var node = modifier.addListItem( comment, caseItem.replyIndentation || 'invisible' );
const node = modifier.addListItem( comment, caseItem.replyIndentation || 'invisible' );
node.textContent = 'Reply to ' + comment.id;
nodes.push( node );
} );
@ -43,7 +43,7 @@ require( '../cases/modified.json' ).forEach( ( caseItem ) => {
// Uncomment this to get updated content for the "modified HTML" files, for copy/paste:
// console.log( container.innerHTML );
var actualHtml = container.innerHTML;
const actualHtml = container.innerHTML;
assert.strictEqual(
actualHtml,
@ -56,7 +56,7 @@ require( '../cases/modified.json' ).forEach( ( caseItem ) => {
modifier.removeAddedListItem( node );
} );
var reverseActualHtml = container.innerHTML;
const reverseActualHtml = container.innerHTML;
assert.strictEqual(
reverseActualHtml,
reverseExpectedHtml,
@ -66,26 +66,26 @@ require( '../cases/modified.json' ).forEach( ( caseItem ) => {
} );
QUnit.test( '#addReplyLink', ( assert ) => {
var cases = require( '../cases/reply.json' );
const cases = require( '../cases/reply.json' );
cases.forEach( ( caseItem ) => {
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
config = require( caseItem.config ),
data = require( caseItem.data );
testUtils.overrideMwConfig( config );
var expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
const expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
var container = testUtils.getThreadContainer( dom );
var title = mw.Title.newFromText( caseItem.title );
var threadItemSet = new Parser( data ).parse( container, title );
var comments = threadItemSet.getCommentItems();
const container = testUtils.getThreadContainer( dom );
const title = mw.Title.newFromText( caseItem.title );
const threadItemSet = new Parser( data ).parse( container, title );
const comments = threadItemSet.getCommentItems();
// Add a reply link to every comment.
comments.forEach( ( comment ) => {
var linkNode = document.createElement( 'a' );
const linkNode = document.createElement( 'a' );
linkNode.textContent = 'Reply';
linkNode.href = '#';
modifier.addReplyLink( comment, linkNode );
@ -94,7 +94,7 @@ QUnit.test( '#addReplyLink', ( assert ) => {
// Uncomment this to get updated content for the "reply HTML" files, for copy/paste:
// console.log( container.innerHTML );
var actualHtml = container.innerHTML;
const actualHtml = container.innerHTML;
assert.strictEqual(
actualHtml,
@ -105,10 +105,10 @@ QUnit.test( '#addReplyLink', ( assert ) => {
} );
QUnit.test( '#unwrapList', ( assert ) => {
var cases = require( '../cases/unwrap.json' );
const cases = require( '../cases/unwrap.json' );
cases.forEach( ( caseItem ) => {
var container = document.createElement( 'div' );
const container = document.createElement( 'div' );
container.innerHTML = caseItem.html;
modifier.unwrapList( container.childNodes[ caseItem.index || 0 ] );
@ -122,7 +122,7 @@ QUnit.test( '#unwrapList', ( assert ) => {
} );
QUnit.test( 'sanitizeWikitextLinebreaks', ( assert ) => {
var cases = require( '../cases/sanitize-wikitext-linebreaks.json' );
const cases = require( '../cases/sanitize-wikitext-linebreaks.json' );
cases.forEach( ( caseItem ) => {
assert.strictEqual(

View file

@ -6,7 +6,7 @@ var
QUnit.module( 'mw.dt.Parser', QUnit.newMwEnvironment() );
QUnit.test( '#getTimestampRegexp', ( assert ) => {
var cases = require( '../cases/timestamp-regex.json' ),
const cases = require( '../cases/timestamp-regex.json' ),
parser = new Parser( require( '../data-en.json' ) );
cases.forEach( ( caseItem ) => {
@ -19,11 +19,11 @@ QUnit.test( '#getTimestampRegexp', ( assert ) => {
} );
QUnit.test( '#getTimestampParser', ( assert ) => {
var cases = require( '../cases/timestamp-parser.json' ),
const cases = require( '../cases/timestamp-parser.json' ),
parser = new Parser( require( '../data-en.json' ) );
cases.forEach( ( caseItem ) => {
var tsParser = parser.getTimestampParser( 'en', caseItem.format, caseItem.digits, 'UTC', { UTC: 'UTC' } ),
const tsParser = parser.getTimestampParser( 'en', caseItem.format, caseItem.digits, 'UTC', { UTC: 'UTC' } ),
expectedDate = moment( caseItem.expected );
assert.true(
@ -34,11 +34,11 @@ QUnit.test( '#getTimestampParser', ( assert ) => {
} );
QUnit.test( '#getTimestampParser (at DST change)', ( assert ) => {
var cases = require( '../cases/timestamp-parser-dst.json' ),
const cases = require( '../cases/timestamp-parser-dst.json' ),
parser = new Parser( require( '../data-en.json' ) );
cases.forEach( ( caseItem ) => {
var regexp = parser.getTimestampRegexp( 'en', caseItem.format, '\\d', caseItem.timezoneAbbrs ),
const regexp = parser.getTimestampRegexp( 'en', caseItem.format, '\\d', caseItem.timezoneAbbrs ),
tsParser = parser.getTimestampParser( 'en', caseItem.format, null, caseItem.timezone, caseItem.timezoneAbbrs ),
date = tsParser( caseItem.sample.match( regexp ) ).date;
@ -55,19 +55,19 @@ QUnit.test( '#getTimestampParser (at DST change)', ( assert ) => {
require( '../cases/comments.json' ).forEach( ( caseItem ) => {
var testName = '#getThreads (' + caseItem.name + ')';
const testName = '#getThreads (' + caseItem.name + ')';
QUnit.test( testName, ( assert ) => {
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
expected = require( caseItem.expected ),
config = require( caseItem.config ),
data = require( caseItem.data );
testUtils.overrideMwConfig( config );
var container = testUtils.getThreadContainer( dom );
var title = mw.Title.newFromText( caseItem.title );
var threadItemSet = new Parser( data ).parse( container, title );
var threads = threadItemSet.getThreads();
const container = testUtils.getThreadContainer( dom );
const title = mw.Title.newFromText( caseItem.title );
const threadItemSet = new Parser( data ).parse( container, title );
const threads = threadItemSet.getThreads();
threads.forEach( ( thread, i ) => {
testUtils.serializeComments( thread, container );

View file

@ -24,8 +24,8 @@ module.exports.overrideMwConfig = function ( config ) {
module.exports.getThreadContainer = function ( doc ) {
// In tests created from Parsoid output, comments are contained directly in <body>.
// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
var body = doc.body;
var wrapper = body.querySelector( 'div.mw-parser-output' );
const body = doc.body;
const wrapper = body.querySelector( 'div.mw-parser-output' );
return wrapper || body;
};
@ -38,7 +38,7 @@ module.exports.getThreadContainer = function ( doc ) {
* @return {string} The offset path
*/
function getOffsetPath( ancestor, node, nodeOffset ) {
var path = [ nodeOffset ];
const path = [ nodeOffset ];
while ( node !== ancestor ) {
if ( node.parentNode === null ) {
// eslint-disable-next-line no-console

View file

@ -3,26 +3,26 @@ var utils = require( 'ext.discussionTools.init' ).utils;
QUnit.module( 'mw.dt.utils', QUnit.newMwEnvironment() );
QUnit.test( '#linearWalk', ( assert ) => {
var cases = require( '../cases/linearWalk.json' );
const cases = require( '../cases/linearWalk.json' );
cases.forEach( ( caseItem ) => {
var
const
doc = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
expected = require( caseItem.expected );
var actual = [];
const actual = [];
utils.linearWalk( doc, ( event, node ) => {
actual.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
} );
var actualBackwards = [];
const actualBackwards = [];
utils.linearWalkBackwards( doc, ( event, node ) => {
actualBackwards.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
} );
assert.deepEqual( actual, expected, caseItem.name );
var expectedBackwards = expected.slice().reverse().map( ( a ) => ( a.slice( 0, 5 ) === 'enter' ? 'leave' : 'enter' ) + a.slice( 5 ) );
const expectedBackwards = expected.slice().reverse().map( ( a ) => ( a.slice( 0, 5 ) === 'enter' ? 'leave' : 'enter' ) + a.slice( 5 ) );
assert.deepEqual( actualBackwards, expectedBackwards, caseItem.name + ' (backwards)' );
// Uncomment this to get updated content for the JSON files, for copy/paste: